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

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

commit 8771d4e43021897316085790e440eb7be66e4cc5
Author: Claus Ibsen <[email protected]>
AuthorDate: Thu Jun 4 18:56:00 2026 +0200

    CAMEL-23672: Add stub endpoint detection and direction arrows to 
BacklogTracer and TUI History
    
    Co-Authored-By: Claude <[email protected]>
    Signed-off-by: Claus Ibsen <[email protected]>
---
 .../camel/spi/BacklogTracerEventMessage.java       |  7 ++
 .../apache/camel/impl/debugger/BacklogTracer.java  | 10 ++-
 .../debugger/DefaultBacklogTracerEventMessage.java | 12 ++++
 .../camel/impl/engine/CamelInternalProcessor.java  | 24 +++++--
 .../dsl/jbang/core/commands/tui/HistoryEntry.java  |  2 +
 .../dsl/jbang/core/commands/tui/HistoryTab.java    | 83 +++++++++++++++++-----
 .../dsl/jbang/core/commands/tui/StatusParser.java  | 46 ++++++++++--
 .../dsl/jbang/core/commands/tui/TraceEntry.java    |  2 +
 8 files changed, 154 insertions(+), 32 deletions(-)

diff --git 
a/core/camel-api/src/main/java/org/apache/camel/spi/BacklogTracerEventMessage.java
 
