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

davsclaus pushed a commit to branch worktree-more-tui-2
in repository https://gitbox.apache.org/repos/asf/camel.git

commit 45c9ff33120f10d25b7a5b60cddeeddf97597685
Author: Claus Ibsen <[email protected]>
AuthorDate: Tue May 19 17:22:11 2026 +0200

    camel-jbang - Add circuit breaker state machine diagram to TUI
    
    Shows an ASCII art state diagram below the circuit breaker table for the
    selected entry, illustrating the three states (CLOSED/OPEN/HALF_OPEN),
    their transitions, and what each state means. The active state is
    highlighted in color. Metrics are shown below the diagram.
    
    Co-Authored-By: Claude Opus 4.6 <[email protected]>
---
 .../dsl/jbang/core/commands/tui/CamelMonitor.java  | 125 ++++++++++++++++++++-
 1 file changed, 124 insertions(+), 1 deletion(-)

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 0defa6007812..cc05d09012a1 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
@@ -2914,6 +2914,19 @@ public class CamelMonitor extends CamelCommand {
                     Cell.from(""), Cell.from(""), Cell.from(""), 
Cell.from(""), Cell.from("")));
         }
 
+        // Split area: table on top, state diagram below for the selected entry
+        CircuitBreakerInfo selectedCb = null;
+        Integer sel = cbTableState.selected();
+        if (sel != null && sel >= 0 && sel < sorted.size()) {
+            selectedCb = sorted.get(sel);
+        }
+        boolean showDiagram = selectedCb != null;
+        List<Rect> chunks = showDiagram
+                ? Layout.vertical()
+                        .constraints(Constraint.fill(), Constraint.length(15))
+                        .split(area)
+                : List.of(area);
+
         Table table = Table.builder()
                 .rows(rows)
                 .header(Row.from(
@@ -2941,7 +2954,117 @@ public class CamelMonitor extends CamelCommand {
                 .block(Block.builder().borderType(BorderType.ROUNDED).title(" 
Circuit Breaker ").build())
                 .build();
 
-        frame.renderStatefulWidget(table, area, cbTableState);
+        frame.renderStatefulWidget(table, chunks.get(0), cbTableState);
+
+        if (showDiagram) {
+            renderCircuitBreakerDiagram(frame, chunks.get(1), selectedCb);
+        }
+    }
+
+    private void renderCircuitBreakerDiagram(Frame frame, Rect area, 
CircuitBreakerInfo cb) {
+        String state = cb.state != null ? cb.state.toLowerCase() : "";
+        boolean isClosed = state.equals("closed");
+        boolean isOpen = state.equals("open") || state.equals("forced_open");
+
+        Style closedBox = isClosed ? Style.EMPTY.fg(Color.GREEN).bold() : 
Style.EMPTY;
+        Style openBox = isOpen ? Style.EMPTY.fg(Color.LIGHT_RED).bold() : 
Style.EMPTY;
+        Style halfOpenBox = !isClosed && !isOpen ? 
Style.EMPTY.fg(Color.YELLOW).bold() : Style.EMPTY;
+        Style lbl = Style.EMPTY.dim();
+
+        List<Line> lines = new ArrayList<>();
+        // ┌──────────────┐              ┌──────────────┐
+        lines.add(Line.from(
+                Span.raw("   "),
+                Span.styled("┌──────────────┐", closedBox),
+                Span.raw("              "),
+                Span.styled("┌──────────────┐", openBox)));
+        // │    CLOSED    │─────────────►│     OPEN     │◄─┐
+        lines.add(Line.from(
+                Span.raw("   "),
+                Span.styled("│    CLOSED    │", closedBox),
+                Span.raw("─────────────►"),
+                Span.styled("│     OPEN     │", openBox),
+                Span.raw("◄─┐")));
+        // │  (flowing)   │ failure rate │  (blocked)   │  │
+        lines.add(Line.from(
+                Span.raw("   "),
+                Span.styled("│  (flowing)   │", closedBox),
+                Span.styled(" failure rate ", lbl),
+                Span.styled("│  (blocked)   │", openBox),
+                Span.raw("  │")));
+        // └──────▲───────┘              └───────┬──────┘  │
+        lines.add(Line.from(
+                Span.raw("   "),
+                Span.styled("└──────", closedBox),
+                Span.raw("▲"),
+                Span.styled("───────┘", closedBox),
+                Span.raw("              "),
+                Span.styled("└───────", openBox),
+                Span.raw("┬"),
+                Span.styled("──────┘", openBox),
+                Span.raw("  │")));
+        //          │                              │         │
+        lines.add(Line.from(Span.raw(
+                "          │                              │         │")));
+        //          │ success                wait timeout    │ fail
+        lines.add(Line.from(
+                Span.raw("          │"),
+                Span.styled(" success", lbl),
+                Span.raw("                "),
+                Span.styled("wait timeout", lbl),
+                Span.raw("    │"),
+                Span.styled(" fail", lbl)));
+        //          │                              │         │
+        lines.add(Line.from(Span.raw(
+                "          │                              │         │")));
+        //          │                      ┌───────▼──────┐  │
+        lines.add(Line.from(
+                Span.raw("          │                      "),
+                Span.styled("┌───────", halfOpenBox),
+                Span.raw("▼"),
+                Span.styled("──────┐", halfOpenBox),
+                Span.raw("  │")));
+        //          └──────────────────────┤  HALF_OPEN   ├──┘
+        lines.add(Line.from(
+                Span.raw("          └──────────────────────"),
+                Span.styled("┤  HALF_OPEN   ├", halfOpenBox),
+                Span.raw("──┘")));
+        //                                 │   (probe)    │
+        lines.add(Line.from(
+                Span.raw("                                 "),
+                Span.styled("│   (probe)    │", halfOpenBox)));
+        //                                 └──────────────┘
+        lines.add(Line.from(
+                Span.raw("                                 "),
+                Span.styled("└──────────────┘", halfOpenBox)));
+        // Metrics
+        lines.add(Line.from(Span.raw("")));
+        lines.add(Line.from(
+                Span.raw("   "),
+                Span.styled("success:", lbl),
+                Span.raw(cb.successfulCalls + "  "),
+                Span.styled("fail:", lbl),
+                Span.raw(cb.failedCalls + "  "),
+                Span.styled("rate:", lbl),
+                Span.raw((cb.failureRate >= 0 ? String.format("%.0f%%", 
cb.failureRate) : "n/a") + "  "),
+                Span.styled("pending:", lbl),
+                Span.raw(cb.bufferedCalls + "  "),
+                Span.styled("rejected:", lbl),
+                Span.raw(String.valueOf(cb.notPermittedCalls))));
+
+        String title = " ";
+        if (cb.id != null && !cb.id.isEmpty()) {
+            title += cb.id;
+        }
+        if (cb.component != null) {
+            title += " (" + cb.component + ")";
+        }
+        title += " ";
+
+        frame.renderWidget(Paragraph.builder()
+                .text(Text.from(lines))
+                
.block(Block.builder().borderType(BorderType.ROUNDED).title(title).build())
+                .build(), area);
     }
 
     private String cbSortLabel(String label, String column) {

Reply via email to