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()) {