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]);
+    }
+}

Reply via email to