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 e6cc48e9c1d7 CAMEL-24007: Skip redundant TUI re-render on idle ticks
e6cc48e9c1d7 is described below
commit e6cc48e9c1d7165ac20a0477f96f36a118ee934e
Author: Adriano Machado <[email protected]>
AuthorDate: Sun Jul 12 16:05:45 2026 -0400
CAMEL-24007: Skip redundant TUI re-render on idle ticks
The tick handler unconditionally returned true, causing ~25 full redraws/s
even while idle. Now returns true only when the periodic data refresh fires
or an animation is active (shell/AI panel, log-pin slide, canvas, caption,
notification wave, keystroke highlight). Idle redraw drops from ~25 to ~2
fps.
The redraw decision is extracted into a testable needsAnimationRedraw()
method.
Closes #24613
Co-Authored-By: Claude Code (Claude Opus 4.8) <[email protected]>
---
.../dsl/jbang/core/commands/tui/CamelMonitor.java | 40 ++++++++++++--
.../commands/tui/CamelMonitorTickRedrawTest.java | 63 ++++++++++++++++++++++
2 files changed, 100 insertions(+), 3 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 790f15b6a689..89fdcde8f723 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
@@ -1153,13 +1153,47 @@ public class CamelMonitor extends CamelCommand {
boolean anyDiagramShowing = tabRegistry.routesTab().isShowDiagram()
|| tabRegistry.diagramTab().isShowDiagram();
long interval = anyDiagramShowing ? Math.max(refreshInterval, 1000) :
refreshInterval;
+ boolean dataRefreshed = false;
if (now - dataService.lastRefresh() >= interval) {
dataService.refresh(runner, this::refreshLogData,
this::refreshConditionalData);
tabRegistry.routesTab().refreshDiagramIfNeeded();
tabRegistry.diagramTab().refreshDiagramIfNeeded();
- return true;
- }
- return true;
+ dataRefreshed = true;
+ }
+ // Redraw only when the periodic data refresh fired or an animation is
in flight.
+ // Everything else (footer indicators, auto-dismiss overlays, live tab
data) is
+ // refreshed by the next data-refresh redraw, which is bounded by
{@code interval}.
+ // Without this gate the tick loop forced a full redraw on every tick
(~25/s at the
+ // default 40ms tick rate) even while idle.
+ return dataRefreshed || needsAnimationRedraw(
+ shellPanel.isOpen(),
+ aiPanel.isOpen(),
+ logPinAnim.isAnimating(),
+ canvasOverlay.isVisible(),
+ captionOverlay.isVisible(),
+ monitorNotification != null,
+ recordingManager.isRecording() &&
!recordingManager.getRecentKeys().isEmpty());
+ }
+
+ /**
+ * Decides whether a tick must trigger a redraw because something is
animating faster than the periodic data
+ * refresh. Kept as a pure static method so the redraw intent can be
unit-tested without wiring up the whole
+ * monitor.
+ *
+ * @param shellOpen the shell panel is open (live terminal
output)
+ * @param aiOpen the AI panel is open (streaming output /
thinking spinner)
+ * @param logPinAnimating the pinned-log panel is sliding open/closed
+ * @param canvasVisible a full-screen canvas animation is playing
+ * @param captionVisible a caption is typing or fading
+ * @param notificationActive a header notification wave is animating
+ * @param recordingKeysVisible recorded keystrokes are shown (1s
highlight then fade-out)
+ * @return true if the frame must be redrawn on this
tick
+ */
+ static boolean needsAnimationRedraw(
+ boolean shellOpen, boolean aiOpen, boolean logPinAnimating,
boolean canvasVisible,
+ boolean captionVisible, boolean notificationActive, boolean
recordingKeysVisible) {
+ return shellOpen || aiOpen || logPinAnimating || canvasVisible
+ || captionVisible || notificationActive ||
recordingKeysVisible;
}
private void resetIntegrationTabState() {
diff --git
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/CamelMonitorTickRedrawTest.java
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/CamelMonitorTickRedrawTest.java
new file mode 100644
index 000000000000..e2b09ecdb623
--- /dev/null
+++
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/CamelMonitorTickRedrawTest.java
@@ -0,0 +1,63 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.dsl.jbang.core.commands.tui;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * Verifies that an idle tick does not force a redraw while any active
animation still does. This is the core of the fix
+ * that stopped the TUI from rebuilding the whole frame on every tick (~25
times/second at the default 40ms tick rate)
+ * even when nothing on screen was changing.
+ */
+class CamelMonitorTickRedrawTest {
+
+ @Test
+ void idleTickDoesNotRedraw() {
+ // Nothing is animating: the tick must NOT request a redraw. The
periodic data refresh
+ // (handled separately) is the only redraw driver while idle.
+ assertFalse(CamelMonitor.needsAnimationRedraw(
+ false, false, false, false, false, false, false),
+ "an idle tick must not force a redraw");
+ }
+
+ @Test
+ void eachAnimationSourceForcesRedraw() {
+ // Every animation runs faster than the data-refresh cadence, so each
one alone must
+ // keep the frame updating on every tick.
+ assertTrue(only(0), "open shell panel must redraw");
+ assertTrue(only(1), "open AI panel must redraw");
+ assertTrue(only(2), "sliding pinned-log panel must redraw");
+ assertTrue(only(3), "canvas animation must redraw");
+ assertTrue(only(4), "typing/fading caption must redraw");
+ assertTrue(only(5), "notification wave must redraw");
+ assertTrue(only(6), "recorded-keystroke highlight must redraw");
+ }
+
+ /**
+ * Invokes {@link CamelMonitor#needsAnimationRedraw} with exactly one of
the seven flags set, proving that source
+ * triggers a redraw on its own.
+ */
+ private static boolean only(int index) {
+ boolean[] flags = new boolean[7];
+ flags[index] = true;
+ return CamelMonitor.needsAnimationRedraw(
+ flags[0], flags[1], flags[2], flags[3], flags[4], flags[5],
flags[6]);
+ }
+}