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

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

commit be552fe5ad11523c5ec7c89998dec2150c746b2a
Author: Claus Ibsen <[email protected]>
AuthorDate: Sat Nov 7 16:15:50 2026 +0100

    CAMEL-26700: About - a cursor on In, Routes and Out, and Enter opens a 
route on the Diagram or an endpoint on the Endpoint tab
    
    Up/down move over the lines of In, Routes and Out that are about a route or 
an
    endpoint. Enter on a route opens it drilled down on the Diagram (once the
    diagram has loaded); on an endpoint a route sends to, the Endpoint tab with 
it
    selected and its details. Without a cursor, Enter does as before.
    
    Co-Authored-By: Claude Opus 5.5 <[email protected]>
    Signed-off-by: Claus Ibsen <[email protected]>
---
 .../dsl/jbang/core/commands/tui/AboutView.java     | 105 +++++++++++++++++++--
 .../dsl/jbang/core/commands/tui/CamelMonitor.java  |   3 +
 .../dsl/jbang/core/commands/tui/DiagramTab.java    |  23 +++++
 .../dsl/jbang/core/commands/tui/EndpointsTab.java  |  28 ++++++
 .../jbang/core/commands/tui/MonitorContext.java    |   2 +
 .../dsl/jbang/core/commands/tui/OverviewTab.java   |  15 +++
 .../dsl/jbang/core/commands/tui/TabRegistry.java   |   9 ++
 .../src/main/resources/tui/help/overview.md        |   5 +-
 .../dsl/jbang/core/commands/tui/AboutViewTest.java |  18 ++++
 .../core/commands/tui/EndpointsTabRenderTest.java  |  14 +++
 10 files changed, 214 insertions(+), 8 deletions(-)

diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/AboutView.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/AboutView.java
index 6609884bb175..cd90522c73bd 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/AboutView.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/AboutView.java
@@ -78,6 +78,19 @@ final class AboutView {
     // -1 follows the last second recorded
     private int zoom = TimelineTab.WINDOWS.length - 1;
     private long stripsEnd = -1;
+    // the line of In, Routes or Out the cursor is on (↑ ↓), in the order of 
the columns; -1 when none is
+    private int cursor = -1;
+    private volatile List<Target> targets = List.of();
+
+    /**
+     * What a line of In, Routes or Out is about, for Enter: a route (opened 
on the Diagram) or an endpoint a route
+     * sends to (opened on the Endpoint tab).
+     *
+     * @param routeId  the route
+     * @param endpoint the URI of the endpoint as the sources have it, or null 
for a route
+     */
+    record Target(String routeId, String endpoint) {
+    }
 
     AboutView(MonitorContext ctx) {
         this.ctx = ctx;
@@ -132,6 +145,9 @@ final class AboutView {
         if (info == null) {
             return false;
         }
+        if (ke.isConfirm()) {
+            return open();
+        }
         long end = ctx.timeline.endSecond(info.pid);
         if (ke.isChar('+') || ke.isChar('=')) {
             if (zoom > 0 && end >= 0) {
@@ -161,6 +177,34 @@ final class AboutView {
         return false;
     }
 
+    /** ↑ ↓: the cursor moves over the lines of In, Routes and Out that are 
about a route or an endpoint. */
+    void moveCursor(int delta) {
+        int n = targets.size();
+        if (n == 0) {
+            cursor = -1;
+        } else if (cursor < 0) {
+            cursor = delta > 0 ? 0 : n - 1;
+        } else {
+            cursor = Math.max(0, Math.min(n - 1, cursor + delta));
+        }
+    }
+
+    /** The line the cursor is on, or null. */
+    Target selected() {
+        List<Target> t = targets;
+        return cursor >= 0 && cursor < t.size() ? t.get(cursor) : null;
+    }
+
+    /** Enter: opens what the cursor is on; false when it is on nothing. */
+    boolean open() {
+        Target t = selected();
+        if (t == null || ctx.openFromAbout == null) {
+            return false;
+        }
+        ctx.openFromAbout.accept(t);
+        return true;
+    }
+
     private int seconds() {
         return TimelineTab.WINDOWS[zoom];
     }
@@ -219,9 +263,26 @@ final class AboutView {
         List<Line> in = new ArrayList<>();
         List<Line> routes = new ArrayList<>();
         List<Line> out = new ArrayList<>();
+        List<Target> inTargets = new ArrayList<>();
+        List<Target> routeTargets = new ArrayList<>();
+        List<Target> outTargets = new ArrayList<>();
         if (o != null) {
-            columns(o, info, in, routes, out);
+            columns(o, info, in, routes, out, inTargets, routeTargets, 
outTargets);
+        }
+        // the cursor goes down In, then Routes, then Out
+        List<Target> all = new ArrayList<>();
+        for (List<Target> column : List.of(inTargets, routeTargets, 
outTargets)) {
+            for (Target t : column) {
+                if (t != null) {
+                    all.add(t);
+                }
+            }
+        }
+        targets = all;
+        if (cursor >= all.size()) {
+            cursor = all.size() - 1;
         }
+        highlight(in, inTargets, routes, routeTargets, out, outTargets);
         int columnRows = Math.max(1, Math.max(in.size(), 
Math.max(routes.size(), out.size())));
         List<Line> strips = strips(info, width);
         int headHeight = head.size() + 2;
@@ -414,8 +475,31 @@ final class AboutView {
 
     // ---- In, Routes, Out ----
 
+    /** The line the cursor is on, in the selection colour. */
+    private void highlight(
+            List<Line> in, List<Target> inTargets, List<Line> routes, 
List<Target> routeTargets, List<Line> out,
+            List<Target> outTargets) {
+        int n = 0;
+        List<List<Line>> columns = List.of(in, routes, out);
+        List<List<Target>> columnTargets = List.of(inTargets, routeTargets, 
outTargets);
+        for (int c = 0; c < columns.size(); c++) {
+            List<Target> t = columnTargets.get(c);
+            for (int i = 0; i < t.size(); i++) {
+                if (t.get(i) != null && n++ == cursor) {
+                    List<Span> spans = new ArrayList<>();
+                    for (Span s : columns.get(c).get(i).spans()) {
+                        spans.add(Span.styled(s.content(), 
s.style().patch(Theme.selectionBg())));
+                    }
+                    columns.get(c).set(i, Line.from(spans));
+                    return;
+                }
+            }
+        }
+    }
+
     private static void columns(
-            ProjectOverview.Overview o, IntegrationInfo info, List<Line> in, 
List<Line> routes, List<Line> out) {
+            ProjectOverview.Overview o, IntegrationInfo info, List<Line> in, 
List<Line> routes, List<Line> out,
+            List<Target> inTargets, List<Target> routeTargets, List<Target> 
outTargets) {
         Map<String, Long> totals = new LinkedHashMap<>();
         for (RouteInfo r : info.routes) {
             if (r.routeId != null) {
@@ -429,9 +513,11 @@ final class AboutView {
                     ? e.label().substring(0, e.label().indexOf(':')) : 
e.kind();
             in.add(Line.from(Span.raw(" " + icon(kind) + " "), 
Span.styled(e.label(), Style.EMPTY.bold()),
                     Span.styled(total != null ? "  " + total : "", 
Theme.success())));
+            inTargets.add(e.route() != null ? new Target(e.route(), null) : 
null);
         }
         if (in.isEmpty()) {
             in.add(Line.from(Span.styled(" nothing from outside", 
Theme.muted())));
+            inTargets.add(null);
         }
 
         // the routes that something else in the integration calls go under 
their caller
@@ -446,13 +532,13 @@ final class AboutView {
         Set<String> shown = new LinkedHashSet<>();
         for (ProjectRoutes.Route r : o.flows()) {
             if (!called.contains(r.key())) {
-                routeLines(o, r, 0, null, calledBy, totals, shown, routes);
+                routeLines(o, r, 0, null, calledBy, totals, shown, routes, 
routeTargets);
             }
         }
         // what is left: routes called only by routes in a cycle
         for (ProjectRoutes.Route r : o.flows()) {
             if (!shown.contains(r.key())) {
-                routeLines(o, r, 0, null, calledBy, totals, shown, routes);
+                routeLines(o, r, 0, null, calledBy, totals, shown, routes, 
routeTargets);
             }
         }
 
@@ -467,11 +553,14 @@ final class AboutView {
                 out.add(Line.from(Span.raw(" " + icon(s.category()) + " "), 
Span.styled(shortUri(s.uri()), Style.EMPTY.bold()),
                         Span.styled("  " + s.direction(), Theme.muted()),
                         Span.styled(hits != null ? "  " + hits : "", 
Theme.success())));
+                outTargets.add(new Target(s.route(), s.uri()));
                 out.add(Line.from(Span.styled("    from " + s.route(), 
Theme.muted())));
+                outTargets.add(null);
             }
         }
         if (out.isEmpty()) {
             out.add(Line.from(Span.styled(" nothing outside the integration", 
Theme.muted())));
+            outTargets.add(null);
         }
     }
 
@@ -492,7 +581,7 @@ final class AboutView {
     }
 
     /** The URI without its options, and the same with or without the // after 
the scheme. */
-    private static String baseUri(String uri) {
+    static String baseUri(String uri) {
         if (uri == null) {
             return "";
         }
@@ -505,7 +594,7 @@ final class AboutView {
     private static void routeLines(
             ProjectOverview.Overview o, ProjectRoutes.Route r, int depth, 
String how,
             Map<String, List<ProjectOverview.Link>> calledBy, Map<String, 
Long> totals, Set<String> shown,
-            List<Line> lines) {
+            List<Line> lines, List<Target> targets) {
         if (!shown.add(r.key()) || depth > 6) {
             return;
         }
@@ -519,11 +608,13 @@ final class AboutView {
             spans.add(Span.styled("  " + total, Theme.success()));
         }
         lines.add(Line.from(spans));
+        targets.add(new Target(r.id() != null ? r.id() : r.key(), null));
         for (ProjectOverview.Link l : calledBy.getOrDefault(r.key(), 
List.of())) {
             ProjectRoutes.Route target = o.route(l.to());
             if (target != null) {
                 // ↳ a call that waits for the route, ⇢ a hand-off that does 
not
-                routeLines(o, target, depth + 1, "call".equals(l.kind()) ? "↳" 
: "⇢", calledBy, totals, shown, lines);
+                routeLines(o, target, depth + 1, "call".equals(l.kind()) ? "↳" 
: "⇢", calledBy, totals, shown, lines,
+                        targets);
             }
         }
     }
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 b2236102e287..dc41edae958f 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
@@ -1505,6 +1505,9 @@ public class CamelMonitor extends CamelCommand {
 
         int tab = tabRegistry.selectedTabIndex();
         if (ke.isConfirm() && tab == TAB_OVERVIEW) {
+            if (tabRegistry.overviewTab().openFromAbout()) {
+                return true;
+            }
             tabRegistry.overviewTab().selectCurrentIntegration();
             if (ctx.selectedPid != null) {
                 if (isPhantomSelected()) {
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 535779af9f0e..90f6877aa898 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
@@ -54,6 +54,9 @@ class DiagramTab extends AbstractDiagramTab {
     }
 
     private int infoPanelWidth = 30;
+    // a route to open drilled down once the diagram has loaded (Enter on 
About), and until when to wait for it
+    private volatile String pendingRoute;
+    private long pendingRouteUntil;
     private final DragSplit hSplit = new DragSplit();
 
     private final ArchitectureView architecture
@@ -578,6 +581,9 @@ class DiagramTab extends AbstractDiagramTab {
 
     @Override
     public void render(Frame frame, Rect area) {
+        if (pendingRoute != null && isDiagramShown()) {
+            openPendingRoute();
+        }
         IntegrationInfo info = ctx.findSelectedIntegration();
         if (info == null) {
             renderNoSelection(frame, area);
@@ -770,6 +776,23 @@ class DiagramTab extends AbstractDiagramTab {
         return !diagram.isLoading() && !diagram.getNodeBoxes().isEmpty();
     }
 
+    /**
+     * Opens the route drilled down, as Enter on it does (from About): now, or 
once the diagram has loaded, for up to 10
+     * seconds.
+     */
+    void openRoute(String routeId) {
+        pendingRoute = routeId;
+        pendingRouteUntil = System.currentTimeMillis() + 10_000;
+        openPendingRoute();
+    }
+
+    private void openPendingRoute() {
+        String routeId = pendingRoute;
+        if (routeId != null && (selectNode(routeId, null) || 
System.currentTimeMillis() > pendingRouteUntil)) {
+            pendingRoute = null;
+        }
+    }
+
     boolean selectRoute(String routeId) {
         int idx = diagram.findNodeIndexByRouteId(routeId);
         if (idx < 0) {
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 b8b8135cc2b8..71672ff5813c 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
@@ -425,6 +425,34 @@ class EndpointsTab extends AbstractTableTab {
         ChaosHere.hint(spans, ctx.findSelectedIntegration());
     }
 
+    /**
+     * Selects the endpoint a route sends to (from About, where the URI is as 
the sources have it), with its details;
+     * the filter is cleared when it hides the endpoint.
+     */
+    void selectEndpoint(String uri, String routeId) {
+        IntegrationInfo info = ctx.findSelectedIntegration();
+        if (info == null) {
+            return;
+        }
+        String base = AboutView.baseUri(uri);
+        for (int attempt = 0; attempt < 2; attempt++) {
+            List<EndpointInfo> sorted = new ArrayList<>(info.endpoints);
+            sorted.removeIf(ep -> !matchesFilter(ep));
+            sorted.sort(this::sortEndpoint);
+            for (int i = 0; i < sorted.size(); i++) {
+                EndpointInfo ep = sorted.get(i);
+                if ("out".equals(ep.direction) && (routeId == null || 
routeId.equals(ep.routeId))
+                        && base.equals(AboutView.baseUri(ep.uri))) {
+                    tableState.select(i);
+                    panelMode = PANEL_DETAIL;
+                    detailScroll = 0;
+                    return;
+                }
+            }
+            filter = 0;
+        }
+    }
+
     /** The endpoint selected in the table, as filtered and sorted. */
     private EndpointInfo selectedEndpoint(IntegrationInfo info) {
         Integer sel = tableState.selected();
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/MonitorContext.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/MonitorContext.java
index 4de39d756175..e993f61030b6 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/MonitorContext.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/MonitorContext.java
@@ -94,6 +94,8 @@ class MonitorContext {
     BiConsumer<String, Boolean> notificationCallback;
     // opens the Chaos screen with the known rules for a target ({@code !} on 
an endpoint, a consumer or a diagram node)
     Consumer<ChaosHere.Target> chaosHere;
+    // opens what a line of About is about (Enter): a route on the Diagram, an 
endpoint on the Endpoint tab
+    Consumer<AboutView.Target> openFromAbout;
     // the exchanges chaos rules faulted, for the marks on the Activity, 
Errors and Inspect tabs
     final ChaosMarks chaosMarks = new ChaosMarks();
     // what happened second by second, for the Timeline screen (CAMEL-26691)
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/OverviewTab.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/OverviewTab.java
index b84a8cede24c..4c9304c88ff2 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/OverviewTab.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/OverviewTab.java
@@ -154,6 +154,11 @@ class OverviewTab extends AbstractTab {
         return infos.size() == 1 ? infos.get(0) : null;
     }
 
+    /** Enter on About: opens the route or endpoint the cursor is on; false 
when it is on nothing. */
+    boolean openFromAbout() {
+        return showsAbout() && about.open();
+    }
+
     /** Whether About shows: the view the user picked, else About when one 
integration runs. */
     boolean showsAbout() {
         if (ctx.isInfraSelected()) {
@@ -306,6 +311,10 @@ class OverviewTab extends AbstractTab {
 
     @Override
     public void navigateUp() {
+        if (showsAbout()) {
+            about.moveCursor(-1);
+            return;
+        }
         if (infraFocused) {
             infraTableState.selectPrevious();
         } else {
@@ -316,6 +325,10 @@ class OverviewTab extends AbstractTab {
 
     @Override
     public void navigateDown() {
+        if (showsAbout()) {
+            about.moveCursor(1);
+            return;
+        }
         if (infraFocused) {
             infraTableState.selectNext(ctx.infraData.get().size());
         } else {
@@ -1315,6 +1328,8 @@ class OverviewTab extends AbstractTab {
         hint(spans, "q", "quit");
         if (showsAbout()) {
             // the keys of the table do not apply to About
+            hint(spans, "↑↓", "select");
+            hint(spans, "Enter", "open");
             hint(spans, "+/-", "zoom");
             hint(spans, "←→", "move");
             hint(spans, "4", "timeline");
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/TabRegistry.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/TabRegistry.java
index 9c429f2b302d..7484b3d5d2f9 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/TabRegistry.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/TabRegistry.java
@@ -335,6 +335,15 @@ class TabRegistry {
             selectMoreTab(moreTabIndex("Chaos"));
             chaosTab.openAt(target);
         };
+        ctx.openFromAbout = target -> {
+            if (target.endpoint() != null) {
+                handleTabKey(TAB_ENDPOINTS);
+                endpointsTab.selectEndpoint(target.endpoint(), 
target.routeId());
+            } else {
+                handleTabKey(TAB_DIAGRAM);
+                diagramTab.openRoute(target.routeId());
+            }
+        };
         sqlTraceTab.setEditSqlAction(sql -> {
             selectMoreTab(moreTabIndex("SQL Query"));
             sqlQueryTab.setInputValue("sql", sql);
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/resources/tui/help/overview.md
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/resources/tui/help/overview.md
index b844355ac634..09e736b046c4 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/resources/tui/help/overview.md
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/resources/tui/help/overview.md
@@ -20,7 +20,10 @@ The Overview has two views, switched with **v**:
   **What happened** has a line per route, coloured as the Timeline:
   **+/-** zooms between 5, 2 and 1 minutes (zooming in centres on the
   last trouble, so you see what the other routes did at that moment)
-  and **←→** moves a zoomed window back and forward.
+  and **←→** moves a zoomed window back and forward. **↑↓** select a
+  line of In, Routes or Out, and **Enter** opens it: a route drilled
+  down on the Diagram, an endpoint a route sends to with its details on
+  the Endpoint tab.
 - **Integrations** — the table of all running integrations below, shown
   when several run.
 
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/AboutViewTest.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/AboutViewTest.java
index c2352c5173c4..4701e5c185fc 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/AboutViewTest.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/AboutViewTest.java
@@ -21,6 +21,7 @@ import java.nio.file.Path;
 import java.time.Instant;
 import java.time.ZoneId;
 import java.time.format.DateTimeFormatter;
+import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicReference;
@@ -28,6 +29,7 @@ import java.util.concurrent.atomic.AtomicReference;
 import dev.tamboui.buffer.Buffer;
 import dev.tamboui.layout.Rect;
 import dev.tamboui.terminal.Frame;
+import dev.tamboui.tui.event.KeyCode;
 import dev.tamboui.tui.event.KeyEvent;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.io.TempDir;
@@ -210,5 +212,21 @@ class AboutViewTest {
                     .contains("HttpOperationFailedException 503 
×30").contains("Watch").contains("Next");
             assertThat(screen).contains("What happened").contains("30 
handled");
         });
+
+        // the cursor goes down In, Routes and Out; Enter opens what it is on
+        List<AboutView.Target> opened = new ArrayList<>();
+        ctx.openFromAbout = opened::add;
+        assertThat(about.open()).isFalse();
+        about.moveCursor(1);
+        render(about, info);
+        assertThat(about.selected()).isEqualTo(new AboutView.Target("orders", 
null));
+        about.moveCursor(2);
+        render(about, info);
+        assertThat(about.selected()).isEqualTo(new AboutView.Target("stock", 
null));
+        about.moveCursor(5);
+        render(about, info);
+        assertThat(about.selected()).isEqualTo(new AboutView.Target("orders", 
"http:localhost:8765/ship"));
+        assertThat(about.handleKeyEvent(KeyEvent.ofKey(KeyCode.ENTER), 
info)).isTrue();
+        assertThat(opened).containsExactly(new AboutView.Target("orders", 
"http:localhost:8765/ship"));
     }
 }
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/EndpointsTabRenderTest.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/EndpointsTabRenderTest.java
index 5355400b4c7c..04e8a93be721 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/EndpointsTabRenderTest.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/EndpointsTabRenderTest.java
@@ -232,6 +232,20 @@ class EndpointsTabRenderTest {
 
     // ---- Helper methods ----
 
+    @Test
+    void anEndpointOfARouteIsSelectedWithItsDetails() {
+        addEndpoint("timer", "timer://tick", "in", "a", 10);
+        addEndpoint("http", "http://example.com/api";, "out", "a", 5);
+        addEndpoint("http", "http://example.com/api";, "out", "b", 5);
+        EndpointsTab tab = new EndpointsTab(ctx, new MetricsCollector());
+        TuiTestHelper.renderToString(tab, 140, 30);
+
+        // as About has it: the URI as the sources write it
+        tab.selectEndpoint("http:example.com/api", "b");
+        assertThat(tab.tableState.selected()).isEqualTo(2);
+        assertThat(TuiTestHelper.renderToString(tab, 140, 
30)).contains("http://example.com/api";);
+    }
+
     private EndpointInfo addEndpoint(String component, String uri, String 
direction, String routeId, long hits) {
         EndpointInfo ep = new EndpointInfo();
         ep.component = component;

Reply via email to