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 38097ece3b9e chore(tui): Fix Source tab refresh and improve edit mode 
save
38097ece3b9e is described below

commit 38097ece3b9eb56f0268323b1bd8bfd70a28ccfc
Author: Claus Ibsen <[email protected]>
AuthorDate: Mon Aug 3 15:20:12 2026 +0200

    chore(tui): Fix Source tab refresh and improve edit mode save
    
    Fix Source tab showing empty when navigating to it by adding
    onTabSelected() dispatch in TabRegistry.handleTabKey().
    
    Add Shift+F5 save-and-continue-editing in SourceViewer so users
    can save without closing edit mode. F5 remains save-and-close.
    
    Remove global Shift+F5 screenshot shortcut (still available via
    F2 actions menu). Use notification system for save feedback
    instead of footer message.
    
    chore(tui): Fix horizontal scroll in source editor and update save 
notification test
    
    Remove duplicate ensureCursorVisible call in SourceViewer that had swapped
    parameters (width,height instead of rows,cols), causing premature horizontal
    scrolling. TextArea.render() already calls ensureCursorVisible correctly.
    
    Update test to verify save notification via callback instead of footer 
spans.
    
    Co-Authored-By: Claude Opus 4.6 <[email protected]>
    Signed-off-by: Claus Ibsen <[email protected]>
---
 .../dsl/jbang/core/commands/tui/CamelMonitor.java  |  4 --
 .../dsl/jbang/core/commands/tui/SourceTab.java     |  5 ++
 .../dsl/jbang/core/commands/tui/SourceViewer.java  | 67 +++++++++++-----------
 .../dsl/jbang/core/commands/tui/TabRegistry.java   |  3 +
 .../core/commands/tui/SourceViewerEditTest.java    | 15 +++--
 5 files changed, 51 insertions(+), 43 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 0f388e8a49ba..f153a4adb666 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
@@ -800,10 +800,6 @@ public class CamelMonitor extends CamelCommand {
         }
         MonitorTab activeMonitorTab = tabRegistry.activeTab();
         boolean overlayActive = activeMonitorTab != null && 
