This is an automated email from the ASF dual-hosted git repository.

davsclaus pushed a commit to branch chore-tui-small-bugs-charts
in repository https://gitbox.apache.org/repos/asf/camel.git

commit 1cc1431cf62864a351de2c0d00658d8eed6e5155
Author: Claus Ibsen <[email protected]>
AuthorDate: Mon Nov 9 16:16:07 2026 +0100

    chore: camel-jbang - Process shows each argument after the jar, Source Info 
is about the open file, the heap chart has an axis, and a millisecond of jitter 
is no spike
    
    - Process: the arguments after -jar (run and the files) were joined to the 
-jar
      line and cut off. -jar and -cp take one value, and the arguments after it
      have a line each.
    - Source: the Info panel described the file under the cursor of the list, 
also
      while the viewer had the focus on another file (opened with g or from 
another
      tab). It describes the open file then.
    - Memory: the heap chart said "<peak> committed" when the top was the most 
used
      in its history; it says what is used and committed, and has a y axis (the
      top, the half and 0).
    - Timeline: the mean time sparkline drew 1 ms as a full bar on a route of 0 
and
      1 ms. Its height is at least 10 ms.
    
    Co-Authored-By: Claude Opus 5.5 <[email protected]>
    Signed-off-by: Claus Ibsen <[email protected]>
---
 .../dsl/jbang/core/commands/tui/MemoryTab.java     | 27 ++++++++++++++++---
 .../dsl/jbang/core/commands/tui/ProcessTab.java    | 31 +++++++++++++++++-----
 .../dsl/jbang/core/commands/tui/SourceTab.java     | 29 +++++++++++++++-----
 .../dsl/jbang/core/commands/tui/TimelineTab.java   | 12 +++++++--
 .../core/commands/tui/MemoryTabRenderTest.java     | 22 +++++++++++++++
 .../core/commands/tui/ProcessTabRenderTest.java    | 18 +++++++++++++
 .../core/commands/tui/SourceTabJumpLinksTest.java  | 22 +++++++++++++++
 .../core/commands/tui/TimelineTabRenderTest.java   | 11 ++++++++
 8 files changed, 153 insertions(+), 19 deletions(-)

diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/MemoryTab.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/MemoryTab.java
index 06ccd219bd10..07110641e977 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/MemoryTab.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/MemoryTab.java
@@ -259,23 +259,32 @@ class MemoryTab extends AbstractTab {
             return;
         }
 
-        // Use committed as the scale ceiling
+        // the top of the chart: the committed heap, or the most used in the 
history when that was more (the heap
+        // shrinks); the axis says which height it is
         long observedMax = 
hist.stream().mapToLong(Long::longValue).max().orElse(1);
         long ceiling = info.heapMemCommitted > 0 ? info.heapMemCommitted : 
observedMax;
         if (observedMax > ceiling) {
             ceiling = observedMax;
         }
 
-        String title = String.format(" Heap Usage (%s / %s committed) ", 
formatBytes(info.heapMemUsed), formatBytes(ceiling));
+        String title = String.format(" Heap Usage (%s used, %s committed) ", 
formatBytes(info.heapMemUsed),
+                formatBytes(info.heapMemCommitted > 0 ? info.heapMemCommitted 
: ceiling));
 
         // Render the block border first
         Block block = 
Block.builder().borderType(BorderType.ROUNDED).borders(Borders.ALL).title(title).build();
         frame.renderWidget(block, area);
-        Rect inner = block.inner(area);
+        Rect box = block.inner(area);
 
-        if (inner.isEmpty() || ceiling <= 0) {
+        if (box.isEmpty() || ceiling <= 0) {
             return;
         }
+        // the y axis at the left: the top, the half and 0
+        String top = formatBytes(ceiling);
+        int axisW = top.length() + 1;
+        if (box.width() <= axisW + 10) {
+            axisW = 0;
+        }
+        Rect inner = new Rect(box.x() + axisW, box.y(), box.width() - axisW, 
box.height());
 
         int chartW = inner.width();
         int chartH = inner.height() - 1;
@@ -325,6 +334,16 @@ class MemoryTab extends AbstractTab {
             }
         }
 
+        if (axisW > 0) {
+            Style axis = Style.EMPTY.dim();
+            buf.setString(box.x(), inner.y(), top, axis);
+            if (chartH >= 5) {
+                String half = formatBytes(ceiling / 2);
+                buf.setString(box.x() + axisW - 1 - half.length(), inner.y() + 
chartH / 2, half, axis);
+            }
+            buf.setString(box.x() + axisW - 2, inner.y() + chartH - 1, "0", 
axis);
+        }
+
         // Render time axis on the bottom row of the inner area (inside the 
block border)
         renderTimeAxis(frame, inner, info, chartW);
     }
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/ProcessTab.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/ProcessTab.java
index 6fc5c2cd078d..f5aab1d8d794 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/ProcessTab.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/ProcessTab.java
@@ -18,6 +18,7 @@ package org.apache.camel.dsl.jbang.core.commands.tui;
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Set;
 
 import dev.tamboui.layout.Constraint;
 import dev.tamboui.layout.Layout;
