You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by da...@apache.org on 2026/05/17 16:18:38 UTC
(camel) branch fix/camel-tui-endpoint-2 created (now 32501077fbf7)
This is an automated email from the ASF dual-hosted git repository.
davsclaus pushed a change to branch fix/camel-tui-endpoint-2
in repository https://gitbox.apache.org/repos/asf/camel.git
at 32501077fbf7 TUI: endpoint tab UX improvements - remote filter, colors, layout
This branch includes the following new commits:
new 32501077fbf7 TUI: endpoint tab UX improvements - remote filter, colors, layout
The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
(camel) 01/01: TUI: endpoint tab UX improvements - remote filter, colors, layout
Posted by da...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
davsclaus pushed a commit to branch fix/camel-tui-endpoint-2
in repository https://gitbox.apache.org/repos/asf/camel.git
commit 32501077fbf75b6caa22b6d0191beb6caed60abb
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Sun May 17 18:18:04 2026 +0200
TUI: endpoint tab UX improvements - remote filter, colors, layout
- Endpoint flow panel and mirrored sparkline now respect the remote
on/off toggle: maintain two parallel sliding window histories (all vs
remote-only) and switch between them at render time; flow totals also
filtered accordingly
- Replace Color.BLUE with Color.CYAN throughout the endpoints tab
(DIR out arrow, flow panel, chart legend, MirroredSparkline) and in
the routes diagram (to/toD/wireTap/enrich/pollEnrich nodes) for
visibility on dark terminals
- Use BRIGHT_GREEN (AnsiColor) for in-traffic: throughput chart bars
and legend, endpoint DIR in arrow, flow panel in-arrow and legend,
MirroredSparkline top bars
- Integration name box in flow diagram coloured yellow bold
- Flow diagram in/out direction arrows and labels always shown in their
direction colour regardless of traffic level; only count numbers dim
when zero
- "in"/"out" labels centered within their fixed-width slot so they no
longer shift when counts change length or remote toggle changes totals
Co-Authored-By: Claude Sonnet 4.6 <no...@anthropic.com>
---
.../dsl/jbang/core/commands/tui/CamelMonitor.java | 106 ++++++++++++++-------
1 file changed, 69 insertions(+), 37 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 2f3da9f81ac9..65ddec4afe86 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
@@ -48,6 +48,7 @@ import dev.tamboui.layout.Alignment;
import dev.tamboui.layout.Constraint;
import dev.tamboui.layout.Layout;
import dev.tamboui.layout.Rect;
+import dev.tamboui.style.AnsiColor;
import dev.tamboui.style.Color;
import dev.tamboui.style.Overflow;
import dev.tamboui.style.Style;
@@ -169,12 +170,18 @@ public class CamelMonitor extends CamelCommand {
// Track last time a sparkline point was recorded
private final Map<String, Long> previousExchangesTime = new ConcurrentHashMap<>();
- // Endpoint in/out sliding window history per PID (one point per second, 20 points)
+ // Endpoint in/out sliding window history per PID — all endpoints
private final Map<String, LinkedList<Long>> endpointInHistory = new ConcurrentHashMap<>();
private final Map<String, LinkedList<Long>> endpointOutHistory = new ConcurrentHashMap<>();
private final Map<String, LinkedList<long[]>> endpointSamples = new ConcurrentHashMap<>();
private final Map<String, Long> previousEndpointTime = new ConcurrentHashMap<>();
+ // Endpoint in/out sliding window history per PID — remote endpoints only
+ private final Map<String, LinkedList<Long>> endpointRemoteInHistory = new ConcurrentHashMap<>();
+ private final Map<String, LinkedList<Long>> endpointRemoteOutHistory = new ConcurrentHashMap<>();
+ private final Map<String, LinkedList<long[]>> endpointRemoteSamples = new ConcurrentHashMap<>();
+ private final Map<String, Long> previousEndpointRemoteTime = new ConcurrentHashMap<>();
+
// Overview sort state
private String overviewSort = "name";
private int overviewSortIndex = 1;
@@ -1290,26 +1297,26 @@ public class CamelMonitor extends CamelCommand {
Span.raw(" ["),
Span.styled(chartName, Style.EMPTY.fg(Color.YELLOW)),
Span.raw(String.format("] Throughput: %d msg/s ", curTp)),
- Span.styled("■", Style.EMPTY.fg(Color.GREEN)),
+ Span.styled("■", Style.EMPTY.fg(Color.ansi(AnsiColor.BRIGHT_GREEN))),
Span.raw(String.format(" ok:%d ", curOk)),
Span.styled("■", Style.EMPTY.fg(Color.RED)),
Span.raw(String.format(" fail:%d ", curFailed)));
} else {
titleLine = Line.from(
Span.raw(String.format(" [All] Throughput: %d msg/s ", curTp)),
- Span.styled("■", Style.EMPTY.fg(Color.GREEN)),
+ Span.styled("■", Style.EMPTY.fg(Color.ansi(AnsiColor.BRIGHT_GREEN))),
Span.raw(String.format(" ok:%d ", curOk)),
Span.styled("■", Style.EMPTY.fg(Color.RED)),
Span.raw(String.format(" fail:%d ", curFailed)));
}
- // Build bar groups (ok=green, failed=red), no bar value labels
+ // Build bar groups (ok=bright green, failed=red), no bar value labels
List<BarGroup> groups = new ArrayList<>();
for (int i = 0; i < renderPoints; i++) {
long failed = Math.min(mergedFailed[i], mergedTotal[i]);
long ok = Math.max(0, mergedTotal[i] - failed);
groups.add(BarGroup.of(
- Bar.builder().value(ok).textValue("").style(Style.EMPTY.fg(Color.GREEN)).build(),
+ Bar.builder().value(ok).textValue("").style(Style.EMPTY.fg(Color.ansi(AnsiColor.BRIGHT_GREEN))).build(),
Bar.builder().value(failed).textValue("").style(Style.EMPTY.fg(Color.RED)).build()));
}
@@ -2239,7 +2246,7 @@ public class CamelMonitor extends CamelCommand {
}
return switch (type) {
case "from" -> Color.GREEN;
- case "to", "toD", "wireTap", "enrich", "pollEnrich" -> Color.BLUE;
+ case "to", "toD", "wireTap", "enrich", "pollEnrich" -> Color.CYAN;
case "choice", "when", "otherwise" -> Color.YELLOW;
case "marshal", "unmarshal", "transform", "setBody", "setHeader", "setProperty",
"convertBodyTo", "removeHeader", "removeHeaders", "removeProperty", "removeProperties" ->
@@ -2918,8 +2925,8 @@ public class CamelMonitor extends CamelCommand {
for (EndpointInfo ep : sortedEndpoints) {
String dir = ep.direction != null ? ep.direction : "";
Style dirStyle = switch (dir) {
- case "in" -> Style.EMPTY.fg(Color.GREEN);
- case "out" -> Style.EMPTY.fg(Color.BLUE);
+ case "in" -> Style.EMPTY.fg(Color.ansi(AnsiColor.BRIGHT_GREEN));
+ case "out" -> Style.EMPTY.fg(Color.CYAN);
default -> Style.EMPTY.fg(Color.YELLOW);
};
String arrow = switch (dir) {
@@ -2980,17 +2987,18 @@ public class CamelMonitor extends CamelCommand {
frame.renderStatefulWidget(table, chunks.get(0), endpointTableState);
long inTotal = info.endpoints.stream()
- .filter(ep -> "in".equals(ep.direction))
+ .filter(ep -> "in".equals(ep.direction) && (!showOnlyRemote || ep.remote))
.mapToLong(ep -> ep.hits)
.sum();
long outTotal = info.endpoints.stream()
- .filter(ep -> "out".equals(ep.direction))
+ .filter(ep -> "out".equals(ep.direction) && (!showOnlyRemote || ep.remote))
.mapToLong(ep -> ep.hits)
.sum();
- renderEndpointFlow(frame, chunks.get(1), inTotal, outTotal, info.name, info.pid);
+ renderEndpointFlow(frame, chunks.get(1), inTotal, outTotal, info.name, info.pid, showOnlyRemote);
}
- private void renderEndpointFlow(Frame frame, Rect area, long inTotal, long outTotal, String name, String pid) {
+ private void renderEndpointFlow(
+ Frame frame, Rect area, long inTotal, long outTotal, String name, String pid, boolean remoteOnly) {
List<Rect> hSplit = Layout.horizontal()
.constraints(Constraint.length(38), Constraint.fill())
.split(area);
@@ -3016,8 +3024,14 @@ public class CamelMonitor extends CamelCommand {
int centerGap = boxLen + 2;
int outPad = Math.max(0, sideLen - outStr.length());
- Style inStyle = Style.EMPTY.fg(Color.GREEN);
- Style outStyle = Style.EMPTY.fg(Color.BLUE);
+ // Centre "in"/"out" labels within the fixed sideLen slot, independent of count width
+ int inLabelPad = (sideLen - 2) / 2;
+ int outLabelPad = (sideLen - 3) / 2;
+ String inLabelStr = " ".repeat(inLabelPad) + "in" + " ".repeat(sideLen - inLabelPad - 2);
+ String outLabelStr = " ".repeat(outLabelPad) + "out";
+
+ Style inStyle = Style.EMPTY.fg(Color.ansi(AnsiColor.BRIGHT_GREEN));
+ Style outStyle = Style.EMPTY.fg(Color.CYAN);
Style dimStyle = Style.EMPTY.dim();
List<Line> flowLines = new ArrayList<>();
@@ -3026,15 +3040,15 @@ public class CamelMonitor extends CamelCommand {
Span.raw(" ".repeat(centerGap)),
Span.styled(outStr + " ".repeat(outPad), outTotal > 0 ? outStyle : dimStyle)));
flowLines.add(Line.from(
- Span.styled(arrowStr, inTotal > 0 ? inStyle : dimStyle),
+ Span.styled(arrowStr, inStyle),
Span.raw(" "),
- Span.styled(box, Style.EMPTY.fg(Color.CYAN).bold()),
+ Span.styled(box, Style.EMPTY.fg(Color.YELLOW).bold()),
Span.raw(" "),
- Span.styled(arrowStr, outTotal > 0 ? outStyle : dimStyle)));
+ Span.styled(arrowStr, outStyle)));
flowLines.add(Line.from(
- Span.styled(" ".repeat(inPad) + "in", inTotal > 0 ? inStyle.dim() : dimStyle),
+ Span.styled(inLabelStr, inStyle.dim()),
Span.raw(" ".repeat(centerGap)),
- Span.styled("out" + " ".repeat(Math.max(0, outPad - 2)), outTotal > 0 ? outStyle.dim() : dimStyle)));
+ Span.styled(outLabelStr, outStyle.dim())));
frame.renderWidget(Paragraph.builder()
.text(Text.from(flowLines))
@@ -3042,8 +3056,10 @@ public class CamelMonitor extends CamelCommand {
.build(), hSplit.get(0));
// --- Right: 60-second sliding window chart (in=green up, out=blue down) ---
- LinkedList<Long> inHist = endpointInHistory.getOrDefault(pid, new LinkedList<>());
- LinkedList<Long> outHist = endpointOutHistory.getOrDefault(pid, new LinkedList<>());
+ LinkedList<Long> inHist = (remoteOnly ? endpointRemoteInHistory : endpointInHistory)
+ .getOrDefault(pid, new LinkedList<>());
+ LinkedList<Long> outHist = (remoteOnly ? endpointRemoteOutHistory : endpointOutHistory)
+ .getOrDefault(pid, new LinkedList<>());
int renderPoints = MAX_ENDPOINT_CHART_POINTS;
long[] inArr = new long[renderPoints];
@@ -3062,17 +3078,17 @@ public class CamelMonitor extends CamelCommand {
long curOut = outArr[renderPoints - 1];
Line chartTitle = Line.from(
- Span.styled("▬", Style.EMPTY.fg(Color.GREEN)),
+ Span.styled("▬", Style.EMPTY.fg(Color.ansi(AnsiColor.BRIGHT_GREEN))),
Span.raw(String.format(" in:%-4d ", curIn)),
- Span.styled("▬", Style.EMPTY.fg(Color.BLUE)),
+ Span.styled("▬", Style.EMPTY.fg(Color.CYAN)),
Span.raw(String.format(" out:%-4d msg/s", curOut)));
Rect rightArea = hSplit.get(1);
frame.renderWidget(MirroredSparkline.builder()
.topData(inArr)
.bottomData(outArr)
- .topStyle(Style.EMPTY.fg(Color.GREEN))
- .bottomStyle(Style.EMPTY.fg(Color.BLUE))
+ .topStyle(Style.EMPTY.fg(Color.ansi(AnsiColor.BRIGHT_GREEN)))
+ .bottomStyle(Style.EMPTY.fg(Color.CYAN))
.xLabels("-" + renderPoints + "s", "-" + (renderPoints * 3 / 4) + "s",
"-" + (renderPoints / 2) + "s", "-" + (renderPoints / 4) + "s", "now")
.block(Block.builder().borderType(BorderType.ROUNDED)
@@ -4123,6 +4139,10 @@ public class CamelMonitor extends CamelCommand {
endpointOutHistory.remove(entry.getKey());
endpointSamples.remove(entry.getKey());
previousEndpointTime.remove(entry.getKey());
+ endpointRemoteInHistory.remove(entry.getKey());
+ endpointRemoteOutHistory.remove(entry.getKey());
+ endpointRemoteSamples.remove(entry.getKey());
+ previousEndpointRemoteTime.remove(entry.getKey());
} else if (!livePids.contains(entry.getKey())) {
IntegrationInfo ghost = entry.getValue().info;
ghost.vanishing = true;
@@ -4213,38 +4233,50 @@ public class CamelMonitor extends CamelCommand {
private void updateEndpointHistory(IntegrationInfo info) {
long inTotal = info.endpoints.stream()
.filter(ep -> "in".equals(ep.direction))
- .mapToLong(ep -> ep.hits)
- .sum();
+ .mapToLong(ep -> ep.hits).sum();
long outTotal = info.endpoints.stream()
.filter(ep -> "out".equals(ep.direction))
- .mapToLong(ep -> ep.hits)
- .sum();
+ .mapToLong(ep -> ep.hits).sum();
+ long inRemote = info.endpoints.stream()
+ .filter(ep -> "in".equals(ep.direction) && ep.remote)
+ .mapToLong(ep -> ep.hits).sum();
+ long outRemote = info.endpoints.stream()
+ .filter(ep -> "out".equals(ep.direction) && ep.remote)
+ .mapToLong(ep -> ep.hits).sum();
long now = System.currentTimeMillis();
String pid = info.pid;
- LinkedList<long[]> samples = endpointSamples.computeIfAbsent(pid, k -> new LinkedList<>());
- samples.add(new long[] { now, inTotal, outTotal });
+ recordEndpointSample(pid, now, inTotal, outTotal,
+ endpointSamples, previousEndpointTime, endpointInHistory, endpointOutHistory);
+ recordEndpointSample(pid, now, inRemote, outRemote,
+ endpointRemoteSamples, previousEndpointRemoteTime, endpointRemoteInHistory, endpointRemoteOutHistory);
+ }
+
+ private void recordEndpointSample(
+ String pid, long now, long inTotal, long outTotal,
+ Map<String, LinkedList<long[]>> samplesMap, Map<String, Long> prevTimeMap,
+ Map<String, LinkedList<Long>> inHistMap, Map<String, LinkedList<Long>> outHistMap) {
+ LinkedList<long[]> samples = samplesMap.computeIfAbsent(pid, k -> new LinkedList<>());
+ samples.add(new long[] { now, inTotal, outTotal });
while (!samples.isEmpty() && now - samples.get(0)[0] > 1000) {
samples.remove(0);
}
-
if (samples.size() >= 2) {
long[] oldest = samples.get(0);
long[] newest = samples.get(samples.size() - 1);
long deltaMs = newest[0] - oldest[0];
long inRate = deltaMs > 0 ? (newest[1] - oldest[1]) * 1000 / deltaMs : 0;
long outRate = deltaMs > 0 ? (newest[2] - oldest[2]) * 1000 / deltaMs : 0;
-
- Long lastTime = previousEndpointTime.get(pid);
+ Long lastTime = prevTimeMap.get(pid);
if (lastTime == null || now - lastTime >= 1000) {
- previousEndpointTime.put(pid, now);
- LinkedList<Long> inHist = endpointInHistory.computeIfAbsent(pid, k -> new LinkedList<>());
+ prevTimeMap.put(pid, now);
+ LinkedList<Long> inHist = inHistMap.computeIfAbsent(pid, k -> new LinkedList<>());
inHist.add(Math.max(0, inRate));
while (inHist.size() > MAX_ENDPOINT_CHART_POINTS) {
inHist.remove(0);
}
- LinkedList<Long> outHist = endpointOutHistory.computeIfAbsent(pid, k -> new LinkedList<>());
+ LinkedList<Long> outHist = outHistMap.computeIfAbsent(pid, k -> new LinkedList<>());
outHist.add(Math.max(0, outRate));
while (outHist.size() > MAX_ENDPOINT_CHART_POINTS) {
outHist.remove(0);