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

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

commit 75e83695fbbee780638cd7c9346b191d3b9798d4
Author: Claus Ibsen <[email protected]>
AuthorDate: Thu Jun 4 09:22:33 2026 +0200

    CAMEL-23672: camel-tui - Diagram info panel improvements
    
    - Show external indicator (external → / → external) instead of link
      indicator for remote endpoints
    - Dashed arrows to remote endpoint nodes
    - Stable info panel height to prevent flicker when scrolling
    - Remove conditional Enter hint from footer (keep in F1 help)
    - Add since-last success/fail timestamps to both info panels
    - Move coverage next to uptime/throughput in topology info
    - Show Topology[camelId] with yellow styled name in title
    - Rename EIP Info to Info
    
    Co-Authored-By: Claude Opus 4.6 <[email protected]>
    Signed-off-by: Claus Ibsen <[email protected]>
---
 .../jbang/core/commands/tui/DiagramSupport.java    |  4 +-
 .../dsl/jbang/core/commands/tui/DiagramTab.java    | 68 ++++++++++++++++++----
 .../commands/tui/diagram/RouteDiagramWidget.java   |  6 +-
 3 files changed, 62 insertions(+), 16 deletions(-)

diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/DiagramSupport.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/DiagramSupport.java
index 30d2de167198..0b9d6635a401 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/DiagramSupport.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/DiagramSupport.java
@@ -520,10 +520,10 @@ class DiagramSupport {
         return boxes;
     }
 
