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

davsclaus pushed a commit to branch CAMEL-26864-tui-walk-bugs
in repository https://gitbox.apache.org/repos/asf/camel.git

commit 99d1391bb7f44c4aec488d7bcc811ee9ca7d2492
Author: Claus Ibsen <[email protected]>
AuthorDate: Wed Nov 11 17:46:15 2026 +0100

    CAMEL-26864: camel-jbang - Diagram drill-down from the architecture, 
Inspect diagram follows the history, avg body without hand-offs, MCP keys go to 
the tab
    
    - Diagram: drilling into a route (or selecting one) closes the architecture
      view, which kept being drawn while the state said route, and left the node
      boxes of the route drawn before; diagramMode says architecture.
    - Inspect: the Diagram view draws the exchange of the history when it is 
read
      again (tab opened again, F5), so the screen and the table agree.
    - Endpoints: the avg body chart leaves out the hand-offs between the routes,
      as the counts do.
    - tui_send_keys: the keys go to the active tab, not the AI panel's input, 
and
      the result says which tab.
    
    Co-Authored-By: Claude Opus 5.5 <[email protected]>
    Signed-off-by: Claus Ibsen <[email protected]>
---
 .../dsl/jbang/core/commands/tui/CamelMonitor.java  |  2 +-
 .../dsl/jbang/core/commands/tui/Confirmations.java |  8 +++
 .../dsl/jbang/core/commands/tui/DiagramTab.java    |  9 ++-
 .../dsl/jbang/core/commands/tui/EndpointFlow.java  | 19 +++++-
 .../dsl/jbang/core/commands/tui/HistoryTab.java    | 18 ++++++
 .../dsl/jbang/core/commands/tui/McpFacade.java     | 12 +++-
 .../jbang/core/commands/tui/MetricsCollector.java  | 10 +--
 .../core/commands/tui/TuiToolDefinitions.java      | 10 +--
 .../jbang/core/commands/tui/TuiToolRegistry.java   |  4 +-
 .../src/main/resources/tui/help/endpoints.md       |  4 +-
 .../jbang/core/commands/tui/ConfirmationsTest.java | 33 +++++++++-
 .../tui/DiagramTabArchitectureNavigationTest.java  | 75 ++++++++++++++++++++++
 .../jbang/core/commands/tui/EndpointFlowTest.java  | 19 +++++-
 13 files changed, 197 insertions(+), 26 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 204eb57dac71..ca05f124963a 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
