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) {
