This is an automated email from the ASF dual-hosted git repository.

davsclaus pushed a commit to branch tui-turbo-pascal-theme
in repository https://gitbox.apache.org/repos/asf/camel.git

commit 91455105573cec332ae56429064eb66f0625397d
Author: Claus Ibsen <[email protected]>
AuthorDate: Thu Sep 17 11:06:22 2026 +0200

    camel-jbang-tui: Add Turbo Pascal theme and optional syntax highlighting 
tokens
    
    Adds a turbo-pascal theme that recreates Borland's IDE: yellow text on the
    blue editor field, cyan window frames, grey menu bar and a yellow selection
    bar. The palette is adapted from the Omarchy Turbo Pascal theme by
    Alexander Zeitler (MIT).
    
    Themes may now define optional syntax-* tokens (comment, string, keyword,
    function, type, constant, text) that drive the Source tab highlighter and
    the fenced code blocks in AI answers. Themes without them keep the fixed
    Monokai (dark) or GitHub-inspired (light) palettes as before. Turbo Pascal
    uses them for the editor's three-colour look: reserved words white, code
    yellow, comments grey, strings cyan. Resolved colors are now cached per
    token like styles already were, since the highlighter asks per token per
    line.
    
    Also corrects the user manual, which listed 15 themes while 20 shipped, and
    adds the missing light/variant themes to the theme.md table.
    
    Follow-up for theme-specific syntax palettes on the other themes: 
CAMEL-24793
    
    Co-Authored-By: Claude Fable 5.1 <[email protected]>
    Signed-off-by: Claus Ibsen <[email protected]>
---
 .../modules/ROOT/pages/camel-jbang-tui.adoc        |  21 +++--
 .../jbang/core/commands/tui/SyntaxHighlighter.java |  32 ++++---
 .../camel/dsl/jbang/core/commands/tui/Theme.java   | 102 +++++++++++++++------
 .../dsl/jbang/core/commands/tui/ThemeMode.java     |   3 +-
 .../src/main/resources/tui/themes/theme.md         |  32 ++++++-
 .../main/resources/tui/themes/turbo-pascal.tcss    |  70 ++++++++++++++
 .../tui/ThemeModeCompletionCandidatesTest.java     |   2 +-
 .../dsl/jbang/core/commands/tui/ThemeModeTest.java |   5 +-
 .../dsl/jbang/core/commands/tui/ThemeTest.java     |  59 ++++++++++++
 9 files changed, 271 insertions(+), 55 deletions(-)

diff --git a/docs/user-manual/modules/ROOT/pages/camel-jbang-tui.adoc 
b/docs/user-manual/modules/ROOT/pages/camel-jbang-tui.adoc
index 3b601f254a46..ccdebce532f5 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-jbang-tui.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-jbang-tui.adoc
@@ -533,19 +533,26 @@ open at the bottom by default; see *Panel Position* in 
<<_settings>>.
 
 == Theme
 
-The TUI ships with 15 color themes defined as CSS stylesheets:
+The TUI ships with 21 color themes defined as CSS stylesheets:
 
 * *Dark themes* -- Dark (default), Dracula, Nord, Solarized Dark, Gruvbox Dark,
-  Catppuccin Mocha, Tokyo Night, Rosé Pine, Kanagawa, Everforest, Monochrome, 
CRT
-* *Light themes* -- Light, Solarized Light, Catppuccin Latte
+  Catppuccin Mocha, Catppuccin Frappé, Tokyo Night, Rosé Pine, Rosé Pine Moon, 
Kanagawa,
+  Everforest, Monochrome, CRT, Turbo Pascal
+* *Light themes* -- Light, Nord Light, Solarized Light, Gruvbox Light, 
Catppuccin Latte,
+  Everforest Light
 
 Open the *F2* actions menu and choose *Settings...* to switch themes, or pass
 `--theme=<name>` on the command line (e.g., `--theme=tokyo-night`). The CLI 
value
 overrides the persisted preference from `.camel-cli.properties`; runtime 
toggles and
 the config file still apply on later launches when `--theme` is omitted.
 
