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 29c99a449ef9 CAMEL-23420: Add diagram modes and fullscreen scrolling 
to TUI monitor (#23192)
29c99a449ef9 is described below

commit 29c99a449ef9f2f1e0eb9617828f136ae1a60c7e
Author: Claus Ibsen <[email protected]>
AuthorDate: Wed May 13 15:46:35 2026 +0200

    CAMEL-23420: Add diagram modes and fullscreen scrolling to TUI monitor 
(#23192)
    
    * CAMEL-23420: Add text diagram mode (Shift+D) to TUI monitor
    
    Co-Authored-By: Claude Opus 4.7 <[email protected]>
    
    * CAMEL-23420: Fullscreen text diagram with scrollbars in TUI monitor
    
    Co-Authored-By: Claude Opus 4.7 <[email protected]>
    
    * CAMEL-23420: Show matching close key (d/D) in diagram footer
    
    Co-Authored-By: Claude Opus 4.7 <[email protected]>
    
    * CAMEL-23420: Show live route info header in fullscreen text diagram
    
    Co-Authored-By: Claude Opus 4.7 <[email protected]>
    
    * CAMEL-23420: Hide diagram block title in fullscreen text mode
    
    Co-Authored-By: Claude Opus 4.7 <[email protected]>
    
    * CAMEL-23420: Fullscreen image diagram with scrollbars and flicker fix
    
    Co-Authored-By: Claude Opus 4.7 <[email protected]>
    
    * CAMEL-23420: Add Home/End keys for diagram top/bottom scrolling
    
    Co-Authored-By: Claude Opus 4.7 <[email protected]>
    
    ---------
    
    Co-authored-by: Claude Opus 4.7 <[email protected]>
---
 .../dsl/jbang/core/commands/tui/CamelMonitor.java  | 387 +++++++++++++++++----
 1 file changed, 315 insertions(+), 72 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 7fcb1d958e2f..2c20cbb0a26f 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
@@ -66,6 +66,8 @@ import dev.tamboui.widgets.block.Block;
 import dev.tamboui.widgets.block.BorderType;
 import dev.tamboui.widgets.gauge.Gauge;
 import dev.tamboui.widgets.paragraph.Paragraph;
+import dev.tamboui.widgets.scrollbar.Scrollbar;
+import dev.tamboui.widgets.scrollbar.ScrollbarState;
 import dev.tamboui.widgets.table.Cell;
 import dev.tamboui.widgets.table.Row;
 import dev.tamboui.widgets.table.Table;
@@ -169,11 +171,20 @@ public class CamelMonitor extends CamelCommand {
 
     // Diagram state
     private boolean showDiagram;
+    private boolean diagramTextMode;
     private List<String> diagramLines = Collections.emptyList();
     private int diagramScroll;
+    private int diagramScrollX;
+    private final ScrollbarState diagramVScrollState = new ScrollbarState();
+    private final ScrollbarState diagramHScrollState = new ScrollbarState();
     private String diagramRouteId;
     private ImageData diagramImageData;
+    private ImageData diagramFullImageData;
     private ImageProtocol diagramProtocol;
+    private int diagramCropX = -1;
+    private int diagramCropY = -1;
+    private int diagramCropW = -1;
+    private int diagramCropH = -1;
 
     private volatile long lastRefresh;
 
@@ -210,13 +221,13 @@ public class CamelMonitor extends CamelCommand {
 
     private boolean handleEvent(Event event, TuiRunner runner) {
         if (event instanceof MouseEvent me) {
-            if (showDiagram && diagramImageData == null && 
tabsState.selected() == TAB_ROUTES) {
+            if (showDiagram && tabsState.selected() == TAB_ROUTES) {
                 if (me.kind() == MouseEventKind.SCROLL_UP) {
                     diagramScroll = Math.max(0, diagramScroll - 3);
                     return true;
                 }
                 if (me.kind() == MouseEventKind.SCROLL_DOWN) {
-                    diagramScroll = Math.min(Math.max(0, diagramLines.size() - 
1), diagramScroll + 3);
+                    diagramScroll += 3;
                     return true;
                 }
             }
@@ -227,6 +238,7 @@ public class CamelMonitor extends CamelCommand {
                 if (showDiagram) {
                     showDiagram = false;
                     diagramImageData = null;
+                    diagramFullImageData = null;
                     return true;
                 }
                 // If in a detail tab, go back to overview first
@@ -278,7 +290,7 @@ public class CamelMonitor extends CamelCommand {
 
             // Navigation (all tabs)
             if (ke.isUp()) {
-                if (showDiagram && diagramImageData == null && tab == 
TAB_ROUTES) {
+                if (showDiagram && tab == TAB_ROUTES) {
                     diagramScroll = Math.max(0, diagramScroll - 1);
                 } else {
                     navigateUp();
@@ -286,15 +298,15 @@ public class CamelMonitor extends CamelCommand {
                 return true;
             }
             if (ke.isDown()) {
-                if (showDiagram && diagramImageData == null && tab == 
TAB_ROUTES) {
-                    diagramScroll = Math.min(Math.max(0, diagramLines.size() - 
1), diagramScroll + 1);
+                if (showDiagram && tab == TAB_ROUTES) {
+                    diagramScroll++;
                 } else {
                     navigateDown();
                 }
                 return true;
             }
             if (ke.isKey(KeyCode.PAGE_UP)) {
-                if (showDiagram && diagramImageData == null && tab == 
TAB_ROUTES) {
+                if (showDiagram && tab == TAB_ROUTES) {
                     diagramScroll = Math.max(0, diagramScroll - 20);
                 } else if (tab == TAB_LOG) {
                     logFollowMode = false;
@@ -305,8 +317,8 @@ public class CamelMonitor extends CamelCommand {
                 return true;
             }
             if (ke.isKey(KeyCode.PAGE_DOWN)) {
-                if (showDiagram && diagramImageData == null && tab == 
TAB_ROUTES) {
-                    diagramScroll = Math.min(Math.max(0, diagramLines.size() - 
1), diagramScroll + 20);
+                if (showDiagram && tab == TAB_ROUTES) {
+                    diagramScroll += 20;
                 } else if (tab == TAB_LOG) {
                     for (int i = 0; i < 20; i++) {
                         logTableState.selectNext(filteredLogEntries.size());
@@ -314,6 +326,31 @@ public class CamelMonitor extends CamelCommand {
                 }
                 return true;
             }
+            if (ke.isLeft()) {
+                if (showDiagram && tab == TAB_ROUTES) {
+                    diagramScrollX = Math.max(0, diagramScrollX - 1);
+                    return true;
+                }
+            }
+            if (ke.isRight()) {
+                if (showDiagram && tab == TAB_ROUTES) {
+                    diagramScrollX++;
+                    return true;
+                }
+            }
+            if (ke.isKey(KeyCode.HOME)) {
+                if (showDiagram && tab == TAB_ROUTES) {
+                    diagramScroll = 0;
+                    diagramScrollX = 0;
+                    return true;
+                }
+            }
+            if (ke.isKey(KeyCode.END)) {
+                if (showDiagram && tab == TAB_ROUTES) {
+                    diagramScroll = Integer.MAX_VALUE;
+                    return true;
+                }
+            }
 
             // Enter to drill into selected integration
             if (ke.isKey(KeyCode.ENTER) && tab == TAB_OVERVIEW) {
@@ -330,11 +367,24 @@ public class CamelMonitor extends CamelCommand {
                 routeSort = ROUTE_SORT_COLUMNS[routeSortIndex];
                 return true;
             }
-            if (tab == TAB_ROUTES && ke.isCharIgnoreCase('d')) {
+            if (tab == TAB_ROUTES && ke.isChar('d')) {
                 if (showDiagram) {
                     showDiagram = false;
                     diagramImageData = null;
+                    diagramFullImageData = null;
                 } else {
+                    diagramTextMode = false;
+                    loadDiagramForSelectedRoute();
+                }
+                return true;
+            }
+            if (tab == TAB_ROUTES && ke.isChar('D')) {
+                if (showDiagram) {
+                    showDiagram = false;
+                    diagramImageData = null;
+                    diagramFullImageData = null;
+                } else {
+                    diagramTextMode = true;
                     loadDiagramForSelectedRoute();
                 }
                 return true;
@@ -412,10 +462,13 @@ public class CamelMonitor extends CamelCommand {
         }
         if (event instanceof TickEvent) {
             long now = System.currentTimeMillis();
-            if (now - lastRefresh >= refreshInterval) {
+            long interval = showDiagram ? Math.max(refreshInterval, 1000) : 
refreshInterval;
+            if (now - lastRefresh >= interval) {
                 refreshData();
+                return true;
             }
-            return true;
+            // Skip re-render when showing image diagram to prevent flicker
+            return diagramFullImageData == null;
         }
         return false;
     }
@@ -682,6 +735,17 @@ public class CamelMonitor extends CamelCommand {
             return;
         }
 
+        // Fullscreen diagram mode
+        if (showDiagram && (diagramTextMode ? !diagramLines.isEmpty() : 
diagramFullImageData != null)) {
+            // Split: route info header (4 rows) + diagram (fill)
+            List<Rect> fullChunks = Layout.vertical()
+                    .constraints(Constraint.length(4), Constraint.fill())
+                    .split(area);
+            renderRouteHeader(frame, fullChunks.get(0), info);
+            renderDiagram(frame, fullChunks.get(1));
+            return;
+        }
+
         // Sort routes
         List<RouteInfo> sortedRoutes = new ArrayList<>(info.routes);
         sortedRoutes.sort(this::sortRoute);
@@ -830,40 +894,222 @@ public class CamelMonitor extends CamelCommand {
         frame.renderStatefulWidget(table, area, new TableState());
     }
 
+    private void renderRouteHeader(Frame frame, Rect area, IntegrationInfo 
info) {
+        RouteInfo route = null;
+        if (diagramRouteId != null) {
+            for (RouteInfo r : info.routes) {
+                if (diagramRouteId.equals(r.routeId)) {
+                    route = r;
+                    break;
+                }
+            }
+        }
+
+        List<Row> rows = new ArrayList<>();
+        if (route != null) {
+            Style stateStyle = "Started".equals(route.state)
+                    ? Style.create().fg(Color.GREEN)
+                    : Style.create().fg(Color.RED);
+            Style failStyle = route.failed > 0
+                    ? Style.create().fg(Color.RED).bold()
+                    : Style.create();
+            rows.add(Row.from(
+                    Cell.from(Span.styled(route.routeId != null ? 
route.routeId : "", Style.create().fg(Color.CYAN))),
+                    Cell.from(route.from != null ? route.from : ""),
+                    Cell.from(Span.styled(route.state != null ? route.state : 
"", stateStyle)),
+                    Cell.from(route.uptime != null ? route.uptime : ""),
+                    Cell.from(route.throughput != null ? route.throughput : 
""),
+                    Cell.from(String.valueOf(route.total)),
+                    Cell.from(Span.styled(String.valueOf(route.failed), 
failStyle)),
+                    Cell.from(route.meanTime + "/" + route.maxTime)));
+        }
+
+        Table table = Table.builder()
+                .rows(rows)
+                .header(Row.from(
+                        Cell.from(Span.styled("ROUTE", Style.create().bold())),
+                        Cell.from(Span.styled("FROM", Style.create().bold())),
+                        Cell.from(Span.styled("STATE", Style.create().bold())),
+                        Cell.from(Span.styled("UPTIME", 
Style.create().bold())),
+                        Cell.from(Span.styled("THRUPUT", 
Style.create().bold())),
+                        Cell.from(Span.styled("TOTAL", Style.create().bold())),
+                        Cell.from(Span.styled("FAILED", 
Style.create().bold())),
+                        Cell.from(Span.styled("MEAN/MAX", 
Style.create().bold()))))
+                .widths(
+                        Constraint.length(12),
+                        Constraint.fill(),
+                        Constraint.length(10),
+                        Constraint.length(8),
+                        Constraint.length(10),
+                        Constraint.length(8),
+                        Constraint.length(8),
+                        Constraint.length(12))
+                .highlightStyle(Style.create().fg(Color.WHITE).bold().onBlue())
+                .block(Block.builder().borderType(BorderType.ROUNDED)
+                        .title(" Route [" + info.name + "] ").build())
+                .build();
+
+        frame.renderStatefulWidget(table, area, new TableState());
+    }
+
     private void renderDiagram(Frame frame, Rect area) {
         Block block = Block.builder()
                 .borderType(BorderType.ROUNDED)
-                .title(" Diagram [" + diagramRouteId + "] ")
+                .title(diagramTextMode ? "" : " Diagram [" + diagramRouteId + 
"] ")
                 .build();
 
-        if (diagramImageData != null) {
-            Image img = Image.builder()
-                    .data(diagramImageData)
-                    .protocol(diagramProtocol)
-                    .scaling(ImageScaling.FIT)
-                    .block(block)
-                    .build();
-            frame.renderWidget(img, area);
+        if (diagramFullImageData != null) {
+            renderImageDiagram(frame, area, block);
             return;
         }
 
+        // Compute max width for horizontal scrolling
+        int maxWidth = 0;
+        for (String line : diagramLines) {
+            maxWidth = Math.max(maxWidth, line.length());
+        }
+
         Rect inner = block.inner(area);
-        int visibleLines = inner.height();
-        int maxScroll = Math.max(0, diagramLines.size() - visibleLines);
-        diagramScroll = Math.min(diagramScroll, maxScroll);
+        // Reserve 1 col for vertical scrollbar, 1 row for horizontal scrollbar
+        int visibleLines = Math.max(1, inner.height() - 1);
+        int visibleCols = Math.max(1, inner.width() - 1);
 
+        int maxVScroll = Math.max(0, diagramLines.size() - visibleLines);
+        int maxHScroll = Math.max(0, maxWidth - visibleCols);
+        diagramScroll = Math.min(diagramScroll, maxVScroll);
+        diagramScrollX = Math.min(diagramScrollX, maxHScroll);
+
+        // Build visible lines with horizontal offset applied
         List<Line> lines = new ArrayList<>();
         int end = Math.min(diagramScroll + visibleLines, diagramLines.size());
         for (int i = diagramScroll; i < end; i++) {
-            lines.add(styleDiagramLine(diagramLines.get(i)));
+            String line = diagramLines.get(i);
+            if (diagramScrollX > 0) {
+                line = diagramScrollX < line.length() ? 
line.substring(diagramScrollX) : "";
+            }
+            lines.add(styleDiagramLine(line));
         }
 
+        // Layout: outer block wraps everything, inner splits content + 
scrollbars
+        frame.renderWidget(block, area);
+
+        // Vertical layout inside the block: [content row (fill), horizontal 
scrollbar (1 row)]
+        List<Rect> vChunks = Layout.vertical()
+                .constraints(Constraint.fill(), Constraint.length(1))
+                .split(inner);
+
+        // Horizontal layout for content row: [text (fill), vertical scrollbar 
(1 col)]
+        List<Rect> hChunks = Layout.horizontal()
+                .constraints(Constraint.fill(), Constraint.length(1))
+                .split(vChunks.get(0));
+
+        // Render diagram text
         Paragraph paragraph = Paragraph.builder()
                 .text(Text.from(lines))
-                .block(block)
                 .build();
+        frame.renderWidget(paragraph, hChunks.get(0));
+
+        // Render vertical scrollbar
+        diagramVScrollState.contentLength(diagramLines.size());
+        diagramVScrollState.viewportContentLength(visibleLines);
+        diagramVScrollState.position(diagramScroll);
+        frame.renderStatefulWidget(
+                Scrollbar.builder()
+                        .thumbStyle(Style.create().fg(Color.rgb(0xF6, 0x91, 
0x23)))
+                        .build(),
+                hChunks.get(1), diagramVScrollState);
 
-        frame.renderWidget(paragraph, area);
+        // Render horizontal scrollbar
+        if (maxWidth > visibleCols) {
+            diagramHScrollState.contentLength(maxWidth);
+            diagramHScrollState.viewportContentLength(visibleCols);
+            diagramHScrollState.position(diagramScrollX);
+            frame.renderStatefulWidget(
+                    Scrollbar.horizontal(),
+                    vChunks.get(1), diagramHScrollState);
+        }
+    }
+
+    private void renderImageDiagram(Frame frame, Rect area, Block block) {
+        int imgW = diagramFullImageData.width();
+        int imgH = diagramFullImageData.height();
+
+        Rect inner = block.inner(area);
+        // Convert cell area to pixel viewport using protocol resolution
+        int pxPerCol = diagramProtocol.resolution().widthMultiplier();
+        int pxPerRow = diagramProtocol.resolution().heightMultiplier();
+        // Reserve 1 col for vertical scrollbar, 1 row for horizontal scrollbar
+        int viewCols = Math.max(1, inner.width() - 1);
+        int viewRows = Math.max(1, inner.height() - 1);
+        int viewW = viewCols * pxPerCol;
+        int viewH = viewRows * pxPerRow;
+
+        // Scroll units are in cells; convert to pixels for clamping
+        int maxScrollY = Math.max(0, (imgH - viewH + pxPerRow - 1) / pxPerRow);
+        int maxScrollX = Math.max(0, (imgW - viewW + pxPerCol - 1) / pxPerCol);
+        diagramScroll = Math.min(diagramScroll, maxScrollY);
+        diagramScrollX = Math.min(diagramScrollX, maxScrollX);
+
+        int cropX = Math.min(diagramScrollX * pxPerCol, imgW);
+        int cropY = Math.min(diagramScroll * pxPerRow, imgH);
+        int cropW = Math.min(viewW, imgW - cropX);
+        int cropH = Math.min(viewH, imgH - cropY);
+
+        if (cropW > 0 && cropH > 0) {
+            if (cropX != diagramCropX || cropY != diagramCropY
+                    || cropW != diagramCropW || cropH != diagramCropH) {
+                diagramImageData = diagramFullImageData.crop(cropX, cropY, 
cropW, cropH);
+                diagramCropX = cropX;
+                diagramCropY = cropY;
+                diagramCropW = cropW;
+                diagramCropH = cropH;
+            }
+        } else if (diagramImageData != diagramFullImageData) {
+            diagramImageData = diagramFullImageData;
+        }
+
+        // Render the outer block border
+        frame.renderWidget(block, area);
+
+        // Vertical layout inside the block: [image+vscrollbar (fill), 
hscrollbar (1 row)]
+        List<Rect> vChunks = Layout.vertical()
+                .constraints(Constraint.fill(), Constraint.length(1))
+                .split(inner);
+
+        // Horizontal layout: [image (fill), vertical scrollbar (1 col)]
+        List<Rect> hChunks = Layout.horizontal()
+                .constraints(Constraint.fill(), Constraint.length(1))
+                .split(vChunks.get(0));
+
+        // Render cropped image
+        Image img = Image.builder()
+                .data(diagramImageData)
+                .protocol(diagramProtocol)
+                .scaling(ImageScaling.FIT)
+                .build();
+        frame.renderWidget(img, hChunks.get(0));
+
+        // Render vertical scrollbar
+        int totalRows = (imgH + pxPerRow - 1) / pxPerRow;
+        diagramVScrollState.contentLength(totalRows);
+        diagramVScrollState.viewportContentLength(viewRows);
+        diagramVScrollState.position(diagramScroll);
+        frame.renderStatefulWidget(
+                Scrollbar.builder()
+                        .thumbStyle(Style.create().fg(Color.rgb(0xF6, 0x91, 
0x23)))
+                        .build(),
+                hChunks.get(1), diagramVScrollState);
+
+        // Render horizontal scrollbar
+        if (imgW > viewW) {
+            int totalCols = (imgW + pxPerCol - 1) / pxPerCol;
+            diagramHScrollState.contentLength(totalCols);
+            diagramHScrollState.viewportContentLength(viewCols);
+            diagramHScrollState.position(diagramScrollX);
+            frame.renderStatefulWidget(
+                    Scrollbar.horizontal(),
+                    vChunks.get(1), diagramHScrollState);
+        }
     }
 
     private Line styleDiagramLine(String text) {
@@ -999,61 +1245,53 @@ public class CamelMonitor extends CamelCommand {
 
         diagramRouteId = selectedRoute.routeId;
         diagramScroll = 0;
+        diagramScrollX = 0;
+        diagramCropX = -1;
+        diagramCropY = -1;
+        diagramCropW = -1;
+        diagramCropH = -1;
 
-        TerminalImageCapabilities caps = TerminalImageCapabilities.detect();
-        if (caps.supportsNativeImages()) {
-            RouteDiagramLayoutEngine engine = new RouteDiagramLayoutEngine();
-            List<RouteDiagramLayoutEngine.LayoutRoute> layoutRoutes = new 
ArrayList<>();
-            int totalHeight = 0;
-            for (RouteDiagramLayoutEngine.RouteInfo r : diagramRoutes) {
-                RouteDiagramLayoutEngine.LayoutRoute lr = 
engine.layoutRoute(r, totalHeight);
-                layoutRoutes.add(lr);
-                totalHeight = lr.maxY;
-            }
-            RouteDiagramRenderer renderer = new RouteDiagramRenderer();
-            RouteDiagramRenderer.DiagramColors colors = 
RouteDiagramRenderer.DiagramColors.parse("transparent");
-            java.awt.image.BufferedImage image = 
renderer.renderDiagram(layoutRoutes, totalHeight, colors);
-            diagramImageData = ImageData.fromBufferedImage(image);
-            diagramProtocol = caps.bestProtocol();
-            diagramLines = Collections.emptyList();
-        } else {
+        if (diagramTextMode) {
             diagramImageData = null;
+            diagramFullImageData = null;
             diagramProtocol = null;
 
-            StringBuilder sb = new StringBuilder();
-            org.apache.camel.dsl.jbang.core.common.Printer capturingPrinter
-                    = new org.apache.camel.dsl.jbang.core.common.Printer() {
-                        @Override
-                        public void println() {
-                            sb.append('\n');
-                        }
-
-                        @Override
-                        public void println(String line) {
-                            sb.append(line).append('\n');
-                        }
-
-                        @Override
-                        public void print(String output) {
-                            sb.append(output);
-                        }
-
-                        @Override
-                        public void printf(String format, Object... args) {
-                            sb.append(String.format(format, args));
-                        }
-                    };
-
             String ascii = renderAscii(diagramRoutes, 
RouteDiagramLayoutEngine.DEFAULT_BOX_WIDTH, "CODE", true);
-            sb.append(ascii);
 
             List<String> result = new ArrayList<>();
-            for (String line : sb.toString().split("\n", -1)) {
+            for (String line : ascii.split("\n", -1)) {
                 if (!line.isEmpty()) {
                     result.add(line);
                 }
             }
             diagramLines = result;
+        } else {
+            TerminalImageCapabilities caps = 
TerminalImageCapabilities.detect();
+            if (caps.supportsNativeImages()) {
+                RouteDiagramLayoutEngine engine = new 
RouteDiagramLayoutEngine();
+                List<RouteDiagramLayoutEngine.LayoutRoute> layoutRoutes = new 
ArrayList<>();
+                int totalHeight = 0;
+                for (RouteDiagramLayoutEngine.RouteInfo r : diagramRoutes) {
+                    RouteDiagramLayoutEngine.LayoutRoute lr = 
engine.layoutRoute(r, totalHeight);
+                    layoutRoutes.add(lr);
+                    totalHeight = lr.maxY;
+                }
+                RouteDiagramRenderer renderer = new RouteDiagramRenderer();
+                RouteDiagramRenderer.DiagramColors colors = 
RouteDiagramRenderer.DiagramColors.parse("transparent");
+                java.awt.image.BufferedImage image = 
renderer.renderDiagram(layoutRoutes, totalHeight, colors);
+                ImageData fullImage = ImageData.fromBufferedImage(image);
+                diagramFullImageData = fullImage.resize(fullImage.width() / 2, 
fullImage.height() / 2);
+                diagramImageData = diagramFullImageData;
+                diagramProtocol = caps.bestProtocol();
+                diagramLines = Collections.emptyList();
+            } else {
+                diagramImageData = null;
+                diagramFullImageData = null;
+                diagramProtocol = null;
+                diagramLines = List.of(
+                        "(Terminal does not support image rendering)",
+                        "(Press Shift+D for text diagram)");
+            }
         }
 
         showDiagram = true;
@@ -1680,15 +1918,18 @@ public class CamelMonitor extends CamelCommand {
                     Span.styled("Refresh: " + refreshLabel, 
Style.create().dim()));
         } else if (tab == TAB_ROUTES) {
             if (showDiagram) {
+                String closeKey = diagramTextMode ? "D" : "d";
                 footer = Line.from(
-                        Span.styled(" d", 
Style.create().fg(Color.YELLOW).bold()),
+                        Span.styled(" " + closeKey, 
Style.create().fg(Color.YELLOW).bold()),
                         Span.raw("/"),
                         Span.styled("Esc", 
Style.create().fg(Color.YELLOW).bold()),
                         Span.raw(" close  "),
-                        Span.styled("\u2191\u2193", 
Style.create().fg(Color.YELLOW).bold()),
+                        Span.styled("\u2191\u2193\u2190\u2192", 
Style.create().fg(Color.YELLOW).bold()),
                         Span.raw(" scroll  "),
                         Span.styled("PgUp/PgDn", 
Style.create().fg(Color.YELLOW).bold()),
-                        Span.raw(" page"));
+                        Span.raw(" page  "),
+                        Span.styled("Home/End", 
Style.create().fg(Color.YELLOW).bold()),
+                        Span.raw(" top/bottom"));
             } else {
                 footer = Line.from(
                         Span.styled(" Esc", 
Style.create().fg(Color.YELLOW).bold()),
@@ -1699,6 +1940,8 @@ public class CamelMonitor extends CamelCommand {
                         Span.raw(" sort  "),
                         Span.styled("d", 
Style.create().fg(Color.YELLOW).bold()),
                         Span.raw(" diagram  "),
+                        Span.styled("D", 
Style.create().fg(Color.YELLOW).bold()),
+                        Span.raw(" text diagram  "),
                         Span.styled("1-6", 
Style.create().fg(Color.YELLOW).bold()),
                         Span.raw(" tabs  "),
                         Span.styled("Refresh: " + refreshLabel, 
Style.create().dim()));

Reply via email to