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

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

commit c7319a4e1786630ad99f35da282f855016fd47c8
Author: Claus Ibsen <[email protected]>
AuthorDate: Thu Jun 4 19:39:22 2026 +0200

    CAMEL-23672: TUI History tab improvements - wider route column, step count 
in titles, waterfall selection highlight, pgup/pgdn jump, diagram hint in 
waterfall mode
    
    Co-Authored-By: Claude <[email protected]>
    Signed-off-by: Claus Ibsen <[email protected]>
---
 .../dsl/jbang/core/commands/tui/HistoryTab.java    | 66 ++++++++++++++++------
 1 file changed, 49 insertions(+), 17 deletions(-)

diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/HistoryTab.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/HistoryTab.java
index 52e1f4b69f86..53c3129d350c 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/HistoryTab.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/HistoryTab.java
@@ -123,13 +123,17 @@ class HistoryTab implements MonitorTab {
         if (ke.isPageUp() || ke.isKey(KeyCode.PAGE_UP)) {
             if (tracerActive && traceDetailView) {
                 if (showWaterfall) {
-                    waterfallScroll = Math.max(0, waterfallScroll - 10);
+                    for (int i = 0; i < 10; i++) {
+                        traceStepTableState.selectPrevious();
+                    }
                 } else {
                     traceDetailScroll = Math.max(0, traceDetailScroll - 5);
                 }
             } else {
                 if (showWaterfall) {
-                    waterfallScroll = Math.max(0, waterfallScroll - 10);
+                    for (int i = 0; i < 10; i++) {
+                        historyTableState.selectPrevious();
+                    }
                 } else {
                     historyDetailScroll = Math.max(0, historyDetailScroll - 5);
                 }
@@ -139,13 +143,18 @@ class HistoryTab implements MonitorTab {
         if (ke.isPageDown() || ke.isKey(KeyCode.PAGE_DOWN)) {
             if (tracerActive && traceDetailView) {
                 if (showWaterfall) {
-                    waterfallScroll += 10;
+                    List<TraceEntry> steps = 
getTraceSteps(traceSelectedExchangeId);
+                    for (int i = 0; i < 10; i++) {
+                        traceStepTableState.selectNext(steps.size());
+                    }
                 } else {
                     traceDetailScroll += 5;
                 }
             } else {
                 if (showWaterfall) {
-                    waterfallScroll += 10;
+                    for (int i = 0; i < 10; i++) {
+                        historyTableState.selectNext(historyEntries.size());
+                    }
                 } else {
                     historyDetailScroll += 5;
                 }
@@ -408,8 +417,8 @@ class HistoryTab implements MonitorTab {
             }
             hint(spans, "n", "description" + (showDescription ? " [on]" : ""));
             hint(spans, "g", "waterfall" + (showWaterfall ? " [on]" : ""));
+            hint(spans, "d", "diagram");
             if (!showWaterfall) {
-                hint(spans, "d", "diagram");
                 hint(spans, "p", "properties" + (showTraceProperties ? " [on]" 
: " [off]"));
                 hint(spans, "v", "variables" + (showTraceVariables ? " [on]" : 
" [off]"));
                 hint(spans, "h", "headers" + (showTraceHeaders ? " [on]" : " 
[off]"));
@@ -433,8 +442,8 @@ class HistoryTab implements MonitorTab {
             }
             hint(spans, "n", "description" + (showDescription ? " [on]" : ""));
             hint(spans, "g", "waterfall" + (showWaterfall ? " [on]" : ""));
+            hint(spans, "d", "diagram");
             if (!showWaterfall) {
-                hint(spans, "d", "diagram");
                 hint(spans, "p", "properties" + (showHistoryProperties ? " 
[on]" : " [off]"));
                 hint(spans, "v", "variables" + (showHistoryVariables ? " [on]" 
: " [off]"));
                 hint(spans, "h", "headers" + (showHistoryHeaders ? " [on]" : " 
[off]"));
@@ -586,7 +595,7 @@ class HistoryTab implements MonitorTab {
             rows.add(Row.from(
                     Cell.from(s.timestamp != null ? truncate(s.timestamp, 12) 
: ""),
                     Cell.from(Span.styled(
-                            s.routeId != null ? truncate(s.routeId, 20) : "",
+                            s.routeId != null ? truncate(s.routeId, 25) : "",
                             Style.EMPTY.fg(Color.CYAN))),
                     Cell.from(Span.styled(s.status, statusStyle)),
                     rightCell(s.elapsed + "ms", 10),
@@ -638,12 +647,14 @@ class HistoryTab implements MonitorTab {
                     entry.timestamp, entry.routeId, entry.nodeId, 
entry.processor, desc, entry.elapsed));
         }
 
-        String stepTitle = String.format(" Trace [%s] ", 
truncate(traceSelectedExchangeId, 30));
+        String stepTitle = String.format(" Trace [%s] — %d steps ", 
truncate(traceSelectedExchangeId, 30), steps.size());
         frame.renderStatefulWidget(
                 buildStepTable(rows, stepTitle, showDescription), 
chunks.get(0), traceStepTableState);
 
         if (showWaterfall) {
-            renderWaterfall(frame, chunks.get(2), 
steps.stream().map(WaterfallStep::fromTrace).toList());
+            Integer sel = traceStepTableState.selected();
+            renderWaterfall(frame, chunks.get(2), 
steps.stream().map(WaterfallStep::fromTrace).toList(),
+                    sel != null ? sel : -1);
         } else {
             renderTraceStepDetail(frame, chunks.get(2), steps);
         }
@@ -716,14 +727,20 @@ class HistoryTab implements MonitorTab {
         }
     }
 
-    private void renderWaterfall(Frame frame, Rect area, List<WaterfallStep> 
allSteps) {
+    private void renderWaterfall(Frame frame, Rect area, List<WaterfallStep> 
allSteps, int selectedIndex) {
         // Copy the elapsed from matching last entries onto first entries
         // (first entries have elapsed=0, the total is on the last entry)
         List<WaterfallStep> forward = new ArrayList<>();
-        for (WaterfallStep e : allSteps) {
+        // Map original allSteps index to forward index for selection highlight
+        int selectedForwardIndex = -1;
+        for (int idx = 0; idx < allSteps.size(); idx++) {
+            WaterfallStep e = allSteps.get(idx);
             if ("<--".equals(e.direction)) {
                 continue;
             }
+            if (idx == selectedIndex) {
+                selectedForwardIndex = forward.size();
+            }
             if (e.first) {
                 long totalElapsed = e.elapsed;
                 for (WaterfallStep other : allSteps) {
@@ -789,11 +806,20 @@ class HistoryTab implements MonitorTab {
 
         int barMaxWidth = Math.max(10, inner.width() - labelWidth - 12);
 
+        // Auto-scroll to keep selected step visible
+        if (selectedForwardIndex >= 0) {
+            if (selectedForwardIndex < waterfallScroll) {
+                waterfallScroll = selectedForwardIndex;
+            } else if (selectedForwardIndex >= waterfallScroll + visibleLines) 
{
+                waterfallScroll = selectedForwardIndex - visibleLines + 1;
+            }
+        }
+
         int end = Math.min(waterfallScroll + visibleLines, forward.size());
         List<Line> lines = new ArrayList<>();
         for (int i = waterfallScroll; i < end; i++) {
             lines.add(renderWaterfallStep(forward.get(i), labelWidth, 
barMaxWidth,
-                    maxElapsed, minDuration, maxDuration));
+                    maxElapsed, minDuration, maxDuration, i == 
selectedForwardIndex));
         }
 
         List<Rect> hChunks = Layout.horizontal()
@@ -813,7 +839,8 @@ class HistoryTab implements MonitorTab {
 
     private static Line renderWaterfallStep(
             WaterfallStep entry, int labelWidth, int maxBarWidth,
-            long maxElapsed, long minDuration, long maxDuration) {
+            long maxElapsed, long minDuration, long maxDuration, boolean 
selected) {
+        String indicator = selected ? ">> " : "   ";
         String indent = "  ".repeat(entry.nodeLevel);
         String label = indent + entry.label();
         if (label.length() > labelWidth) {
@@ -832,8 +859,11 @@ class HistoryTab implements MonitorTab {
         String durationStr = entry.elapsed + "ms";
         int pad = Math.max(1, 8 - durationStr.length());
 
+        Style labelStyle = selected ? Style.EMPTY.fg(Color.CYAN).bold() : 
Style.EMPTY.fg(Color.CYAN);
+
         return Line.from(
-                Span.styled(label, Style.EMPTY.fg(Color.CYAN)),
+                Span.styled(indicator, Style.EMPTY.fg(Color.YELLOW).bold()),
+                Span.styled(label, labelStyle),
                 Span.styled(bar, bandStyle),
                 Span.raw(" ".repeat(pad)),
                 Span.styled(durationStr, isRoute ? Style.EMPTY.dim() : 
Style.EMPTY.fg(Color.WHITE).bold()));
@@ -875,7 +905,9 @@ class HistoryTab implements MonitorTab {
                 buildStepTable(rows, historyTitle, showDescription), 
chunks.get(0), historyTableState);
 
         if (showWaterfall) {
-            renderWaterfall(frame, chunks.get(2), 
current.stream().map(WaterfallStep::fromHistory).toList());
+            Integer sel = historyTableState.selected();
+            renderWaterfall(frame, chunks.get(2), 
current.stream().map(WaterfallStep::fromHistory).toList(),
+                    sel != null ? sel : -1);
         } else {
             renderHistoryDetail(frame, chunks.get(2), current);
         }
@@ -993,7 +1025,7 @@ class HistoryTab implements MonitorTab {
         return Row.from(
                 Cell.from(Span.styled(direction, dirStyle)),
                 Cell.from(timestamp != null ? truncate(timestamp, 12) : ""),
-                Cell.from(Span.styled(routeId != null ? truncate(routeId, 20) 
: "", Style.EMPTY.fg(Color.CYAN))),
+                Cell.from(Span.styled(routeId != null ? truncate(routeId, 25) 
: "", Style.EMPTY.fg(Color.CYAN))),
                 Cell.from(nodeId != null ? truncate(nodeId, 15) : ""),
                 Cell.from(display),
                 rightCell(elapsedStr, 10));
@@ -1043,7 +1075,7 @@ class HistoryTab implements MonitorTab {
         }
 
         List<Span> spans = new ArrayList<>();
-        spans.add(Span.raw(" History of last completed ("));
+        spans.add(Span.raw(" History of last completed — " + entries.size() + 
" steps ("));
         boolean failed = last.failed;
         spans.add(Span.styled("status:" + (failed ? "failed" : "success"),
                 failed ? Style.EMPTY.fg(Color.LIGHT_RED).bold() : 
Style.EMPTY.fg(Color.GREEN).bold()));

Reply via email to