This is an automated email from the ASF dual-hosted git repository.

davsclaus pushed a commit to branch fix/CAMEL-23802
in repository https://gitbox.apache.org/repos/asf/camel.git

commit 3241898f8675cd1759c399dbfadcdf0c766003a9
Author: Claus Ibsen <[email protected]>
AuthorDate: Fri Jun 19 10:09:55 2026 +0200

    CAMEL-23802: Remove TamboUI hacks when upgrading to 0.4
    
    Upgrade TamboUI from 0.3.0 to 0.4.0 and remove workarounds that are
    no longer needed:
    
    - Delete custom MirroredSparkline (456 lines) and use upstream DualSparkline
    - Remove reflection hack in TuiBackendHelper for JLineBackend(Terminal) 
constructor
    - Remove class preloading workaround for JVM shutdown ClassNotFoundException
    
    Signed-off-by: Claus Ibsen <[email protected]>
    Co-Authored-By: Claude Opus 4.6 <[email protected]>
    Signed-off-by: Claus Ibsen <[email protected]>
---
 .../jbang/core/commands/tui/CamelCatalogTui.java   |   1 -
 .../dsl/jbang/core/commands/tui/CamelMonitor.java  |   1 -
 .../jbang/core/commands/tui/CircuitBreakerTab.java |   4 +-
 .../dsl/jbang/core/commands/tui/EndpointsTab.java  |  26 +-
 .../jbang/core/commands/tui/MirroredSparkline.java | 456 ---------------------
 .../jbang/core/commands/tui/TuiBackendHelper.java  |  21 +-
 .../dsl/jbang/core/commands/tui/TuiHelper.java     |  13 -
 parent/pom.xml                                     |   2 +-
 8 files changed, 13 insertions(+), 511 deletions(-)

diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/CamelCatalogTui.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/CamelCatalogTui.java
index 40e20580ae6c..9710a6506445 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/CamelCatalogTui.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/CamelCatalogTui.java
@@ -92,7 +92,6 @@ public class CamelCatalogTui extends CamelCommand {
     public Integer doCall() throws Exception {
         // to make ServiceLoader work with tamboui for downloaded JARs
         Thread.currentThread().setContextClassLoader(classLoader);
-        TuiHelper.preloadClasses(classLoader);
 
         loadCatalog();
 
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 452c15c3324b..d1595e561505 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
@@ -265,7 +265,6 @@ public class CamelMonitor extends CamelCommand {
 
         // to make ServiceLoader work with tamboui for downloaded JARs
         Thread.currentThread().setContextClassLoader(classLoader);
-        TuiHelper.preloadClasses(classLoader);
 
         // Create shared context and tab instances
         ctx = new MonitorContext(data, infraData);
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/CircuitBreakerTab.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/CircuitBreakerTab.java
index ac683ae3563c..af0da28d17a2 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/CircuitBreakerTab.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/CircuitBreakerTab.java
@@ -35,6 +35,7 @@ import dev.tamboui.widgets.block.Block;
 import dev.tamboui.widgets.block.BorderType;
 import dev.tamboui.widgets.block.Title;
 import dev.tamboui.widgets.paragraph.Paragraph;
+import dev.tamboui.widgets.sparkline.DualSparkline;
 import dev.tamboui.widgets.table.Cell;
 import dev.tamboui.widgets.table.Row;
 import dev.tamboui.widgets.table.Table;
@@ -376,11 +377,12 @@ class CircuitBreakerTab implements MonitorTab {
                 Span.raw(String.format(" ok:%-4d ", curSuccess)),
                 Span.styled("▬", Style.EMPTY.fg(Color.LIGHT_RED)),
                 Span.raw(String.format(" fail:%-4d msg/s", curFail)));
-        frame.renderWidget(MirroredSparkline.builder()
+        frame.renderWidget(DualSparkline.builder()
                 .topData(successArr)
                 .bottomData(failArr)
                 .topStyle(Style.EMPTY.fg(Color.GREEN))
                 .bottomStyle(Style.EMPTY.fg(Color.LIGHT_RED))
+                .showYAxis(true)
                 .xLabels("-60s", "-45s", "-30s", "-15s", "now")
                 .block(Block.builder().borderType(BorderType.ROUNDED)
                         .title(Title.from(chartTitle)).build())
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/EndpointsTab.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/EndpointsTab.java
index 3d97c4f23bab..14b08694517c 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/EndpointsTab.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/EndpointsTab.java
@@ -37,6 +37,7 @@ import dev.tamboui.widgets.block.Block;
 import dev.tamboui.widgets.block.BorderType;
 import dev.tamboui.widgets.block.Title;
 import dev.tamboui.widgets.paragraph.Paragraph;
+import dev.tamboui.widgets.sparkline.DualSparkline;
 import dev.tamboui.widgets.table.Cell;
 import dev.tamboui.widgets.table.Row;
 import dev.tamboui.widgets.table.Table;
@@ -447,11 +448,12 @@ class EndpointsTab implements MonitorTab {
                 Span.raw(String.format(" out:%-4d msg/s", curOut)));
 
         Rect rightArea = hSplit.get(1);
-        frame.renderWidget(MirroredSparkline.builder()
+        frame.renderWidget(DualSparkline.builder()
                 .topData(inArr)
                 .bottomData(outArr)
                 .topStyle(Style.EMPTY.fg(Color.ansi(AnsiColor.BRIGHT_GREEN)))
                 .bottomStyle(Style.EMPTY.fg(Color.CYAN))
+                .showYAxis(true)
                 .xLabels("-" + renderPoints + "s", "-" + (renderPoints * 3 / 
4) + "s",
                         "-" + (renderPoints / 2) + "s", "-" + (renderPoints / 
4) + "s", "now")
                 .block(Block.builder().borderType(BorderType.ROUNDED)
@@ -556,11 +558,12 @@ class EndpointsTab implements MonitorTab {
                 Span.styled("▬", Style.EMPTY.fg(Color.CYAN)),
                 Span.raw(String.format(" out:%-4d msg/s", curOut)));
 
-        frame.renderWidget(MirroredSparkline.builder()
+        frame.renderWidget(DualSparkline.builder()
                 .topData(inArr)
                 .bottomData(outArr)
                 .topStyle(Style.EMPTY.fg(Color.ansi(AnsiColor.BRIGHT_GREEN)))
                 .bottomStyle(Style.EMPTY.fg(Color.CYAN))
+                .showYAxis(true)
                 .xLabels("-" + renderPoints + "s", "-" + (renderPoints * 3 / 
4) + "s",
                         "-" + (renderPoints / 2) + "s", "-" + (renderPoints / 
4) + "s", "now")
                 .block(Block.builder().borderType(BorderType.ROUNDED)
@@ -594,12 +597,12 @@ class EndpointsTab implements MonitorTab {
                 Span.styled("▬", Style.EMPTY.fg(Color.MAGENTA)),
                 Span.raw(String.format(" out:%-8s avg body", 
sizeToString(curOut))));
 
-        frame.renderWidget(MirroredSparkline.builder()
+        frame.renderWidget(DualSparkline.builder()
                 .topData(inArr)
                 .bottomData(outArr)
                 .topStyle(Style.EMPTY.fg(Color.YELLOW))
                 .bottomStyle(Style.EMPTY.fg(Color.MAGENTA))
-                .yLabelFormatter(EndpointsTab::sizeToYLabel)
+                .showYAxis(true)
                 .xLabels("-" + renderPoints + "s", "-" + (renderPoints * 3 / 
4) + "s",
                         "-" + (renderPoints / 2) + "s", "-" + (renderPoints / 
4) + "s", "now")
                 .block(Block.builder().borderType(BorderType.ROUNDED)
@@ -611,21 +614,6 @@ class EndpointsTab implements MonitorTab {
         return value != null ? value : 0L;
     }
 
-    private static String sizeToYLabel(long size) {
-        if (size <= 0) {
-            return "0 B";
-        }
-        if (size < 1024) {
-            return size + "B";
-        } else if (size < 1024 * 1024) {
-            long kb = size / 1024;
-            return kb + "KB";
-        } else {
-            long mb = size / (1024 * 1024);
-            return mb + "MB";
-        }
-    }
-
     static String sizeToString(long size) {
         if (size < 0) {
             return "-";
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/MirroredSparkline.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/MirroredSparkline.java
deleted file mode 100644
index 30b8ad52e573..000000000000
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/MirroredSparkline.java
+++ /dev/null
@@ -1,456 +0,0 @@
-/*
- * 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.util.List;
-import java.util.function.LongFunction;
-
-import dev.tamboui.buffer.Buffer;
-import dev.tamboui.layout.Rect;
-import dev.tamboui.style.Style;
-import dev.tamboui.widget.Widget;
-import dev.tamboui.widgets.block.Block;
-import dev.tamboui.widgets.sparkline.Sparkline;
-
-/**
- * A mirrored sparkline widget that displays two time-series datasets as 
vertical bars growing in opposite directions
- * from a shared centre axis.
- * <p>
- * The top series renders as bars growing <em>upward</em> from the centre; the 
bottom series renders as bars growing
- * <em>downward</em> from the centre. Sub-pixel resolution is achieved using 
Unicode block characters (▁▂▃▄▅▆▇█), giving
- * smooth visual gradation within a single character row. This layout matches 
the style of macOS Activity Monitor's
- * network and disk activity graphs.
- * <p>
- * Example usage:
- *
- * <pre>{@code
- * MirroredSparkline chart = MirroredSparkline.builder()
- *         .topData(inRates)
- *         .bottomData(outRates)
- *         .topStyle(Style.EMPTY.fg(Color.GREEN))
- *         .bottomStyle(Style.EMPTY.fg(Color.BLUE))
- *         .xLabels("-60s", "-45s", "-30s", "-15s", "now")
- *         .block(Block.builder().borderType(BorderType.ROUNDED)
- *                 .title(Title.from("In / Out  msg/s")).build())
- *         .build();
- * }</pre>
- *
- * <h2>Differences from {@link Sparkline}</h2>
- * <table>
- * <caption>Feature comparison between Sparkline and 
MirroredSparkline</caption>
- * <tr>
- * <th></th>
- * <th>{@code Sparkline}</th>
- * <th>{@code MirroredSparkline}</th>
- * </tr>
- * <tr>
- * <td>Series</td>
- * <td>1</td>
- * <td>2 (top + bottom)</td>
- * </tr>
- * <tr>
- * <td>Growth direction</td>
- * <td>always upward from bottom row</td>
- * <td>top grows up, bottom grows down, from a shared centre separator row</td>
- * </tr>
- * <tr>
- * <td>Height</td>
- * <td>1 row (fixed at bottom of area)</td>
- * <td>fills the full area height</td>
- * </tr>
- * <tr>
- * <td>Y-axis labels</td>
- * <td>none</td>
- * <td>optional: max / 0 / max at top, centre, and bottom rows</td>
- * </tr>
- * <tr>
- * <td>X-axis labels</td>
- * <td>none</td>
- * <td>optional label row rendered below the chart body</td>
- * </tr>
- * <tr>
- * <td>BarSet</td>
- * <td>yes ({@link Sparkline.BarSet})</td>
- * <td>yes (reuses {@link Sparkline.BarSet})</td>
- * </tr>
- * </table>
- *
- * <p>
- * This class is intended for contribution to the TamboUI project as a 
first-class widget under
- * {@code dev.tamboui.widgets.sparkline}. The package and license header would 
change accordingly upon contribution.
- */
-public final class MirroredSparkline implements Widget {
-
-    private static final int Y_LABEL_WIDTH = 4;
-    private static final Style DIM = Style.EMPTY.dim();
-    private static final String CENTRE_SEPARATOR = "─";
-
-    private final long[] topData;
-    private final long[] bottomData;
-    private final Style topStyle;
-    private final Style bottomStyle;
-    private final Long max;
-    private final Block block;
-    private final Sparkline.BarSet barSet;
-    private final boolean showYAxis;
-    private final String[] xLabels;
-    private final LongFunction<String> yLabelFormatter;
-
-    private MirroredSparkline(Builder builder) {
-        this.topData = builder.topData;
-        this.bottomData = builder.bottomData;
-        this.topStyle = builder.topStyle;
-        this.bottomStyle = builder.bottomStyle;
-        this.max = builder.max;
-        this.block = builder.block;
-        this.barSet = builder.barSet;
-        this.showYAxis = builder.showYAxis;
-        this.xLabels = builder.xLabels;
-        this.yLabelFormatter = builder.yLabelFormatter;
-    }
-
-    /**
-     * Creates a new builder.
-     *
-     * @return a new Builder
-     */
-    public static Builder builder() {
-        return new Builder();
-    }
-
-    @Override
-    public void render(Rect area, Buffer buffer) {
-        if (area.isEmpty()) {
-            return;
-        }
-
-        Rect inner = area;
-        if (block != null) {
-            block.render(area, buffer);
-            inner = block.inner(area);
-        }
-
-        if (inner.isEmpty()) {
-            return;
-        }
-
-        int innerH = inner.height();
-        int innerW = inner.width();
-
-        boolean hasXAxis = xLabels != null && xLabels.length > 0;
-        // Reserve one row at the bottom for x-axis labels when configured
-        int chartBodyRows = hasXAxis ? Math.max(2, innerH - 1) : innerH;
-        int halfH = Math.max(1, (chartBodyRows - 1) / 2);
-        int centerRow = halfH;
-
-        int yLabelW = showYAxis ? Y_LABEL_WIDTH : 0;
-        int chartW = Math.max(1, innerW - yLabelW);
-
-        int dataLen = Math.max(topData.length, bottomData.length);
-        int ticks = Math.min(dataLen, chartW);
-
-        long effectiveMax = computeMax();
-
-        // --- Bar rows ---
-        for (int r = 0; r < chartBodyRows; r++) {
-            int y = inner.y() + r;
-
-            if (showYAxis) {
-                String label;
-                if (r == 0 || r == chartBodyRows - 1) {
-                    label = formatYLabel(effectiveMax);
-                } else if (r == centerRow) {
-                    label = "   0";
-                } else {
-                    label = "    ";
-                }
-                buffer.setString(inner.x(), y, label, DIM);
-            }
-
-            for (int t = 0; t < ticks; t++) {
-                int x = inner.x() + yLabelW + t;
-                int dataIdx = dataLen - ticks + t;
-                long topVal = dataIdx >= 0 && dataIdx < topData.length ? 
topData[dataIdx] : 0;
-                long botVal = dataIdx >= 0 && dataIdx < bottomData.length ? 
bottomData[dataIdx] : 0;
-
-                String ch;
-                Style style;
-
-                if (r < centerRow) {
-                    // Top series: bars grow upward from the centre
-                    int rowOffset = centerRow - 1 - r; // 0 at the row nearest 
the centre
-                    long barPx = topVal * halfH * 8 / effectiveMax;
-                    long threshold = (long) rowOffset * 8;
-                    if (barPx >= threshold + 8) {
-                        ch = barSet.full();
-                    } else if (barPx > threshold) {
-                        ch = barSet.symbolForLevel((double) (barPx - 
threshold) / 8.0);
-                    } else {
-                        ch = barSet.empty();
-                    }
-                    style = topStyle;
-                } else if (r == centerRow) {
-                    ch = CENTRE_SEPARATOR;
-                    style = DIM;
-                } else {
-                    // Bottom series: bars grow downward from the centre
-                    int rowOffset = r - centerRow - 1; // 0 at the row nearest 
the centre
-                    long barPx = botVal * halfH * 8 / effectiveMax;
-                    long threshold = (long) rowOffset * 8;
-                    if (barPx >= threshold + 8) {
-                        ch = barSet.full();
-                    } else if (barPx > threshold) {
-                        ch = barSet.symbolForLevel((double) (barPx - 
threshold) / 8.0);
-                    } else {
-                        ch = barSet.empty();
-                    }
-                    style = bottomStyle;
-                }
-
-                buffer.setString(x, y, ch, style);
-            }
-        }
-
-        // --- X-axis label row ---
-        if (hasXAxis) {
-            int xAxisY = inner.y() + chartBodyRows;
-            char[] xChars = new char[chartW];
-            for (int i = 0; i < chartW; i++) {
-                xChars[i] = ' ';
-            }
-            // Distribute labels evenly across the tick range
-            for (int li = 0; li < xLabels.length; li++) {
-                String lbl = xLabels[li];
-                double fraction = xLabels.length > 1 ? (double) li / 
(xLabels.length - 1) : 0;
-                int col = (int) Math.round(fraction * (ticks - 1));
-                // Right-align the last label so it doesn't run past the right 
edge
-                int start = li == xLabels.length - 1
-                        ? Math.max(0, col - lbl.length() + 1)
-                        : col;
-                for (int k = 0; k < lbl.length() && start + k < chartW; k++) {
-                    xChars[start + k] = lbl.charAt(k);
-                }
-            }
-            if (showYAxis) {
-                buffer.setString(inner.x(), xAxisY, " ".repeat(yLabelW), DIM);
-            }
-            buffer.setString(inner.x() + yLabelW, xAxisY, new String(xChars), 
DIM);
-        }
-    }
-
-    private String formatYLabel(long value) {
-        if (yLabelFormatter != null) {
-            String s = yLabelFormatter.apply(value);
-            if (s.length() >= Y_LABEL_WIDTH) {
-                return s.substring(0, Y_LABEL_WIDTH);
-            }
-            return " ".repeat(Y_LABEL_WIDTH - s.length()) + s;
-        }
-        return value > 9999 ? "999+" : String.format("%4d", value);
-    }
-
-    private long computeMax() {
-        if (max != null) {
-            return Math.max(1, max);
-        }
-        long m = 1;
-        for (long v : topData) {
-            m = Math.max(m, v);
-        }
-        for (long v : bottomData) {
-            m = Math.max(m, v);
-        }
-        return m;
-    }
-
-    /**
-     * Builder for {@link MirroredSparkline}.
-     */
-    public static final class Builder {
-        private long[] topData = new long[0];
-        private long[] bottomData = new long[0];
-        private Style topStyle = Style.EMPTY;
-        private Style bottomStyle = Style.EMPTY;
-        private Long max;
-        private Block block;
-        private Sparkline.BarSet barSet = Sparkline.BarSet.NINE_LEVELS;
-        private boolean showYAxis = true;
-        private String[] xLabels;
-        private LongFunction<String> yLabelFormatter;
-
-        private Builder() {
-        }
-
-        /**
-         * Sets the top series data (bars grow upward from centre).
-         *
-         * @param  data the data values
-         * @return      this builder
-         */
-        public Builder topData(long... data) {
-            this.topData = data != null ? data.clone() : new long[0];
-            return this;
-        }
-
-        /**
-         * Sets the top series data from a list (bars grow upward from centre).
-         *
-         * @param  data the data values
-         * @return      this builder
-         */
-        public Builder topData(List<Long> data) {
-            this.topData = data == null ? new long[0] : 
data.stream().mapToLong(Long::longValue).toArray();
-            return this;
-        }
-
-        /**
-         * Sets the bottom series data (bars grow downward from centre).
-         *
-         * @param  data the data values
-         * @return      this builder
-         */
-        public Builder bottomData(long... data) {
-            this.bottomData = data != null ? data.clone() : new long[0];
-            return this;
-        }
-
-        /**
-         * Sets the bottom series data from a list (bars grow downward from 
centre).
-         *
-         * @param  data the data values
-         * @return      this builder
-         */
-        public Builder bottomData(List<Long> data) {
-            this.bottomData = data == null ? new long[0] : 
data.stream().mapToLong(Long::longValue).toArray();
-            return this;
-        }
-
-        /**
-         * Sets the style for the top series bars.
-         *
-         * @param  style the style
-         * @return       this builder
-         */
-        public Builder topStyle(Style style) {
-            this.topStyle = style != null ? style : Style.EMPTY;
-            return this;
-        }
-
-        /**
-         * Sets the style for the bottom series bars.
-         *
-         * @param  style the style
-         * @return       this builder
-         */
-        public Builder bottomStyle(Style style) {
-            this.bottomStyle = style != null ? style : Style.EMPTY;
-            return this;
-        }
-
-        /**
-         * Sets an explicit maximum value for scaling both series. When not 
set the maximum value across both datasets
-         * is used.
-         *
-         * @param  max the maximum value
-         * @return     this builder
-         */
-        public Builder max(long max) {
-            this.max = max;
-            return this;
-        }
-
-        /**
-         * Clears an explicit maximum, reverting to auto-scaling from the data.
-         *
-         * @return this builder
-         */
-        public Builder autoMax() {
-            this.max = null;
-            return this;
-        }
-
-        /**
-         * Wraps the chart in a block (border + optional title).
-         *
-         * @param  block the block
-         * @return       this builder
-         */
-        public Builder block(Block block) {
-            this.block = block;
-            return this;
-        }
-
-        /**
-         * Sets the bar symbol set used for sub-pixel rendering.
-         *
-         * @param  barSet the bar set
-         * @return        this builder
-         */
-        public Builder barSet(Sparkline.BarSet barSet) {
-            this.barSet = barSet != null ? barSet : 
Sparkline.BarSet.NINE_LEVELS;
-            return this;
-        }
-
-        /**
-         * Controls whether a Y-axis label column is rendered on the left. 
Shows the shared maximum at the top and
-         * bottom rows and {@code 0} at the centre row. Defaults to {@code 
true}.
-         *
-         * @param  show whether to show the y-axis labels
-         * @return      this builder
-         */
-        public Builder showYAxis(boolean show) {
-            this.showYAxis = show;
-            return this;
-        }
-
-        /**
-         * Sets the x-axis labels rendered as a single row below the chart 
body. Labels are distributed evenly across
-         * the data range. The last label is right-aligned at its position so 
it does not overflow the right edge.
-         * <p>
-         * Example: {@code xLabels("-60s", "-45s", "-30s", "-15s", "now")}
-         *
-         * @param  labels the labels, distributed left-to-right
-         * @return        this builder
-         */
-        public Builder xLabels(String... labels) {
-            this.xLabels = labels != null ? labels.clone() : null;
-            return this;
-        }
-
-        /**
-         * Sets a custom formatter for Y-axis max labels. The function 
receives the max value and should return a short
-         * string (up to 4 chars). When not set, values are formatted as 
integers with {@code 999+} for values above
-         * 9999.
-         *
-         * @param  formatter the formatter function
-         * @return           this builder
-         */
-        public Builder yLabelFormatter(LongFunction<String> formatter) {
-            this.yLabelFormatter = formatter;
-            return this;
-        }
-
-        /**
-         * Builds the widget.
-         *
-         * @return a new MirroredSparkline
-         */
-        public MirroredSparkline build() {
-            return new MirroredSparkline(this);
-        }
-    }
-}
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/TuiBackendHelper.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/TuiBackendHelper.java
index b8f38e7a358f..748ff2980acd 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/TuiBackendHelper.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/TuiBackendHelper.java
@@ -16,10 +16,7 @@
  */
 package org.apache.camel.dsl.jbang.core.commands.tui;
 
-import java.lang.reflect.Constructor;
-
 import dev.tamboui.backend.jline3.JLineBackend;
-import dev.tamboui.terminal.Backend;
 import dev.tamboui.tui.TuiConfig;
 import dev.tamboui.tui.TuiRunner;
 import org.apache.camel.dsl.jbang.core.common.EnvironmentHelper;
@@ -33,23 +30,9 @@ final class TuiBackendHelper {
     static TuiRunner createTuiRunner() throws Exception {
         Terminal activeTerminal = EnvironmentHelper.getActiveTerminal();
         if (activeTerminal != null) {
-            Backend backend = createBackendForTerminal(activeTerminal);
-            if (backend != null) {
-                return 
TuiRunner.create(TuiConfig.builder().backend(backend).mouseCapture(true).build());
-            }
+            JLineBackend backend = new JLineBackend(activeTerminal);
+            return 
TuiRunner.create(TuiConfig.builder().backend(backend).mouseCapture(true).build());
         }
         return 
TuiRunner.create(TuiConfig.builder().mouseCapture(true).build());
     }
-
-    private static Backend createBackendForTerminal(Terminal terminal) {
-        try {
-            Constructor<JLineBackend> ctor = 
JLineBackend.class.getDeclaredConstructor(Terminal.class);
-            return ctor.newInstance(terminal);
-        } catch (NoSuchMethodException e) {
-            // JLineBackend(Terminal) not available in this version, fall back
-            return null;
-        } catch (Exception e) {
-            return null;
-        }
-    }
 }
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/TuiHelper.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/TuiHelper.java
index 3fe39d4bb64c..d7335aab1f9e 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/TuiHelper.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/TuiHelper.java
@@ -31,7 +31,6 @@ import dev.tamboui.text.Span;
 import org.apache.camel.dsl.jbang.core.common.ProcessHelper;
 import org.apache.camel.support.PatternHelper;
 import org.apache.camel.util.FileUtil;
-import org.apache.camel.util.ObjectHelper;
 import org.apache.camel.util.json.JsonObject;
 import org.apache.camel.util.json.Jsoner;
 
@@ -43,18 +42,6 @@ final class TuiHelper {
     private TuiHelper() {
     }
 
-    /**
-     * Eagerly load classes used by the TUI input reader daemon thread and 
picocli post-processing. Without this, during
-     * JVM shutdown the classloader may already be closing while the input 
reader thread is still trying to load these
-     * classes lazily — causing ClassNotFoundException stack traces on exit.
-     */
-    static void preloadClasses(ClassLoader cl) {
-        ObjectHelper.loadClass("dev.tamboui.tui.event.KeyModifiers", cl);
-        ObjectHelper.loadClass("dev.tamboui.tui.event.KeyEvent", cl);
-        ObjectHelper.loadClass("dev.tamboui.tui.event.KeyCode", cl);
-        ObjectHelper.loadClass("picocli.CommandLine$IExitCodeGenerator", cl);
-    }
-
     /**
      * Find PIDs of running Camel integrations matching the given name pattern.
      */
diff --git a/parent/pom.xml b/parent/pom.xml
index d651ee9c7c0b..b2e7388b6d13 100644
--- a/parent/pom.xml
+++ b/parent/pom.xml
@@ -504,7 +504,7 @@
         
<swagger-request-validator-version>2.46.1</swagger-request-validator-version>
         <stringtemplate-version>4.3.4</stringtemplate-version>
         <tahu-version>1.0.19</tahu-version>
-        <tamboui-version>0.3.0</tamboui-version>
+        <tamboui-version>0.4.0</tamboui-version>
         <testcontainers-version>2.0.5</testcontainers-version>
         <thymeleaf-version>3.1.5.RELEASE</thymeleaf-version>
         <tika-version>3.3.1</tika-version>

Reply via email to