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 c916a75ec8b8 CAMEL-23831: Refactor Classpath tab to use / filter with
substring matching in camel-tui
c916a75ec8b8 is described below
commit c916a75ec8b807e7628e72d23fec9644a55b40e0
Author: Claus Ibsen <[email protected]>
AuthorDate: Sat Jul 4 14:26:41 2026 +0200
CAMEL-23831: Refactor Classpath tab to use / filter with substring matching
in camel-tui
CAMEL-23831: Make Consumers and DataSource tabs read-only in camel-tui
CAMEL-23831: Polish read-only tabs, memory trend, process user, and startup
in camel-tui
- Add left padding on first column for Consumers, DataSource, Health,
Inflight tabs
- Add border to DataSource tab
- Make Health and Inflight tabs read-only (no selection)
- Memory trend threshold relative to committed size to avoid false alarms
- Show process user in Process tab
- Remove unnecessary F5 reload from Startup tab
Co-Authored-By: Claude Opus 4.6 <[email protected]>
Signed-off-by: Claus Ibsen <[email protected]>
---
.../dsl/jbang/core/commands/tui/CamelMonitor.java | 6 +-
.../dsl/jbang/core/commands/tui/ClasspathTab.java | 150 ++++++++++-----------
.../dsl/jbang/core/commands/tui/ConsumersTab.java | 30 ++++-
.../dsl/jbang/core/commands/tui/DataSourceTab.java | 33 ++++-
.../dsl/jbang/core/commands/tui/HealthTab.java | 29 +++-
.../dsl/jbang/core/commands/tui/InflightTab.java | 30 ++++-
.../dsl/jbang/core/commands/tui/MemoryTab.java | 11 +-
.../dsl/jbang/core/commands/tui/ProcessTab.java | 16 +++
.../dsl/jbang/core/commands/tui/StartupTab.java | 7 +-
.../dsl/jbang/core/commands/tui/TabRegistry.java | 4 +
10 files changed, 220 insertions(+), 96 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 cd21ef80a14d..e109d54a0683 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
@@ -569,10 +569,14 @@ public class CamelMonitor extends CamelCommand {
boolean beanFilterActive = tabRegistry.selectedTabIndex() == TAB_MORE
&& tabRegistry.getActiveMoreTab() == tabRegistry.beansTab()
&& tabRegistry.beansTab().isFilterInputActive();
+ boolean classpathFilterActive = tabRegistry.selectedTabIndex() ==
TAB_MORE
+ && tabRegistry.getActiveMoreTab() == tabRegistry.classpathTab()
+ && tabRegistry.classpathTab().isFilterInputActive();
boolean sqlInputActive = tabRegistry.selectedTabIndex() == TAB_MORE
&& tabRegistry.getActiveMoreTab() == tabRegistry.sqlQueryTab()
&& tabRegistry.sqlQueryTab().isInputActive();
- boolean textEditing = probeEditing || logSearchActive ||
spanFilterActive || beanFilterActive || sqlInputActive;
+ boolean textEditing = probeEditing || logSearchActive ||
spanFilterActive || beanFilterActive
+ || classpathFilterActive || sqlInputActive;
if (!textEditing && (ke.isCharIgnoreCase('q') || ke.isCtrlC())) {
runner.quit();
return true;
diff --git
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/ClasspathTab.java
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/ClasspathTab.java
index f418f2aca1b1..abc3d512f251 100644
---
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/ClasspathTab.java
+++
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/ClasspathTab.java
@@ -36,6 +36,7 @@ import dev.tamboui.tui.event.MouseEventKind;
import dev.tamboui.widgets.block.Block;
import dev.tamboui.widgets.block.BorderType;
import dev.tamboui.widgets.block.Borders;
+import dev.tamboui.widgets.input.TextInputState;
import dev.tamboui.widgets.list.ListItem;
import dev.tamboui.widgets.list.ListState;
import dev.tamboui.widgets.list.ListWidget;
@@ -51,16 +52,16 @@ import static
org.apache.camel.dsl.jbang.core.commands.tui.TuiHelper.*;
class ClasspathTab extends AbstractTab {
- private static final Style MATCH_STYLE =
Style.EMPTY.fg(Color.YELLOW).bold();
-
private final ListState listState = new ListState();
private final ScrollbarState listScrollState = new ScrollbarState();
- private final FuzzyFilter fuzzyFilter = new FuzzyFilter();
private final AtomicBoolean loading = new AtomicBoolean(false);
private Rect lastArea;
+ private boolean filterInputActive;
+ private TextInputState filterInputState = new TextInputState("");
+ private String filterTerm;
private List<JarEntry> allEntries = Collections.emptyList();
- private List<FilteredEntry> filteredEntries = Collections.emptyList();
+ private List<JarEntry> filteredEntries = Collections.emptyList();
private String lastPid;
private String errorMessage;
private boolean dataLoaded;
@@ -86,7 +87,8 @@ class ClasspathTab extends AbstractTab {
public void onIntegrationChanged() {
allEntries = Collections.emptyList();
filteredEntries = Collections.emptyList();
- fuzzyFilter.clearFilter();
+ filterTerm = null;
+ filterInputActive = false;
lastPid = null;
errorMessage = null;
dataLoaded = false;
@@ -94,12 +96,13 @@ class ClasspathTab extends AbstractTab {
@Override
public boolean handleKeyEvent(KeyEvent ke) {
- if (ke.isDeleteBackward()) {
- if (fuzzyFilter.hasFilter()) {
- fuzzyFilter.deleteChar();
- refilter();
- return true;
- }
+ if (filterInputActive) {
+ return handleFilterInput(ke);
+ }
+ if (ke.isChar('/')) {
+ filterInputActive = true;
+ filterInputState = new TextInputState(filterTerm != null ?
filterTerm : "");
+ return true;
}
if (ke.isPageUp() || ke.isKey(KeyCode.PAGE_UP)) {
for (int i = 0; i < 20 && listState.selected() != null &&
listState.selected() > 0; i++) {
@@ -113,12 +116,27 @@ class ClasspathTab extends AbstractTab {
}
return true;
}
- if (ke.code() == KeyCode.CHAR) {
- fuzzyFilter.appendChar(ke.string().charAt(0));
+ return false;
+ }
+
+ private boolean handleFilterInput(KeyEvent ke) {
+ if (ke.isKey(KeyCode.ESCAPE)) {
+ filterInputActive = false;
+ return true;
+ }
+ if (ke.isConfirm()) {
+ String text = filterInputState.text().trim();
+ filterTerm = text.isEmpty() ? null : text;
+ filterInputActive = false;
refilter();
return true;
}
- return false;
+ FormHelper.handleTextInput(ke, filterInputState);
+ return true;
+ }
+
+ boolean isFilterInputActive() {
+ return filterInputActive;
}
@Override
@@ -143,8 +161,8 @@ class ClasspathTab extends AbstractTab {
@Override
public boolean handleEscape() {
- if (fuzzyFilter.hasFilter()) {
- fuzzyFilter.clearFilter();
+ if (filterTerm != null) {
+ filterTerm = null;
refilter();
return true;
}
@@ -194,28 +212,19 @@ class ClasspathTab extends AbstractTab {
int contentW = Math.max(10, area.width() - 4);
List<ListItem> items = new ArrayList<>();
- for (FilteredEntry fe : filteredEntries) {
- String displayText = formatEntry(fe.entry(), contentW);
- Style normalStyle = fe.entry().isCamel() ? Style.EMPTY :
Style.EMPTY.dim();
- if (fe.matchPositions() != null && fe.matchPositions().length > 0)
{
- int[] adjusted = adjustPositions(fe.matchPositions(),
fe.entry(), displayText);
- Line line = FuzzyFilter.highlightLine(displayText, adjusted,
normalStyle, MATCH_STYLE);
- items.add(ListItem.from(Text.from(line)));
- } else {
- items.add(ListItem.from(displayText).style(normalStyle));
- }
+ for (JarEntry entry : filteredEntries) {
+ String displayText = formatEntry(entry, contentW);
+ Style normalStyle = entry.isCamel() ? Style.EMPTY :
Style.EMPTY.dim();
+ items.add(ListItem.from(displayText).style(normalStyle));
}
if (items.isEmpty() && dataLoaded) {
items.add(ListItem.from(" No classpath entries
found").style(Style.EMPTY.dim()));
}
- String title = " Classpath (" + filteredEntries.size();
- if (fuzzyFilter.hasFilter()) {
- title += "/" + allEntries.size() + ") [" + fuzzyFilter.filter() +
"] ";
- } else {
- title += ") ";
- }
+ String title = filterTerm != null
+ ? String.format(" Classpath [%d/%d] filter:\"%s\" ",
filteredEntries.size(), allEntries.size(), filterTerm)
+ : String.format(" Classpath [%d] ", filteredEntries.size());
ListWidget list = ListWidget.builder()
.items(items.toArray(ListItem[]::new))
@@ -240,9 +249,21 @@ class ClasspathTab extends AbstractTab {
@Override
public void renderFooter(List<Span> spans) {
- hint(spans, "Esc", fuzzyFilter.hasFilter() ? "clear" : "back");
- hint(spans, "↑↓", "navigate");
- hintLast(spans, "type", "filter");
+ if (filterInputActive) {
+ spans.add(Span.styled(" /", Style.EMPTY.fg(Color.YELLOW).bold()));
+ spans.add(Span.raw(filterInputState.text() + "█ "));
+ hint(spans, "Enter", "filter");
+ hintLast(spans, "Esc", "cancel");
+ return;
+ }
+ hint(spans, "Esc", filterTerm != null ? "clear" : "back");
+ if (filterTerm != null) {
+ spans.add(Span.styled(" /", Style.EMPTY.fg(Color.YELLOW).bold()));
+ spans.add(Span.raw("\"" + filterTerm + "\" "));
+ } else {
+ hint(spans, "/", "filter");
+ }
+ hintLast(spans, "↑↓", "navigate");
}
private void loadClasspath() {
@@ -318,34 +339,17 @@ class ClasspathTab extends AbstractTab {
}
private void refilter() {
- List<FilteredEntry> result = new ArrayList<>();
+ List<JarEntry> result = new ArrayList<>();
+ String ft = filterTerm != null ? filterTerm.toLowerCase() : null;
for (JarEntry entry : allEntries) {
- if (!fuzzyFilter.hasFilter()) {
- result.add(new FilteredEntry(entry, null));
- } else {
- int[] positions = fuzzyFilter.match(entry.display());
- if (positions != null) {
- result.add(new FilteredEntry(entry, positions));
- }
+ if (ft == null || entry.display().toLowerCase().contains(ft)) {
+ result.add(entry);
}
}
filteredEntries = result;
listState.select(filteredEntries.isEmpty() ? null : 0);
}
- private int[] adjustPositions(int[] matchPositions, JarEntry entry, String
displayText) {
- String searchText = entry.display();
- int offset = displayText.indexOf(searchText.substring(0,
Math.min(searchText.length(), 5)));
- if (offset < 0) {
- offset = 2;
- }
- int[] adjusted = new int[matchPositions.length];
- for (int i = 0; i < matchPositions.length; i++) {
- adjusted[i] = matchPositions[i] + offset;
- }
- return adjusted;
- }
-
private String formatEntry(JarEntry entry, int width) {
if (entry.groupId() != null) {
String gav = entry.groupId() + ":" + entry.artifactId();
@@ -384,12 +388,7 @@ class ClasspathTab extends AbstractTab {
@Override
public boolean setFilter(String filter) {
- fuzzyFilter.clearFilter();
- if (filter != null && !filter.isEmpty()) {
- for (char c : filter.toCharArray()) {
- fuzzyFilter.appendChar(c);
- }
- }
+ filterTerm = filter != null && !filter.isEmpty() ? filter : null;
refilter();
return true;
}
@@ -420,9 +419,6 @@ class ClasspathTab extends AbstractTab {
}
}
- record FilteredEntry(JarEntry entry, int[] matchPositions) {
- }
-
@Override
public String description() {
return "JVM classpath entries with filtering";
@@ -458,16 +454,16 @@ class ClasspathTab extends AbstractTab {
org.slf4j:slf4j-api 2.0.16
```
- ## Fuzzy Filter
+ ## Filter
- Start typing to filter the classpath. The filter uses fuzzy
matching
- so you can type partial strings like `kafka` to find all
Kafka-related
- JARs, or `jackson` to find Jackson dependencies. Matching
characters
- are highlighted in yellow.
+ Press `/` to open the filter input. Type a search term and
press
+ `Enter` to filter the classpath by substring match. For
example,
+ type `kafka` to find all Kafka-related JARs, or `jackson` to
find
+ Jackson dependencies.
- - Type any characters to filter
- - `Backspace` to delete characters from filter
- - `Esc` to clear filter
+ - `/` — open filter input
+ - `Enter` — apply filter
+ - `Esc` — clear filter
## When To Use
@@ -480,23 +476,21 @@ class ClasspathTab extends AbstractTab {
- `Up/Down` — navigate entries
- `PgUp/PgDn` — scroll by page
- - `type` — fuzzy filter
- - `Backspace` — delete filter character
+ - `/` — open filter
- `Esc` — clear filter or back
""";
}
@Override
public JsonObject getTableDataAsJson() {
- List<FilteredEntry> entries = filteredEntries;
+ List<JarEntry> entries = filteredEntries;
if (entries.isEmpty()) {
return null;
}
JsonObject result = new JsonObject();
result.put("tab", "Classpath");
JsonArray rows = new JsonArray();
- for (FilteredEntry fe : entries) {
- JarEntry j = fe.entry();
+ for (JarEntry j : entries) {
JsonObject row = new JsonObject();
if (j.groupId() != null) {
row.put("groupId", j.groupId());
diff --git
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/ConsumersTab.java
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/ConsumersTab.java
index 6feb26e385cc..2bea6caa2865 100644
---
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/ConsumersTab.java
+++
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/ConsumersTab.java
@@ -27,6 +27,9 @@ import dev.tamboui.style.Color;
import dev.tamboui.style.Style;
import dev.tamboui.terminal.Frame;
import dev.tamboui.text.Span;
+import dev.tamboui.tui.event.KeyCode;
+import dev.tamboui.tui.event.KeyEvent;
+import dev.tamboui.tui.event.MouseEvent;
import dev.tamboui.widgets.block.Block;
import dev.tamboui.widgets.block.BorderType;
import dev.tamboui.widgets.block.Borders;
@@ -44,6 +47,29 @@ class ConsumersTab extends AbstractTableTab {
super(ctx, "id", "status", "type", "inflight", "polls", "uri");
}
+ @Override
+ public void navigateUp() {
+ }
+
+ @Override
+ public void navigateDown() {
+ }
+
+ @Override
+ public boolean handleKeyEvent(KeyEvent ke) {
+ if (ke.isPageUp() || ke.isKey(KeyCode.PAGE_UP)
+ || ke.isPageDown() || ke.isKey(KeyCode.PAGE_DOWN)
+ || ke.isHome() || ke.isEnd()) {
+ return false;
+ }
+ return super.handleKeyEvent(ke);
+ }
+
+ @Override
+ public boolean handleMouseEvent(MouseEvent me, Rect area) {
+ return false;
+ }
+
@Override
protected int getRowCount() {
IntegrationInfo info = ctx.findSelectedIntegration();
@@ -74,7 +100,7 @@ class ConsumersTab extends AbstractTableTab {
: (ci.uri != null ? ci.uri : "");
rows.add(Row.from(
- Cell.from(Span.styled(ci.id != null ? ci.id : "",
Style.EMPTY.fg(Color.CYAN))),
+ Cell.from(Span.styled(" " + (ci.id != null ? ci.id : ""),
Style.EMPTY.fg(Color.CYAN))),
Cell.from(Span.styled(statusText, statusStyle)),
Cell.from(type),
rightCell(String.valueOf(ci.inflight), 8),
@@ -91,7 +117,7 @@ class ConsumersTab extends AbstractTableTab {
Table table = Table.builder()
.rows(rows)
.header(Row.from(
- Cell.from(Span.styled(sortLabel("ROUTE", "id"),
sortStyle("id"))),
+ Cell.from(Span.styled(" " + sortLabel("ROUTE", "id"),
sortStyle("id"))),
Cell.from(Span.styled(sortLabel("STATUS", "status"),
sortStyle("status"))),
Cell.from(Span.styled(sortLabel("TYPE", "type"),
sortStyle("type"))),
rightCell(sortLabel("INFLIGHT", "inflight"), 8,
sortStyle("inflight")),
diff --git
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/DataSourceTab.java
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/DataSourceTab.java
index de6011fe2255..6d434e75c943 100644
---
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/DataSourceTab.java
+++
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/DataSourceTab.java
@@ -25,8 +25,12 @@ import dev.tamboui.style.Color;
import dev.tamboui.style.Style;
import dev.tamboui.terminal.Frame;
import dev.tamboui.text.Span;
+import dev.tamboui.tui.event.KeyCode;
+import dev.tamboui.tui.event.KeyEvent;
+import dev.tamboui.tui.event.MouseEvent;
import dev.tamboui.widgets.block.Block;
import dev.tamboui.widgets.block.BorderType;
+import dev.tamboui.widgets.block.Borders;
import dev.tamboui.widgets.table.Cell;
import dev.tamboui.widgets.table.Row;
import dev.tamboui.widgets.table.Table;
@@ -41,6 +45,29 @@ class DataSourceTab extends AbstractTableTab {
super(ctx, "name", "pool", "active", "idle", "total", "max",
"waiting");
}
+ @Override
+ public void navigateUp() {
+ }
+
+ @Override
+ public void navigateDown() {
+ }
+
+ @Override
+ public boolean handleKeyEvent(KeyEvent ke) {
+ if (ke.isPageUp() || ke.isKey(KeyCode.PAGE_UP)
+ || ke.isPageDown() || ke.isKey(KeyCode.PAGE_DOWN)
+ || ke.isHome() || ke.isEnd()) {
+ return false;
+ }
+ return super.handleKeyEvent(ke);
+ }
+
+ @Override
+ public boolean handleMouseEvent(MouseEvent me, Rect area) {
+ return false;
+ }
+
@Override
protected int getRowCount() {
IntegrationInfo info = ctx.findSelectedIntegration();
@@ -67,7 +94,7 @@ class DataSourceTab extends AbstractTableTab {
String poolLabel = di.poolName != null ? di.poolName :
(di.poolType != null ? di.poolType : "");
rows.add(Row.from(
- Cell.from(Span.styled(di.name != null ? di.name : "",
Style.EMPTY.fg(Color.CYAN))),
+ Cell.from(Span.styled(" " + (di.name != null ? di.name :
""), Style.EMPTY.fg(Color.CYAN))),
Cell.from(poolLabel),
rightCell(String.valueOf(di.active), 8, activeStyle),
rightCell(String.valueOf(di.idle), 8),
@@ -84,7 +111,7 @@ class DataSourceTab extends AbstractTableTab {
Table table = Table.builder()
.rows(rows)
.header(Row.from(
- Cell.from(Span.styled(sortLabel("NAME", "name"),
sortStyle("name"))),
+ Cell.from(Span.styled(" " + sortLabel("NAME", "name"),
sortStyle("name"))),
Cell.from(Span.styled(sortLabel("POOL", "pool"),
sortStyle("pool"))),
rightCell(sortLabel("ACTIVE", "active"), 8,
sortStyle("active")),
rightCell(sortLabel("IDLE", "idle"), 8,
sortStyle("idle")),
@@ -101,7 +128,7 @@ class DataSourceTab extends AbstractTableTab {
Constraint.length(8),
Constraint.length(8),
Constraint.fill())
- .block(Block.builder().borderType(BorderType.ROUNDED)
+
.block(Block.builder().borderType(BorderType.ROUNDED).borders(Borders.ALL)
.title(" DataSource sort:" + sort + " ").build())
.build();
diff --git
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/HealthTab.java
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/HealthTab.java
index c2a4a6d04e11..705d20a2ef5b 100644
---
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/HealthTab.java
+++
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/HealthTab.java
@@ -25,7 +25,9 @@ import dev.tamboui.style.Color;
import dev.tamboui.style.Style;
import dev.tamboui.terminal.Frame;
import dev.tamboui.text.Span;
+import dev.tamboui.tui.event.KeyCode;
import dev.tamboui.tui.event.KeyEvent;
+import dev.tamboui.tui.event.MouseEvent;
import dev.tamboui.widgets.block.Block;
import dev.tamboui.widgets.block.BorderType;
import dev.tamboui.widgets.block.Borders;
@@ -47,6 +49,29 @@ class HealthTab extends AbstractTableTab {
sort = "name";
}
+ @Override
+ public void navigateUp() {
+ }
+
+ @Override
+ public void navigateDown() {
+ }
+
+ @Override
+ public boolean handleKeyEvent(KeyEvent ke) {
+ if (ke.isPageUp() || ke.isKey(KeyCode.PAGE_UP)
+ || ke.isPageDown() || ke.isKey(KeyCode.PAGE_DOWN)
+ || ke.isHome() || ke.isEnd()) {
+ return false;
+ }
+ return super.handleKeyEvent(ke);
+ }
+
+ @Override
+ public boolean handleMouseEvent(MouseEvent me, Rect area) {
+ return false;
+ }
+
@Override
protected int getRowCount() {
IntegrationInfo info = ctx.findSelectedIntegration();
@@ -91,7 +116,7 @@ class HealthTab extends AbstractTableTab {
}
rows.add(Row.from(
- Cell.from(Span.styled(hc.group != null ? hc.group : "",
Style.EMPTY.dim())),
+ Cell.from(Span.styled(" " + (hc.group != null ? hc.group :
""), Style.EMPTY.dim())),
Cell.from(Span.styled(hc.name != null ? hc.name : "",
Style.EMPTY.fg(Color.CYAN))),
Cell.from(Span.styled(icon + hc.state, stateStyle)),
Cell.from(kind),
@@ -115,7 +140,7 @@ class HealthTab extends AbstractTableTab {
Table table = Table.builder()
.rows(rows)
.header(Row.from(
- Cell.from(Span.styled(sortLabel("GROUP", "group"),
sortStyle("group"))),
+ Cell.from(Span.styled(" " + sortLabel("GROUP",
"group"), sortStyle("group"))),
Cell.from(Span.styled(sortLabel("NAME", "name"),
sortStyle("name"))),
Cell.from(Span.styled(sortLabel("STATUS", "status"),
sortStyle("status"))),
Cell.from(Span.styled("KIND", Style.EMPTY.bold())),
diff --git
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/InflightTab.java
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/InflightTab.java
index 27f5656de1d7..9fc158057591 100644
---
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/InflightTab.java
+++
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/InflightTab.java
@@ -27,6 +27,9 @@ import dev.tamboui.terminal.Frame;
import dev.tamboui.text.Line;
import dev.tamboui.text.Span;
import dev.tamboui.text.Text;
+import dev.tamboui.tui.event.KeyCode;
+import dev.tamboui.tui.event.KeyEvent;
+import dev.tamboui.tui.event.MouseEvent;
import dev.tamboui.widgets.block.Block;
import dev.tamboui.widgets.block.BorderType;
import dev.tamboui.widgets.block.Borders;
@@ -55,6 +58,29 @@ class InflightTab extends AbstractTableTab {
sort = "duration";
}
+ @Override
+ public void navigateUp() {
+ }
+
+ @Override
+ public void navigateDown() {
+ }
+
+ @Override
+ public boolean handleKeyEvent(KeyEvent ke) {
+ if (ke.isPageUp() || ke.isKey(KeyCode.PAGE_UP)
+ || ke.isPageDown() || ke.isKey(KeyCode.PAGE_DOWN)
+ || ke.isHome() || ke.isEnd()) {
+ return false;
+ }
+ return super.handleKeyEvent(ke);
+ }
+
+ @Override
+ public boolean handleMouseEvent(MouseEvent me, Rect area) {
+ return false;
+ }
+
@Override
protected int getRowCount() {
IntegrationInfo info = ctx.findSelectedIntegration();
@@ -90,7 +116,7 @@ class InflightTab extends AbstractTableTab {
Span barSpan = buildDurationBar(ii.duration, maxDuration, 20);
rows.add(Row.from(
- Cell.from(Span.styled(status, statusStyle)),
+ Cell.from(Span.styled(" " + status, statusStyle)),
Cell.from(Span.styled(ii.exchangeId != null ?
ii.exchangeId : "", Style.EMPTY.fg(Color.CYAN))),
Cell.from(routeNode),
rightCell(duration, 14, durationStyle),
@@ -106,7 +132,7 @@ class InflightTab extends AbstractTableTab {
Table table = Table.builder()
.rows(rows)
.header(Row.from(
- Cell.from(Span.styled(sortLabel("STATUS", "status"),
sortStyle("status"))),
+ Cell.from(Span.styled(" " + sortLabel("STATUS",
"status"), sortStyle("status"))),
Cell.from(Span.styled(sortLabel("EXCHANGE ID",
"exchange"), sortStyle("exchange"))),
Cell.from(Span.styled(sortLabel("ROUTE/NODE",
"route"), sortStyle("route"))),
rightCell(sortLabel("DURATION", "duration"), 14,
sortStyle("duration")),
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 3081c54bff79..1cad072dc3c8 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
@@ -131,7 +131,7 @@ class MemoryTab extends AbstractTab {
// Compute heap trend from history
LinkedList<Long> hist = heapMemHistory.get(info.pid);
- Span trendSpan = computeTrendSpan(hist);
+ Span trendSpan = computeTrendSpan(hist, info.heapMemCommitted);
lines.add(Line.from(
Span.styled(" used: ", Style.EMPTY.dim()),
@@ -380,7 +380,7 @@ class MemoryTab extends AbstractTab {
}
}
- private static Span computeTrendSpan(LinkedList<Long> hist) {
+ private static Span computeTrendSpan(LinkedList<Long> hist, long
heapCeiling) {
// need at least 30 samples (~2.5 min at 5s intervals) for a
meaningful trend
if (hist == null || hist.size() < 30) {
return null;
@@ -409,6 +409,13 @@ class MemoryTab extends AbstractTab {
// each sample is taken every 5 seconds (HEAP_SAMPLE_INTERVAL_MS)
long seconds = size * 5L;
String period = seconds >= 60 ? (seconds / 60) + "m" : seconds + "s";
+
+ // ignore small fluctuations relative to heap capacity (at least 5M or
1% of ceiling)
+ long threshold = Math.max(5 * 1024 * 1024, (long) (heapCeiling *
0.01));
+ if (Math.abs(diff) < threshold) {
+ return Span.styled(" → stable over last " + period,
Style.EMPTY.fg(Color.GREEN));
+ }
+
if (change > 0.05) {
return Span.styled(
String.format(" ↑ growing by %d%% (%s) over last %s",
pct, formatBytes(diff), period),
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 12215adc5db9..836c2e06ea36 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
@@ -111,6 +111,7 @@ class ProcessTab extends AbstractTab {
List<Line> lines = new ArrayList<>();
addField(lines, "PID", info.pid);
+ addField(lines, "User", getProcessUser(info.pid));
addField(lines, "Name", info.name);
addField(lines, "Camel", info.camelVersion);
@@ -213,6 +214,10 @@ class ProcessTab extends AbstractTab {
result.put("tab", "Process");
JsonObject data = new JsonObject();
data.put("pid", info.pid);
+ String user = getProcessUser(info.pid);
+ if (user != null) {
+ data.put("user", user);
+ }
data.put("name", info.name);
data.put("camelVersion", info.camelVersion);
data.put("platform", info.platform);
@@ -241,6 +246,17 @@ class ProcessTab extends AbstractTab {
Span.styled(value, Style.EMPTY.fg(Color.WHITE).bold())));
}
+ private static String getProcessUser(String pid) {
+ try {
+ long p = Long.parseLong(pid);
+ return ProcessHandle.of(p)
+ .flatMap(ph -> ph.info().user())
+ .orElse(null);
+ } catch (Exception e) {
+ return null;
+ }
+ }
+
private static String getCommandLine(String pid) {
try {
long p = Long.parseLong(pid);
diff --git
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/StartupTab.java
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/StartupTab.java
index 3f097462f4f1..a24c300fb311 100644
---
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/StartupTab.java
+++
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/StartupTab.java
@@ -103,10 +103,6 @@ class StartupTab extends AbstractTab {
scrollOffset = Integer.MAX_VALUE;
return true;
}
- if (ke.isKey(KeyCode.F5)) {
- loadStartupData();
- return true;
- }
return false;
}
@@ -260,8 +256,7 @@ class StartupTab extends AbstractTab {
public void renderFooter(List<Span> spans) {
hint(spans, "Esc", "back");
hint(spans, "↑↓", "scroll");
- hint(spans, "PgUp/Dn", "page");
- hintLast(spans, "F5", "reload");
+ hintLast(spans, "PgUp/Dn", "page");
}
private void loadStartupData() {
diff --git
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/TabRegistry.java
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/TabRegistry.java
index db4d61526229..51a667b10340 100644
---
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/TabRegistry.java
+++
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/TabRegistry.java
@@ -321,6 +321,10 @@ class TabRegistry {
return sqlQueryTab;
}
+ ClasspathTab classpathTab() {
+ return classpathTab;
+ }
+
// ---- Tab entries for Go-to and MCP ----
record TabEntry(String name, String description, String shortcut, int
tabIndex, int moreIndex) {