-The brand orange accent is consistent across all themes; status colors 
(success,
-warning, error) and borders adapt for readability on each palette.
+The brand orange accent is consistent across most themes; status colors 
(success,
+warning, error) and borders adapt for readability on each palette. A few 
themes trade
+the brand accent for their own identity: Monochrome, CRT and Turbo Pascal. The 
Turbo
+Pascal theme recreates Borland's IDE (yellow text on the blue editor field, 
cyan window
+frames, grey menu bar) and also restyles source code the way that editor did: 
reserved
+words in white, code in yellow, comments in grey. Other themes highlight code 
with a
+fixed Monokai (dark) or GitHub-inspired (light) palette.
 
 Your choice is remembered: it is saved as `camel.tui.theme` in 
`.camel-cli.properties`
 and restored the next time you open the TUI.
@@ -554,7 +561,7 @@ and restored the next time you open the TUI.
 
 Open the *F2* actions menu and choose *Settings...* to change TUI preferences 
in one place:
 
-* *Theme* -- cycle through 15 available themes (applied immediately on save).
+* *Theme* -- cycle through 21 available themes (applied immediately on save).
 * *Starting Tab* -- the tab shown when the TUI launches; any tab (primary or 
under *More*) can be
   chosen. Defaults to *Overview*.
 * *Select Tab* -- the tab to switch to when selecting an integration from the 
Overview tab.
@@ -1306,7 +1313,7 @@ vhs demo.tape               # .tape -> .gif
 | `100`
 
 | `--theme`
