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

davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git


The following commit(s) were added to refs/heads/main by this push:
     new e6f7fb89711d camel-jbang-tui: Add Turbo Pascal theme and optional 
syntax highlighting tokens (#26547)
e6f7fb89711d is described below

commit e6f7fb89711d8fee6d37f025715225dc7219c8d8
Author: Claus Ibsen <[email protected]>
AuthorDate: Thu Sep 17 13:06:01 2026 +0200

    camel-jbang-tui: Add Turbo Pascal theme and optional syntax highlighting 
tokens (#26547)
    
    Adds a turbo-pascal theme recreating Borland's IDE palette, and optional 
syntax-* stylesheet tokens so a theme can give the Source tab and markdown code 
blocks its own code palette. Themes without the tokens keep the Monokai/light 
fallback. Docs updated to list all 21 themes.
    
    Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
    Signed-off-by: Claus Ibsen <[email protected]>
---
 .../modules/ROOT/pages/camel-jbang-tui.adoc        |  21 +++--
 .../jbang/core/commands/tui/SyntaxHighlighter.java |  35 ++++---
 .../camel/dsl/jbang/core/commands/tui/Theme.java   | 104 +++++++++++++++------
 .../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, 275 insertions(+), 56 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..e5421f975fad 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
@@ -66,6 +66,9 @@ class SyntaxHighlighter {
     private static final Pattern XML_ATTR_NAME = 
Pattern.compile("\\s([\\w:.-]+)=");
     private static final Pattern XML_ENTITY = Pattern.compile("&[^;]+;");
 
+    // Fallback palettes used when a theme defines no syntax-* tokens (see 
Theme). Package-private so ThemeTest can
+    // assert the fallback colors; not a stable API for other callers.
+
     // Monokai color palette (dark themes)
     static final Color MONOKAI_COMMENT = Color.rgb(117, 113, 94);
     static final Color MONOKAI_STRING = Color.rgb(230, 219, 116);
@@ -76,40 +79,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..1a951d7d04af 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();
-        }
+    public static synchronized SyntaxTheme syntaxTheme() {
         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