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 5f0651081f0b CAMEL-24793: camel-jbang - theme-matched code colors, and 
code styles for Monochrome and CRT (#27295)
5f0651081f0b is described below

commit 5f0651081f0bcb0dd5e371ba35c35cfe961dd4b1
Author: Claus Ibsen <[email protected]>
AuthorDate: Fri Oct 2 19:54:49 2026 +0200

    CAMEL-24793: camel-jbang - theme-matched code colors, and code styles for 
Monochrome and CRT (#27295)
    
    Until now only Turbo Pascal had its own code colors; every other theme used
    Monokai (dark) or GitHub (light), which was hard to read on several themes
    (comments at 2.5 contrast on Nord, Catppuccin Frappe, Everforest) and 
clashed
    with the rest of the screen.
    
    16 themes ported from editor color schemes (Dracula, Nord, Solarized, 
Gruvbox,
    Catppuccin, Tokyo Night, Rose Pine, Kanagawa, Everforest) now define the
    syntax-* tokens from their scheme's published mapping, with colors below a 
3:1
    contrast lifted slightly while keeping their hue.
    
    syntax-* tokens may also set text-style (bold, italic, underline) through 
new
    Theme.syntax*Style() methods, used by the Source tab and by code blocks in 
AI
    answers. Monochrome uses bold keywords and italic comments; CRT gets a green
    phosphor look. Dim is rejected because terminals blend it toward the 
background.
    
    ThemeSyntaxPaletteTest guards contrast, distinct keyword/string/comment 
colors,
    all-or-nothing token sets, no dim/hidden, and code text equal to base-fg for
    every theme including the fallback. Docs describe the code colors with
    screenshots.
    
    Closes #27295
    
    Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
    Signed-off-by: Claus Ibsen <[email protected]>
---
 .../images/jbang/camel-tui-theme-crt-source.png    | Bin 0 -> 258285 bytes
 .../images/jbang/camel-tui-theme-nord-source.png   | Bin 0 -> 257363 bytes
 .../ROOT/pages/camel-jbang-tui-settings.adoc       |  24 ++-
 .../jbang/core/commands/tui/SyntaxHighlighter.java |  76 ++++-----
 .../camel/dsl/jbang/core/commands/tui/Theme.java   |  64 ++++++--
 .../resources/tui/themes/catppuccin-frappe.tcss    |  11 ++
 .../resources/tui/themes/catppuccin-latte.tcss     |  12 ++
 .../resources/tui/themes/catppuccin-mocha.tcss     |  11 ++
 .../src/main/resources/tui/themes/crt.tcss         |  14 ++
 .../src/main/resources/tui/themes/dracula.tcss     |  11 ++
 .../resources/tui/themes/everforest-light.tcss     |  12 ++
 .../src/main/resources/tui/themes/everforest.tcss  |  11 ++
 .../main/resources/tui/themes/gruvbox-dark.tcss    |  11 ++
 .../main/resources/tui/themes/gruvbox-light.tcss   |  11 ++
 .../src/main/resources/tui/themes/kanagawa.tcss    |  11 ++
 .../src/main/resources/tui/themes/monochrome.tcss  |  12 ++
 .../src/main/resources/tui/themes/nord-light.tcss  |  11 ++
 .../src/main/resources/tui/themes/nord.tcss        |  12 ++
 .../main/resources/tui/themes/rose-pine-moon.tcss  |  11 ++
 .../src/main/resources/tui/themes/rose-pine.tcss   |  11 ++
 .../main/resources/tui/themes/solarized-dark.tcss  |  12 ++
 .../main/resources/tui/themes/solarized-light.tcss |  12 ++
 .../src/main/resources/tui/themes/theme.md         |  20 +++
 .../src/main/resources/tui/themes/tokyo-night.tcss |  12 ++
 .../core/commands/tui/ThemeSyntaxPaletteTest.java  | 175 +++++++++++++++++++++
 .../dsl/jbang/core/commands/tui/ThemeTest.java     |  20 +++
 26 files changed, 537 insertions(+), 50 deletions(-)

diff --git 
a/docs/user-manual/modules/ROOT/images/jbang/camel-tui-theme-crt-source.png 
b/docs/user-manual/modules/ROOT/images/jbang/camel-tui-theme-crt-source.png
new file mode 100644
index 000000000000..5d4f15bbccd1
Binary files /dev/null and 
b/docs/user-manual/modules/ROOT/images/jbang/camel-tui-theme-crt-source.png 
differ
diff --git 
a/docs/user-manual/modules/ROOT/images/jbang/camel-tui-theme-nord-source.png 
b/docs/user-manual/modules/ROOT/images/jbang/camel-tui-theme-nord-source.png
new file mode 100644
index 000000000000..f4426b858328
Binary files /dev/null and 
b/docs/user-manual/modules/ROOT/images/jbang/camel-tui-theme-nord-source.png 
differ
diff --git a/docs/user-manual/modules/ROOT/pages/camel-jbang-tui-settings.adoc 
b/docs/user-manual/modules/ROOT/pages/camel-jbang-tui-settings.adoc
index 85e17f2d1bc5..ba167fc08492 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-jbang-tui-settings.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-jbang-tui-settings.adoc
@@ -96,12 +96,32 @@ warning, error) and borders adapt for readability on each 
palette. A few themes
 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.
+words in white, code in yellow, comments in grey.
+
+Source code in the *Source* tab and in code blocks of AI answers is colored to 
match the
+theme. Themes based on an editor color scheme (Dracula, Nord, Solarized, 
Gruvbox,
+Catppuccin, Tokyo Night, Rosé Pine, Kanagawa and Everforest) use that scheme's 
own colors
+for keywords, strings, comments and so on. Where a scheme's color is too faint 
to read on
+the TUI background, it is made slightly lighter (or darker on light themes). 
Dark
+highlights code with the Monokai palette, and Light with a GitHub-inspired 
palette.
+
+Monochrome and CRT have no colors to spare, so they tell code apart by 
brightness and
+weight. Monochrome shows keywords in bold white and comments in italic grey. 
CRT looks
+like a green phosphor terminal of the 1980s: keywords in bold high intensity, 
strings in
+the bright glow, comments in the darker half intensity green.
 
 Your choice is remembered: it is saved as `camel.tui.theme` in 
`.camel-cli.properties`
 and restored the next time you open the TUI.
 
+=== Theme examples
+
+.Nord: a YAML route in the *Source* tab with the scheme's own code colors
+image::jbang/camel-tui-theme-nord-source.png[A YAML route in the Source tab 
with the Nord theme's code colors]
+
+.CRT: a green phosphor terminal of the 1980s, code told apart by brightness 
and bold
+image::jbang/camel-tui-theme-crt-source.png[A YAML route in the Source tab 
with the CRT theme]
+
+.Catppuccin Latte: a light theme on the *Overview* tab
 image::jbang/camel-tui-theme-latte.png[The Overview in the Catppuccin Latte 
theme]
 
 == Settings
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 e5421f975fad..ab8afd3c1911 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
@@ -87,126 +87,126 @@ class SyntaxHighlighter {
     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.syntaxComment();
+    // Theme-aware palette: a theme may override these through optional 
syntax-* stylesheet tokens (see Theme), color
+    // and text-style (bold, italic, dim, underline); otherwise the Monokai 
(dark) or light palette above applies.
+    private static Style comment() {
+        return Theme.syntaxCommentStyle();
     }
 
-    private static Color string() {
-        return Theme.syntaxString();
+    private static Style string() {
+        return Theme.syntaxStringStyle();
     }
 
-    private static Color keyword() {
-        return Theme.syntaxKeyword();
+    private static Style keyword() {
+        return Theme.syntaxKeywordStyle();
     }
 
-    private static Color function() {
-        return Theme.syntaxFunction();
+    private static Style function() {
+        return Theme.syntaxFunctionStyle();
     }
 
-    private static Color type() {
-        return Theme.syntaxType();
+    private static Style type() {
+        return Theme.syntaxTypeStyle();
     }
 
-    private static Color constant() {
-        return Theme.syntaxConstant();
+    private static Style constant() {
+        return Theme.syntaxConstantStyle();
     }
 
-    private static Color text() {
-        return Theme.syntaxText();
+    private static Style text() {
+        return Theme.syntaxTextStyle();
     }
 
     // Java styles
     private static Style javaComment() {
-        return Style.EMPTY.fg(comment());
+        return comment();
     }
 
     private static Style javaString() {
-        return Style.EMPTY.fg(string());
+        return string();
     }
 
     private static Style javaAnnotation() {
-        return Style.EMPTY.fg(function());
+        return function();
     }
 
     private static Style javaModifier() {
-        return Style.EMPTY.fg(keyword());
+        return keyword();
     }
 
     private static Style javaKeyword() {
-        return Style.EMPTY.fg(keyword());
+        return keyword();
     }
 
     private static Style javaType() {
-        return Style.EMPTY.fg(type());
+        return type();
     }
 
     private static Style javaBoolean() {
-        return Style.EMPTY.fg(constant());
+        return constant();
     }
 
     private static Style javaNumber() {
-        return Style.EMPTY.fg(constant());
+        return constant();
     }
 
     // YAML styles
     private static Style yamlComment() {
-        return Style.EMPTY.fg(comment());
+        return comment();
     }
 
     private static Style yamlKey() {
-        return Style.EMPTY.fg(keyword());
+        return keyword();
     }
 
     private static Style yamlValue() {
-        return Style.EMPTY.fg(string());
+        return string();
     }
 
     private static Style yamlSpecial() {
-        return Style.EMPTY.fg(constant());
+        return constant();
     }
 
     private static Style yamlSeparator() {
-        return Style.EMPTY.fg(text()).bold();
+        return text().bold();
     }
 
     // XML styles
     private static Style xmlComment() {
-        return Style.EMPTY.fg(comment());
+        return comment();
     }
 
     private static Style xmlTag() {
-        return Style.EMPTY.fg(keyword());
+        return keyword();
     }
 
     private static Style xmlAttrName() {
-        return Style.EMPTY.fg(function());
+        return function();
     }
 
     private static Style xmlAttrValue() {
-        return Style.EMPTY.fg(string());
+        return string();
     }
 
     private static Style xmlEntity() {
-        return Style.EMPTY.fg(constant());
+        return constant();
     }
 
     // Properties styles
     private static Style propsComment() {
-        return Style.EMPTY.fg(comment());
+        return comment();
     }
 
     private static Style propsKey() {
-        return Style.EMPTY.fg(keyword());
+        return keyword();
     }
 
     private static Style propsSeparator() {
-        return Style.EMPTY.fg(text()).bold();
+        return text().bold();
     }
 
     private static Style propsValue() {
-        return Style.EMPTY.fg(string());
+        return string();
     }
 
     private SyntaxHighlighter() {
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 02d5b61b6db4..735f2587b3b2 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
@@ -303,16 +303,16 @@ public final class Theme {
      */
     public static synchronized SyntaxTheme syntaxTheme() {
         return SyntaxTheme.builder()
-                .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()))
+                .token(TokenType.COMMENT, syntaxCommentStyle())
+                .token(TokenType.KEYWORD, syntaxKeywordStyle())
+                .token(TokenType.TYPE, syntaxTypeStyle())
+                .token(TokenType.STRING, syntaxStringStyle())
+                .token(TokenType.NUMBER, syntaxConstantStyle())
+                .token(TokenType.CONSTANT, syntaxConstantStyle())
+                .token(TokenType.FUNCTION, syntaxFunctionStyle())
+                .token(TokenType.ANNOTATION, syntaxFunctionStyle())
+                .token(TokenType.TAG, syntaxKeywordStyle())
+                .token(TokenType.ATTRIBUTE, syntaxFunctionStyle())
                 .build();
     }
 
@@ -365,6 +365,50 @@ public final class Theme {
                 isDark() ? SyntaxHighlighter.MONOKAI_TEXT : 
SyntaxHighlighter.LIGHT_TEXT);
     }
 
+    // ---- Syntax highlighting styles ----
+    //
+    // The syntax color plus any text-style the token sets (bold, italic, dim, 
underline), so a theme without colors to
+    // spare can tell code apart by weight, as the Monochrome and CRT themes 
do.
+
+    /** Syntax style for comments. */
+    public static synchronized Style syntaxCommentStyle() {
+        return syntaxStyle("syntax-comment", syntaxComment());
+    }
+
+    /** Syntax style for string literals and values. */
+    public static synchronized Style syntaxStringStyle() {
+        return syntaxStyle("syntax-string", syntaxString());
+    }
+
+    /** Syntax style for keywords, YAML/properties keys and XML tags. */
+    public static synchronized Style syntaxKeywordStyle() {
+        return syntaxStyle("syntax-keyword", syntaxKeyword());
+    }
+
+    /** Syntax style for functions, annotations and XML attribute names. */
+    public static synchronized Style syntaxFunctionStyle() {
+        return syntaxStyle("syntax-function", syntaxFunction());
+    }
+
+    /** Syntax style for types. */
+    public static synchronized Style syntaxTypeStyle() {
+        return syntaxStyle("syntax-type", syntaxType());
+    }
+
+    /** Syntax style for numbers, booleans, null and entities. */
+    public static synchronized Style syntaxConstantStyle() {
+        return syntaxStyle("syntax-constant", syntaxConstant());
+    }
+
+    /** Syntax style for plain code text such as separators. */
+    public static synchronized Style syntaxTextStyle() {
+        return syntaxStyle("syntax-text", syntaxText());
+    }
+
+    private static Style syntaxStyle(String id, Color color) {
+        return style(id, Style.EMPTY).fg(color);
+    }
+
     /**
      * Text an AI wrote shown among facts from the sources and the runtime, 
such as a route description suggested by the
      * AI project overview (CAMEL-25143): italic in the optional {@code 
ai-assisted} color, else the theme's notice
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/resources/tui/themes/catppuccin-frappe.tcss
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/resources/tui/themes/catppuccin-frappe.tcss
index bc1e0bbf36e6..d1e3a7c11ed5 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/resources/tui/themes/catppuccin-frappe.tcss
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/resources/tui/themes/catppuccin-frappe.tcss
@@ -52,3 +52,14 @@ $frappe-fg: #C6D0F5;
 #diagram-action  { color: #CA9EE6; }
 #diagram-eip     { color: #BABBF1; }
 #diagram-default { color: #737994; }
+
+/*
+ * Syntax tokens (optional), after the Catppuccin style guide: mauve keywords, 
green strings, blue functions, yellow types, peach constants, overlay2 comments.
+ */
+#syntax-comment  { color: #949CBB; }
+#syntax-string   { color: #A6D189; }
+#syntax-keyword  { color: #CA9EE6; }
+#syntax-function { color: #8CAAEE; }
+#syntax-type     { color: #E5C890; }
+#syntax-constant { color: #EF9F76; }
+#syntax-text     { color: $frappe-fg; }
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/resources/tui/themes/catppuccin-latte.tcss
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/resources/tui/themes/catppuccin-latte.tcss
index 295d336a3db3..e7cf7549bc23 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/resources/tui/themes/catppuccin-latte.tcss
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/resources/tui/themes/catppuccin-latte.tcss
@@ -52,3 +52,15 @@ $latte-fg: #4C4F69;
 #diagram-action  { color: #8839EF; }
 #diagram-eip     { color: #7287FD; }
 #diagram-default { color: #9CA0B0; }
+
+/*
+ * Syntax tokens (optional), after the Catppuccin style guide: mauve keywords, 
green strings, blue functions, yellow types, peach constants, overlay2 comments.
+ * Lifted to a 3:1 contrast on this background: string, type, constant.
+ */
+#syntax-comment  { color: #7C7F93; }
+#syntax-string   { color: #3E9A29; }
+#syntax-keyword  { color: #8839EF; }
+#syntax-function { color: #1E66F5; }
+#syntax-type     { color: #BD7819; }
+#syntax-constant { color: #EC5701; }
+#syntax-text     { color: $latte-fg; }
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/resources/tui/themes/catppuccin-mocha.tcss
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/resources/tui/themes/catppuccin-mocha.tcss
index e704f5f4828f..01614f5fe492 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/resources/tui/themes/catppuccin-mocha.tcss
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/resources/tui/themes/catppuccin-mocha.tcss
@@ -52,3 +52,14 @@ $mocha-fg: #CDD6F4;
 #diagram-action  { color: #CBA6F7; }
 #diagram-eip     { color: #B4BEFE; }
 #diagram-default { color: #6C7086; }
+
+/*
+ * Syntax tokens (optional), after the Catppuccin style guide: mauve keywords, 
green strings, blue functions, yellow types, peach constants, overlay2 comments.
+ */
+#syntax-comment  { color: #9399B2; }
+#syntax-string   { color: #A6E3A1; }
+#syntax-keyword  { color: #CBA6F7; }
+#syntax-function { color: #89B4FA; }
+#syntax-type     { color: #F9E2AF; }
+#syntax-constant { color: #FAB387; }
+#syntax-text     { color: $mocha-fg; }
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/resources/tui/themes/crt.tcss 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/resources/tui/themes/crt.tcss
index b391f99c4a8f..9367aaad5bbd 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/resources/tui/themes/crt.tcss
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/resources/tui/themes/crt.tcss
@@ -52,3 +52,17 @@ $dark-fg: #33CC33;
 #diagram-action  { color: #119911; }
 #diagram-eip     { color: #33CC33; }
 #diagram-default { color: #1A661A; }
+
+/*
+ * Syntax tokens (optional): a green phosphor terminal of the 1980s had one 
color and two intensities, so code is
+ * told apart by brightness and weight. Keywords and types in bold high 
intensity, strings and constants in the
+ * bright glow, comments in the darker half intensity green, code in the 
normal phosphor green. Not text-style
+ * dim: terminals blend dim colors toward the background, which leaves 
comments too faint to read.
+ */
+#syntax-comment  { color: #22AA22; }
+#syntax-string   { color: #88FF88; }
+#syntax-keyword  { color: #66FF66; text-style: bold; }
+#syntax-function { color: #66FF66; }
+#syntax-type     { color: #66FF66; text-style: bold; }
+#syntax-constant { color: #88FF88; }
+#syntax-text     { color: $dark-fg; }
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/resources/tui/themes/dracula.tcss
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/resources/tui/themes/dracula.tcss
index d7e616fd87c5..c56ad31f1681 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/resources/tui/themes/dracula.tcss
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/resources/tui/themes/dracula.tcss
@@ -52,3 +52,14 @@ $dracula-fg: #F8F8F2;
 #diagram-action  { color: #FF79C6; }
 #diagram-eip     { color: #BD93F9; }
 #diagram-default { color: #6272A4; }
+
+/*
+ * Syntax tokens (optional), after the Dracula spec: pink keywords, yellow 
strings, green functions, cyan types, purple constants.
+ */
+#syntax-comment  { color: #6272A4; }
+#syntax-string   { color: #F1FA8C; }
+#syntax-keyword  { color: #FF79C6; }
+#syntax-function { color: #50FA7B; }
+#syntax-type     { color: #8BE9FD; }
+#syntax-constant { color: #BD93F9; }
+#syntax-text     { color: $dracula-fg; }
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/resources/tui/themes/everforest-light.tcss
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/resources/tui/themes/everforest-light.tcss
index 834ea0ba151a..8c6733b83606 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/resources/tui/themes/everforest-light.tcss
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/resources/tui/themes/everforest-light.tcss
@@ -52,3 +52,15 @@ $everforest-fg: #5C6A72;
 #diagram-action  { color: #E66868; }
 #diagram-eip     { color: #DF69BA; }
 #diagram-default { color: #939F91; }
+
+/*
+ * Syntax tokens (optional), after Everforest: red keywords, aqua strings, 
green functions, yellow types, purple constants, grey comments.
+ * Lifted to a 3:1 contrast on this background: comment, string, function, 
type, constant.
+ */
+#syntax-comment  { color: #82907F; }
+#syntax-string   { color: #329D75; }
+#syntax-keyword  { color: #F85552; }
+#syntax-function { color: #829401; }
+#syntax-type     { color: #B68300; }
+#syntax-constant { color: #DC5CB4; }
+#syntax-text     { color: $everforest-fg; }
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/resources/tui/themes/everforest.tcss
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/resources/tui/themes/everforest.tcss
index 72fe0546c831..80dc13093bb5 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/resources/tui/themes/everforest.tcss
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/resources/tui/themes/everforest.tcss
@@ -52,3 +52,14 @@ $everforest-fg: #D3C6AA;
 #diagram-action  { color: #E69875; }
 #diagram-eip     { color: #D699B6; }
 #diagram-default { color: #7A8478; }
+
+/*
+ * Syntax tokens (optional), after Everforest: red keywords, aqua strings, 
green functions, yellow types, purple constants, grey comments.
+ */
+#syntax-comment  { color: #859289; }
+#syntax-string   { color: #83C092; }
+#syntax-keyword  { color: #E67E80; }
+#syntax-function { color: #A7C080; }
+#syntax-type     { color: #DBBC7F; }
+#syntax-constant { color: #D699B6; }
+#syntax-text     { color: $everforest-fg; }
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/resources/tui/themes/gruvbox-dark.tcss
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/resources/tui/themes/gruvbox-dark.tcss
index ef54c4a452ca..7167f0875fe4 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/resources/tui/themes/gruvbox-dark.tcss
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/resources/tui/themes/gruvbox-dark.tcss
@@ -52,3 +52,14 @@ $gruvbox-fg: #EBDBB2;
 #diagram-action  { color: #D3869B; }
 #diagram-eip     { color: #8EC07C; }
 #diagram-default { color: #A89984; }
+
+/*
+ * Syntax tokens (optional), after gruvbox: red keywords, green strings, aqua 
annotations, yellow types, purple constants.
+ */
+#syntax-comment  { color: #928374; }
+#syntax-string   { color: #B8BB26; }
+#syntax-keyword  { color: #FB4934; }
+#syntax-function { color: #8EC07C; }
+#syntax-type     { color: #FABD2F; }
+#syntax-constant { color: #D3869B; }
+#syntax-text     { color: $gruvbox-fg; }
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/resources/tui/themes/gruvbox-light.tcss
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/resources/tui/themes/gruvbox-light.tcss
index 8cf77b57ad8e..078820641fc7 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/resources/tui/themes/gruvbox-light.tcss
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/resources/tui/themes/gruvbox-light.tcss
@@ -52,3 +52,14 @@ $gruvbox-fg: #3C3836;
 #diagram-action  { color: #8F3F71; }
 #diagram-eip     { color: #427B58; }
 #diagram-default { color: #928374; }
+
+/*
+ * Syntax tokens (optional), after gruvbox: red keywords, green strings, aqua 
annotations, yellow types, purple constants.
+ */
+#syntax-comment  { color: #928374; }
+#syntax-string   { color: #79740E; }
+#syntax-keyword  { color: #9D0006; }
+#syntax-function { color: #427B58; }
+#syntax-type     { color: #B57614; }
+#syntax-constant { color: #8F3F71; }
+#syntax-text     { color: $gruvbox-fg; }
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/resources/tui/themes/kanagawa.tcss
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/resources/tui/themes/kanagawa.tcss
index fa12fdd01a4d..64b25a32b449 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/resources/tui/themes/kanagawa.tcss
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/resources/tui/themes/kanagawa.tcss
@@ -52,3 +52,14 @@ $kanagawa-fg: #DCD7BA;
 #diagram-action  { color: #D27E99; }
 #diagram-eip     { color: #957FB8; }
 #diagram-default { color: #727169; }
+
+/*
+ * Syntax tokens (optional), after Kanagawa: oniViolet keywords, springGreen 
strings, crystalBlue functions, waveAqua types, surimiOrange constants, 
fujiGray comments.
+ */
+#syntax-comment  { color: #727169; }
+#syntax-string   { color: #98BB6C; }
+#syntax-keyword  { color: #957FB8; }
+#syntax-function { color: #7E9CD8; }
+#syntax-type     { color: #7AA89F; }
+#syntax-constant { color: #FFA066; }
+#syntax-text     { color: $kanagawa-fg; }
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/resources/tui/themes/monochrome.tcss
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/resources/tui/themes/monochrome.tcss
index 1c48b34ce09d..b6afdc47676a 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/resources/tui/themes/monochrome.tcss
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/resources/tui/themes/monochrome.tcss
@@ -52,3 +52,15 @@ $dark-fg: #C8C8C8;
 #diagram-action  { color: #808080; }
 #diagram-eip     { color: #707070; }
 #diagram-default { color: #606060; }
+
+/*
+ * Syntax tokens (optional): no color, so code is told apart by brightness and 
style. Keywords in bold white,
+ * strings a step darker than code, comments in italic grey.
+ */
+#syntax-comment  { color: #8A8A8A; text-style: italic; }
+#syntax-string   { color: #A8A8A8; }
+#syntax-keyword  { color: #FFFFFF; text-style: bold; }
+#syntax-function { color: #E0E0E0; }
+#syntax-type     { color: #FFFFFF; }
+#syntax-constant { color: #E0E0E0; }
+#syntax-text     { color: $dark-fg; }
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/resources/tui/themes/nord-light.tcss
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/resources/tui/themes/nord-light.tcss
index bb37a863be54..cc41a51a451e 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/resources/tui/themes/nord-light.tcss
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/resources/tui/themes/nord-light.tcss
@@ -52,3 +52,14 @@ $nord-fg: #2E3440;
 #diagram-action  { color: #B48EAD; }
 #diagram-eip     { color: #5E81AC; }
 #diagram-default { color: #7B88A1; }
+
+/*
+ * Syntax tokens (optional), after Nord has no light editor scheme: Nord's 
hues darkened to read on the light base.
+ */
+#syntax-comment  { color: #7B88A1; }
+#syntax-string   { color: #5E8045; }
+#syntax-keyword  { color: #5E81AC; }
+#syntax-function { color: #3B7A8A; }
+#syntax-type     { color: #4C7A79; }
+#syntax-constant { color: #8F5C88; }
+#syntax-text     { color: $nord-fg; }
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/resources/tui/themes/nord.tcss
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/resources/tui/themes/nord.tcss
index b32e287beeee..b811774ef40f 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/resources/tui/themes/nord.tcss
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/resources/tui/themes/nord.tcss
@@ -52,3 +52,15 @@ $nord-fg: #D8DEE9;
 #diagram-action  { color: #B48EAD; }
 #diagram-eip     { color: #5E81AC; }
 #diagram-default { color: #4C566A; }
+
+/*
+ * Syntax tokens (optional), after Nord's own editor mapping (frost keywords 
and functions, green strings, purple numbers).
+ * Lifted to a 3:1 contrast on this background: comment.
+ */
+#syntax-comment  { color: #72809B; }
+#syntax-string   { color: #A3BE8C; }
+#syntax-keyword  { color: #81A1C1; }
+#syntax-function { color: #88C0D0; }
+#syntax-type     { color: #8FBCBB; }
+#syntax-constant { color: #B48EAD; }
+#syntax-text     { color: $nord-fg; }
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/resources/tui/themes/rose-pine-moon.tcss
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/resources/tui/themes/rose-pine-moon.tcss
index 251fad5896f8..a7ea736e89bb 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/resources/tui/themes/rose-pine-moon.tcss
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/resources/tui/themes/rose-pine-moon.tcss
@@ -52,3 +52,14 @@ $moon-fg: #E0DEF4;
 #diagram-action  { color: #EB6F92; }
 #diagram-eip     { color: #C4A7E7; }
 #diagram-default { color: #6E6A86; }
+
+/*
+ * Syntax tokens (optional), after Rosé Pine: pine keywords, gold strings, 
rose functions, foam types, iris constants, muted comments.
+ */
+#syntax-comment  { color: #6E6A86; }
+#syntax-string   { color: #F6C177; }
+#syntax-keyword  { color: #3E8FB0; }
+#syntax-function { color: #EA9A97; }
+#syntax-type     { color: #9CCFD8; }
+#syntax-constant { color: #C4A7E7; }
+#syntax-text     { color: $moon-fg; }
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/resources/tui/themes/rose-pine.tcss
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/resources/tui/themes/rose-pine.tcss
index 1a4aab49fb32..163bd8e19820 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/resources/tui/themes/rose-pine.tcss
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/resources/tui/themes/rose-pine.tcss
@@ -52,3 +52,14 @@ $rosepine-fg: #E0DEF4;
 #diagram-action  { color: #EB6F92; }
 #diagram-eip     { color: #C4A7E7; }
 #diagram-default { color: #6E6A86; }
+
+/*
+ * Syntax tokens (optional), after Rosé Pine: pine keywords, gold strings, 
rose functions, foam types, iris constants, muted comments.
+ */
+#syntax-comment  { color: #6E6A86; }
+#syntax-string   { color: #F6C177; }
+#syntax-keyword  { color: #31748F; }
+#syntax-function { color: #EBBCBA; }
+#syntax-type     { color: #9CCFD8; }
+#syntax-constant { color: #C4A7E7; }
+#syntax-text     { color: $rosepine-fg; }
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/resources/tui/themes/solarized-dark.tcss
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/resources/tui/themes/solarized-dark.tcss
index 02c270178775..28a56d4530f9 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/resources/tui/themes/solarized-dark.tcss
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/resources/tui/themes/solarized-dark.tcss
@@ -52,3 +52,15 @@ $solar-fg: #839496;
 #diagram-action  { color: #D33682; }
 #diagram-eip     { color: #6C71C4; }
 #diagram-default { color: #586E75; }
+
+/*
+ * Syntax tokens (optional), after Solarized: green keywords, cyan strings, 
blue functions, yellow types, magenta numbers.
+ * Lifted to a 3:1 contrast on this background: comment.
+ */
+#syntax-comment  { color: #5F767E; }
+#syntax-string   { color: #2AA198; }
+#syntax-keyword  { color: #859900; }
+#syntax-function { color: #268BD2; }
+#syntax-type     { color: #B58900; }
+#syntax-constant { color: #D33682; }
+#syntax-text     { color: $solar-fg; }
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/resources/tui/themes/solarized-light.tcss
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/resources/tui/themes/solarized-light.tcss
index b607d7083ad6..3ab3d8ffe293 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/resources/tui/themes/solarized-light.tcss
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/resources/tui/themes/solarized-light.tcss
@@ -52,3 +52,15 @@ $solar-fg: #657B83;
 #diagram-action  { color: #D33682; }
 #diagram-eip     { color: #6C71C4; }
 #diagram-default { color: #93A1A1; }
+
+/*
+ * Syntax tokens (optional), after Solarized: green keywords, cyan strings, 
blue functions, yellow types, magenta numbers.
+ * Lifted to a 3:1 contrast on this background: comment, string, keyword, type.
+ */
+#syntax-comment  { color: #7F8F8F; }
+#syntax-string   { color: #289B92; }
+#syntax-keyword  { color: #819400; }
+#syntax-function { color: #268BD2; }
+#syntax-type     { color: #B08500; }
+#syntax-constant { color: #D33682; }
+#syntax-text     { color: $solar-fg; }
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 1afba159483e..0e2d0fc9296e 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
@@ -83,6 +83,26 @@ 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.
 
+Themes ported from an editor color scheme (Dracula, Nord, Solarized, Gruvbox,
+Catppuccin, Tokyo Night, Rosé Pine, Kanagawa, Everforest) take the scheme's own
+code colors. Dark and Light keep the built-in palette.
+
+A syntax token may also set `text-style` (`bold`, `italic`, `underline`), so a
+theme with few colors can tell code apart by weight: Monochrome uses bold
+keywords and italic comments, CRT bold keywords and types. Do not use `dim`:
+terminals blend dim text toward the background, which undoes the contrast 
check.
+
+A theme defines all 7 tokens or none. `ThemeSyntaxPaletteTest` checks every
+theme, including the built-in fallback, for:
+
+- a contrast of at least 3:1 between each code color and `base-bg`; lift a
+  scheme color that falls short (lighter on dark themes, darker on light ones)
+  and keep its hue, noting it in the stylesheet comment
+- keywords, strings and comments in different colors, and none of them in the
+  color of plain code text
+- `syntax-text` equal to `base-fg` (except Turbo Pascal and the fallback 
themes)
+- no `dim` or `hidden` text-style on a syntax token
+
 | Token | Type | Purpose |
 |-------|------|---------|
 | `syntax-comment` | fg | Comments |
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/resources/tui/themes/tokyo-night.tcss
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/resources/tui/themes/tokyo-night.tcss
index d84a93243c5b..c6a437a37b39 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/resources/tui/themes/tokyo-night.tcss
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/resources/tui/themes/tokyo-night.tcss
@@ -52,3 +52,15 @@ $tokyo-fg: #A9B1D6;
 #diagram-action  { color: #BB9AF7; }
 #diagram-eip     { color: #73DACA; }
 #diagram-default { color: #565F89; }
+
+/*
+ * Syntax tokens (optional), after Tokyo Night: purple keywords, green 
strings, blue functions, cyan types, orange constants.
+ * Lifted to a 3:1 contrast on this background: comment.
+ */
+#syntax-comment  { color: #5D6794; }
+#syntax-string   { color: #9ECE6A; }
+#syntax-keyword  { color: #BB9AF7; }
+#syntax-function { color: #7AA2F7; }
+#syntax-type     { color: #2AC3DE; }
+#syntax-constant { color: #FF9E64; }
+#syntax-text     { color: $tokyo-fg; }
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/ThemeSyntaxPaletteTest.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/ThemeSyntaxPaletteTest.java
new file mode 100644
index 000000000000..98b19174812e
--- /dev/null
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/ThemeSyntaxPaletteTest.java
@@ -0,0 +1,175 @@
+/*
+ * 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.
+ */
+package org.apache.camel.dsl.jbang.core.commands.tui;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.EnumSet;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import dev.tamboui.style.Color;
+import dev.tamboui.style.Modifier;
+import dev.tamboui.style.Style;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.parallel.Isolated;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.EnumSource;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * Guards the code colors of every theme: whether they come from the theme's 
own {@code syntax-*} tokens or from the
+ * Monokai / GitHub fallback, code must stay readable on the theme's 
background.
+ */
+@Isolated
+class ThemeSyntaxPaletteTest {
+
+    /** Minimum contrast of a code color on the theme background (WCAG ratio; 
3:1 is the large-text level). */
+    private static final double MIN_CONTRAST = 3.0;
+
+    private static final List<String> SYNTAX_TOKENS = List.of(
+            "syntax-comment", "syntax-string", "syntax-keyword", 
"syntax-function", "syntax-type",
+            "syntax-constant", "syntax-text");
+
+    /** Themes that keep the built-in Monokai (dark) or GitHub (light) code 
palette on purpose. */
+    private static final Set<ThemeMode> FALLBACK_PALETTE = 
EnumSet.of(ThemeMode.DARK, ThemeMode.LIGHT);
+
+    @BeforeEach
+    void setUp() {
+        Theme.resetForTesting();
+    }
+
+    @AfterEach
+    void tearDown() {
+        Theme.resetForTesting();
+    }
+
+    @ParameterizedTest
+    @EnumSource(ThemeMode.class)
+    void codeColorsAreReadableOnTheBackground(ThemeMode mode) {
+        Theme.setMode(mode.id());
+        Color bg = Theme.baseBg();
+        syntaxColors().forEach((name, color) -> {
+            double ratio = contrast(color, bg);
+            assertTrue(ratio >= MIN_CONTRAST,
+                    () -> String.format("%s: %s %s has contrast %.2f on %s, 
below %.1f",
+                            mode.id(), name, color, ratio, bg, MIN_CONTRAST));
+        });
+    }
+
+    @ParameterizedTest
+    @EnumSource(ThemeMode.class)
+    void keywordsStringsAndCommentsStandApart(ThemeMode mode) {
+        Theme.setMode(mode.id());
+        Color keyword = Theme.syntaxKeyword();
+        Color string = Theme.syntaxString();
+        Color comment = Theme.syntaxComment();
+        Color text = Theme.syntaxText();
+        assertNotEquals(keyword, string, mode.id() + ": keywords and strings 
share a color");
+        assertNotEquals(keyword, comment, mode.id() + ": keywords and comments 
share a color");
+        assertNotEquals(string, comment, mode.id() + ": strings and comments 
share a color");
+        for (Color c : List.of(keyword, string, comment)) {
+            assertNotEquals(text, c, mode.id() + ": plain code text has the 
color of keywords, strings or comments");
+        }
+    }
+
+    @ParameterizedTest
+    @EnumSource(ThemeMode.class)
+    void editorSchemeThemesDefineTheirOwnCodePalette(ThemeMode mode) throws 
IOException {
+        String css = stylesheet(mode);
+        if (FALLBACK_PALETTE.contains(mode)) {
+            assertTrue(SYNTAX_TOKENS.stream().noneMatch(t -> css.contains("#" 
+ t)),
+                    mode.id() + " is expected to keep the built-in code 
palette");
+            return;
+        }
+        // A theme defines all syntax tokens or none, so a half-defined 
palette never mixes with Monokai.
+        for (String token : SYNTAX_TOKENS) {
+            assertTrue(css.contains("#" + token), mode.id() + " lacks " + 
token);
+        }
+        Theme.setMode(mode.id());
+        Color fallback = mode.isLight() ? SyntaxHighlighter.LIGHT_KEYWORD : 
SyntaxHighlighter.MONOKAI_KEYWORD;
+        assertNotEquals(fallback, Theme.syntaxKeyword(), mode.id() + " still 
shows the fallback keyword color");
+    }
+
+    @ParameterizedTest
+    @EnumSource(ThemeMode.class)
+    void plainCodeTextMatchesTheThemeText(ThemeMode mode) {
+        if (FALLBACK_PALETTE.contains(mode) || mode == ThemeMode.TURBO_PASCAL) 
{
+            return;
+        }
+        Theme.setMode(mode.id());
+        assertEquals(Theme.baseFg(), Theme.syntaxText(), mode.id() + ": code 
text should be the theme's text color");
+    }
+
+    @ParameterizedTest
+    @EnumSource(ThemeMode.class)
+    void codeStylesNeverDimOrHideText(ThemeMode mode) {
+        // Terminals draw dim text by blending toward the background, which 
undoes the contrast check above.
+        Theme.setMode(mode.id());
+        for (Style style : List.of(Theme.syntaxCommentStyle(), 
Theme.syntaxStringStyle(), Theme.syntaxKeywordStyle(),
+                Theme.syntaxFunctionStyle(), Theme.syntaxTypeStyle(), 
Theme.syntaxConstantStyle(),
+                Theme.syntaxTextStyle())) {
+            assertFalse(style.effectiveModifiers().contains(Modifier.DIM), 
mode.id() + ": " + style + " is dim");
+            assertFalse(style.effectiveModifiers().contains(Modifier.HIDDEN), 
mode.id() + ": " + style + " is hidden");
+        }
+    }
+
+    private static Map<String, Color> syntaxColors() {
+        Map<String, Color> colors = new LinkedHashMap<>();
+        colors.put("comment", Theme.syntaxComment());
+        colors.put("string", Theme.syntaxString());
+        colors.put("keyword", Theme.syntaxKeyword());
+        colors.put("function", Theme.syntaxFunction());
+        colors.put("type", Theme.syntaxType());
+        colors.put("constant", Theme.syntaxConstant());
+        colors.put("text", Theme.syntaxText());
+        return colors;
+    }
+
+    private static String stylesheet(ThemeMode mode) throws IOException {
+        try (InputStream in = ThemeSyntaxPaletteTest.class.getClassLoader()
+                .getResourceAsStream(mode.stylesheetResource())) {
+            assertNotNull(in, "missing " + mode.stylesheetResource());
+            return new String(in.readAllBytes(), StandardCharsets.UTF_8);
+        }
+    }
+
+    static double contrast(Color a, Color b) {
+        double la = luminance(a);
+        double lb = luminance(b);
+        return (Math.max(la, lb) + 0.05) / (Math.min(la, lb) + 0.05);
+    }
+
+    private static double luminance(Color color) {
+        Color.Rgb rgb = color.toRgb();
+        return 0.2126 * channel(rgb.r()) + 0.7152 * channel(rgb.g()) + 0.0722 
* channel(rgb.b());
+    }
+
+    private static double channel(int value) {
+        double c = value / 255.0;
+        return c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);
+    }
+}
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 080c0fc2eea5..a4d3bf3b521f 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
@@ -292,6 +292,26 @@ class ThemeTest {
         assertNotEquals(SyntaxHighlighter.MONOKAI_KEYWORD, 
Theme.syntaxKeyword());
     }
 
+    @Test
+    void syntaxStylesCarryTextStyleFromStylesheet() {
+        // CRT: a green phosphor terminal tells code apart by weight, keywords 
in bold high intensity.
+        Theme.setMode("crt");
+        assertEquals(Style.EMPTY.fg(Color.rgb(0x66, 0xFF, 0x66)).bold(), 
Theme.syntaxKeywordStyle());
+        assertEquals(Style.EMPTY.fg(Color.rgb(0x22, 0xAA, 0x22)), 
Theme.syntaxCommentStyle());
+
+        // Monochrome: no color, comments in italic grey.
+        Theme.setMode("monochrome");
+        assertEquals(Style.EMPTY.fg(Color.rgb(0x8A, 0x8A, 0x8A)).italic(), 
Theme.syntaxCommentStyle());
+        assertEquals(Style.EMPTY.fg(Color.rgb(0xFF, 0xFF, 0xFF)).bold(), 
Theme.syntaxKeywordStyle());
+    }
+
+    @Test
+    void syntaxStylesArePlainColorsForThemesWithoutSyntaxTokens() {
+        Theme.setMode("dark");
+        assertEquals(Style.EMPTY.fg(SyntaxHighlighter.MONOKAI_KEYWORD), 
Theme.syntaxKeywordStyle());
+        assertEquals(Style.EMPTY.fg(SyntaxHighlighter.MONOKAI_COMMENT), 
Theme.syntaxCommentStyle());
+    }
+
     @Test
     void syntaxColorsFollowThemeSwitches() {
         Theme.setMode("turbo-pascal");

Reply via email to