activeMonitorTab.isOverlayActive();
-        if (ke.isKey(KeyCode.F5) && ke.hasShift()) {
-            recordingManager.takeScreenshot();
-            return true;
-        }
         if (opensHelp(ke, textEditing)) {
             // Only opens the overlay: while it is visible, dispatch delegates 
to
             // helpOverlay.handleKeyEvent (which handles F1/?/q/Esc to close) 
before reaching here.
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 920791f1b574..13e04b3ba9f5 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
@@ -106,6 +106,11 @@ class SourceTab extends AbstractTab {
 
     SourceTab(MonitorContext ctx) {
         super(ctx);
+        sourceViewer.setNotificationCallback((msg, error) -> {
+            if (ctx.notificationCallback != null) {
+                ctx.notificationCallback.accept(msg, error);
+            }
+        });
     }
 
     boolean isSourceViewerEditMode() {
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/SourceViewer.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/SourceViewer.java
index 7e981b42f99c..7f28e7afd4f8 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/SourceViewer.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/SourceViewer.java
@@ -27,6 +27,7 @@ import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.function.BiConsumer;
 import java.util.function.IntConsumer;
 
 import dev.tamboui.layout.Constraint;
@@ -124,10 +125,9 @@ class SourceViewer {
     private Path editableFile;
     private boolean editMode;
     private final TextAreaState editState = new TextAreaState();
-    private String saveMessage;
-    private boolean saveError;
     /** Markdown render mode prior to entering edit; restored on cancel. */
     private boolean markdownModeBeforeEdit;
+    private BiConsumer<String, Boolean> notificationCallback;
 
     private record CachedSource(
             List<String> lines, List<JsonObject> codeData,
@@ -150,6 +150,10 @@ class SourceViewer {
         this.focused = focused;
     }
 
+    void setNotificationCallback(BiConsumer<String, Boolean> callback) {
+        this.notificationCallback = callback;
+    }
+
     void hide() {
         exitEditMode();
         visible = false;
@@ -158,8 +162,6 @@ class SourceViewer {
         quickDocEntries = Collections.emptyMap();
         deprecatedLines = Collections.emptySet();
         editableFile = null;
-        saveMessage = null;
-        saveError = false;
     }
 
     void reset() {
@@ -191,8 +193,6 @@ class SourceViewer {
         deprecatedLineScanner = null;
         deprecatedLines = Collections.emptySet();
         editableFile = null;
-        saveMessage = null;
-        saveError = false;
     }
 
     boolean isMarkdownMode() {
@@ -389,6 +389,10 @@ class SourceViewer {
             exitEditMode();
             return true;
         }
+        if (ke.isKey(KeyCode.F5) && ke.hasShift()) {
+            saveContinueEdit();
+            return true;
+        }
         if (ke.isKey(KeyCode.F5)) {
             saveEdit();
             return true;
@@ -465,8 +469,6 @@ class SourceViewer {
         markdownMode = false;
         quickDocEnabled = false;
         search.reset();
-        saveMessage = null;
-        saveError = false;
         editMode = true;
     }
 
@@ -495,12 +497,27 @@ class SourceViewer {
             if (isMarkdownFile) {
                 markdownMode = restoreMarkdownMode;
             }
-            // Preserve save feedback after reload
-            saveMessage = "Saved";
-            saveError = false;
+            notifySave("Saved: " + editableFile.getFileName(), false);
+        } catch (IOException e) {
+            notifySave("Save failed: " + e.getMessage(), true);
+        }
+    }
+
+    private void saveContinueEdit() {
+        if (!editMode || editableFile == null) {
+            return;
+        }
+        try {
+            Files.writeString(editableFile, editState.text(), 
StandardCharsets.UTF_8);
+            notifySave("Saved: " + editableFile.getFileName(), false);
         } catch (IOException e) {
-            saveMessage = "Save failed: " + e.getMessage();
-            saveError = true;
+            notifySave("Save failed: " + e.getMessage(), true);
+        }
+    }
+
+    private void notifySave(String message, boolean error) {
+        if (notificationCallback != null) {
+            notificationCallback.accept(message, error);
         }
     }
 
@@ -747,9 +764,6 @@ class SourceViewer {
         List<Span> titleSpans = new ArrayList<>();
         String info = title != null ? title : "";
         titleSpans.add(Span.styled(" Edit [" + info + "] ", ts));
-        if (saveMessage != null) {
-            titleSpans.add(Span.styled(saveMessage + " ", saveError ? 
Theme.error() : Theme.success()));
-        }
         Block.Builder blockBuilder = Block.builder()
                 .borderType(BorderType.ROUNDED).borders(Borders.ALL)
                 .title(Title.from(Line.from(titleSpans)));
@@ -762,7 +776,6 @@ class SourceViewer {
         lastVisibleLines = Math.max(1, inner.height());
         frame.renderWidget(block, area);
 
-        editState.ensureCursorVisible(inner.width(), inner.height());
         TextArea textArea = TextArea.builder()
                 .cursorStyle(Style.EMPTY.reversed())
                 .showLineNumbers(true)
@@ -774,11 +787,9 @@ class SourceViewer {
     void renderFooter(List<Span> spans) {
         if (editMode) {
             TuiHelper.hint(spans, "Esc", "cancel");
-            TuiHelper.hint(spans, "F5", "save");
+            TuiHelper.hint(spans, "F5", "save & close");
+            TuiHelper.hint(spans, "Shift+F5", "save");
             TuiHelper.hint(spans, TuiIcons.HINT_SCROLL, "move");
-            if (saveMessage != null) {
-                spans.add(Span.styled("  " + saveMessage, saveError ? 
Theme.error() : Theme.success()));
-            }
             return;
         }
         if (markdownMode) {
@@ -815,9 +826,6 @@ class SourceViewer {
         if (onLineSelected != null) {
             TuiHelper.hint(spans, "Enter", "select node");
         }
-        if (saveMessage != null) {
-            spans.add(Span.styled("  " + saveMessage, saveError ? 
Theme.error() : Theme.success()));
-        }
     }
 
     /**
@@ -832,8 +840,6 @@ class SourceViewer {
         editMode = false;
         editState.clear();
         markdownModeBeforeEdit = false;
-        saveMessage = null;
-        saveError = false;
         String fileName = filePath.getFileName().toString();
         boolean isMd = fileName.toLowerCase().endsWith(".md");
         try {
@@ -894,8 +900,6 @@ class SourceViewer {
         editableFile = null;
         editMode = false;
         editState.clear();
-        saveMessage = null;
-        saveError = false;
 
         if (ctx.selectedPid == null || ctx.runner == null) {
             return;
@@ -1108,12 +1112,7 @@ class SourceViewer {
             return Title.from(Line.from(spans));
         }
         if (currentRouteId == null) {
-            List<Span> spans = new ArrayList<>();
-            spans.add(Span.styled(" Source [" + info + "] ", ts));
-            if (saveMessage != null) {
-                spans.add(Span.styled(saveMessage + " ", saveError ? 
Theme.error() : Theme.success()));
-            }
-            return Title.from(Line.from(spans));
+            return Title.from(Span.styled(" Source [" + info + "] ", ts));
         }
 
         List<Span> spans = new ArrayList<>();
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 abbc1770e79c..80b783a1b2d2 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
@@ -302,6 +302,9 @@ class TabRegistry {
             routesTab.preloadDiagram();
             diagramTab.preloadDiagram();
         }
+        if (tab == TAB_SOURCE) {
+            sourceTab.onTabSelected();
+        }
         if (tab == TAB_LOG) {
             callbacks.refreshLogData();
             logTab.onTabSelected();
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/SourceViewerEditTest.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/SourceViewerEditTest.java
index 3b0f6974ce94..02c344e678b6 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/SourceViewerEditTest.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/SourceViewerEditTest.java
@@ -48,11 +48,17 @@ class SourceViewerEditTest {
 
     private SourceViewer viewer;
     private Path sourceFile;
+    private final AtomicReference<String> lastNotification = new 
AtomicReference<>();
+    private final AtomicReference<Boolean> lastNotificationError = new 
AtomicReference<>();
 
     @BeforeEach
     void setUp() throws Exception {
         Theme.resetForTesting();
         viewer = new SourceViewer();
+        viewer.setNotificationCallback((msg, error) -> {
+            lastNotification.set(msg);
+            lastNotificationError.set(error);
+        });
         sourceFile = tempDir.resolve("route.camel.yaml");
         Files.writeString(sourceFile, """
                 - route:
@@ -398,21 +404,20 @@ class SourceViewerEditTest {
     }
 
     @Test
-    void escDismissClearsSaveMessageBeforeReopen() {
+    void saveNotifiesViaCallback() {
         viewer.loadFile(sourceFile);
         viewer.enterEditMode();
         viewer.handleKeyEvent(KeyEvent.ofChar('x', KeyModifiers.NONE));
         viewer.handleKeyEvent(KeyEvent.ofKey(KeyCode.F5, KeyModifiers.NONE));
 
-        List<Span> spans = new ArrayList<>();
-        viewer.renderFooter(spans);
-        assertThat(spansToString(spans)).contains("Saved");
+        assertThat(lastNotification.get()).contains("Saved");
+        assertThat(lastNotificationError.get()).isFalse();
 
         viewer.handleKeyEvent(KeyEvent.ofKey(KeyCode.ESCAPE, 
KeyModifiers.NONE));
         assertThat(viewer.isVisible()).isFalse();
 
+        List<Span> spans = new ArrayList<>();
         viewer.loadFile(sourceFile);
-        spans.clear();
         viewer.renderFooter(spans);
         assertThat(spansToString(spans)).doesNotContain("Saved");
     }

Reply via email to