-    void renderNativeDiagram(Frame frame, Rect area, String title, boolean 
metrics) {
+    void renderNativeDiagram(Frame frame, Rect area, Line title, boolean 
metrics) {
         Block block = Block.builder()
                 .borderType(BorderType.ROUNDED)
-                .title(title)
+                .title(Title.from(title))
                 .build();
         frame.renderWidget(block, area);
 
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/DiagramTab.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/DiagramTab.java
index 3cfa55e4fe7d..f48e7854eabe 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/DiagramTab.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/DiagramTab.java
@@ -34,6 +34,7 @@ import dev.tamboui.tui.event.KeyEvent;
 import dev.tamboui.widgets.block.Block;
 import dev.tamboui.widgets.block.BorderType;
 import dev.tamboui.widgets.paragraph.Paragraph;
+import org.apache.camel.util.TimeUtils;
 import org.apache.camel.util.json.JsonObject;
 
 import static org.apache.camel.dsl.jbang.core.commands.tui.MonitorContext.*;
@@ -308,7 +309,15 @@ class DiagramTab implements MonitorTab {
             String selectedRouteId = topologyMode ? 
diagram.getSelectedRouteId() : drillDownRouteId;
 
             if (topologyMode && diagram.hasNativeLayout()) {
-                String title = " Topology ";
+                Line title;
+                if (info.name != null) {
+                    title = Line.from(
+                            Span.raw(" Topology ["),
+                            Span.styled(info.name, 
Style.EMPTY.fg(Color.YELLOW).bold()),
+                            Span.raw("] "));
+                } else {
+                    title = Line.from(Span.raw(" Topology "));
+                }
                 if (selectedRouteId != null && area.width() > 60) {
                     int panelWidth = 30;
                     List<Rect> hChunks = Layout.horizontal()
@@ -381,6 +390,11 @@ class DiagramTab implements MonitorTab {
             lines.add(Line.from(
                     Span.styled(" Throughput: ", Style.EMPTY.dim()),
                     Span.raw(route.throughput != null ? route.throughput : 
"")));
+            if (route.coverage != null) {
+                lines.add(Line.from(
+                        Span.styled(" Coverage:   ", Style.EMPTY.dim()),
+                        Span.raw(route.coverage)));
+            }
 
             lines.add(Line.from(Span.raw("")));
             int w = numWidth(route.total, route.failed, route.inflight);
@@ -409,12 +423,23 @@ class DiagramTab implements MonitorTab {
                         Span.raw(String.format("%" + tw + "d ms", 
route.minTime))));
             }
 
-            if (route.coverage != null) {
+            if (route.sinceLastCompleted != null || route.sinceLastFailed != 
null) {
                 lines.add(Line.from(Span.raw("")));
                 lines.add(Line.from(
-                        Span.styled(" Coverage: ", Style.EMPTY.dim()),
-                        Span.raw(route.coverage)));
+                        Span.styled(" Since last:", Style.EMPTY.dim())));
+                if (route.sinceLastCompleted != null) {
+                    lines.add(Line.from(
+                            Span.styled("   success: ", Style.EMPTY.dim()),
+                            Span.raw(route.sinceLastCompleted)));
+                }
+                if (route.sinceLastFailed != null) {
+                    lines.add(Line.from(
+                            Span.styled("   fail:    ", Style.EMPTY.dim()),
+                            Span.styled(route.sinceLastFailed,
+                                    Style.EMPTY.fg(Color.LIGHT_RED))));
+                }
             }
+
         } else {
             // External endpoint — show topology node data
             var topoNode = diagram.getSelectedTopologyNode();
@@ -491,12 +516,18 @@ class DiagramTab implements MonitorTab {
                         Span.raw(ln.id)));
             }
 
+            lines.add(Line.from(Span.raw("")));
             String linkedRoute = diagram.findLinkedRouteId(drillDownRouteId);
-            if (linkedRoute != null) {
-                lines.add(Line.from(Span.raw("")));
+            if (linkedRoute != null && diagram.getRouteLayout(linkedRoute) != 
null) {
                 lines.add(Line.from(
                         Span.styled(" ↵ ", 
Style.EMPTY.fg(Color.YELLOW).bold()),
                         Span.styled(linkedRoute, 
Style.EMPTY.fg(Color.WHITE))));
+            } else if (ln.treeNode != null && ln.treeNode.info.remote) {
+                String arrow = "from".equals(ln.type) ? " external → " : " → 
external";
+                lines.add(Line.from(
+                        Span.styled(arrow, Style.EMPTY.fg(Color.DARK_GRAY))));
+            } else {
+                lines.add(Line.from(Span.raw("")));
             }
 
             if (ln.treeNode != null && ln.treeNode.info.stat != null) {
@@ -531,6 +562,26 @@ class DiagramTab implements MonitorTab {
                     lines.add(Line.from(
                             Span.styled(" Last: ", Style.EMPTY.dim()),
                             Span.raw(String.format("%" + tw + "d ms", 
stat.lastProcessingTime))));
+
+                    if (stat.lastCompletedExchangeTimestamp > 0 || 
stat.lastFailedExchangeTimestamp > 0) {
+                        long now = System.currentTimeMillis();
+                        lines.add(Line.from(Span.raw("")));
+                        lines.add(Line.from(
+                                Span.styled(" Since last:", 
Style.EMPTY.dim())));
+                        if (stat.lastCompletedExchangeTimestamp > 0) {
+                            long ago = now - 
stat.lastCompletedExchangeTimestamp;
+                            lines.add(Line.from(
+                                    Span.styled("   success: ", 
Style.EMPTY.dim()),
+                                    Span.raw(TimeUtils.printDuration(ago, 
true))));
+                        }
+                        if (stat.lastFailedExchangeTimestamp > 0) {
+                            long ago = now - stat.lastFailedExchangeTimestamp;
+                            lines.add(Line.from(
+                                    Span.styled("   fail:    ", 
Style.EMPTY.dim()),
+                                    Span.styled(TimeUtils.printDuration(ago, 
true),
+                                            Style.EMPTY.fg(Color.LIGHT_RED))));
+                        }
+                    }
                 }
             }
         } else {
@@ -540,7 +591,7 @@ class DiagramTab implements MonitorTab {
         Paragraph paragraph = Paragraph.builder()
                 .text(Text.from(lines))
                 .block(Block.builder().borderType(BorderType.ROUNDED)
-                        .title(" EIP Info ").build())
+                        .title(" Info ").build())
                 .build();
         frame.renderWidget(paragraph, area);
     }
@@ -556,9 +607,6 @@ class DiagramTab implements MonitorTab {
                 hint(spans, "Esc", "back");
                 hint(spans, "t", "topology");
                 hint(spans, "↑↓←→", "navigate");
-                if (diagram.findLinkedRouteId(drillDownRouteId) != null) {
-                    hint(spans, "Enter", "jump to route");
-                }
                 hint(spans, "PgUp/PgDn", "page");
                 hint(spans, "c", "source");
             } else if (!topologyMode) {
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/diagram/RouteDiagramWidget.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/diagram/RouteDiagramWidget.java
index 97e3cfc8367b..4b616eb51af8 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/diagram/RouteDiagramWidget.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/diagram/RouteDiagramWidget.java
@@ -193,9 +193,7 @@ public class RouteDiagramWidget implements Widget {
         // Link indicator for nodes that connect to other routes
         String linkedRouteId = findLinkedRouteId(node);
         if (linkedRouteId != null) {
-            Style linkStyle = selected
-                    ? 
Style.EMPTY.fg(Color.YELLOW).bold().patch(SELECTION_STYLE)
-                    : Style.EMPTY.fg(Color.YELLOW).bold();
+            Style linkStyle = Style.EMPTY.fg(Color.YELLOW).bold();
             writeText(buffer, area, bottom, col + boxWidth, " ↵ " + 
linkedRouteId, linkStyle);
         }
 
@@ -210,7 +208,7 @@ public class RouteDiagramWidget implements Widget {
 
         StatInfo stat = resolveStatInfo(to);
         long total = stat != null ? stat.exchangesTotal : 0;
-        boolean dashed = showMetrics && total == 0;
+        boolean dashed = (showMetrics && total == 0) || isExternalEndpoint(to);
 
         drawArrowPath(buffer, area, fromCx, fromBottom, toCx, toTop, dashed);
         drawCounters(buffer, area, toCx, toTop, stat);

Reply via email to