-| Color theme for this session (e.g., `dark`, `tokyo-night`, `dracula`). See 
<<Theme>> for the full list of 15 themes. Overrides the persisted 
`camel.tui.theme` preference when set.
+| Color theme for this session (e.g., `dark`, `tokyo-night`, `dracula`). See 
<<Theme>> for the full list of 21 themes. Overrides the persisted 
`camel.tui.theme` preference when set.
 |
 
 | `--record`
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/SyntaxHighlighter.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/SyntaxHighlighter.java
index af4bd0306061..096a2f26cb03 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/SyntaxHighlighter.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/SyntaxHighlighter.java
@@ -76,40 +76,42 @@ class SyntaxHighlighter {
     static final Color MONOKAI_TEXT = Color.rgb(248, 248, 242);
 
     // Light color palette (readable on light backgrounds)
-    private static final Color LIGHT_COMMENT = Color.rgb(106, 115, 125);
-    private static final Color LIGHT_STRING = Color.rgb(3, 47, 98);
-    private static final Color LIGHT_KEYWORD = Color.rgb(215, 58, 73);
-    private static final Color LIGHT_FUNCTION = Color.rgb(0, 92, 197);
-    private static final Color LIGHT_TYPE = Color.rgb(0, 92, 197);
-    private static final Color LIGHT_CONSTANT = Color.rgb(111, 66, 193);
-    private static final Color LIGHT_TEXT = Color.rgb(36, 41, 46);
-
+    static final Color LIGHT_COMMENT = Color.rgb(106, 115, 125);
+    static final Color LIGHT_STRING = Color.rgb(3, 47, 98);
+    static final Color LIGHT_KEYWORD = Color.rgb(215, 58, 73);
+    static final Color LIGHT_FUNCTION = Color.rgb(0, 92, 197);
+    static final Color LIGHT_TYPE = Color.rgb(0, 92, 197);
+    static final Color LIGHT_CONSTANT = Color.rgb(111, 66, 193);
+    static final Color LIGHT_TEXT = Color.rgb(36, 41, 46);
+
+    // Theme-aware palette: a theme may override these through optional 
syntax-* stylesheet tokens (see Theme);
+    // otherwise the Monokai (dark) or light palette above applies.
     private static Color comment() {
-        return Theme.isDark() ? MONOKAI_COMMENT : LIGHT_COMMENT;
+        return Theme.syntaxComment();
     }
 
     private static Color string() {
-        return Theme.isDark() ? MONOKAI_STRING : LIGHT_STRING;
+        return Theme.syntaxString();
     }
 
     private static Color keyword() {
-        return Theme.isDark() ? MONOKAI_KEYWORD : LIGHT_KEYWORD;
+        return Theme.syntaxKeyword();
     }
 
     private static Color function() {
-        return Theme.isDark() ? MONOKAI_FUNCTION : LIGHT_FUNCTION;
+        return Theme.syntaxFunction();
     }
 
     private static Color type() {
-        return Theme.isDark() ? MONOKAI_TYPE : LIGHT_TYPE;
+        return Theme.syntaxType();
     }
 
     private static Color constant() {
-        return Theme.isDark() ? MONOKAI_CONSTANT : LIGHT_CONSTANT;
+        return Theme.syntaxConstant();
     }
 
     private static Color text() {
-        return Theme.isDark() ? MONOKAI_TEXT : LIGHT_TEXT;
+        return Theme.syntaxText();
     }
 
     // Java styles
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/Theme.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/Theme.java
index 6043ff456ad6..f721446699d0 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/Theme.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/Theme.java
@@ -101,6 +101,7 @@ public final class Theme {
     };
 
     private static final Map<String, Style> CACHE = new HashMap<>();
+    private static final Map<String, Color> COLOR_CACHE = new HashMap<>();
 
     private static boolean initialized;
     private static boolean persistedModeLoaded;
@@ -296,38 +297,74 @@ public final class Theme {
     }
 
     /**
-     * Theme-aware syntax highlighting palette for fenced code blocks in 
MarkdownView. Reuses the Monokai (dark) and
-     * GitHub-inspired (light) palettes from {@link SyntaxHighlighter}.
+     * Theme-aware syntax highlighting palette for fenced code blocks in 
MarkdownView. Uses the same optional
+     * {@code syntax-*} stylesheet tokens as {@link SyntaxHighlighter}, so the 
Source tab and markdown code blocks
+     * agree.
      */
     public static SyntaxTheme syntaxTheme() {
-        if (isDark()) {
-            return SyntaxTheme.builder()
-                    .token(TokenType.COMMENT, 
Style.EMPTY.fg(SyntaxHighlighter.MONOKAI_COMMENT))
-                    .token(TokenType.KEYWORD, 
Style.EMPTY.fg(SyntaxHighlighter.MONOKAI_KEYWORD))
-                    .token(TokenType.TYPE, 
Style.EMPTY.fg(SyntaxHighlighter.MONOKAI_TYPE))
-                    .token(TokenType.STRING, 
Style.EMPTY.fg(SyntaxHighlighter.MONOKAI_STRING))
-                    .token(TokenType.NUMBER, 
Style.EMPTY.fg(SyntaxHighlighter.MONOKAI_CONSTANT))
-                    .token(TokenType.CONSTANT, 
Style.EMPTY.fg(SyntaxHighlighter.MONOKAI_CONSTANT))
-                    .token(TokenType.FUNCTION, 
Style.EMPTY.fg(SyntaxHighlighter.MONOKAI_FUNCTION))
-                    .token(TokenType.ANNOTATION, 
Style.EMPTY.fg(SyntaxHighlighter.MONOKAI_FUNCTION))
-                    .token(TokenType.TAG, 
Style.EMPTY.fg(SyntaxHighlighter.MONOKAI_KEYWORD))
-                    .token(TokenType.ATTRIBUTE, 
Style.EMPTY.fg(SyntaxHighlighter.MONOKAI_FUNCTION))
-                    .build();
-        }
         return SyntaxTheme.builder()
-                .token(TokenType.COMMENT, Style.EMPTY.fg(Color.rgb(106, 115, 
125)))
-                .token(TokenType.KEYWORD, Style.EMPTY.fg(Color.rgb(215, 58, 
73)))
-                .token(TokenType.TYPE, Style.EMPTY.fg(Color.rgb(0, 92, 197)))
-                .token(TokenType.STRING, Style.EMPTY.fg(Color.rgb(3, 47, 98)))
-                .token(TokenType.NUMBER, Style.EMPTY.fg(Color.rgb(111, 66, 
193)))
-                .token(TokenType.CONSTANT, Style.EMPTY.fg(Color.rgb(111, 66, 
193)))
-                .token(TokenType.FUNCTION, Style.EMPTY.fg(Color.rgb(0, 92, 
197)))
-                .token(TokenType.ANNOTATION, Style.EMPTY.fg(Color.rgb(0, 92, 
197)))
-                .token(TokenType.TAG, Style.EMPTY.fg(Color.rgb(215, 58, 73)))
-                .token(TokenType.ATTRIBUTE, Style.EMPTY.fg(Color.rgb(0, 92, 
197)))
+                .token(TokenType.COMMENT, Style.EMPTY.fg(syntaxComment()))
+                .token(TokenType.KEYWORD, Style.EMPTY.fg(syntaxKeyword()))
+                .token(TokenType.TYPE, Style.EMPTY.fg(syntaxType()))
+                .token(TokenType.STRING, Style.EMPTY.fg(syntaxString()))
+                .token(TokenType.NUMBER, Style.EMPTY.fg(syntaxConstant()))
+                .token(TokenType.CONSTANT, Style.EMPTY.fg(syntaxConstant()))
+                .token(TokenType.FUNCTION, Style.EMPTY.fg(syntaxFunction()))
+                .token(TokenType.ANNOTATION, Style.EMPTY.fg(syntaxFunction()))
+                .token(TokenType.TAG, Style.EMPTY.fg(syntaxKeyword()))
+                .token(TokenType.ATTRIBUTE, Style.EMPTY.fg(syntaxFunction()))
                 .build();
     }
 
+    // ---- Syntax highlighting colors ----
+    //
+    // Optional stylesheet tokens: a theme may define #syntax-comment, 
#syntax-string, #syntax-keyword,
+    // #syntax-function, #syntax-type, #syntax-constant and #syntax-text to 
give code its own look (e.g. the
+    // Turbo Pascal theme). Themes that omit them fall back to the Monokai 
(dark) or GitHub-inspired (light)
+    // palettes in SyntaxHighlighter.
+
+    /** Syntax color for comments. */
+    public static synchronized Color syntaxComment() {
+        return color("syntax-comment",
+                isDark() ? SyntaxHighlighter.MONOKAI_COMMENT : 
SyntaxHighlighter.LIGHT_COMMENT);
+    }
+
+    /** Syntax color for string literals and values. */
+    public static synchronized Color syntaxString() {
+        return color("syntax-string",
+                isDark() ? SyntaxHighlighter.MONOKAI_STRING : 
SyntaxHighlighter.LIGHT_STRING);
+    }
+
+    /** Syntax color for keywords, YAML/properties keys and XML tags. */
+    public static synchronized Color syntaxKeyword() {
+        return color("syntax-keyword",
+                isDark() ? SyntaxHighlighter.MONOKAI_KEYWORD : 
SyntaxHighlighter.LIGHT_KEYWORD);
+    }
+
+    /** Syntax color for functions, annotations and XML attribute names. */
+    public static synchronized Color syntaxFunction() {
+        return color("syntax-function",
+                isDark() ? SyntaxHighlighter.MONOKAI_FUNCTION : 
SyntaxHighlighter.LIGHT_FUNCTION);
+    }
+
+    /** Syntax color for types. */
+    public static synchronized Color syntaxType() {
+        return color("syntax-type",
+                isDark() ? SyntaxHighlighter.MONOKAI_TYPE : 
SyntaxHighlighter.LIGHT_TYPE);
+    }
+
+    /** Syntax color for numbers, booleans, null and entities. */
+    public static synchronized Color syntaxConstant() {
+        return color("syntax-constant",
+                isDark() ? SyntaxHighlighter.MONOKAI_CONSTANT : 
SyntaxHighlighter.LIGHT_CONSTANT);
+    }
+
+    /** Syntax color for plain code text such as separators. */
+    public static synchronized Color syntaxText() {
+        return color("syntax-text",
+                isDark() ? SyntaxHighlighter.MONOKAI_TEXT : 
SyntaxHighlighter.LIGHT_TEXT);
+    }
+
     /** Diagram box-drawing border color. */
     public static synchronized Color diagramBorder() {
         return color("diagram-border", FALLBACK_DIAGRAM_BORDER);
@@ -477,6 +514,7 @@ public final class Theme {
         persistedModeLoaded = false;
         previewOriginal = null;
         CACHE.clear();
+        COLOR_CACHE.clear();
         engine = null;
         initialized = false;
         mode = ThemeMode.DARK;
@@ -488,6 +526,7 @@ public final class Theme {
             persistedModeLoaded = true;
         }
         CACHE.clear();
+        COLOR_CACHE.clear();
         engine = null;
         initialized = false;
         engine();
@@ -498,9 +537,18 @@ public final class Theme {
         if (e == null) {
             return fallback;
         }
+        Color cached = COLOR_CACHE.get(id);
+        if (cached != null) {
+            return cached;
+        }
         try {
-            return e.resolve(new Token(id)).foreground().orElse(fallback);
+            // A token the stylesheet does not define resolves to the 
fallback; that is cached too because the
+            // stylesheet cannot change without activate() clearing the cache.
+            Color resolved = e.resolve(new 
Token(id)).foreground().orElse(fallback);
+            COLOR_CACHE.put(id, resolved);
+            return resolved;
         } catch (RuntimeException ex) {
+            // Not cached: a transient resolution failure must not permanently 
lock this token to the fallback.
             logTokenFallbackOnce(ex);
             return fallback;
         }
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/ThemeMode.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/ThemeMode.java
index 8a101c6880f0..e597492ac4ba 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/ThemeMode.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/ThemeMode.java
@@ -43,7 +43,8 @@ enum ThemeMode {
     EVERFOREST("everforest", "Everforest", false),
     EVERFOREST_LIGHT("everforest-light", "Everforest Light", true),
     MONOCHROME("monochrome", "Monochrome", false),
-    CRT("crt", "CRT", false);
+    CRT("crt", "CRT", false),
+    TURBO_PASCAL("turbo-pascal", "Turbo Pascal", false);
 
     private final String id;
     private final String label;
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/resources/tui/themes/theme.md 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/resources/tui/themes/theme.md
index 027afa416e1a..47341bede173 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/resources/tui/themes/theme.md
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/resources/tui/themes/theme.md
@@ -1,8 +1,9 @@
 # Camel TUI Theme Reference
 
 This document describes the CSS token system used by the Camel TUI.
-Every `.tcss` theme file must define all 28 tokens listed below.
-Missing tokens cause a startup validation error.
+Every `.tcss` theme file must define all 28 required tokens listed below.
+Missing required tokens cause a startup validation error. The 7 syntax
+tokens are optional.
 
 ## File Format
 
@@ -74,6 +75,24 @@ $brand: #F69123;
 | `diagram-eip` | fg | Routing EIPs (`split`, `aggregate`, `multicast`, etc.) |
 | `diagram-default` | fg | Fallback for unspecified EIP types |
 
+### Syntax (7 tokens, optional)
+
+Code in the Source tab and in fenced code blocks of AI answers is highlighted
+with these tokens. They are optional: a theme that omits them gets the
+built-in Monokai palette (dark themes) or GitHub-inspired palette (light
+themes). Define them when the theme calls for its own editor look, as the
+Turbo Pascal theme does, but keep code readable.
+
+| Token | Type | Purpose |
+|-------|------|---------|
+| `syntax-comment` | fg | Comments |
+| `syntax-string` | fg | String literals, YAML and properties values, XML 
attribute values |
+| `syntax-keyword` | fg | Keywords and modifiers, YAML and properties keys, 
XML tags |
+| `syntax-function` | fg | Annotations, XML attribute names |
+| `syntax-type` | fg | Primitive and built-in types |
+| `syntax-constant` | fg | Numbers, booleans, null, XML entities |
+| `syntax-text` | fg | Plain code text such as `:` and `=` separators |
+
 ## Design Guidelines
 
 When creating a new theme:
@@ -90,6 +109,9 @@ When creating a new theme:
   each other. They appear as foreground text on `base-bg`.
 - **Zebra striping**: `row-alt` background should be subtle, just enough
   to distinguish alternating rows without clashing with `selection`.
+- **Syntax tokens**: only override them when the theme has a distinct
+  editor identity. Keywords, strings and comments must stay distinguishable
+  from each other and from `base-fg`.
 
 ## Built-in Themes
 
@@ -99,14 +121,20 @@ When creating a new theme:
 | `light` | `light.tcss` | GitHub-inspired light palette |
 | `dracula` | `dracula.tcss` | Dracula purple-and-pink-on-dark palette |
 | `nord` | `nord.tcss` | Nord arctic, bluish dark palette |
+| `nord-light` | `nord-light.tcss` | Nord arctic light palette |
 | `solarized-dark` | `solarized-dark.tcss` | Solarized dark palette |
 | `solarized-light` | `solarized-light.tcss` | Solarized light palette |
 | `gruvbox-dark` | `gruvbox-dark.tcss` | Gruvbox retro-groove dark palette |
+| `gruvbox-light` | `gruvbox-light.tcss` | Gruvbox retro-groove light palette |
 | `catppuccin-mocha` | `catppuccin-mocha.tcss` | Catppuccin Mocha pastel dark 
palette |
+| `catppuccin-frappe` | `catppuccin-frappe.tcss` | Catppuccin Frappé pastel 
dark palette |
 | `catppuccin-latte` | `catppuccin-latte.tcss` | Catppuccin Latte pastel light 
palette |
 | `tokyo-night` | `tokyo-night.tcss` | Tokyo Night neon-on-dark palette |
 | `rose-pine` | `rose-pine.tcss` | Rosé Pine muted dark palette |
+| `rose-pine-moon` | `rose-pine-moon.tcss` | Rosé Pine Moon softer dark 
palette |
 | `kanagawa` | `kanagawa.tcss` | Kanagawa Japanese-wave-inspired dark palette |
 | `everforest` | `everforest.tcss` | Everforest warm, green-forest dark 
palette |
+| `everforest-light` | `everforest-light.tcss` | Everforest warm, green-forest 
light palette |
 | `monochrome` | `monochrome.tcss` | Grayscale palette, no color, brightness 
only |
 | `crt` | `crt.tcss` | Retro green-phosphor terminal palette |
+| `turbo-pascal` | `turbo-pascal.tcss` | Borland Turbo Pascal IDE: yellow on 
blue, cyan frames, grey menu bar; overrides the syntax tokens |
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/resources/tui/themes/turbo-pascal.tcss
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/resources/tui/themes/turbo-pascal.tcss
new file mode 100644
index 000000000000..40a83e7b742b
--- /dev/null
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/resources/tui/themes/turbo-pascal.tcss
@@ -0,0 +1,70 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Camel TUI Turbo Pascal theme — Borland's IDE: the blue editor field, the 
grey menu bar, the cyan frame.
+ * Palette adapted from the Omarchy Turbo Pascal theme by Alexander Zeitler 
(MIT),
+ * https://github.com/AlexZeitler/omarchy-turbo-pascal-theme
+ */
+
+$brand: #0BDFEA;
+$dark-bg: #010378;
+$dark-fg: #FFFF55;
+$bright: #FFFFFF;
+$grey: #AAAAAA;
+$highlight: #F7EE0B;
+
+#accent         { color: $brand; }
+#accent-bg      { color: $dark-bg; background: $grey; text-style: bold; }
+#hint-key       { color: $dark-bg; background: $brand; text-style: bold; }
+#border         { color: $grey; }
+#border-focused { color: $brand; }
+#title          { color: $brand; text-style: bold; }
+#success        { color: #5BC98A; }
+#warning        { color: #FFA94D; }
+#error          { color: #FF7A66; }
+#muted          { color: $grey; }
+#selection      { color: $dark-bg; background: $highlight; text-style: bold; }
+#info           { color: #9FBEFF; }
+#notice         { color: #E07CE0; }
+#row-alt        { background: #010556; }
+#base-bg        { background: $dark-bg; }
+#base-fg        { color: $dark-fg; }
+
+/* Content tokens */
+#label          { color: $bright; }
+#change         { color: $highlight; }
+#search-match   { color: $dark-bg; background: #B0F5FB; }
+#mnemonic       { color: $bright; text-style: bold underline; }
+
+/* Diagram tokens */
+#diagram-border  { color: $grey; }
+#diagram-id      { color: $dark-fg; }
+#diagram-from    { color: #5BC98A; }
+#diagram-to      { color: $brand; }
+#diagram-choice  { color: $highlight; }
+#diagram-action  { color: #E07CE0; }
+#diagram-eip     { color: #9FBEFF; }
+#diagram-default { color: $grey; }
+
+/* Syntax tokens (optional) — the IDE's three-colour editor: reserved words 
white, code yellow, comments grey. */
+#syntax-comment  { color: $grey; }
+#syntax-string   { color: $brand; }
+#syntax-keyword  { color: $bright; }
+#syntax-function { color: $dark-fg; }
+#syntax-type     { color: $bright; }
+#syntax-constant { color: #B0F5FB; }
+#syntax-text     { color: $dark-fg; }
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/ThemeModeCompletionCandidatesTest.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/ThemeModeCompletionCandidatesTest.java
index a63d956a1086..2d77bbd314af 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/ThemeModeCompletionCandidatesTest.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/ThemeModeCompletionCandidatesTest.java
@@ -34,6 +34,6 @@ class ThemeModeCompletionCandidatesTest {
                 "dark", "light", "dracula", "nord", "nord-light", 
"solarized-dark", "solarized-light",
                 "gruvbox-dark", "gruvbox-light", "catppuccin-mocha", 
"catppuccin-frappe", "catppuccin-latte",
                 "tokyo-night", "rose-pine", "rose-pine-moon", "kanagawa", 
"everforest", "everforest-light",
-                "monochrome", "crt"), candidates);
+                "monochrome", "crt", "turbo-pascal"), candidates);
     }
 }
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/ThemeModeTest.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/ThemeModeTest.java
index 4c182af12bae..61848b23a8c6 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/ThemeModeTest.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/ThemeModeTest.java
@@ -62,7 +62,8 @@ class ThemeModeTest {
     void nextCyclesToNextThemeInDeclarationOrder() {
         assertEquals(ThemeMode.LIGHT, ThemeMode.DARK.next());
         assertEquals(ThemeMode.DRACULA, ThemeMode.LIGHT.next());
-        assertEquals(ThemeMode.DARK, ThemeMode.CRT.next());
+        assertEquals(ThemeMode.TURBO_PASCAL, ThemeMode.CRT.next());
+        assertEquals(ThemeMode.DARK, ThemeMode.TURBO_PASCAL.next());
     }
 
     @Test
@@ -71,7 +72,7 @@ class ThemeModeTest {
                 "dark", "light", "dracula", "nord", "nord-light", 
"solarized-dark", "solarized-light",
                 "gruvbox-dark", "gruvbox-light", "catppuccin-mocha", 
"catppuccin-frappe", "catppuccin-latte",
                 "tokyo-night", "rose-pine", "rose-pine-moon", "kanagawa", 
"everforest", "everforest-light",
-                "monochrome", "crt"), ThemeMode.ids());
+                "monochrome", "crt", "turbo-pascal"), ThemeMode.ids());
     }
 
     @Test
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/ThemeTest.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/ThemeTest.java
index eac4f2e90391..080c0fc2eea5 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/ThemeTest.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/ThemeTest.java
@@ -260,6 +260,65 @@ class ThemeTest {
         assertEquals("dark", Theme.mode());
     }
 
