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 eaeab68116b0 camel-jbang-tui: Polish TUI text and Go to Tab popup width
eaeab68116b0 is described below
commit eaeab68116b07c8f2ff1f8bb74fbda2efe93c805
Author: Claus Ibsen <[email protected]>
AuthorDate: Tue Jul 28 21:10:52 2026 +0200
camel-jbang-tui: Polish TUI text and Go to Tab popup width
camel-jbang-tui: Add five new mid-tone color themes
Add Catppuccin Frappé, Rosé Pine Moon, Gruvbox Light, Everforest Light,
and Nord Light themes to bridge the gap between dark and light palettes.
camel-jbang-tui: Fix Source tab showing wrong files after integration switch
MCP selectIntegration did not call resetIntegrationTabState, so tabs
kept stale state from the previous integration. Also fix SourceTab
rootDir getting locked to the fallback .camel directory when
configProperties is not yet populated on initial resolve.
camel-jbang-tui: Use light-friendly syntax highlighting on light themes
Replace hardcoded Monokai colors with theme-aware palette selection.
Dark themes keep Monokai, light themes use a GitHub-inspired palette
with darker string/keyword/comment colors that are readable on light
backgrounds.
camel-jbang-tui: Fix test failures after theme-aware syntax highlighting
Use Objects.equals() instead of == for Style comparison in buildLine()
since theme-aware style methods return new instances per call. Add
Theme.resetForTesting() to SyntaxHighlighterTest to prevent persisted
theme from affecting test results. Update ThemeModeTest and
ThemeModeCompletionCandidatesTest for the five new themes.
Co-Authored-By: Claude Opus 4.6 <[email protected]>
Signed-off-by: Claus Ibsen <[email protected]>
---
.../dsl/jbang/core/commands/tui/CamelMonitor.java | 5 +
.../dsl/jbang/core/commands/tui/GotoTabPopup.java | 4 +-
.../dsl/jbang/core/commands/tui/McpFacade.java | 3 +
.../dsl/jbang/core/commands/tui/OverviewTab.java | 20 +-
.../dsl/jbang/core/commands/tui/SourceTab.java | 2 +-
.../jbang/core/commands/tui/SyntaxHighlighter.java | 207 ++++++++++++++++-----
.../dsl/jbang/core/commands/tui/ThemeMode.java | 5 +
.../resources/tui/themes/catppuccin-frappe.tcss | 54 ++++++
.../resources/tui/themes/everforest-light.tcss | 54 ++++++
.../main/resources/tui/themes/gruvbox-light.tcss | 54 ++++++
.../src/main/resources/tui/themes/nord-light.tcss | 54 ++++++
.../main/resources/tui/themes/rose-pine-moon.tcss | 54 ++++++
.../core/commands/tui/SyntaxHighlighterTest.java | 6 +
.../tui/ThemeModeCompletionCandidatesTest.java | 7 +-
.../dsl/jbang/core/commands/tui/ThemeModeTest.java | 7 +-
15 files changed, 470 insertions(+), 66 deletions(-)
diff --git
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/CamelMonitor.java
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/CamelMonitor.java
index 642b3cdb08c3..cf2eee2e78ca 100644
---
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/CamelMonitor.java
+++
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/CamelMonitor.java
@@ -491,6 +491,11 @@ public class CamelMonitor extends CamelCommand {
public void stopProcess(boolean forceKill) {
stopSelectedProcess(forceKill);
}
+
+ @Override
+ public void resetIntegrationTabState() {
+ CamelMonitor.this.resetIntegrationTabState();
+ }
});
aiPanel.setMcpFacade(mcpFacade);
mcpFacade.setAiActivityLog(aiPanel::getActivityLog);
diff --git
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/GotoTabPopup.java
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/GotoTabPopup.java
index ae28fa7aa250..21b7c19c54d9 100644
---
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/GotoTabPopup.java
+++
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/GotoTabPopup.java
@@ -174,8 +174,8 @@ class GotoTabPopup {
if (filteredEntries == null) {
return;
}
- int nameColWidth = 18;
- int popupW = Math.min(100, area.width() - 4);
+ int nameColWidth = 22;
+ int popupW = Math.min(110, area.width() - 4);
int descColWidth = popupW - nameColWidth - 8;
int contentH = filteredEntries.size() + 2;
int maxH = area.height() - 4;
diff --git
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/McpFacade.java
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/McpFacade.java
index 60436bbcffae..4b99916c4dc9 100644
---
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/McpFacade.java
+++
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/McpFacade.java
@@ -79,6 +79,8 @@ class McpFacade {
void restartProcess();
void stopProcess(boolean forceKill);
+
+ void resetIntegrationTabState();
}
// Tab name constants
@@ -297,6 +299,7 @@ class McpFacade {
if (nameOrPid.equals(info.pid)
|| (info.name != null &&
info.name.equalsIgnoreCase(nameOrPid))) {
ctx.selectedPid = info.pid;
+ bridge.resetIntegrationTabState();
return info.name != null ? info.name : info.pid;
}
}
diff --git
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/OverviewTab.java
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/OverviewTab.java
index 57adf2ef5871..1379044a2ea5 100644
---
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/OverviewTab.java
+++
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/OverviewTab.java
@@ -1355,7 +1355,7 @@ class OverviewTab extends AbstractTab {
When running an example that requires infra services, they are
started
automatically before the example launches.
- Press `i` to toggle focus between the integrations and infra
panels.
+ Press `Tab` to toggle focus between the integrations and infra
panels.
Each panel remembers its own selection. Press `d` while the
infra panel
is focused to toggle a details panel showing the service's
connection
properties (host, port, etc.).
@@ -1367,7 +1367,7 @@ class OverviewTab extends AbstractTab {
## Keys
- `Up/Down` — select within the focused panel
- - `i` — toggle focus between integrations and infra panels
+ - `Tab` — switch between integrations and infra panels (when
infra services are running)
- `d` — toggle infra service details panel (when infra panel
is focused)
- `Enter` — view routes for selected integration
- `s` — cycle sort column (for the focused panel)
@@ -1426,9 +1426,9 @@ class OverviewTab extends AbstractTab {
for (String row : TuiHelper.SMALL_CAMEL) {
lines.add(Line.from(Span.styled(" " + row,
Style.EMPTY.fg(Theme.accent()).bold())));
}
- lines.add(Line.from(Span.styled(" No Active Camel Integrations
Found", Theme.title())));
+ lines.add(Line.from(Span.styled(" No Running Integrations Found",
Theme.title())));
lines.add(Line.from(Span.raw("")));
- lines.add(Line.from(Span.styled(TuiIcons.indent(TuiIcons.TIP) + "How
to monitor integrations:", Style.EMPTY.bold())));
+ lines.add(Line.from(Span.styled(TuiIcons.indent(TuiIcons.TIP) + "How
to get started:", Style.EMPTY.bold())));
lines.add(Line.from(Span.raw(" Run a route or integration in
another terminal window:")));
lines.add(Line.from(Span.styled(" > camel run my-route.yaml",
Theme.success())));
lines.add(Line.from(Span.raw("")));
@@ -1437,7 +1437,15 @@ class OverviewTab extends AbstractTab {
Span.raw(" Press "),
Span.styled(" F2 ", Theme.hintKey()),
Span.raw(" to open Actions and select "),
- Span.styled("Run Example", Style.EMPTY.bold()),
+ Span.styled("Run an Example", Style.EMPTY.bold()),
+ Span.raw("."))));
+ lines.add(Line.from(Span.raw("")));
+ lines.add(Line.from(Span.styled(TuiIcons.indent(TuiIcons.FOLDER) + "Or
run an existing project:", Style.EMPTY.bold())));
+ lines.add(Line.from(List.of(
+ Span.raw(" Press "),
+ Span.styled(" F2 ", Theme.hintKey()),
+ Span.raw(" to open Actions and select "),
+ Span.styled("Run from Folder", Style.EMPTY.bold()),
Span.raw("."))));
lines.add(Line.from(Span.raw("")));
lines.add(Line.from(Span.styled(TuiIcons.indent(TuiIcons.COMPUTER) +
"Or use the embedded JLine shell panel:",
@@ -1462,7 +1470,7 @@ class OverviewTab extends AbstractTab {
.block(Block.builder()
.borderType(BorderType.ROUNDED).borders(Borders.ALL)
.title(Title.from(Line.from(
- Span.styled(" Camel JBang TUI ",
Theme.title()))))
+ Span.styled(" Camel TUI ",
Theme.title()))))
.build())
.build(),
area);
diff --git
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/SourceTab.java
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/SourceTab.java
index 545664646d76..5d94e93dae53 100644
---
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/SourceTab.java
+++
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/SourceTab.java
@@ -373,7 +373,7 @@ class SourceTab extends AbstractTab {
if (dir == null || !Files.isDirectory(dir)) {
return;
}
- if (rootDir == null) {
+ if (rootDir == null || !rootDir.equals(dir)) {
rootDir = dir;
currentDir = dir;
}
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 9ec9fc64923e..af4bd0306061 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
@@ -18,6 +18,7 @@ package org.apache.camel.dsl.jbang.core.commands.tui;
import java.util.ArrayList;
import java.util.List;
+import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -65,7 +66,7 @@ class SyntaxHighlighter {
private static final Pattern XML_ATTR_NAME =
Pattern.compile("\\s([\\w:.-]+)=");
private static final Pattern XML_ENTITY = Pattern.compile("&[^;]+;");
- // Monokai color palette
+ // 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);
static final Color MONOKAI_KEYWORD = Color.rgb(249, 38, 114);
@@ -74,35 +75,134 @@ class SyntaxHighlighter {
static final Color MONOKAI_CONSTANT = Color.rgb(174, 129, 255);
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);
+
+ private static Color comment() {
+ return Theme.isDark() ? MONOKAI_COMMENT : LIGHT_COMMENT;
+ }
+
+ private static Color string() {
+ return Theme.isDark() ? MONOKAI_STRING : LIGHT_STRING;
+ }
+
+ private static Color keyword() {
+ return Theme.isDark() ? MONOKAI_KEYWORD : LIGHT_KEYWORD;
+ }
+
+ private static Color function() {
+ return Theme.isDark() ? MONOKAI_FUNCTION : LIGHT_FUNCTION;
+ }
+
+ private static Color type() {
+ return Theme.isDark() ? MONOKAI_TYPE : LIGHT_TYPE;
+ }
+
+ private static Color constant() {
+ return Theme.isDark() ? MONOKAI_CONSTANT : LIGHT_CONSTANT;
+ }
+
+ private static Color text() {
+ return Theme.isDark() ? MONOKAI_TEXT : LIGHT_TEXT;
+ }
+
// Java styles
- private static final Style JAVA_COMMENT_STYLE =
Style.EMPTY.fg(MONOKAI_COMMENT);
- private static final Style JAVA_STRING_STYLE =
Style.EMPTY.fg(MONOKAI_STRING);
- private static final Style JAVA_ANNOTATION_STYLE =
Style.EMPTY.fg(MONOKAI_FUNCTION);
- private static final Style JAVA_MODIFIER_STYLE =
Style.EMPTY.fg(MONOKAI_KEYWORD);
- private static final Style JAVA_KEYWORD_STYLE =
Style.EMPTY.fg(MONOKAI_KEYWORD);
- private static final Style JAVA_TYPE_STYLE = Style.EMPTY.fg(MONOKAI_TYPE);
- private static final Style JAVA_BOOLEAN_STYLE =
Style.EMPTY.fg(MONOKAI_CONSTANT);
- private static final Style JAVA_NUMBER_STYLE =
Style.EMPTY.fg(MONOKAI_CONSTANT);
+ private static Style javaComment() {
+ return Style.EMPTY.fg(comment());
+ }
+
+ private static Style javaString() {
+ return Style.EMPTY.fg(string());
+ }
+
+ private static Style javaAnnotation() {
+ return Style.EMPTY.fg(function());
+ }
+
+ private static Style javaModifier() {
+ return Style.EMPTY.fg(keyword());
+ }
+
+ private static Style javaKeyword() {
+ return Style.EMPTY.fg(keyword());
+ }
+
+ private static Style javaType() {
+ return Style.EMPTY.fg(type());
+ }
+
+ private static Style javaBoolean() {
+ return Style.EMPTY.fg(constant());
+ }
+
+ private static Style javaNumber() {
+ return Style.EMPTY.fg(constant());
+ }
// YAML styles
- private static final Style YAML_COMMENT_STYLE =
Style.EMPTY.fg(MONOKAI_COMMENT);
- private static final Style YAML_KEY_STYLE =
Style.EMPTY.fg(MONOKAI_KEYWORD);
- private static final Style YAML_VALUE_STYLE =
Style.EMPTY.fg(MONOKAI_STRING);
- private static final Style YAML_SPECIAL_STYLE =
Style.EMPTY.fg(MONOKAI_CONSTANT);
- private static final Style YAML_SEPARATOR_STYLE =
Style.EMPTY.fg(MONOKAI_TEXT).bold();
+ private static Style yamlComment() {
+ return Style.EMPTY.fg(comment());
+ }
+
+ private static Style yamlKey() {
+ return Style.EMPTY.fg(keyword());
+ }
+
+ private static Style yamlValue() {
+ return Style.EMPTY.fg(string());
+ }
+
+ private static Style yamlSpecial() {
+ return Style.EMPTY.fg(constant());
+ }
+
+ private static Style yamlSeparator() {
+ return Style.EMPTY.fg(text()).bold();
+ }
// XML styles
- private static final Style XML_COMMENT_STYLE =
Style.EMPTY.fg(MONOKAI_COMMENT);
- private static final Style XML_TAG_STYLE = Style.EMPTY.fg(MONOKAI_KEYWORD);
- private static final Style XML_ATTR_NAME_STYLE =
Style.EMPTY.fg(MONOKAI_FUNCTION);
- private static final Style XML_ATTR_VALUE_STYLE =
Style.EMPTY.fg(MONOKAI_STRING);
- private static final Style XML_ENTITY_STYLE =
Style.EMPTY.fg(MONOKAI_CONSTANT);
+ private static Style xmlComment() {
+ return Style.EMPTY.fg(comment());
+ }
+
+ private static Style xmlTag() {
+ return Style.EMPTY.fg(keyword());
+ }
+
+ private static Style xmlAttrName() {
+ return Style.EMPTY.fg(function());
+ }
+
+ private static Style xmlAttrValue() {
+ return Style.EMPTY.fg(string());
+ }
+
+ private static Style xmlEntity() {
+ return Style.EMPTY.fg(constant());
+ }
// Properties styles
- private static final Style PROPERTIES_COMMENT_STYLE =
Style.EMPTY.fg(MONOKAI_COMMENT);
- private static final Style PROPERTIES_KEY_STYLE =
Style.EMPTY.fg(MONOKAI_KEYWORD);
- private static final Style PROPERTIES_SEPARATOR_STYLE =
Style.EMPTY.fg(MONOKAI_TEXT).bold();
- private static final Style PROPERTIES_VALUE_STYLE =
Style.EMPTY.fg(MONOKAI_STRING);
+ private static Style propsComment() {
+ return Style.EMPTY.fg(comment());
+ }
+
+ private static Style propsKey() {
+ return Style.EMPTY.fg(keyword());
+ }
+
+ private static Style propsSeparator() {
+ return Style.EMPTY.fg(text()).bold();
+ }
+
+ private static Style propsValue() {
+ return Style.EMPTY.fg(string());
+ }
private SyntaxHighlighter() {
}
@@ -153,14 +253,14 @@ class SyntaxHighlighter {
Style[] charStyles = new Style[len];
// Priority order: comments > strings > annotations > keywords >
numbers
- applyPattern(charStyles, text, JAVA_LINE_COMMENT, JAVA_COMMENT_STYLE);
- applyPattern(charStyles, text, JAVA_STRING, JAVA_STRING_STYLE);
- applyPattern(charStyles, text, JAVA_ANNOTATION, JAVA_ANNOTATION_STYLE);
- applyPattern(charStyles, text, JAVA_MODIFIER, JAVA_MODIFIER_STYLE);
- applyPattern(charStyles, text, JAVA_KEYWORD, JAVA_KEYWORD_STYLE);
- applyPattern(charStyles, text, JAVA_TYPE, JAVA_TYPE_STYLE);
- applyPattern(charStyles, text, JAVA_BOOLEAN_NULL, JAVA_BOOLEAN_STYLE);
- applyPattern(charStyles, text, JAVA_NUMBER, JAVA_NUMBER_STYLE);
+ applyPattern(charStyles, text, JAVA_LINE_COMMENT, javaComment());
+ applyPattern(charStyles, text, JAVA_STRING, javaString());
+ applyPattern(charStyles, text, JAVA_ANNOTATION, javaAnnotation());
+ applyPattern(charStyles, text, JAVA_MODIFIER, javaModifier());
+ applyPattern(charStyles, text, JAVA_KEYWORD, javaKeyword());
+ applyPattern(charStyles, text, JAVA_TYPE, javaType());
+ applyPattern(charStyles, text, JAVA_BOOLEAN_NULL, javaBoolean());
+ applyPattern(charStyles, text, JAVA_NUMBER, javaNumber());
return buildLine(text, charStyles);
}
@@ -170,38 +270,41 @@ class SyntaxHighlighter {
Style[] charStyles = new Style[len];
// Comments have highest priority
- applyPattern(charStyles, text, YAML_COMMENT, YAML_COMMENT_STYLE);
+ applyPattern(charStyles, text, YAML_COMMENT, yamlComment());
// Key portion (before colon)
+ Style ykStyle = yamlKey();
+ Style ysSep = yamlSeparator();
Matcher keyMatcher = YAML_KEY.matcher(text);
if (keyMatcher.find()) {
int keyStart = keyMatcher.start(2);
int keyEnd = keyMatcher.end(2);
for (int i = keyStart; i < keyEnd && i < len; i++) {
if (charStyles[i] == null) {
- charStyles[i] = YAML_KEY_STYLE;
+ charStyles[i] = ykStyle;
}
}
// Colon separator
int colonIdx = text.indexOf(':', keyEnd);
if (colonIdx >= 0 && colonIdx < len && charStyles[colonIdx] ==
null) {
- charStyles[colonIdx] = YAML_SEPARATOR_STYLE;
+ charStyles[colonIdx] = ysSep;
}
}
// String values
- applyPattern(charStyles, text, YAML_STRING_VALUE, YAML_VALUE_STYLE);
+ applyPattern(charStyles, text, YAML_STRING_VALUE, yamlValue());
// Special values (boolean, null, numbers) after colon
- applyPatternGroup(charStyles, text, YAML_BOOLEAN_NULL, 1,
YAML_SPECIAL_STYLE);
- applyPatternGroup(charStyles, text, YAML_NUMBER, 1,
YAML_SPECIAL_STYLE);
+ Style ysSpecial = yamlSpecial();
+ applyPatternGroup(charStyles, text, YAML_BOOLEAN_NULL, 1, ysSpecial);
+ applyPatternGroup(charStyles, text, YAML_NUMBER, 1, ysSpecial);
// List markers
Matcher listMarker = Pattern.compile("^(\\s*)(-)(\\s)").matcher(text);
if (listMarker.find()) {
int dashIdx = listMarker.start(2);
if (dashIdx < len && charStyles[dashIdx] == null) {
- charStyles[dashIdx] = YAML_SEPARATOR_STYLE;
+ charStyles[dashIdx] = ysSep;
}
}
@@ -222,7 +325,7 @@ class SyntaxHighlighter {
}
if (!hasSpecial) {
for (int i = valueStart; i < len; i++) {
- charStyles[i] = YAML_VALUE_STYLE;
+ charStyles[i] = yamlValue();
}
}
}
@@ -236,29 +339,31 @@ class SyntaxHighlighter {
Style[] charStyles = new Style[len];
// Comments highest priority
- applyPattern(charStyles, text, XML_COMMENT, XML_COMMENT_STYLE);
+ applyPattern(charStyles, text, XML_COMMENT, xmlComment());
// Attribute values (before tag names so tags don't override)
- applyPattern(charStyles, text, XML_ATTR_VALUE, XML_ATTR_VALUE_STYLE);
+ applyPattern(charStyles, text, XML_ATTR_VALUE, xmlAttrValue());
// Attribute names
+ Style xan = xmlAttrName();
Matcher attrMatcher = XML_ATTR_NAME.matcher(text);
while (attrMatcher.find()) {
int start = attrMatcher.start(1);
int end = attrMatcher.end(1);
for (int i = start; i < end; i++) {
if (charStyles[i] == null) {
- charStyles[i] = XML_ATTR_NAME_STYLE;
+ charStyles[i] = xan;
}
}
}
// Tag names
- applyPattern(charStyles, text, XML_OPEN_TAG, XML_TAG_STYLE);
- applyPattern(charStyles, text, XML_CLOSE_BRACKET, XML_TAG_STYLE);
+ Style xt = xmlTag();
+ applyPattern(charStyles, text, XML_OPEN_TAG, xt);
+ applyPattern(charStyles, text, XML_CLOSE_BRACKET, xt);
// Entity references
- applyPattern(charStyles, text, XML_ENTITY, XML_ENTITY_STYLE);
+ applyPattern(charStyles, text, XML_ENTITY, xmlEntity());
return buildLine(text, charStyles);
}
@@ -282,7 +387,7 @@ class SyntaxHighlighter {
char first = text.charAt(start);
if (first == '#' || first == '!') {
for (int i = start; i < len; i++) {
- charStyles[i] = PROPERTIES_COMMENT_STYLE;
+ charStyles[i] = propsComment();
}
return buildLine(text, charStyles);
}
@@ -304,14 +409,14 @@ class SyntaxHighlighter {
// key with no separator and no value (e.g. a lone "enabled")
if (keyEnd < 0) {
for (int i = start; i < len; i++) {
- charStyles[i] = PROPERTIES_KEY_STYLE;
+ charStyles[i] = propsKey();
}
return buildLine(text, charStyles);
}
// key
for (int i = start; i < keyEnd; i++) {
- charStyles[i] = PROPERTIES_KEY_STYLE;
+ charStyles[i] = propsKey();
}
// an explicit '=' or ':' separator may follow optional whitespace
@@ -320,7 +425,7 @@ class SyntaxHighlighter {
i++;
}
if (i < len && (text.charAt(i) == '=' || text.charAt(i) == ':')) {
- charStyles[i] = PROPERTIES_SEPARATOR_STYLE;
+ charStyles[i] = propsSeparator();
i++;
}
@@ -329,7 +434,7 @@ class SyntaxHighlighter {
i++;
}
for (; i < len; i++) {
- charStyles[i] = PROPERTIES_VALUE_STYLE;
+ charStyles[i] = propsValue();
}
return buildLine(text, charStyles);
@@ -365,7 +470,7 @@ class SyntaxHighlighter {
while (i < len) {
Style current = charStyles[i];
int start = i;
- while (i < len && charStyles[i] == current) {
+ while (i < len && Objects.equals(charStyles[i], current)) {
i++;
}
String segment = text.substring(start, i);
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 a800053c8de0..8a101c6880f0 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
@@ -28,15 +28,20 @@ enum ThemeMode {
LIGHT("light", "Light", true),
DRACULA("dracula", "Dracula", false),
NORD("nord", "Nord", false),
+ NORD_LIGHT("nord-light", "Nord Light", true),
SOLARIZED_DARK("solarized-dark", "Solarized Dark", false),
SOLARIZED_LIGHT("solarized-light", "Solarized Light", true),
GRUVBOX_DARK("gruvbox-dark", "Gruvbox Dark", false),
+ GRUVBOX_LIGHT("gruvbox-light", "Gruvbox Light", true),
CATPPUCCIN_MOCHA("catppuccin-mocha", "Catppuccin Mocha", false),
+ CATPPUCCIN_FRAPPE("catppuccin-frappe", "Catppuccin Frappé", false),
CATPPUCCIN_LATTE("catppuccin-latte", "Catppuccin Latte", true),
TOKYO_NIGHT("tokyo-night", "Tokyo Night", false),
ROSE_PINE("rose-pine", "Rosé Pine", false),
+ ROSE_PINE_MOON("rose-pine-moon", "Rosé Pine Moon", false),
KANAGAWA("kanagawa", "Kanagawa", false),
EVERFOREST("everforest", "Everforest", false),
+ EVERFOREST_LIGHT("everforest-light", "Everforest Light", true),
MONOCHROME("monochrome", "Monochrome", false),
CRT("crt", "CRT", false);
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
new file mode 100644
index 000000000000..bc1e0bbf36e6
--- /dev/null
+++
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/resources/tui/themes/catppuccin-frappe.tcss
@@ -0,0 +1,54 @@
+/*
+ * 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 Catppuccin Frappé theme. Soft pastel palette on a warm gray base.
*/
+
+$brand: #CA9EE6;
+$frappe-bg: #303446;
+$frappe-fg: #C6D0F5;
+
+#accent { color: $brand; }
+#accent-bg { color: white; background: $brand; text-style: bold; }
+#hint-key { color: black; background: $brand; text-style: bold; }
+#border { color: #51576D; }
+#border-focused { color: $brand; }
+#title { color: $brand; text-style: bold; }
+#success { color: #A6D189; }
+#warning { color: #E5C890; }
+#error { color: #E78284; }
+#muted { color: #737994; }
+#selection { color: white; background: #51576D; text-style: bold; }
+#info { color: #8CAAEE; }
+#notice { color: #CA9EE6; }
+#row-alt { background: #383A4E; }
+#base-bg { background: $frappe-bg; }
+#base-fg { color: $frappe-fg; }
+
+/* Content tokens — labels, change indicators, search highlight */
+#label { color: #E5C890; }
+#change { color: #E5C890; }
+#search-match { color: black; background: #E5C890; }
+#mnemonic { color: #E5C890; text-style: bold underline; }
+
+/* Diagram tokens */
+#diagram-border { color: #737994; }
+#diagram-id { color: $frappe-fg; }
+#diagram-from { color: #A6D189; }
+#diagram-to { color: #81C8BE; }
+#diagram-choice { color: #E5C890; }
+#diagram-action { color: #CA9EE6; }
+#diagram-eip { color: #BABBF1; }
+#diagram-default { color: #737994; }
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
new file mode 100644
index 000000000000..834ea0ba151a
--- /dev/null
+++
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/resources/tui/themes/everforest-light.tcss
@@ -0,0 +1,54 @@
+/*
+ * 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 Everforest Light theme. Soft green-tinted light palette inspired
by natural forests. */
+
+$brand: #8DA101;
+$everforest-bg: #FDF6E3;
+$everforest-fg: #5C6A72;
+
+#accent { color: $brand; }
+#accent-bg { color: white; background: $brand; text-style: bold; }
+#hint-key { color: white; background: $brand; text-style: bold; }
+#border { color: #D5C4A1; }
+#border-focused { color: $brand; }
+#title { color: $brand; text-style: bold; }
+#success { color: #8DA101; }
+#warning { color: #DFA000; }
+#error { color: #F85552; }
+#muted { color: #939F91; }
+#selection { color: white; background: #93B259; text-style: bold; }
+#info { color: #3A94C5; }
+#notice { color: #DF69BA; }
+#row-alt { background: #F4EBD0; }
+#base-bg { background: $everforest-bg; }
+#base-fg { color: $everforest-fg; }
+
+/* Content tokens — labels, change indicators, search highlight */
+#label { color: #DFA000; }
+#change { color: #DFA000; }
+#search-match { color: $everforest-fg; background: #DFA000; }
+#mnemonic { color: #DFA000; text-style: bold underline; }
+
+/* Diagram tokens */
+#diagram-border { color: #939F91; }
+#diagram-id { color: $everforest-fg; }
+#diagram-from { color: #8DA101; }
+#diagram-to { color: #3A94C5; }
+#diagram-choice { color: #DFA000; }
+#diagram-action { color: #E66868; }
+#diagram-eip { color: #DF69BA; }
+#diagram-default { color: #939F91; }
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
new file mode 100644
index 000000000000..e8902dead615
--- /dev/null
+++
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/resources/tui/themes/gruvbox-light.tcss
@@ -0,0 +1,54 @@
+/*
+ * 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 Gruvbox Light theme. Warm sepia-toned light palette with earthy
colors. */
+
+$brand: #AF3A03;
+$gruvbox-bg: #FBF1C7;
+$gruvbox-fg: #3C3836;
+
+#accent { color: $brand; }
+#accent-bg { color: white; background: $brand; text-style: bold; }
+#hint-key { color: white; background: $brand; text-style: bold; }
+#border { color: #BDAE93; }
+#border-focused { color: $brand; }
+#title { color: $brand; text-style: bold; }
+#success { color: #79740E; }
+#warning { color: #B57614; }
+#error { color: #CC241D; }
+#muted { color: #928374; }
+#selection { color: white; background: #D65D0E; text-style: bold; }
+#info { color: #076678; }
+#notice { color: #8F3F71; }
+#row-alt { background: #F2E5BC; }
+#base-bg { background: $gruvbox-bg; }
+#base-fg { color: $gruvbox-fg; }
+
+/* Content tokens — labels, change indicators, search highlight */
+#label { color: #B57614; }
+#change { color: #B57614; }
+#search-match { color: $gruvbox-fg; background: #B57614; }
+#mnemonic { color: #B57614; text-style: bold underline; }
+
+/* Diagram tokens */
+#diagram-border { color: #928374; }
+#diagram-id { color: $gruvbox-fg; }
+#diagram-from { color: #79740E; }
+#diagram-to { color: #076678; }
+#diagram-choice { color: #B57614; }
+#diagram-action { color: #8F3F71; }
+#diagram-eip { color: #427B58; }
+#diagram-default { color: #928374; }
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
new file mode 100644
index 000000000000..bb37a863be54
--- /dev/null
+++
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/resources/tui/themes/nord-light.tcss
@@ -0,0 +1,54 @@
+/*
+ * 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 Nord Light theme. Cool blue-gray light palette from the Nord
color scheme. */
+
+$brand: #5E81AC;
+$nord-bg: #ECEFF4;
+$nord-fg: #2E3440;
+
+#accent { color: $brand; }
+#accent-bg { color: white; background: $brand; text-style: bold; }
+#hint-key { color: white; background: $brand; text-style: bold; }
+#border { color: #D8DEE9; }
+#border-focused { color: $brand; }
+#title { color: $brand; text-style: bold; }
+#success { color: #A3BE8C; }
+#warning { color: #EBCB8B; }
+#error { color: #BF616A; }
+#muted { color: #7B88A1; }
+#selection { color: white; background: #5E81AC; text-style: bold; }
+#info { color: #5E81AC; }
+#notice { color: #B48EAD; }
+#row-alt { background: #E5E9F0; }
+#base-bg { background: $nord-bg; }
+#base-fg { color: $nord-fg; }
+
+/* Content tokens — labels, change indicators, search highlight */
+#label { color: #D08770; }
+#change { color: #D08770; }
+#search-match { color: $nord-fg; background: #EBCB8B; }
+#mnemonic { color: #D08770; text-style: bold underline; }
+
+/* Diagram tokens */
+#diagram-border { color: #7B88A1; }
+#diagram-id { color: $nord-fg; }
+#diagram-from { color: #A3BE8C; }
+#diagram-to { color: #88C0D0; }
+#diagram-choice { color: #EBCB8B; }
+#diagram-action { color: #B48EAD; }
+#diagram-eip { color: #5E81AC; }
+#diagram-default { color: #7B88A1; }
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
new file mode 100644
index 000000000000..251fad5896f8
--- /dev/null
+++
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/resources/tui/themes/rose-pine-moon.tcss
@@ -0,0 +1,54 @@
+/*
+ * 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 Rosé Pine Moon theme. Muted purple-gray variant, slightly
brighter than Rosé Pine. */
+
+$brand: #EA9A97;
+$moon-bg: #232136;
+$moon-fg: #E0DEF4;
+
+#accent { color: $brand; }
+#accent-bg { color: white; background: $brand; text-style: bold; }
+#hint-key { color: black; background: $brand; text-style: bold; }
+#border { color: #6E6A86; }
+#border-focused { color: $brand; }
+#title { color: $brand; text-style: bold; }
+#success { color: #9CCFD8; }
+#warning { color: #F6C177; }
+#error { color: #EB6F92; }
+#muted { color: #6E6A86; }
+#selection { color: white; background: #393552; text-style: bold; }
+#info { color: #EA9A97; }
+#notice { color: #C4A7E7; }
+#row-alt { background: #2A283E; }
+#base-bg { background: $moon-bg; }
+#base-fg { color: $moon-fg; }
+
+/* Content tokens — labels, change indicators, search highlight */
+#label { color: #F6C177; }
+#change { color: #F6C177; }
+#search-match { color: black; background: #F6C177; }
+#mnemonic { color: #F6C177; text-style: bold underline; }
+
+/* Diagram tokens */
+#diagram-border { color: #6E6A86; }
+#diagram-id { color: $moon-fg; }
+#diagram-from { color: #9CCFD8; }
+#diagram-to { color: #EA9A97; }
+#diagram-choice { color: #F6C177; }
+#diagram-action { color: #EB6F92; }
+#diagram-eip { color: #C4A7E7; }
+#diagram-default { color: #6E6A86; }
diff --git
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/SyntaxHighlighterTest.java
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/SyntaxHighlighterTest.java
index 871fffd6b7db..8d58ab215013 100644
---
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/SyntaxHighlighterTest.java
+++
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/SyntaxHighlighterTest.java
@@ -19,6 +19,7 @@ package org.apache.camel.dsl.jbang.core.commands.tui;
import dev.tamboui.style.Color;
import dev.tamboui.text.Line;
import dev.tamboui.text.Span;
+import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -26,6 +27,11 @@ import static org.junit.jupiter.api.Assertions.assertNull;
class SyntaxHighlighterTest {
+ @BeforeAll
+ static void initTheme() {
+ Theme.resetForTesting();
+ }
+
@Test
void detectsPropertiesLanguage() {
assertEquals(SyntaxHighlighter.Language.PROPERTIES,
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 3288e9f4276d..a63d956a1086 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
@@ -31,8 +31,9 @@ class ThemeModeCompletionCandidatesTest {
new ThemeModeCompletionCandidates().forEach(candidates::add);
assertEquals(List.of(
- "dark", "light", "dracula", "nord", "solarized-dark",
"solarized-light",
- "gruvbox-dark", "catppuccin-mocha", "catppuccin-latte",
"tokyo-night",
- "rose-pine", "kanagawa", "everforest", "monochrome", "crt"),
candidates);
+ "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);
}
}
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 eb98f28b2b4e..4c182af12bae 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
@@ -68,9 +68,10 @@ class ThemeModeTest {
@Test
void idsListsAllCanonicalValuesInDeclarationOrder() {
assertEquals(List.of(
- "dark", "light", "dracula", "nord", "solarized-dark",
"solarized-light",
- "gruvbox-dark", "catppuccin-mocha", "catppuccin-latte",
"tokyo-night",
- "rose-pine", "kanagawa", "everforest", "monochrome", "crt"),
ThemeMode.ids());
+ "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());
}
@Test