@@ -270,16 +271,34 @@ class ProcessTab extends AbstractTab {
         }
     }
 
-    private static List<String> splitCommandLine(String cmdLine) {
+    // options of the JVM whose value is the next token, after which come the 
main class or jar and its arguments
+    private static final Set<String> ONE_VALUE_OPTIONS = Set.of("-jar", "-cp", 
"-classpath", "--class-path");
+
+    /**
+     * The command line a line per option, with its value: an option of the 
JVM that takes one value (-jar, -cp) has
+     * just that one, and the arguments after it (the main class, the files of 
camel run) a line each, so none is cut
+     * off at the end of a long -jar line.
+     */
+    static List<String> splitCommandLine(String cmdLine) {
         List<String> parts = new ArrayList<>();
-        for (String token : cmdLine.split("\\s+")) {
-            if (token.startsWith("-") && !parts.isEmpty()) {
+        boolean oneValue = false;
+        boolean arguments = false;
+        for (String token : cmdLine.trim().split("\\s+")) {
+            if (parts.isEmpty()) {
+                parts.add(token);
+            } else if (token.startsWith("-")) {
                 parts.add(token);
-            } else if (parts.isEmpty()) {
+                oneValue = ONE_VALUE_OPTIONS.contains(token);
+                arguments = false;
+            } else if (oneValue) {
+                parts.set(parts.size() - 1, parts.get(parts.size() - 1) + " " 
+ token);
+                oneValue = false;
+                arguments = true;
+            } else if (arguments) {
                 parts.add(token);
             } else {
-                String last = parts.get(parts.size() - 1);
-                parts.set(parts.size() - 1, last + " " + token);
+                // the values of an option, such as --name foo
+                parts.set(parts.size() - 1, parts.get(parts.size() - 1) + " " 
+ token);
             }
         }
         return parts;
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 af9f5b566109..a7e361624029 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
@@ -1096,15 +1096,30 @@ class SourceTab extends AbstractTab {
 
     private static final DateTimeFormatter DATE_FMT = 
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
 
+    /**
+     * The file the Info panel describes, the one the focus is on: the open 
file when the viewer has the focus, else the
+     * one under the cursor of the list (the open file can be another one, 
opened with g or from another tab).
+     */
+    Path infoPath() {
+        String viewed = sourceViewer.isVisible() && focusOnViewer ? 
sourceViewer.getCurrentFilePath() : null;
+        if (viewed != null) {
+            return Path.of(viewed);
+        }
+        Integer sel = listState.selected();
+        return sel != null && sel < entries.size() ? 
Path.of(entries.get(sel).path()) : null;
+    }
+
     private void renderInfoPanel(Frame frame, Rect area) {
         List<Line> lines = new ArrayList<>();
+        Path path = infoPath();
         Integer sel = listState.selected();
-        if (sel != null && sel < entries.size()) {
-            FilesBrowser.FileEntry entry = entries.get(sel);
-            Path path = Path.of(entry.path());
+        boolean directory = path != null && sel != null && sel < entries.size()
+                && entries.get(sel).directory() && 
path.toString().equals(entries.get(sel).path());
+        if (path != null) {
+            String name = path.getFileName() != null ? 
path.getFileName().toString() : "";
             try {
                 BasicFileAttributes attrs = Files.readAttributes(path, 
BasicFileAttributes.class);
-                if (!entry.directory()) {
+                if (!directory) {
                     lines.add(Line.from(
                             Span.styled(" Size: ", Style.EMPTY.dim()),
                             
Span.raw(FilesBrowser.formatFileSize(attrs.size()))));
@@ -1114,11 +1129,11 @@ class SourceTab extends AbstractTab {
                 lines.add(Line.from(
                         Span.styled(" Modified: ", Style.EMPTY.dim()),
                         Span.raw(modified)));
-                if (!entry.directory()) {
+                if (!directory) {
                     String ext = "";
-                    int dot = entry.name().lastIndexOf('.');
+                    int dot = name.lastIndexOf('.');
                     if (dot > 0) {
-                        ext = entry.name().substring(dot + 1);
+                        ext = name.substring(dot + 1);
                     }
                     if (!ext.isEmpty()) {
                         lines.add(Line.from(
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/TimelineTab.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/TimelineTab.java
index 641d1231a954..3b4153546b59 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/TimelineTab.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/TimelineTab.java
@@ -56,6 +56,8 @@ import static 
org.apache.camel.dsl.jbang.core.commands.tui.TuiHelper.hint;
  */
 class TimelineTab extends AbstractTableTab {
 
+    // the mean time sparkline is at least this many milliseconds high: below 
it, a time is noise
+    static final long MIN_MEAN_SCALE_MILLIS = 10;
     static final int[] WINDOWS = { 60, 120, 300 };
     private static final String BLOCKS = "▁▂▃▄▅▆▇█";
     private static final int CAUSE_ROWS = 4;
@@ -742,8 +744,9 @@ class TimelineTab extends AbstractTableTab {
         }
         List<Span> meanLine = new ArrayList<>();
         meanLine.add(Span.styled(pad("   mean time", label), Theme.label()));
-        // relative to twice the average, so a steady time is a calm line and 
a slow second stands out
-        long scale = n > 0 ? Math.max(maxMean, 2 * sumMean / n) : 0;
+        // relative to twice the average, so a steady time is a calm line and 
a slow second stands out; at least
+        // MIN_MEAN_SCALE_MILLIS, so a millisecond of jitter on a fast route 
is not a full bar
+        long scale = n > 0 ? meanScale(maxMean, sumMean / n) : 0;
         meanLine.add(Span.styled(spark(mean, scale), Theme.info()));
         meanLine.add(Span.raw("  " + (n > 0 ? "avg " + 
ChaosTab.shortTime(sumMean / n) : "no exchange")));
         if (max > 0) {
@@ -853,6 +856,11 @@ class TimelineTab extends AbstractTableTab {
     }
 
     /** A sparkline of values (-1 is unknown, blank), relative to the highest. 
*/
+    /** The height of the mean time sparkline: twice the average or the 
slowest second, at least 10 ms. */
+    static long meanScale(long maxMean, long avg) {
+        return Math.max(MIN_MEAN_SCALE_MILLIS, Math.max(maxMean, 2 * avg));
+    }
+
     static String spark(long[] values, long max) {
         StringBuilder sb = new StringBuilder();
         for (long v : values) {
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/MemoryTabRenderTest.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/MemoryTabRenderTest.java
index f9f2029e80f8..0b15d581997f 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/MemoryTabRenderTest.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/MemoryTabRenderTest.java
@@ -17,6 +17,7 @@
 package org.apache.camel.dsl.jbang.core.commands.tui;
 
 import java.util.ArrayList;
+import java.util.LinkedList;
 import java.util.List;
 import java.util.concurrent.atomic.AtomicReference;
 
@@ -74,6 +75,27 @@ class MemoryTabRenderTest {
         assertTrue(rendered.contains("Heap"), "Should show Heap memory section 
header");
     }
 
+    @Test
+    void theHeapChartSaysWhatTheTopIsAndWhatIsCommitted() {
+        info.heapMemUsed = 38L * 1024 * 1024;
+        info.heapMemCommitted = 48L * 1024 * 1024;
+        info.heapMemMax = 512L * 1024 * 1024;
+        MetricsCollector metrics = new MetricsCollector();
+        // the heap was 64M a while ago, and has shrunk since
+        LinkedList<Long> history = new LinkedList<>();
+        for (int i = 0; i < 100; i++) {
+            history.add((i < 50 ? 64L : 38L) * 1024 * 1024);
+        }
+        metrics.getHeapMemHistory().put("1234", history);
+
+        String rendered = TuiTestHelper.renderToString(new MemoryTab(ctx, 
metrics), 160, 50);
+
+        assertTrue(rendered.contains("Heap Usage (38M used, 48M committed)"), 
rendered);
+        // the y axis: the top of the chart is the most the heap used, then 
the half
+        assertTrue(rendered.contains("│64M"), rendered);
+        assertTrue(rendered.contains("│32M"), rendered);
+    }
+
     @Test
     void renderShowsThreadCount() {
         info.threadCount = 42;
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/ProcessTabRenderTest.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/ProcessTabRenderTest.java
index d521d7402208..43182a9da38d 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/ProcessTabRenderTest.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/ProcessTabRenderTest.java
@@ -24,6 +24,7 @@ import dev.tamboui.text.Span;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 
+import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
 /**
@@ -102,4 +103,21 @@ class ProcessTabRenderTest {
         assertTrue(footer.contains("Esc"), "Footer should contain Esc hint");
     }
 
+    @Test
+    void theArgumentsAfterTheJarHaveALineEach() {
+        List<String> parts = ProcessTab.splitCommandLine(
+                "/usr/bin/java -Xmx512m -XX:+AutoCreateSharedArchive -jar 
/opt/camel/lib/camel-launcher.jar run README.md"
+                                                         + " 
application.properties orders.camel.yaml --logging-color=true"
+                                                         + " --name=orders 
--port 8080");
+
+        assertEquals(List.of("/usr/bin/java", "-Xmx512m", 
"-XX:+AutoCreateSharedArchive",
+                "-jar /opt/camel/lib/camel-launcher.jar", "run", "README.md", 
"application.properties",
+                "orders.camel.yaml", "--logging-color=true", "--name=orders", 
"--port 8080"), parts);
+    }
+
+    @Test
+    void theMainClassAfterTheClasspathHasALineOfItsOwn() {
+        assertEquals(List.of("java", "-cp a.jar:b.jar", "com.acme.Main", 
"--verbose"),
+                ProcessTab.splitCommandLine("java -cp a.jar:b.jar 
com.acme.Main --verbose"));
+    }
 }
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/SourceTabJumpLinksTest.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/SourceTabJumpLinksTest.java
index 13be5dae4a48..b7da0484a30b 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/SourceTabJumpLinksTest.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/SourceTabJumpLinksTest.java
@@ -257,6 +257,28 @@ class SourceTabJumpLinksTest {
         
assertThat(tab.computeJumpLinks(billing).get(4).filePath()).isEqualTo(orders.toString());
     }
 
+    @Test
+    void theInfoPanelDescribesTheOpenFileWhenTheViewerHasTheFocus() throws 
Exception {
+        Files.writeString(dir.resolve("application.properties"), 
"greeting=hello\n");
+        Path routes = Files.writeString(dir.resolve("orders.camel.yaml"), """
+                - route:
+                    id: orders
+                    from:
+                      uri: direct:orders
+                      steps:
+                        - to:
+                            uri: log:orders
+                """);
+        SourceTab tab = newTab();
+        assertThat(tab.loadDirectory(dir)).isTrue();
+        // the list has its cursor on its first entry, not on the routes
+        assertThat(tab.infoPath()).isNotNull().isNotEqualTo(routes);
+
+        // the routes are opened another way (as g does): the list keeps its 
cursor, the viewer has the focus
+        tab.openFileForReplay(routes);
+        assertThat(tab.infoPath()).isEqualTo(routes);
+    }
+
     private static SourceTab newTab() {
         return new SourceTab(new MonitorContext(new 
AtomicReference<>(List.of()), new AtomicReference<>(List.of())));
     }
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/TimelineTabRenderTest.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/TimelineTabRenderTest.java
index c5b277032565..f90675123177 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/TimelineTabRenderTest.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/TimelineTabRenderTest.java
@@ -426,4 +426,15 @@ class TimelineTabRenderTest {
         }
         throw new AssertionError("no line with " + text);
     }
+
+    @Test
+    void aMillisecondOfJitterIsNoFullBar() {
+        // a fast route: 0 and 1 ms seconds are noise at the bottom, not spikes
+        assertThat(TimelineTab.meanScale(1, 0)).isEqualTo(10);
+        assertThat(TimelineTab.spark(new long[] { 0, 1, 0, 1 }, 
TimelineTab.meanScale(1, 0)))
+                .doesNotContain("█");
+        // a slow second still stands out against a steady time
+        assertThat(TimelineTab.meanScale(400, 50)).isEqualTo(400);
+        assertThat(TimelineTab.meanScale(60, 50)).isEqualTo(100);
+    }
 }

Reply via email to