@@ -1238,7 +1238,7 @@ public class CamelMonitor extends CamelCommand {
             if (popupManager.handleKeyEvent(ke, 
tabRegistry.selectedTabIndex(), TAB_LOG)) {
                 return true;
             }
-            if (aiPanel.isOpen()) {
+            if (aiPanel.isOpen() && !confirmations.isDeliveringMcpKey()) {
                 if (ke.isKey(KeyCode.F8) && ke.hasShift()) {
                     if (lastContentArea != null) {
                         aiPanel.cycleHeight(lastContentArea.height());
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/Confirmations.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/Confirmations.java
index 9076cd41f9bb..c9dde27d35c9 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/Confirmations.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/Confirmations.java
@@ -44,6 +44,7 @@ final class Confirmations {
     private final List<Dialog> dialogs = new ArrayList<>();
     private final Queue<McpFacade.PendingKey> pendingKeys;
     private boolean delivering;
+    private boolean deliveringFromMcp;
 
     Confirmations(Queue<McpFacade.PendingKey> pendingKeys) {
         this.pendingKeys = pendingKeys;
@@ -118,10 +119,12 @@ final class Confirmations {
                 continue;
             }
             delivering = true;
+            deliveringFromMcp = key.fromMcp();
             try {
                 handler.accept(key.event());
             } finally {
                 delivering = false;
+                deliveringFromMcp = false;
             }
         }
         return taken;
@@ -135,4 +138,9 @@ final class Confirmations {
     boolean isDelivering() {
         return delivering;
     }
+
+    /** Whether the key being handled was sent by an MCP client: it is for the 
active tab, not the AI panel's input. */
+    boolean isDeliveringMcpKey() {
+        return deliveringFromMcp;
+    }
 }
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 44200a466296..5512f2ba7042 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
@@ -19,6 +19,7 @@ package org.apache.camel.dsl.jbang.core.commands.tui;
 import java.util.ArrayList;
 import java.util.LinkedHashSet;
 import java.util.List;
+import java.util.Locale;
 import java.util.Map;
 import java.util.Set;
 
@@ -799,6 +800,8 @@ class DiagramTab extends AbstractDiagramTab {
         if (idx < 0) {
             return false;
         }
+        // the route is selected in the topology, so that is the level shown, 
not the architecture over it
+        architecture.close();
         diagram.setSelectedNodeIndex(idx);
         diagram.scrollToSelectedNode();
         return true;
@@ -816,6 +819,9 @@ class DiagramTab extends AbstractDiagramTab {
             if (info == null || info.routes.stream().noneMatch(r -> 
routeId.equals(r.routeId))) {
                 return false;
             }
+            // the route is shown, not the architecture: while that is active 
the route is not drawn, and its node
+            // boxes stay those of the route drawn before (CAMEL-26864)
+            architecture.close();
             routeNavigationStack.clear();
             drillDownRouteId = routeId;
             topologyMode = false;
@@ -943,7 +949,8 @@ class DiagramTab extends AbstractDiagramTab {
             return null;
         }
         JsonObject result = new JsonObject();
-        result.put("diagramMode", topologyMode ? "topology" : "route");
+        // the level the screen shows: the architecture is drawn over the 
topology or route
+        result.put("diagramMode", level().name().toLowerCase(Locale.ROOT));
         result.put("zoom", zoomStateAsJson());
 
         if (!topologyMode && drillDownRouteId != null) {
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/EndpointFlow.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/EndpointFlow.java
index 486d0fefadc0..3928e7fe11f2 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/EndpointFlow.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/EndpointFlow.java
@@ -19,6 +19,7 @@ package org.apache.camel.dsl.jbang.core.commands.tui;
 import java.util.HashSet;
 import java.util.Set;
 import java.util.function.Predicate;
+import java.util.stream.Stream;
 
 /**
  * The messages that come into an integration and go out of it, for the flow 
of the Endpoints tab and its rate chart. An
@@ -50,10 +51,22 @@ final class EndpointFlow {
 
     /** The hits of the endpoints of the direction the filter takes, without 
the hand-offs between its routes. */
     static long total(IntegrationInfo info, String direction, 
Predicate<EndpointInfo> filter) {
+        return flow(info, direction).filter(filter).mapToLong(ep -> 
ep.hits).sum();
+    }
+
+    /**
+     * The largest mean body size of the endpoints of the direction, without 
the hand-offs between its routes, as the
+     * counts of the flow (CAMEL-26864: the avg body chart said out 7 B where 
the flow said 0 out).
+     */
+    static long meanBodySize(IntegrationInfo info, String direction) {
+        return flow(info, direction).filter(ep -> ep.meanBodySize >= 
0).mapToLong(ep -> ep.meanBodySize).max()
+                .orElse(0);
+    }
+
+    private static Stream<EndpointInfo> flow(IntegrationInfo info, String 
direction) {
         Set<String> handOffs = handOffs(info);
         return info.endpoints.stream()
-                .filter(ep -> direction.equals(ep.direction) && 
filter.test(ep))
-                .filter(ep -> ep.uri == null || 
!handOffs.contains(DiagramTopology.endpointKey(ep.uri)))
-                .mapToLong(ep -> ep.hits).sum();
+                .filter(ep -> direction.equals(ep.direction))
+                .filter(ep -> ep.uri == null || 
!handOffs.contains(DiagramTopology.endpointKey(ep.uri)));
     }
 }
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 6345bae4b9dd..6fb7c97f113a 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
@@ -723,6 +723,7 @@ class HistoryTab extends AbstractTab {
         if (!historyEntries.isEmpty()) {
             historyTableState.select(0);
         }
+        redrawHistoryDiagram();
         traceDetailView = false;
         traceSelectedExchangeId = null;
         if (!traces.get().isEmpty()) {
@@ -740,10 +741,27 @@ class HistoryTab extends AbstractTab {
         if (historyRefreshRequested || historyFollow) {
             historyRefreshRequested = false;
             historyEntries = data.loadHistoryData(pids);
+            redrawHistoryDiagram();
         }
         data.refreshTraceData(pids);
     }
 
+    /**
+     * The Diagram view draws the exchange of the history as it was when the 
view opened: when the history was read
+     * again and is another exchange now, the diagram is drawn for that one, 
so the screen and the table agree
+     * (CAMEL-26864).
+     */
+    private void redrawHistoryDiagram() {
+        if (!diagram.isShowDiagram() || !traces.get().isEmpty() || 
historyEntries.isEmpty()
+                || diagramHistorySteps.isEmpty()) {
+            return;
+        }
+        String shown = diagramHistorySteps.get(0).exchangeId;
+        if (!Objects.equals(shown, 
reorderHistoryDepthFirst(historyEntries).get(0).exchangeId)) {
+            loadDiagramForCurrentView();
+        }
+    }
+
     boolean isTraceDetailView() {
         return traceDetailView;
     }
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/McpFacade.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/McpFacade.java
index 486c1750122e..e7b8cb52f2b9 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/McpFacade.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/McpFacade.java
@@ -65,7 +65,15 @@ class McpFacade {
      * Pending key event queued for injection into the TUI event loop, by an 
MCP client or a tape. It fires on the first
      * tick at or after fireAt, a {@link MonotonicClock} time.
      */
-    record PendingKey(KeyEvent event, long fireAt) {
+    /**
+     * A key sent to the monitor. One from an MCP client ({@code fromMcp}) is 
for the active tab: the AI panel's input
+     * takes only what the user types (CAMEL-26864). One from a tape is as the 
user typed it.
+     */
+    record PendingKey(KeyEvent event, long fireAt, boolean fromMcp) {
+
+        PendingKey(KeyEvent event, long fireAt) {
+            this(event, fireAt, false);
+        }
 
         /** Whether the key fires at the given {@link MonotonicClock} time. */
         boolean isDue(long now) {
@@ -662,7 +670,7 @@ class McpFacade {
         for (String key : keys) {
             KeyEvent ke = parseKey(key);
             if (ke != null) {
-                pendingKeys.add(new PendingKey(ke, fireAt));
+                pendingKeys.add(new PendingKey(ke, fireAt, true));
                 fireAt += delayMs;
                 count++;
             }
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/MetricsCollector.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/MetricsCollector.java
index e6838df8cd42..a050ef0cb194 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/MetricsCollector.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/MetricsCollector.java
@@ -268,13 +268,9 @@ class MetricsCollector {
                 endpointRemoteStubSamples, previousEndpointRemoteStubTime,
                 endpointRemoteStubInHistory, endpointRemoteStubOutHistory);
 
-        // Record payload size snapshots (mean body size per direction)
-        long inMeanSize = info.endpoints.stream()
-                .filter(ep -> "in".equals(ep.direction) && ep.meanBodySize >= 
0)
-                .mapToLong(ep -> ep.meanBodySize).max().orElse(0);
-        long outMeanSize = info.endpoints.stream()
-                .filter(ep -> "out".equals(ep.direction) && ep.meanBodySize >= 
0)
-                .mapToLong(ep -> ep.meanBodySize).max().orElse(0);
+        // Record payload size snapshots (mean body size per direction), of 
what comes in and goes out as above
+        long inMeanSize = EndpointFlow.meanBodySize(info, "in");
+        long outMeanSize = EndpointFlow.meanBodySize(info, "out");
         Long lastSizeTime = previousEndpointSizeTime.get(pid);
         if (lastSizeTime == null || now - lastSizeTime >= 1000) {
             previousEndpointSizeTime.put(pid, now);
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/TuiToolDefinitions.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/TuiToolDefinitions.java
index 965dc9246727..a61e49fb9b7e 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/TuiToolDefinitions.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/TuiToolDefinitions.java
@@ -140,11 +140,11 @@ final class TuiToolDefinitions {
 
         tools.add(tool(toolDef(
                 "tui_send_keys",
-                "Sends key presses to the TUI. A human is watching the screen, 
so keys should be paced naturally "
-                                 + "like a skilled user would type. Key names: 
Enter, Esc, Tab, Backspace, Delete, Up, "
-                                 + "Down, Left, Right, Home, End, PgUp, PgDn, 
Space, F1-F12, or any single character. "
-                                 + "Modifiers: Ctrl+x, Shift+x, Ctrl+Shift+x. 
Keys never answer a dialog that asks the "
-                                 + "user to confirm an action (quit, a file 
write): only the user does.",
+                "Sends key presses to the active tab (not the AI panel's 
input). A human is watching, so pace "
+                                 + "them as a skilled user types. Key names: 
Enter, Esc, Tab, Backspace, Delete, Up, Down, "
+                                 + "Left, Right, Home, End, PgUp, PgDn, Space, 
F1-F12, or any single character. Modifiers: "
+                                 + "Ctrl+x, Shift+x, Ctrl+Shift+x. Keys never 
answer a dialog that asks the user to "
+                                 + "confirm an action (quit, a file write): 
only the user does.",
                 Map.of("keys", propDef("array", "Array of key name strings to 
send"),
                         "delay", propDef("integer",
                                 "Delay in milliseconds between keys (default 
150, minimum 80)"),
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/TuiToolRegistry.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/TuiToolRegistry.java
index 2761f41ef244..adb18f905e87 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/TuiToolRegistry.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/TuiToolRegistry.java
@@ -537,7 +537,8 @@ class TuiToolRegistry {
         int sent = facade.injectKeys(keys, delay);
 
         if (!wait) {
-            return "Queued " + sent + " key(s) with " + delay + "ms delay";
+            // for the active tab: the AI panel's input, when it is open, 
takes only what the user types
+            return "Queued " + sent + " key(s) with " + delay + "ms delay for 
the " + facade.getActiveTabName() + " tab";
         }
 
         long lastKeyFireAt = MonotonicClock.millis() + (long) (sent - 1) * 
delay;
@@ -563,6 +564,7 @@ class TuiToolRegistry {
         Buffer buf = facade.getLastBuffer();
         JsonObject result = new JsonObject();
         result.put("sent", sent);
+        result.put("activeTab", facade.getActiveTabName());
         result.put("delay", delay);
         result.put("waitedMs", MonotonicClock.millis() - start);
         if (buf != null) {
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/resources/tui/help/endpoints.md
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/resources/tui/help/endpoints.md
index 430a77ef4600..b9d9d7f390f6 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/resources/tui/help/endpoints.md
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/resources/tui/help/endpoints.md
@@ -49,8 +49,8 @@ The left box lists all inbound components (consumers), the 
right
 box lists all outbound components (producers). The integration
 name sits in the middle, showing the overall message flow direction.
 
-The numbers, and the in and out of the throughput chart, are the
-messages that come into the integration and go out of it. An endpoint
+The numbers, the in and out of the throughput chart, and the avg body
+chart are of the messages that come into the integration and go out of it. An 
endpoint
 one of its routes sends to and another consumes (`direct`, `seda`) is
 a hand-off between its routes and is left out: it would count each
 message twice, as an out and an in, and say it left. The table still
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/ConfirmationsTest.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/ConfirmationsTest.java
index 6b81a39fd80f..ad0c58fd8edf 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/ConfirmationsTest.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/ConfirmationsTest.java
@@ -29,6 +29,7 @@ import java.util.concurrent.atomic.AtomicReference;
 import dev.tamboui.tui.event.KeyCode;
 import dev.tamboui.tui.event.KeyEvent;
 import dev.tamboui.tui.event.KeyModifiers;
+import dev.tamboui.widgets.tabs.TabsState;
 import org.apache.camel.dsl.jbang.core.common.CommandLineHelper;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.io.TempDir;
@@ -63,6 +64,31 @@ class ConfirmationsTest {
         assertThat(confirmations.isDelivering()).isFalse();
     }
 
+    @Test
+    void aKeyOfAnMcpClientIsForTheActiveTabNotTheAiPanelInput() {
+        // CAMEL-26864: tui_send_keys sent Enter while the AI panel was open, 
and the panel's input took it
+        pending.add(new McpFacade.PendingKey(KeyEvent.ofChar('a'), 0, true));
+        pending.add(due(KeyEvent.ofChar('b')));
+        List<String> fromMcp = new ArrayList<>();
+
+        confirmations.deliverDue(0, key -> {
+            if (confirmations.isDeliveringMcpKey()) {
+                fromMcp.add(key.string());
+            }
+        });
+
+        // a tape plays the keys as the user typed them, into the panel too
+        assertThat(fromMcp).containsExactly("a");
+        assertThat(confirmations.isDeliveringMcpKey()).isFalse();
+
+        // tui_send_keys marks its keys
+        McpFacade facade = new McpFacade(
+                new MonitorContext(new AtomicReference<>(List.of()), new 
AtomicReference<>(List.of())),
+                new AtomicReference<>(List.of()), null, null, null, null, 
null, null, null, null, null, pending, null);
+        assertThat(facade.injectKeys(List.of("Enter"), 100)).isEqualTo(1);
+        assertThat(pending.poll().fromMcp()).isTrue();
+    }
+
     @Test
     void aKeyThatComesWhileADialogAsksToConfirmIsDropped() {
         // q opens the quit confirmation; the Enter queued right behind it 
must not answer it
@@ -128,8 +154,8 @@ class ConfirmationsTest {
         AtomicBoolean chaosConfirm = new AtomicBoolean(true);
         Confirmations chaos = new Confirmations(pending).add("a confirm dialog 
of the tab", chaosConfirm::get, () -> true);
         McpFacade facade = new McpFacade(
-                null, new AtomicReference<>(List.of()), null, new 
RecordingManager(null), null, null, null, null, null,
-                null, null, pending, null);
+                null, new AtomicReference<>(List.of()), new TabsState(0), new 
RecordingManager(null), null, null, null,
+                null, null, null, null, pending, null);
         facade.setConfirmations(chaos);
         TuiToolRegistry tools = new TuiToolRegistry(facade);
 
@@ -137,7 +163,8 @@ class ConfirmationsTest {
                 .startsWith("Not sent: a confirm dialog of the tab is 
open").endsWith("(a lone Esc cancels it).");
         assertThat(tools.callSendKeys(Map.of("keys", List.of("Esc", 
"y")))).startsWith("Not sent");
         assertThat(pending).isEmpty();
-        assertThat(tools.callSendKeys(Map.of("keys", 
List.of("Esc")))).startsWith("Queued 1 key");
+        assertThat(tools.callSendKeys(Map.of("keys", 
List.of("Esc")))).startsWith("Queued 1 key")
+                .endsWith("for the Overview tab");
         assertThat(pending).hasSize(1);
     }
 
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/DiagramTabArchitectureNavigationTest.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/DiagramTabArchitectureNavigationTest.java
new file mode 100644
index 000000000000..1eb33c99bfa2
--- /dev/null
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/DiagramTabArchitectureNavigationTest.java
@@ -0,0 +1,75 @@
+/*
+ * 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.Map;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.apache.camel.diagram.TopologyLayoutEngine.TopologyNodeInfo;
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * CAMEL-26864: tui_navigate drilled into a route while the Diagram tab showed 
the architecture. The state said route,
+ * the screen still drew the architecture, and the route's node boxes stayed 
those of the route drawn before.
+ */
+class DiagramTabArchitectureNavigationTest {
+
+    @Test
+    void goingToARouteOrSelectingOneLeavesTheArchitecture() {
+        IntegrationInfo info = new IntegrationInfo();
+        info.pid = "1234";
+        info.name = "hello";
+        info.routes.add(route("hello"));
+        info.routes.add(route("seven"));
+        MonitorContext ctx = new MonitorContext(new 
AtomicReference<>(List.of(info)), new AtomicReference<>(List.of()));
+        ctx.selectedPid = "1234";
+        DiagramTab tab = new DiagramTab(ctx);
+        // the topology lists the routes in another order than the integration
+        tab.diagram.showCustomTopology(List.of(node("seven"), node("hello")), 
List.of(), Map.of(), null);
+
+        tab.goToLevel(DiagramTab.Level.ARCHITECTURE);
+        assertThat(tab.level()).isEqualTo(DiagramTab.Level.ARCHITECTURE);
+        
assertThat(tab.getDiagramStateAsJson().getString("diagramMode")).as("what the 
screen shows")
+                .isEqualTo("architecture");
+
+        assertThat(tab.selectNode("hello", null)).isTrue();
+        assertThat(tab.level()).isEqualTo(DiagramTab.Level.ROUTE);
+        
assertThat(tab.getDiagramStateAsJson().getString("diagramMode")).isEqualTo("route");
+        
assertThat(tab.getDiagramStateAsJson().getString("routeId")).isEqualTo("hello");
+
+        tab.goToLevel(DiagramTab.Level.ARCHITECTURE);
+        assertThat(tab.selectRoute("seven")).isTrue();
+        assertThat(tab.level()).isEqualTo(DiagramTab.Level.TOPOLOGY);
+    }
+
+    private static RouteInfo route(String id) {
+        RouteInfo r = new RouteInfo();
+        r.routeId = id;
+        return r;
+    }
+
+    private static TopologyNodeInfo node(String id) {
+        TopologyNodeInfo n = new TopologyNodeInfo();
+        n.routeId = id;
+        n.from = "";
+        n.nodeType = "route";
+        return n;
+    }
+}
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/EndpointFlowTest.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/EndpointFlowTest.java
index 354be3b0e870..84bfe9fcafa5 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/EndpointFlowTest.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/EndpointFlowTest.java
@@ -43,12 +43,29 @@ class EndpointFlowTest {
         assertThat(EndpointFlow.total(info, "out", ep -> true)).isEqualTo(5);
     }
 
-    private static void add(IntegrationInfo info, String routeId, String 
direction, String uri, long hits) {
+    @Test
+    void theAvgBodyIsOfWhatComesInAndGoesOutAsTheCounts() {
+        // CAMEL-26864: hello sends to direct:seven, which route seven 
consumes; the flow said 0 out, the avg body 7 B
+        IntegrationInfo info = new IntegrationInfo();
+        add(info, "hello", "in", "timer://tick?period=2000", 123).meanBodySize 
= 0;
+        add(info, "hello", "out", "direct://seven", 26).meanBodySize = 7;
+        add(info, "seven", "in", "direct://seven", 26).meanBodySize = 7;
+
+        assertThat(EndpointFlow.total(info, "out", ep -> true)).isZero();
+        assertThat(EndpointFlow.meanBodySize(info, "in")).isZero();
+        assertThat(EndpointFlow.meanBodySize(info, "out")).isZero();
+
+        add(info, "seven", "out", "kafka:lucky", 26).meanBodySize = 8;
+        assertThat(EndpointFlow.meanBodySize(info, "out")).isEqualTo(8);
+    }
+
+    private static EndpointInfo add(IntegrationInfo info, String routeId, 
String direction, String uri, long hits) {
         EndpointInfo ep = new EndpointInfo();
         ep.routeId = routeId;
         ep.direction = direction;
         ep.uri = uri;
         ep.hits = hits;
         info.endpoints.add(ep);
+        return ep;
     }
 }

Reply via email to