b/core/camel-api/src/main/java/org/apache/camel/spi/BacklogTracerEventMessage.java
index a87c08ec0b6a..95a1405d0982 100644
--- 
a/core/camel-api/src/main/java/org/apache/camel/spi/BacklogTracerEventMessage.java
+++ 
b/core/camel-api/src/main/java/org/apache/camel/spi/BacklogTracerEventMessage.java
@@ -203,6 +203,13 @@ public interface BacklogTracerEventMessage extends 
BacklogEventMessage {
      */
     boolean isRemoteEndpoint();
 
+    /**
+     * Whether the endpoint is a stub endpoint.
+     *
+     * @since 4.21
+     */
+    boolean isStubEndpoint();
+
     /**
      * Gets the endpoint remote address such as URL, hostname, 
connection-string, or cloud region, that are component
      * specific.
diff --git 
a/core/camel-base-engine/src/main/java/org/apache/camel/impl/debugger/BacklogTracer.java
 
b/core/camel-base-engine/src/main/java/org/apache/camel/impl/debugger/BacklogTracer.java
index 873e31e628d1..bda2cccb3516 100644
--- 
a/core/camel-base-engine/src/main/java/org/apache/camel/impl/debugger/BacklogTracer.java
+++ 
b/core/camel-base-engine/src/main/java/org/apache/camel/impl/debugger/BacklogTracer.java
@@ -26,6 +26,7 @@ import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.atomic.AtomicLong;
 
 import org.apache.camel.CamelContext;
+import org.apache.camel.Endpoint;
 import org.apache.camel.Exchange;
 import org.apache.camel.ExchangePropertyKey;
 import org.apache.camel.NamedNode;
@@ -178,7 +179,14 @@ public class BacklogTracer extends ServiceSupport 
implements org.apache.camel.sp
         if ((first || last) && fromRouteId != null) {
             Route route = camelContext.getRoute(fromRouteId);
             if (route != null && route.getConsumer() != null) {
-                
event.setEndpointUri(route.getConsumer().getEndpoint().getEndpointUri());
+                Endpoint ep = route.getConsumer().getEndpoint();
+                String endpointUri = ep.getEndpointUri();
+                event.setEndpointUri(endpointUri);
+                event.setRemoteEndpoint(ep.isRemote());
+                if ((endpointUri != null && endpointUri.startsWith("stub:"))
+                        || 
"StubEndpoint".equals(ep.getClass().getSimpleName())) {
+                    event.setStubEndpoint(true);
+                }
             }
         }
         // synthetic events are snapshots, mark done immediately so elapsed 
doesn't keep growing
diff --git 
a/core/camel-base-engine/src/main/java/org/apache/camel/impl/debugger/DefaultBacklogTracerEventMessage.java
 
b/core/camel-base-engine/src/main/java/org/apache/camel/impl/debugger/DefaultBacklogTracerEventMessage.java
index dc0dd0b8755f..515b6e3e517b 100644
--- 
a/core/camel-base-engine/src/main/java/org/apache/camel/impl/debugger/DefaultBacklogTracerEventMessage.java
+++ 
b/core/camel-base-engine/src/main/java/org/apache/camel/impl/debugger/DefaultBacklogTracerEventMessage.java
@@ -58,6 +58,7 @@ public final class DefaultBacklogTracerEventMessage 
implements BacklogTracerEven
     private final String threadName;
     private String endpointUri;
     private boolean remoteEndpoint;
+    private boolean stubEndpoint;
     private String endpointServiceUrl;
     private String endpointServiceProtocol;
     private Map<String, String> endpointServiceMetadata;
@@ -292,6 +293,15 @@ public final class DefaultBacklogTracerEventMessage 
implements BacklogTracerEven
         this.remoteEndpoint = remoteEndpoint;
     }
 
+    @Override
+    public boolean isStubEndpoint() {
+        return stubEndpoint;
+    }
+
+    public void setStubEndpoint(boolean stubEndpoint) {
+        this.stubEndpoint = stubEndpoint;
+    }
+
     public void setEndpointUri(String endpointUri) {
         this.endpointUri = endpointUri;
         // dirty flag
@@ -370,6 +380,7 @@ public final class DefaultBacklogTracerEventMessage 
implements BacklogTracerEven
         if (endpointUri != null) {
             sb.append(prefix).append("  
<endpointUri>").append(endpointUri).append("</endpointUri>\n");
             sb.append(prefix).append("  
<remoteEndpoint>").append(remoteEndpoint).append("</remoteEndpoint>\n");
+            sb.append(prefix).append("  
<stubEndpoint>").append(stubEndpoint).append("</stubEndpoint>\n");
         }
         if (toNode != null) {
             sb.append(prefix).append("  
<toNode>").append(toNode).append("</toNode>\n");
@@ -562,6 +573,7 @@ public final class DefaultBacklogTracerEventMessage 
implements BacklogTracerEven
         if (endpointUri != null) {
             jo.put("endpointUri", endpointUri);
             jo.put("remoteEndpoint", remoteEndpoint);
+            jo.put("stubEndpoint", stubEndpoint);
         }
         if (routeId != null) {
             jo.put("routeId", routeId);
diff --git 
a/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/CamelInternalProcessor.java
 
b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/CamelInternalProcessor.java
index 530b8534c4d6..57de62d527e9 100644
--- 
a/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/CamelInternalProcessor.java
+++ 
b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/CamelInternalProcessor.java
@@ -727,11 +727,12 @@ public class CamelInternalProcessor extends 
DelegateAsyncProcessor implements In
             String uri = null;
             boolean remote = true;
             Endpoint endpoint = null;
+            Endpoint consumerEndpoint = null;
             if ((data.isFirst() || data.isLast())) {
                 if (route.getConsumer() != null) {
-                    // get the actual resolved uri
-                    uri = route.getConsumer().getEndpoint().getEndpointUri();
-                    remote = route.getConsumer().getEndpoint().isRemote();
+                    consumerEndpoint = route.getConsumer().getEndpoint();
+                    uri = consumerEndpoint.getEndpointUri();
+                    remote = consumerEndpoint.isRemote();
                     endpoint = route.getEndpoint();
                 } else {
                     uri = routeDefinition.getEndpointUrl();
@@ -741,6 +742,11 @@ public class CamelInternalProcessor extends 
DelegateAsyncProcessor implements In
                 data.setEndpointUri(uri);
             }
             data.setRemoteEndpoint(remote);
+            if ((uri != null && uri.startsWith("stub:"))
+                    || (consumerEndpoint != null
+                            && 
"StubEndpoint".equals(consumerEndpoint.getClass().getSimpleName()))) {
+                data.setStubEndpoint(true);
+            }
             if (endpoint instanceof EndpointServiceLocation esl) {
                 data.setEndpointServiceUrl(esl.getServiceUrl());
                 data.setEndpointServiceProtocol(esl.getServiceProtocol());
@@ -1015,6 +1021,7 @@ public class CamelInternalProcessor extends 
DelegateAsyncProcessor implements In
             String uri = null;
             boolean remote = true;
             Endpoint endpoint = notifier.after(exchange);
+            Endpoint resolvedEndpoint = endpoint;
             if (endpoint != null) {
                 uri = endpoint.getEndpointUri();
                 remote = endpoint.isRemote();
@@ -1023,9 +1030,9 @@ public class CamelInternalProcessor extends 
DelegateAsyncProcessor implements In
                 // pseudo first/last event (the from in the route)
                 Route route = 
camelContext.getRoute(routeDefinition.getRouteId());
                 if (route != null && route.getConsumer() != null) {
-                    // get the actual resolved uri
-                    uri = route.getConsumer().getEndpoint().getEndpointUri();
-                    remote = route.getConsumer().getEndpoint().isRemote();
+                    resolvedEndpoint = route.getConsumer().getEndpoint();
+                    uri = resolvedEndpoint.getEndpointUri();
+                    remote = resolvedEndpoint.isRemote();
                 } else {
                     uri = routeDefinition.getEndpointUrl();
                 }
@@ -1034,6 +1041,11 @@ public class CamelInternalProcessor extends 
DelegateAsyncProcessor implements In
                 data.setEndpointUri(uri);
             }
             data.setRemoteEndpoint(remote);
+            if ((uri != null && uri.startsWith("stub:"))
+                    || (resolvedEndpoint != null
+                            && 
"StubEndpoint".equals(resolvedEndpoint.getClass().getSimpleName()))) {
+                data.setStubEndpoint(true);
+            }
             if (endpoint instanceof EndpointServiceLocation esl) {
                 data.setEndpointServiceUrl(esl.getServiceUrl());
                 data.setEndpointServiceProtocol(esl.getServiceProtocol());
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/HistoryEntry.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/HistoryEntry.java
index 33993a524481..638ac9c0d9d5 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/HistoryEntry.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/HistoryEntry.java
@@ -34,6 +34,8 @@ class HistoryEntry {
     boolean first;
     boolean last;
     boolean failed;
+    boolean remoteEndpoint;
+    boolean stubEndpoint;
     int nodeLevel;
     long elapsed;
     long epochMs;
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 9952e4b02dd8..52e1f4b69f86 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
@@ -18,6 +18,7 @@ package org.apache.camel.dsl.jbang.core.commands.tui;
 
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Map;
@@ -80,6 +81,8 @@ class HistoryTab implements MonitorTab {
     private int traceDetailScroll;
     private int traceDetailHScroll;
 
+    private boolean showDescription;
+
     private boolean showWaterfall;
     private int waterfallScroll;
     private final ScrollbarState waterfallScrollState = new ScrollbarState();
@@ -168,6 +171,11 @@ class HistoryTab implements MonitorTab {
             }
         }
 
+        if (ke.isCharIgnoreCase('n')) {
+            showDescription = !showDescription;
+            return true;
+        }
+
         if (tracerActive && traceDetailView) {
             if (ke.isCharIgnoreCase('p')) {
                 showTraceProperties = !showTraceProperties;
@@ -398,6 +406,7 @@ class HistoryTab implements MonitorTab {
             if (!showWaterfall && !traceWordWrap) {
                 hint(spans, "←→", "h-scroll");
             }
+            hint(spans, "n", "description" + (showDescription ? " [on]" : ""));
             hint(spans, "g", "waterfall" + (showWaterfall ? " [on]" : ""));
             if (!showWaterfall) {
                 hint(spans, "d", "diagram");
@@ -411,6 +420,7 @@ class HistoryTab implements MonitorTab {
             hint(spans, "Esc", "back");
             hint(spans, "↑↓", "navigate");
             hint(spans, "s", "sort");
+            hint(spans, "n", "description" + (showDescription ? " [on]" : ""));
             hint(spans, "d", "diagram");
             hint(spans, "Enter", "details");
             hintLast(spans, "F5", "refresh");
@@ -421,6 +431,7 @@ class HistoryTab implements MonitorTab {
             if (!showWaterfall && !historyWordWrap) {
                 hint(spans, "←→", "h-scroll");
             }
+            hint(spans, "n", "description" + (showDescription ? " [on]" : ""));
             hint(spans, "g", "waterfall" + (showWaterfall ? " [on]" : ""));
             if (!showWaterfall) {
                 hint(spans, "d", "diagram");
@@ -575,7 +586,7 @@ class HistoryTab implements MonitorTab {
             rows.add(Row.from(
                     Cell.from(s.timestamp != null ? truncate(s.timestamp, 12) 
: ""),
                     Cell.from(Span.styled(
-                            s.routeId != null ? truncate(s.routeId, 15) : "",
+                            s.routeId != null ? truncate(s.routeId, 20) : "",
                             Style.EMPTY.fg(Color.CYAN))),
                     Cell.from(Span.styled(s.status, statusStyle)),
                     rightCell(s.elapsed + "ms", 10),
@@ -618,16 +629,18 @@ class HistoryTab implements MonitorTab {
                 .constraints(Constraint.length(10), Constraint.length(1), 
Constraint.fill())
                 .split(area);
 
+        Map<String, String> descMap = showDescription ? getRouteDescriptions() 
: Collections.emptyMap();
         List<Row> rows = new ArrayList<>();
         for (TraceEntry entry : steps) {
+            String desc = showDescription ? descMap.get(entry.routeId) : null;
             rows.add(buildStepRow(
                     entry.direction, entry.first, entry.last, entry.failed,
-                    entry.timestamp, entry.routeId, entry.nodeId, 
entry.processor, entry.elapsed));
+                    entry.timestamp, entry.routeId, entry.nodeId, 
entry.processor, desc, entry.elapsed));
         }
 
         String stepTitle = String.format(" Trace [%s] ", 
truncate(traceSelectedExchangeId, 30));
         frame.renderStatefulWidget(
-                buildStepTable(rows, stepTitle), chunks.get(0), 
traceStepTableState);
+                buildStepTable(rows, stepTitle, showDescription), 
chunks.get(0), traceStepTableState);
 
         if (showWaterfall) {
             renderWaterfall(frame, chunks.get(2), 
steps.stream().map(WaterfallStep::fromTrace).toList());
@@ -848,16 +861,18 @@ class HistoryTab implements MonitorTab {
                 .constraints(Constraint.length(10), Constraint.length(1), 
Constraint.fill())
                 .split(area);
 
+        Map<String, String> descMap = showDescription ? getRouteDescriptions() 
: Collections.emptyMap();
         List<Row> rows = new ArrayList<>();
         for (HistoryEntry entry : current) {
+            String desc = showDescription ? descMap.get(entry.routeId) : null;
             rows.add(buildStepRow(
                     entry.direction, entry.first, entry.last, entry.failed,
-                    entry.timestamp, entry.routeId, entry.nodeId, 
entry.processor, entry.elapsed));
+                    entry.timestamp, entry.routeId, entry.nodeId, 
entry.processor, desc, entry.elapsed));
         }
 
         Title historyTitle = buildHistoryTitle(current);
         frame.renderStatefulWidget(
-                buildStepTable(rows, historyTitle), chunks.get(0), 
historyTableState);
+                buildStepTable(rows, historyTitle, showDescription), 
chunks.get(0), historyTableState);
 
         if (showWaterfall) {
             renderWaterfall(frame, chunks.get(2), 
current.stream().map(WaterfallStep::fromHistory).toList());
@@ -908,6 +923,20 @@ class HistoryTab implements MonitorTab {
         historyDetailHScroll = hScroll[0];
     }
 
+    private Map<String, String> getRouteDescriptions() {
+        IntegrationInfo info = ctx.findSelectedIntegration();
+        if (info == null) {
+            return Collections.emptyMap();
+        }
+        Map<String, String> map = new HashMap<>();
+        for (RouteInfo ri : info.routes) {
+            if (ri.routeId != null && ri.description != null && 
!ri.description.isEmpty()) {
+                map.put(ri.routeId, ri.description);
+            }
+        }
+        return map;
+    }
+
     // ---- Shared helpers ----
 
     private List<String> getTraceExchangeIds() {
@@ -951,32 +980,32 @@ class HistoryTab implements MonitorTab {
 
     private static Row buildStepRow(
             String direction, boolean first, boolean last, boolean failed,
-            String timestamp, String routeId, String nodeId, String processor, 
long elapsed) {
+            String timestamp, String routeId, String nodeId, String processor,
+            String description, long elapsed) {
         Style dirStyle;
-        if (first) {
-            dirStyle = Style.EMPTY.fg(Color.GREEN);
-        } else if (last) {
+        if (first || last || !direction.isBlank()) {
             dirStyle = failed ? Style.EMPTY.fg(Color.LIGHT_RED) : 
Style.EMPTY.fg(Color.GREEN);
         } else {
             dirStyle = failed ? Style.EMPTY.fg(Color.LIGHT_RED) : Style.EMPTY;
         }
         String elapsedStr = elapsed >= 0 ? elapsed + "ms" : "";
+        String display = description != null ? description : (processor != 
null ? processor : "");
         return Row.from(
                 Cell.from(Span.styled(direction, dirStyle)),
                 Cell.from(timestamp != null ? truncate(timestamp, 12) : ""),
-                Cell.from(Span.styled(routeId != null ? truncate(routeId, 15) 
: "", Style.EMPTY.fg(Color.CYAN))),
+                Cell.from(Span.styled(routeId != null ? truncate(routeId, 20) 
: "", Style.EMPTY.fg(Color.CYAN))),
                 Cell.from(nodeId != null ? truncate(nodeId, 15) : ""),
-                Cell.from(processor != null ? processor : ""),
+                Cell.from(display),
                 rightCell(elapsedStr, 10));
     }
 
-    private static Table buildStepTable(List<Row> rows, Object title) {
+    private static Table buildStepTable(List<Row> rows, Object title, boolean 
descriptionMode) {
         Row header = Row.from(
                 Cell.from(Span.styled("", Style.EMPTY.bold())),
                 Cell.from(Span.styled("TIME", Style.EMPTY.bold())),
                 Cell.from(Span.styled("ROUTE", Style.EMPTY.bold())),
                 Cell.from(Span.styled("ID", Style.EMPTY.bold())),
-                Cell.from(Span.styled("PROCESSOR", Style.EMPTY.bold())),
+                Cell.from(Span.styled(descriptionMode ? "DESCRIPTION" : 
"PROCESSOR", Style.EMPTY.bold())),
                 rightCell("ELAPSED", 10, Style.EMPTY.bold()));
         Block block = title instanceof Title t
                 ? 
Block.builder().borderType(BorderType.ROUNDED).title(t).build()
@@ -1322,18 +1351,34 @@ class HistoryTab implements MonitorTab {
                 took:
 
                 ```
-                 RouteId        NodeId     Processor            Elapsed
-                 timer-to-log   timer1     from[timer:hello]    0ms
-                 timer-to-log   setBody1   setBody[simple]      0ms
-                 timer-to-log   choice1    choice               0ms
-                 timer-to-log   when1      when[simple]         0ms
-                 timer-to-log   log1       log[HIGH: ${body}]   0ms
+                      RouteId        NodeId     Processor            Elapsed
+                 *->  timer-to-log   timer1     from[timer:hello]    0ms
+                      timer-to-log   setBody1   setBody[simple]      0ms
+                      timer-to-log   choice1    choice               0ms
+                      timer-to-log   when1      when[simple]         0ms
+                 --->  timer-to-log   to1       to[kafka:orders]     2ms
+                      timer-to-log   log1       log[HIGH: ${body}]   0ms
+                 <-*  timer-to-log   timer1     from[timer:hello]    3ms
                 ```
 
                 This tells you the message entered via the timer, went through 
setBody,
                 reached a choice node, matched the `when` condition, and was 
logged.
                 The elapsed time for each step helps identify bottlenecks.
 
+                ## Direction Arrows
+
+                The first column shows direction arrows that indicate the type
+                of each step:
+
+                - `*-->` — First step of a route consuming from a **remote** 
endpoint (e.g., Kafka, HTTP)
+                - `*-> ` — First step of a route consuming from a **local** 
endpoint (e.g., timer, direct)
+                - `<--*` — Last step of a route with a **remote** consumer 
endpoint
+                - `<-* ` — Last step of a route with a **local** consumer 
endpoint
+                - `--->` — A step that sends to a **remote** endpoint (e.g., 
`to[kafka:orders]`)
+                - `~-->` — First step or send to a **stub** endpoint (running 
with `--stub` mode)
+                - `<--~` — Last step of a route with a **stub** consumer 
endpoint
+                - _(blank)_ — A regular processing step (log, setBody, choice, 
etc.)
+
                 **Exchange Content** — Toggle these sections to inspect the 
message:
 
                 - `h` — **Headers**: Key-value pairs carried with the message 
(e.g., `Content-Type`, `CamelFileName`, custom headers)
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/StatusParser.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/StatusParser.java
index 4932e49c4e74..386faca53ebb 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/StatusParser.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/StatusParser.java
@@ -553,12 +553,19 @@ final class StatusParser {
             entry.timestamp = tsObj.toString();
         }
 
+        entry.remoteEndpoint = json.getBooleanOrDefault("remoteEndpoint", 
false);
+        entry.stubEndpoint = json.getBooleanOrDefault("stubEndpoint", false);
+
         if (entry.first || entry.last) {
             entry.nodeLevel = Math.max(0, entry.nodeLevel - 1);
         }
         String indent = "  ".repeat(entry.nodeLevel);
         if (entry.first) {
-            entry.direction = "-->";
+            if (entry.stubEndpoint) {
+                entry.direction = "~-->";
+            } else {
+                entry.direction = entry.remoteEndpoint ? "*-->" : "*-> ";
+            }
             String uri = json.getString("endpointUri");
             if (uri != null) {
                 entry.processor = indent + "from[" + uri + "]";
@@ -566,10 +573,20 @@ final class StatusParser {
                 entry.processor = indent + (entry.nodeLabel != null ? 
entry.nodeLabel : "");
             }
         } else if (entry.last) {
-            entry.direction = "<--";
+            if (entry.stubEndpoint) {
+                entry.direction = "<--~";
+            } else {
+                entry.direction = entry.remoteEndpoint ? "<--*" : "<-* ";
+            }
             entry.processor = indent + (entry.nodeLabel != null ? 
entry.nodeLabel : "");
         } else {
-            entry.direction = "  >";
+            if (entry.stubEndpoint) {
+                entry.direction = "~-->";
+            } else if (entry.remoteEndpoint) {
+                entry.direction = "--->";
+            } else {
+                entry.direction = "    ";
+            }
             entry.processor = indent + (entry.nodeLabel != null ? 
entry.nodeLabel : "");
         }
 
@@ -618,6 +635,9 @@ final class StatusParser {
         entry.failed = json.getBooleanOrDefault("failed", false);
         entry.nodeLevel = json.getIntegerOrDefault("nodeLevel", 0);
 
+        entry.remoteEndpoint = json.getBooleanOrDefault("remoteEndpoint", 
false);
+        entry.stubEndpoint = json.getBooleanOrDefault("stubEndpoint", false);
+
         Object elapsedObj = json.get("elapsed");
         if (elapsedObj instanceof Number n) {
             entry.elapsed = n.longValue();
@@ -626,11 +646,25 @@ final class StatusParser {
         }
 
         if (entry.first) {
-            entry.direction = "-->";
+            if (entry.stubEndpoint) {
+                entry.direction = "~-->";
+            } else {
+                entry.direction = entry.remoteEndpoint ? "*-->" : "*-> ";
+            }
         } else if (entry.last) {
-            entry.direction = "<--";
+            if (entry.stubEndpoint) {
+                entry.direction = "<--~";
+            } else {
+                entry.direction = entry.remoteEndpoint ? "<--*" : "<-* ";
+            }
         } else {
-            entry.direction = "  >";
+            if (entry.stubEndpoint) {
+                entry.direction = "~-->";
+            } else if (entry.remoteEndpoint) {
+                entry.direction = "--->";
+            } else {
+                entry.direction = "    ";
+            }
         }
 
         if (entry.first || entry.last) {
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/TraceEntry.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/TraceEntry.java
index bea88a7f8ee2..3c88ec2a3950 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/TraceEntry.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/TraceEntry.java
@@ -35,6 +35,8 @@ class TraceEntry {
     boolean first;
     boolean last;
     boolean failed;
+    boolean remoteEndpoint;
+    boolean stubEndpoint;
     int nodeLevel;
     long elapsed;
     long epochMs;

Reply via email to