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");