+    @Test
+    void syntaxColorsFallBackToMonokaiForDarkThemesWithoutSyntaxTokens() {
+        Theme.setMode("dark");
+        assertEquals(SyntaxHighlighter.MONOKAI_COMMENT, Theme.syntaxComment());
+        assertEquals(SyntaxHighlighter.MONOKAI_STRING, Theme.syntaxString());
+        assertEquals(SyntaxHighlighter.MONOKAI_KEYWORD, Theme.syntaxKeyword());
+        assertEquals(SyntaxHighlighter.MONOKAI_FUNCTION, 
Theme.syntaxFunction());
+        assertEquals(SyntaxHighlighter.MONOKAI_TYPE, Theme.syntaxType());
+        assertEquals(SyntaxHighlighter.MONOKAI_CONSTANT, 
Theme.syntaxConstant());
+        assertEquals(SyntaxHighlighter.MONOKAI_TEXT, Theme.syntaxText());
+    }
+
+    @Test
+    void syntaxColorsFallBackToLightPaletteForLightThemesWithoutSyntaxTokens() 
{
+        Theme.setMode("light");
+        assertEquals(SyntaxHighlighter.LIGHT_COMMENT, Theme.syntaxComment());
+        assertEquals(SyntaxHighlighter.LIGHT_STRING, Theme.syntaxString());
+        assertEquals(SyntaxHighlighter.LIGHT_KEYWORD, Theme.syntaxKeyword());
+        assertEquals(SyntaxHighlighter.LIGHT_TEXT, Theme.syntaxText());
+    }
+
+    @Test
+    void turboPascalOverridesSyntaxColorsFromStylesheet() {
+        Theme.setMode("turbo-pascal");
+        // Borland's editor: reserved words white, code yellow, comments grey, 
strings cyan.
+        assertEquals(Color.rgb(0xFF, 0xFF, 0xFF), Theme.syntaxKeyword());
+        assertEquals(Color.rgb(0xFF, 0xFF, 0x55), Theme.syntaxText());
+        assertEquals(Color.rgb(0xAA, 0xAA, 0xAA), Theme.syntaxComment());
+        assertEquals(Color.rgb(0x0B, 0xDF, 0xEA), Theme.syntaxString());
+        assertNotEquals(SyntaxHighlighter.MONOKAI_KEYWORD, 
Theme.syntaxKeyword());
+    }
+
+    @Test
+    void syntaxColorsFollowThemeSwitches() {
+        Theme.setMode("turbo-pascal");
+        assertEquals(Color.rgb(0xFF, 0xFF, 0xFF), Theme.syntaxKeyword());
+
+        Theme.setMode("dark");
+        assertEquals(SyntaxHighlighter.MONOKAI_KEYWORD, Theme.syntaxKeyword());
+
+        Theme.preview("turbo-pascal");
+        assertEquals(Color.rgb(0xFF, 0xFF, 0xFF), Theme.syntaxKeyword());
+        Theme.revertPreview();
+        assertEquals(SyntaxHighlighter.MONOKAI_KEYWORD, Theme.syntaxKeyword());
+    }
+
+    @Test
+    void turboPascalPaletteResolvesExpectedTokens() {
+        Theme.setMode("turbo-pascal");
+        assertEquals(Color.rgb(0x01, 0x03, 0x78), Theme.baseBg());
+        assertEquals(Color.rgb(0xFF, 0xFF, 0x55), Theme.baseFg());
+        assertEquals(Color.rgb(0x0B, 0xDF, 0xEA), Theme.accent());
+        assertEquals(Style.EMPTY.fg(Color.rgb(0x0B, 0xDF, 0xEA)), 
Theme.borderFocused());
+        assertEquals(Style.EMPTY.fg(Color.rgb(0xAA, 0xAA, 0xAA)), 
Theme.border());
+        // The selection bar: editor blue on the highlight yellow.
+        assertEquals(Style.EMPTY.fg(Color.rgb(0x01, 0x03, 
0x78)).bg(Color.rgb(0xF7, 0xEE, 0x0B)).bold(),
+                Theme.selectionBg());
+    }
+
     @Test
     void allThemesLoadSuccessfully() {
         for (ThemeMode m : ThemeMode.values()) {

Reply via email to