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

commit 757428f5900797940125777f899fb04cbcadca2c
Author: Claus Ibsen <[email protected]>
AuthorDate: Fri Jul 3 14:46:47 2026 +0200

    CAMEL-23831: Unify notifications through WaveText effect in camel-tui
    
    Co-Authored-By: Claude Opus 4.6 <[email protected]>
    Signed-off-by: Claus Ibsen <[email protected]>
---
 .../dsl/jbang/core/commands/tui/ActionsPopup.java  | 46 +++++------------
 .../dsl/jbang/core/commands/tui/CamelMonitor.java  | 58 +++++++++++++++++-----
 2 files changed, 58 insertions(+), 46 deletions(-)

diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/ActionsPopup.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/ActionsPopup.java
index a9ce44650612..6a85b2f3d91d 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/ActionsPopup.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/ActionsPopup.java
@@ -29,6 +29,7 @@ import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.ScheduledExecutorService;
+import java.util.function.BiConsumer;
 import java.util.function.IntPredicate;
 import java.util.function.Supplier;
 import java.util.stream.Collectors;
@@ -179,9 +180,7 @@ class ActionsPopup {
 
     private final List<PendingLaunch> pendingLaunches = new ArrayList<>();
     private DeferredLaunch deferredLaunch;
-    private String launchNotification;
-    private boolean launchNotificationError;
-    private long launchNotificationExpiry;
+    private BiConsumer<String, Boolean> notificationCallback;
     private volatile String pendingAutoSelect;
     private String preSelectedRouteId;
 
@@ -438,12 +437,8 @@ class ActionsPopup {
         captionOverlay.close();
     }
 
-    String notification() {
-        return launchNotification;
-    }
-
-    boolean notificationError() {
-        return launchNotificationError;
+    void setNotificationCallback(BiConsumer<String, Boolean> callback) {
+        this.notificationCallback = callback;
     }
 
     void handlePaste(String text) {
@@ -948,9 +943,6 @@ class ActionsPopup {
     void tick(long now) {
         monitorPendingLaunches(now);
         checkDeferredLaunch(now);
-        if (launchNotification != null && now > launchNotificationExpiry) {
-            launchNotification = null;
-        }
     }
 
     // ---- Rendering ----
@@ -1181,9 +1173,7 @@ class ActionsPopup {
                 .filter(i -> !i.vanishing && i.readmeFiles != null && 
!i.readmeFiles.isEmpty())
                 .collect(Collectors.toList());
         if (withDocs.isEmpty()) {
-            launchNotification = "No integrations with documentation found";
-            launchNotificationError = true;
-            launchNotificationExpiry = System.currentTimeMillis() + 5000;
+            setNotification("No integrations with documentation found", true);
             return;
         }
         if (withDocs.size() == 1) {
@@ -1231,14 +1221,10 @@ class ActionsPopup {
                 showDocViewer = true;
                 docViewerFromExampleBrowser = false;
             } else {
-                launchNotification = "Could not load documentation";
-                launchNotificationError = true;
-                launchNotificationExpiry = System.currentTimeMillis() + 5000;
+                setNotification("Could not load documentation", true);
             }
         } catch (Exception e) {
-            launchNotification = "Error loading documentation: " + 
e.getMessage();
-            launchNotificationError = true;
-            launchNotificationExpiry = System.currentTimeMillis() + 5000;
+            setNotification("Error loading documentation: " + e.getMessage(), 
true);
         }
     }
 
@@ -1283,9 +1269,9 @@ class ActionsPopup {
     }
 
     private void setNotification(String msg, boolean error) {
-        launchNotification = msg;
-        launchNotificationError = error;
-        launchNotificationExpiry = System.currentTimeMillis() + 10000;
+        if (notificationCallback != null) {
+            notificationCallback.accept(msg, error);
+        }
     }
 
     private void checkStopAllNotification() {
@@ -1718,9 +1704,7 @@ class ActionsPopup {
             exampleCatalog = loadAndSortExamples();
         }
         if (exampleCatalog.isEmpty()) {
-            launchNotification = "No examples found";
-            launchNotificationError = true;
-            launchNotificationExpiry = System.currentTimeMillis() + 5000;
+            setNotification("No examples found", true);
             return;
         }
         showExampleBrowser = true;
@@ -2281,17 +2265,13 @@ class ActionsPopup {
             if (!pl.process().isAlive()) {
                 int exitCode = pl.process().exitValue();
                 if (exitCode == 0) {
-                    launchNotification = "Started: " + pl.name();
-                    launchNotificationError = false;
-                    launchNotificationExpiry = now + 5000;
+                    setNotification("Started: " + pl.name(), false);
                 } else {
                     showFailureLog(pl.name(), pl.outputFile());
                 }
                 it.remove();
             } else if (now - pl.startTime() > 8000) {
-                launchNotification = "Started: " + pl.name();
-                launchNotificationError = false;
-                launchNotificationExpiry = now + 5000;
+                setNotification("Started: " + pl.name(), false);
                 it.remove();
             }
         }
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 0d89f379b83c..32a1d23a9dac 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
@@ -55,6 +55,8 @@ import dev.tamboui.tui.event.TickEvent;
 import dev.tamboui.widgets.paragraph.Paragraph;
 import dev.tamboui.widgets.tabs.Tabs;
 import dev.tamboui.widgets.tabs.TabsState;
+import dev.tamboui.widgets.wavetext.WaveText;
+import dev.tamboui.widgets.wavetext.WaveTextState;
 import org.apache.camel.dsl.jbang.core.commands.CamelCommand;
 import org.apache.camel.dsl.jbang.core.commands.CamelJBangMain;
 import org.apache.camel.dsl.jbang.core.common.CommandLineHelper;
@@ -115,6 +117,8 @@ public class CamelMonitor extends CamelCommand {
     private String monitorNotification;
     private boolean monitorNotificationError;
     private long monitorNotificationExpiry;
+    private final WaveTextState notificationWaveState = new WaveTextState();
+    private String lastWaveNotification;
     private boolean mcpInjectedKey;
     private TuiMcpServer mcpServer;
     private McpFacade mcpFacade;
@@ -242,6 +246,7 @@ public class CamelMonitor extends CamelCommand {
                 dataService::enableBurstMode, dataService.stoppingPids());
 
         actionsPopup.setContext(ctx);
+        actionsPopup.setNotificationCallback((msg, error) -> 
setNotification(msg, error));
         actionsPopup.setResetStatsAction(this::resetStats);
         shellPanel.setContext(ctx);
         aiPanel.setContext(ctx);
@@ -1138,25 +1143,51 @@ public class CamelMonitor extends CamelCommand {
                 titleSpans.add(Span.styled("selected: " + selectedName(), 
Theme.warning()));
             }
         }
-        if (actionsPopup.notification() != null) {
-            titleSpans.add(Span.raw("  "));
-            Style style = actionsPopup.notificationError() ? Theme.error() : 
Theme.success();
-            titleSpans.add(Span.styled(actionsPopup.notification(), style));
-        }
         if (monitorNotification != null) {
             if (System.currentTimeMillis() > monitorNotificationExpiry) {
                 monitorNotification = null;
-            } else {
-                titleSpans.add(Span.raw("  "));
-                Style style = monitorNotificationError ? Theme.error() : 
Theme.success();
-                titleSpans.add(Span.styled(monitorNotification, style));
             }
         }
+        String activeNotification = monitorNotification;
+        boolean activeNotificationError = monitorNotificationError;
         Line titleLine = Line.from(titleSpans);
 
-        frame.renderWidget(
-                Paragraph.builder().text(Text.from(titleLine)).build(),
-                area);
+        if (activeNotification != null) {
+            if (!activeNotification.equals(lastWaveNotification)) {
+                notificationWaveState.reset();
+                lastWaveNotification = activeNotification;
+            }
+            int titleWidth = titleSpans.stream().mapToInt(s -> 
s.width()).sum();
+            int waveWidth = activeNotification.length() + 2;
+            int leftWidth = Math.min(titleWidth + 2, area.width() - waveWidth);
+            if (leftWidth > 0 && waveWidth > 0 && leftWidth + waveWidth <= 
area.width()) {
+                Rect leftArea = new Rect(area.x(), area.y(), leftWidth, 1);
+                Rect waveArea = new Rect(area.x() + leftWidth, area.y(), 
waveWidth, 1);
+                frame.renderWidget(
+                        Paragraph.builder().text(Text.from(titleLine)).build(),
+                        leftArea);
+                Color waveColor = activeNotificationError
+                        ? Theme.error().fg().orElse(Color.LIGHT_RED)
+                        : Theme.success().fg().orElse(Color.LIGHT_GREEN);
+                WaveText wave = WaveText.builder()
+                        .text(activeNotification)
+                        .color(waveColor)
+                        .inverted(false)
+                        .speed(1.2)
+                        .mode(WaveText.Mode.LOOP)
+                        .build();
+                notificationWaveState.advance();
+                frame.renderStatefulWidget(wave, waveArea, 
notificationWaveState);
+            } else {
+                frame.renderWidget(
+                        Paragraph.builder().text(Text.from(titleLine)).build(),
+                        area);
+            }
+        } else {
+            frame.renderWidget(
+                    Paragraph.builder().text(Text.from(titleLine)).build(),
+                    area);
+        }
     }
 
     private void renderTooSmall(Frame frame, Rect area) {
@@ -1559,7 +1590,8 @@ public class CamelMonitor extends CamelCommand {
     private void setNotification(String message, boolean error) {
         monitorNotification = message;
         monitorNotificationError = error;
-        monitorNotificationExpiry = System.currentTimeMillis() + (error ? 
15000 : 5000);
+        monitorNotificationExpiry = System.currentTimeMillis() + (error ? 
20000 : 10000);
+        notificationWaveState.reset();
     }
 
     static List<String> parseCommandLine(String commandLine) {

Reply via email to