This is an automated email from the ASF dual-hosted git repository. davsclaus pushed a commit to branch feature/CAMEL-24759-tamboui-05-cleanup in repository https://gitbox.apache.org/repos/asf/camel.git
commit d5fd95871656793ae0c53f9edd3fba19f3ea80be Author: Claus Ibsen <[email protected]> AuthorDate: Sat Sep 19 10:30:04 2026 +0200 CAMEL-24759: camel-jbang - TUI: web terminal measures VS16 emoji like TamboUI, restoring the CAMEL-23818 icons The --web frontend used the xterm.js Unicode 11 addon, whose tables treat the VS16 variation selector as zero-width, so a text-default glyph such as the keyboard or wastebasket took one column in the browser while TamboUI 0.5.0 reserves two (tamboui/tamboui#388). Swap the addon for @xterm/addon-unicode-graphemes 0.4.0, which promotes base + VS16 to a 2-column cell; every icon in TuiIcons now measures the same in xterm.js and in TamboUI's CharWidth. That makes the CAMEL-23818 workaround reversible: the keystrokes, tape recording and delete actions get their keyboard, record, stop and wastebasket icons back (with VS16, since terminals draw the bare glyphs in one column), and the no-VS16 test rule is replaced by one that requires those icons to carry VS16 and measure two columns. Verified in Chrome: the Screen submenu keeps its alignment and moving the selection over the VS16 rows leaves no stray characters. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]> Signed-off-by: Claus Ibsen <[email protected]> --- .../dsl/jbang/core/commands/tui/ActionsPopup.java | 2 +- .../camel/dsl/jbang/core/commands/tui/TuiIcons.java | 17 ++++++++--------- .../src/main/resources/tui/web/index.html | 11 +++++++---- .../main/resources/tui/web/vendor/LICENSE-xterm.txt | 8 +++++--- .../tui/web/vendor/xterm-addon-unicode-graphemes.js | 2 ++ .../tui/web/vendor/xterm-addon-unicode11.js | 2 -- .../dsl/jbang/core/commands/tui/TabRegistryTest.java | 3 +-- .../dsl/jbang/core/commands/tui/TuiIconsTest.java | 20 ++++++++++++-------- 8 files changed, 36 insertions(+), 29 deletions(-) diff --git a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/ActionsPopup.java b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/ActionsPopup.java index ca2caa18951d..817decfdaad7 100644 --- a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/ActionsPopup.java +++ b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/ActionsPopup.java @@ -1062,7 +1062,7 @@ class ActionsPopup { ? TuiIcons.menuItem(TuiIcons.KEYSTROKES, "Hide Keystrokes") : TuiIcons.menuItem(TuiIcons.KEYSTROKES, "Show Keystrokes"); String tapeLabel = tapeRecordingActive.get() - ? TuiIcons.menuItem(TuiIcons.STOP, "Stop Tape Recording") + ? TuiIcons.menuItem(TuiIcons.STOP_RECORD, "Stop Tape Recording") : TuiIcons.menuItem(TuiIcons.RECORD, "Start Tape Recording"); List<ListItem> items = new ArrayList<>(); items.add(ListItem.from(" ..")); diff --git a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/TuiIcons.java b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/TuiIcons.java index c8e6b7bd725a..f3ee3e6a738d 100644 --- a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/TuiIcons.java +++ b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/TuiIcons.java @@ -21,10 +21,10 @@ import java.util.List; /** * Single source of truth for emoji and symbolic icons used across the Camel TUI. * <p/> - * Tab/menu icons use plain 2-column-wide emoji without VS16 variation selectors (CAMEL-23818). TamboUI 0.5.0 measures a - * base glyph + VS16 sequence as 2 columns (tamboui/tamboui#388), but the xterm.js Unicode 11 width tables that the - * {@code --web} frontend uses treat VS16 as zero-width, so such a sequence still misaligns there. Doctor and legacy - * status glyphs may still use mixed-width symbols until migrated. + * Tab/menu icons are 2-column emoji. A text-default glyph such as the keyboard or the wastebasket must carry the VS16 + * variation selector (U+FE0F): TamboUI counts the bare glyph as 2 columns while terminals draw it in 1, and with VS16 + * both TamboUI 0.5.0 (tamboui/tamboui#388) and the xterm.js grapheme width tables used by {@code --web} agree on 2. + * Doctor and legacy status glyphs may still use mixed-width symbols until migrated. */ final class TuiIcons { @@ -62,18 +62,17 @@ final class TuiIcons { // memo (π) reads as "edit"; the letters glyph reads as changing the name static final String RENAME = "π€"; static final String DUPLICATE = "π"; - // the wastebasket (π) is a text-default glyph: TamboUI counts it as 2 columns while terminals draw it in 1 unless - // it carries VS16, which the --web frontend cannot measure (see the class javadoc), so use the cross-mark instead - static final String DELETE = "β"; + static final String DELETE = "ποΈ"; // ---- Actions menu ---- static final String GO_TO = "π"; static final String MESSAGE = "π©"; - static final String KEYSTROKES = "π€"; + static final String KEYSTROKES = "β¨οΈ"; static final String SLEEP = "π€"; static final String STOP = "π"; static final String QUIT = "πͺ"; - static final String RECORD = "π΄"; + static final String RECORD = "βΊοΈ"; + static final String STOP_RECORD = "βΉοΈ"; static final String DOCTOR = "π©Ί"; static final String RESET = "π"; static final String CLEAN = "π§Ή"; diff --git a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/resources/tui/web/index.html b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/resources/tui/web/index.html index 4362ef184eaf..c9b009654e7c 100644 --- a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/resources/tui/web/index.html +++ b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/resources/tui/web/index.html @@ -143,7 +143,7 @@ <script src="/vendor/xterm.js"></script> <script src="/vendor/xterm-addon-fit.js"></script> - <script src="/vendor/xterm-addon-unicode11.js"></script> + <script src="/vendor/xterm-addon-unicode-graphemes.js"></script> <script> (function () { 'use strict'; @@ -199,9 +199,12 @@ fitAddon = new FitAddon.FitAddon(); term.loadAddon(fitAddon); - var unicode11Addon = new Unicode11Addon.Unicode11Addon(); - term.loadAddon(unicode11Addon); - term.unicode.activeVersion = '11'; + // Unicode 15 width tables with grapheme clustering, so a text-default glyph followed by + // VS16 (e.g. the keyboard or record icons) takes the same 2 columns as in tamboui. + // The Unicode 11 addon treats VS16 as zero-width and misaligns everything after it. + var unicodeGraphemesAddon = new UnicodeGraphemesAddon.UnicodeGraphemesAddon(); + term.loadAddon(unicodeGraphemesAddon); + term.unicode.activeVersion = '15-graphemes'; term.open(document.getElementById('terminal-container')); function sendInit() { diff --git a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/resources/tui/web/vendor/LICENSE-xterm.txt b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/resources/tui/web/vendor/LICENSE-xterm.txt index 9cad528cd98c..b849cd9b852c 100644 --- a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/resources/tui/web/vendor/LICENSE-xterm.txt +++ b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/resources/tui/web/vendor/LICENSE-xterm.txt @@ -14,10 +14,12 @@ https://github.com/xtermjs/xterm.js/tree/master/addons/addon-fit Copyright (c) 2019, The xterm.js authors (https://github.com/xtermjs/xterm.js) -xterm-addon-unicode11 0.9.0 (xterm-addon-unicode11.js) -https://github.com/xtermjs/xterm.js/tree/master/addons/addon-unicode11 +xterm-addon-unicode-graphemes 0.4.0 (xterm-addon-unicode-graphemes.js) +https://github.com/xtermjs/xterm.js/tree/master/addons/addon-unicode-graphemes -Copyright (c) 2019, The xterm.js authors (https://github.com/xtermjs/xterm.js) +Copyright (c) 2023, The xterm.js authors (https://github.com/xtermjs/xterm.js) +The bundled Unicode property tables are generated from +https://github.com/PerBothner/unicode-properties (MIT). All are released under the MIT License: diff --git a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/resources/tui/web/vendor/xterm-addon-unicode-graphemes.js b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/resources/tui/web/vendor/xterm-addon-unicode-graphemes.js new file mode 100644 index 000000000000..b887d1b3f0d2 --- /dev/null +++ b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/resources/tui/web/vendor/xterm-addon-unicode-graphemes.js @@ -0,0 +1,2 @@ +!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.UnicodeGraphemesAddon=t():e.UnicodeGraphemesAddon=t()}(globalThis,(()=>(()=>{"use strict";var e={106:(e,t,s)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.UnicodeGraphemeProvider=void 0;const r=s(765),n=s(200);class i{constructor(e=!0){this.ambiguousCharsAreWide=!1,this.version=e?"15-graphemes":"15",this.handleGraph [...] +//# sourceMappingURL=addon-unicode-graphemes.js.map \ No newline at end of file diff --git a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/resources/tui/web/vendor/xterm-addon-unicode11.js b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/resources/tui/web/vendor/xterm-addon-unicode11.js deleted file mode 100644 index 941130afb3ad..000000000000 --- a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/resources/tui/web/vendor/xterm-addon-unicode11.js +++ /dev/null @@ -1,2 +0,0 @@ -!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.Unicode11Addon=t():e.Unicode11Addon=t()}(globalThis,(()=>(()=>{"use strict";var e={384:(e,t,s)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.UnicodeV11=void 0;const r=s(765),n=[[768,879],[1155,1161],[1425,1469],[1471,1471],[1473,1474],[1476,1477],[1479,1479],[1536,1541],[1552,1562],[1564,1564],[1611,1631],[1648,1648 [...] -//# sourceMappingURL=addon-unicode11.js.map \ No newline at end of file diff --git a/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/TabRegistryTest.java b/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/TabRegistryTest.java index 081443e3dcc5..83036314dc37 100644 --- a/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/TabRegistryTest.java +++ b/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/TabRegistryTest.java @@ -114,10 +114,9 @@ class TabRegistryTest { } @Test - void moreTabIconsAreTwoColumnsWideWithoutVariationSelector() { + void moreTabIconsAreTwoColumnsWide() { for (TabRegistry.MoreTab mt : registry.moreTabs()) { assertEquals(2, CharWidth.of(mt.icon()), "Icon should be 2 terminal columns wide: " + mt.icon()); - assertFalse(mt.icon().contains("\uFE0F"), "Icon should not contain VS16 variation selector: " + mt.icon()); } } diff --git a/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/TuiIconsTest.java b/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/TuiIconsTest.java index 0fa31d88d4bd..60613d46491a 100644 --- a/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/TuiIconsTest.java +++ b/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/TuiIconsTest.java @@ -16,18 +16,19 @@ */ package org.apache.camel.dsl.jbang.core.commands.tui; +import java.util.List; + import dev.tamboui.text.CharWidth; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; /** - * Validates {@link TuiIcons} primary tab emoji width (CAMEL-23818: no VS16 sequences, which the {@code --web} xterm.js - * width tables measure differently from TamboUI) and the mnemonic and runtime/platform icon helpers. More-submenu icons - * and labels are validated in {@link TabRegistryTest} where the {@link TabRegistry.MoreTab} records that own them are - * constructed. + * Validates {@link TuiIcons} emoji widths (CAMEL-23818: every icon has to take the 2 columns TamboUI reserves for it, + * which for text-default glyphs means carrying VS16) and the mnemonic and runtime/platform icon helpers. More-submenu + * icons and labels are validated in {@link TabRegistryTest} where the {@link TabRegistry.MoreTab} records that own them + * are constructed. */ class TuiIconsTest { @@ -60,9 +61,12 @@ class TuiIconsTest { } @Test - void primaryTabEmojisHaveNoVariationSelector() { - for (String icon : TuiIcons.PRIMARY_TAB_ICONS) { - assertFalse(icon.contains("\uFE0F"), "Icon should not contain VS16 variation selector: " + icon); + void textDefaultMenuEmojisCarryVs16AndAreTwoColumnsWide() { + // bare U+2328, U+23F9, U+23FA and U+1F5D1 render in one column on terminals; the VS16 sequence is what + // makes the terminal, the --web xterm.js tables and TamboUI agree on two + for (String icon : List.of(TuiIcons.KEYSTROKES, TuiIcons.RECORD, TuiIcons.STOP_RECORD, TuiIcons.DELETE)) { + assertTrue(icon.endsWith("\uFE0F"), "Icon should end with the VS16 variation selector: " + icon); + assertEquals(2, CharWidth.of(icon), "Icon should be 2 terminal columns wide: " + icon); } }
