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 5ddf812bfe07 CAMEL-24372: Fix Esc closing parent screen when goto 
popup is open
5ddf812bfe07 is described below

commit 5ddf812bfe070354c7e1b32a7e528c074737c087
Author: Claus Ibsen <[email protected]>
AuthorDate: Mon Aug 10 18:27:23 2026 +0200

    CAMEL-24372: Fix Esc closing parent screen when goto popup is open
    
    Handle goto route/node popup Esc in handleEscape() so it closes the
    popup instead of propagating to CamelMonitor which navigates away.
    
    CAMEL-24372: Goto node popup tree format matching diagram panel
    
    Render goto node entries as type[label] with tree connectors (├─/└─)
    matching the Diagram tab's route tree panel. Normalize indent depths
    to sequential levels. Fix endpoint EIP indent using the declaration
    line instead of the deeper uri: line.
    
    Co-Authored-By: Claude Opus 4.6 <[email protected]>
    Signed-off-by: Claus Ibsen <[email protected]>
---
 .../core/commands/tui/GotoSourceNodePopup.java     | 138 +++++++++++++++++----
 .../dsl/jbang/core/commands/tui/SourceTab.java     |   8 ++
 .../core/commands/tui/YamlRouteNodeScanner.java    |   4 +-
 3 files changed, 128 insertions(+), 22 deletions(-)

diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/GotoSourceNodePopup.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/GotoSourceNodePopup.java
index df36e44cdac5..12909344d7c2 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/GotoSourceNodePopup.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/GotoSourceNodePopup.java
@@ -161,50 +161,51 @@ class GotoSourceNodePopup {
         Style normalStyle = Style.EMPTY;
         Style matchStyle = Theme.label().bold();
         Style dimStyle = Style.EMPTY.dim();
+        Style treeStyle = Style.EMPTY.dim();
         Style routeStyle = Theme.label().bold();
 
-        for (YamlRouteNodeScanner.NodeEntry entry : filteredEntries) {
+        for (int idx = 0; idx < filteredEntries.size(); idx++) {
+            YamlRouteNodeScanner.NodeEntry entry = filteredEntries.get(idx);
             List<Span> spans = new ArrayList<>();
-            String indent = " ".repeat(entry.indent() * 2 + 1);
 
             if (entry.kind() == YamlRouteNodeScanner.EntryKind.ROUTE) {
-                spans.add(Span.raw(indent));
-                String routeLabel = entry.routeId() + "  " + entry.fromUri();
+                // route[routeId]  from[uri]
+                spans.add(Span.raw(" "));
+                String searchable = "route[" + entry.routeId() + "]";
+                Color routeColor = DiagramColors.getEipColor("route");
                 if (filter.hasFilter()) {
-                    int[] match = FuzzyFilter.fuzzyMatch(routeLabel, 
filter.filter());
+                    int[] match = FuzzyFilter.fuzzyMatch(searchable, 
filter.filter());
                     if (match != null) {
-                        spans.addAll(FuzzyFilter.highlightLine(routeLabel, 
match, routeStyle, matchStyle).spans());
+                        spans.addAll(FuzzyFilter.highlightLine(searchable, 
match, routeStyle, matchStyle).spans());
                     } else {
-                        spans.add(Span.styled(routeLabel, routeStyle));
+                        spans.add(Span.styled(searchable, routeStyle));
                     }
                 } else {
-                    spans.add(Span.styled(routeLabel, routeStyle));
+                    spans.add(Span.styled(searchable, routeStyle));
                 }
-                String fileName = shortFileName(entry.filePath());
-                spans.add(Span.styled("  " + fileName + ":" + 
(entry.lineIndex() + 1), dimStyle));
             } else {
-                spans.add(Span.raw(indent));
+                // tree prefix + type[label] matching diagram panel format
+                String prefix = buildTreePrefix(filteredEntries, idx, entry);
+                spans.add(Span.styled(prefix, treeStyle));
                 String typeTag = entry.type();
-                String typePad = " ".repeat(Math.max(0, maxTypeW - 
typeTag.length()));
                 Color eipColor = 
DiagramColors.getEipColor(SourceViewer.dashToCamelCase(typeTag));
-                spans.add(Span.styled("[" + typeTag + "]" + typePad, 
Style.EMPTY.fg(eipColor).bold()));
-                spans.add(Span.raw(" "));
-
-                String searchable = entry.label().isBlank() ? entry.type() : 
entry.label();
-                if (searchable.length() > maxLabelW && maxLabelW > 3) {
-                    searchable = searchable.substring(0, maxLabelW - 1) + "…";
+                String label = entry.label().isBlank() ? "" : entry.label();
+                if (label.length() > maxLabelW && maxLabelW > 3) {
+                    label = label.substring(0, maxLabelW - 1) + "…";
                 }
+                String searchable = typeTag + "[" + label + "]";
 
                 if (filter.hasFilter()) {
                     int[] nameMatch = FuzzyFilter.fuzzyMatch(searchable, 
filter.filter());
                     if (nameMatch != null) {
-                        Line hl = FuzzyFilter.highlightLine(searchable, 
nameMatch, normalStyle, matchStyle);
+                        Line hl = FuzzyFilter.highlightLine(searchable, 
nameMatch,
+                                Style.EMPTY.fg(eipColor), matchStyle);
                         spans.addAll(hl.spans());
                     } else {
-                        spans.add(Span.styled(searchable, normalStyle));
+                        spans.add(Span.styled(searchable, 
Style.EMPTY.fg(eipColor)));
                     }
                 } else {
-                    spans.add(Span.styled(searchable, normalStyle));
+                    spans.add(Span.styled(searchable, 
Style.EMPTY.fg(eipColor)));
                 }
             }
 
@@ -245,6 +246,101 @@ class GotoSourceNodePopup {
         }
     }
 
+    private static String buildTreePrefix(
+            List<YamlRouteNodeScanner.NodeEntry> entries, int idx, 
YamlRouteNodeScanner.NodeEntry entry) {
+        // Normalize indent to sequential depth within the route group
+        int depth = normalizeDepth(entries, idx, entry);
+        boolean last = isLastSiblingNorm(entries, idx, entry.indent());
+        StringBuilder sb = new StringBuilder(" ");
+        for (int d = 1; d < depth; d++) {
+            sb.append(hasAncestorSiblingNorm(entries, idx, d, entries) ? "│ " 
: "  ");
+        }
+        if (depth > 0) {
+            sb.append(last ? "└─" : "├─");
+        }
+        return sb.toString();
+    }
+
+    private static int normalizeDepth(
+            List<YamlRouteNodeScanner.NodeEntry> entries, int idx,
+            YamlRouteNodeScanner.NodeEntry entry) {
+        // Walk backwards to find ancestor chain and count distinct indent 
levels
+        int depth = 1;
+        int curIndent = entry.indent();
+        for (int i = idx - 1; i >= 0; i--) {
+            YamlRouteNodeScanner.NodeEntry e = entries.get(i);
+            if (e.kind() == YamlRouteNodeScanner.EntryKind.ROUTE) {
+                break;
+            }
+            if (e.indent() < curIndent) {
+                depth++;
+                curIndent = e.indent();
+            }
+        }
+        return depth;
+    }
+
+    private static boolean 
isLastSiblingNorm(List<YamlRouteNodeScanner.NodeEntry> entries, int idx, int 
rawIndent) {
+        for (int i = idx + 1; i < entries.size(); i++) {
+            YamlRouteNodeScanner.NodeEntry e = entries.get(i);
+            if (e.kind() == YamlRouteNodeScanner.EntryKind.ROUTE) {
+                return true;
+            }
+            if (e.indent() < rawIndent) {
+                return true;
+            }
+            if (e.indent() == rawIndent) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    private static boolean hasAncestorSiblingNorm(
+            List<YamlRouteNodeScanner.NodeEntry> entries, int idx, int 
normDepth,
+            List<YamlRouteNodeScanner.NodeEntry> allEntries) {
+        // Find the raw indent that corresponds to normDepth for this entry's 
route
+        int rawIndent = findRawIndentForDepth(entries, idx, normDepth);
+        if (rawIndent < 0) {
+            return false;
+        }
+        for (int i = idx + 1; i < entries.size(); i++) {
+            YamlRouteNodeScanner.NodeEntry e = entries.get(i);
+            if (e.kind() == YamlRouteNodeScanner.EntryKind.ROUTE) {
+                return false;
+            }
+            if (e.indent() < rawIndent) {
+                return false;
+            }
+            if (e.indent() == rawIndent) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private static int 
findRawIndentForDepth(List<YamlRouteNodeScanner.NodeEntry> entries, int idx, 
int targetDepth) {
+        // Walk backwards collecting ancestor indent levels
+        java.util.List<Integer> indents = new java.util.ArrayList<>();
+        int curIndent = entries.get(idx).indent();
+        indents.add(curIndent);
+        for (int i = idx - 1; i >= 0; i--) {
+            YamlRouteNodeScanner.NodeEntry e = entries.get(i);
+            if (e.kind() == YamlRouteNodeScanner.EntryKind.ROUTE) {
+                break;
+            }
+            if (e.indent() < curIndent) {
+                indents.add(e.indent());
+                curIndent = e.indent();
+            }
+        }
+        java.util.Collections.reverse(indents);
+        if (targetDepth > 0 && targetDepth <= indents.size()) {
+            return indents.get(targetDepth - 1);
+        }
+        return -1;
+    }
+
     private static String shortFileName(String filePath) {
         int lastSep = filePath.lastIndexOf('/');
         return lastSep >= 0 ? filePath.substring(lastSep + 1) : filePath;
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/SourceTab.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/SourceTab.java
index 439e190e7f1b..0586cf23e60f 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/SourceTab.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/SourceTab.java
@@ -290,6 +290,14 @@ class SourceTab extends AbstractTab {
     @Override
     public boolean handleEscape() {
         // Esc is routed here from CamelMonitor before tab key handling — 
cancel overlays locally
+        if (gotoRoutePopup.isVisible()) {
+            gotoRoutePopup.close();
+            return true;
+        }
+        if (gotoSourceNodePopup.isVisible()) {
+            gotoSourceNodePopup.close();
+            return true;
+        }
         if (sourceViewer.cancelEdit()) {
             return true;
         }
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/YamlRouteNodeScanner.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/YamlRouteNodeScanner.java
index c3f74c72b2e4..01ec6ec7abd5 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/YamlRouteNodeScanner.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/YamlRouteNodeScanner.java
@@ -182,14 +182,16 @@ class YamlRouteNodeScanner {
                 if (STRUCTURAL_KEYS.contains(type) && !isUriLine(line)) {
                     continue;
                 }
+                int effectiveIndent = indent;
                 if ("uri".equals(type) && pendingEndpointEip != null && indent 
> pendingEndpointIndent) {
                     type = pendingEndpointEip;
+                    effectiveIndent = pendingEndpointIndent;
                     pendingEndpointEip = null;
                     pendingEndpointIndent = -1;
                 }
                 String routeId = resolveRouteId(currentRouteId, 
currentFromUri);
                 String label = buildNodeLabel(line, lines, i);
-                int nodeIndent = Math.max(1, (indent - activeRouteIndent) / 2);
+                int nodeIndent = Math.max(1, (effectiveIndent - 
activeRouteIndent) / 2);
                 result.add(new NodeEntry(
                         EntryKind.PROCESSOR, routeId, null, type, label, 
filePath, i, nodeIndent,
                         activeRouteFromLine));

Reply via email to