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/12 13:02:40 UTC
(camel) 04/07: CAMEL-23485: camel-diagram - Support --theme=ascii in route-diagram command
This is an automated email from the ASF dual-hosted git repository.
davsclaus pushed a commit to branch camel-23485-ascii-art-renderer
in repository https://gitbox.apache.org/repos/asf/camel.git
commit b4d492f02c8036d15ebfde7c9f07a6728a49cfa8
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Tue May 12 14:12:05 2026 +0200
CAMEL-23485: camel-diagram - Support --theme=ascii in route-diagram command
Co-Authored-By: Claude Opus 4.6 (1M context) <no...@anthropic.com>
---
.../camel-jbang-cmd-route-diagram.adoc | 4 +-
.../META-INF/camel-jbang-commands-metadata.json | 2 +-
.../commands/action/CamelRouteDiagramAction.java | 101 ++++++++++++++-------
3 files changed, 72 insertions(+), 35 deletions(-)
diff --git a/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-cmd-route-diagram.adoc b/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-cmd-route-diagram.adoc
index d118b519b5c9..b5cd5154893d 100644
--- a/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-cmd-route-diagram.adoc
+++ b/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-cmd-route-diagram.adoc
@@ -25,8 +25,8 @@ camel cmd route-diagram [options]
| `--ignore-loading-error` | Whether to ignore route loading and compilation errors (use this with care!) | false | boolean
| `--metric` | Whether to include live metrics (only possible for running Camel application) | true | boolean
| `--node-label` | What text to display in diagram nodes: code, description, or both (default) | both | String
-| `--output` | Save diagram to a PNG file instead of displaying in terminal | | String
-| `--theme` | Color theme preset (dark, light, transparent) or custom colors (e.g. bg=#1e1e1e:from=#2e7d32:to=#1565c0). Values can be #hex or ANSI color names (e.g. from=seagreen:to=steelblue). Use bg= for transparent. Can also be set via DIAGRAM_COLORS env var. | transparent | String
+| `--output` | Save diagram to a file (PNG for image themes, text for ascii theme) | | String
+| `--theme` | Color theme preset (dark, light, transparent, ascii) or custom colors (e.g. bg=#1e1e1e:from=#2e7d32:to=#1565c0). Values can be #hex or ANSI color names (e.g. from=seagreen:to=steelblue). Use bg= for transparent. Use ascii for plain text output. Can also be set via DIAGRAM_COLORS env var. | transparent | String
| `--watch` | Execute periodically and showing output fullscreen | | boolean
| `--width` | Image width in pixels (0 = auto) | 0 | int
| `-h,--help` | Display the help and sub-commands | | boolean
diff --git a/dsl/camel-jbang/camel-jbang-core/src/generated/resources/META-INF/camel-jbang-commands-metadata.json b/dsl/camel-jbang/camel-jbang-core/src/generated/resources/META-INF/camel-jbang-commands-metadata.json
index 9e7a209d62c0..bca47153fd15 100644
--- a/dsl/camel-jbang/camel-jbang-core/src/generated/resources/META-INF/camel-jbang-commands-metadata.json
+++ b/dsl/camel-jbang/camel-jbang-core/src/generated/resources/META-INF/camel-jbang-commands-metadata.json
@@ -2,7 +2,7 @@
"commands": [
{ "name": "bind", "fullName": "bind", "description": "DEPRECATED: Bind source and sink Kamelets as a new Camel integration", "deprecated": true, "sourceClass": "org.apache.camel.dsl.jbang.core.commands.bind.Bind", "options": [ { "names": "--error-handler", "description": "Add error handler (none|log|sink:<endpoint>). Sink endpoints are expected in the format [[apigroup\/]version:]kind:[namespace\/]name, plain Camel URIs or Kamelet name.", "javaType": "java.lang.String", "type": "stri [...]
{ "name": "catalog", "fullName": "catalog", "description": "List artifacts from Camel Catalog", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.catalog.CatalogCommand", "options": [ { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ], "subcommands": [ { "name": "component", "fullName": "catalog component", "description": "List components from the Camel Catalog", "sourceClass": "org.apache.camel.dsl.jbang.co [...]
- { "name": "cmd", "fullName": "cmd", "description": "Performs commands in the running Camel integrations, such as start\/stop route, or change logging levels.", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.action.CamelAction", "options": [ { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ], "subcommands": [ { "name": "browse", "fullName": "cmd browse", "description": "Browse pending messages on endpoints [...]
+ { "name": "cmd", "fullName": "cmd", "description": "Performs commands in the running Camel integrations, such as start\/stop route, or change logging levels.", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.action.CamelAction", "options": [ { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ], "subcommands": [ { "name": "browse", "fullName": "cmd browse", "description": "Browse pending messages on endpoints [...]
{ "name": "completion", "fullName": "completion", "description": "Generate completion script for bash\/zsh", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.Complete", "options": [ { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] },
{ "name": "config", "fullName": "config", "description": "Get and set user configuration values", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.config.ConfigCommand", "options": [ { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ], "subcommands": [ { "name": "get", "fullName": "config get", "description": "Display user configuration value", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.config. [...]
{ "name": "debug", "fullName": "debug", "description": "Debug local Camel integration", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.Debug", "options": [ { "names": "--ago", "description": "Use ago instead of yyyy-MM-dd HH:mm:ss in timestamp.", "javaType": "boolean", "type": "boolean" }, { "names": "--background", "description": "Run in the background", "defaultValue": "false", "javaType": "boolean", "type": "boolean" }, { "names": "--background-wait", "description": "To [...]
diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/CamelRouteDiagramAction.java b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/CamelRouteDiagramAction.java
index 165b53b1cd7e..c14733d80f07 100644
--- a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/CamelRouteDiagramAction.java
+++ b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/CamelRouteDiagramAction.java
@@ -26,6 +26,7 @@ import java.util.List;
import javax.imageio.ImageIO;
+import org.apache.camel.diagram.RouteDiagramAsciiRenderer;
import org.apache.camel.diagram.RouteDiagramHelper;
import org.apache.camel.diagram.RouteDiagramLayoutEngine;
import org.apache.camel.diagram.RouteDiagramLayoutEngine.LayoutRoute;
@@ -68,14 +69,15 @@ public class CamelRouteDiagramAction extends ActionWatchCommand {
int width;
@CommandLine.Option(names = { "--output" },
- description = "Save diagram to a PNG file instead of displaying in terminal")
+ description = "Save diagram to a file (PNG for image themes, text for ascii theme)")
String output;
@CommandLine.Option(names = { "--theme" },
- description = "Color theme preset (dark, light, transparent) or custom colors "
+ description = "Color theme preset (dark, light, transparent, ascii) or custom colors "
+ "(e.g. bg=#1e1e1e:from=#2e7d32:to=#1565c0). Values can be #hex or "
+ "ANSI color names (e.g. from=seagreen:to=steelblue). "
- + "Use bg= for transparent. Can also be set via DIAGRAM_COLORS env var.",
+ + "Use bg= for transparent. Use ascii for plain text output. "
+ + "Can also be set via DIAGRAM_COLORS env var.",
defaultValue = "transparent")
String theme;
@@ -115,18 +117,26 @@ public class CamelRouteDiagramAction extends ActionWatchCommand {
public Integer doCall() throws Exception {
System.setProperty("java.awt.headless", "true");
- // if output in terminal then ensure terminal supports this
- if (output == null) {
+ boolean ascii = isAsciiTheme();
+ if (!ascii) {
String colorSpec = System.getenv("DIAGRAM_COLORS");
colors = DiagramColors.parse(colorSpec != null ? colorSpec : theme);
+ }
+
+ // if output in terminal then set up terminal
+ if (output == null) {
terminal = TerminalBuilder.builder().system(true).build();
- terminalGraphics = TerminalGraphicsManager.getBestProtocol(terminal).orElse(null);
- if (terminalGraphics == null) {
- printer().println("Terminal does not support graphics protocols (Kitty, iTerm2, or Sixel).");
- printer().println("Try running in a supported terminal: Kitty, iTerm2, WezTerm, Ghostty, or VS Code.");
- return 1;
- }
lineReader = LineReaderBuilder.builder().terminal(terminal).build();
+ if (!ascii) {
+ terminalGraphics = TerminalGraphicsManager.getBestProtocol(terminal).orElse(null);
+ if (terminalGraphics == null) {
+ printer().println("Terminal does not support graphics protocols (Kitty, iTerm2, or Sixel).");
+ printer().println(
+ "Try running in a supported terminal: Kitty, iTerm2, WezTerm, Ghostty, or VS Code.");
+ printer().println("Or use --theme=ascii for plain text output.");
+ return 1;
+ }
+ }
}
try {
@@ -190,9 +200,6 @@ public class CamelRouteDiagramAction extends ActionWatchCommand {
NodeLabelMode labelMode = parseNodeLabelMode(nodeLabel);
RouteDiagramLayoutEngine engine = new RouteDiagramLayoutEngine(boxWidth, fontSize, labelMode);
- RouteDiagramRenderer renderer = new RouteDiagramRenderer(
- engine.getNodeWidth(), fontSize * RouteDiagramLayoutEngine.SCALE, engine.getNodeTextPadding(),
- pid > 0 && metric);
List<LayoutRoute> layoutRoutes = new ArrayList<>();
int currentY = RouteDiagramLayoutEngine.PADDING;
@@ -202,27 +209,53 @@ public class CamelRouteDiagramAction extends ActionWatchCommand {
currentY = lr.maxY + RouteDiagramLayoutEngine.V_GAP;
}
- BufferedImage image;
- try {
- image = renderer.renderDiagram(layoutRoutes, currentY, colors);
- } catch (IllegalStateException e) {
- printer().println(e.getMessage());
- return 1;
- }
-
- if (output != null) {
- File file = new File(output);
- File parentDir = file.getParentFile();
- if (parentDir != null) {
- parentDir.mkdirs();
+ if (isAsciiTheme()) {
+ RouteDiagramAsciiRenderer asciiRenderer = new RouteDiagramAsciiRenderer(engine.getNodeWidth());
+ String ascii = asciiRenderer.renderDiagram(layoutRoutes, currentY);
+
+ if (output != null) {
+ String fileName = output.endsWith(".png")
+ ? output.substring(0, output.length() - 4) + ".txt" : output;
+ File file = new File(fileName);
+ File parentDir = file.getParentFile();
+ if (parentDir != null) {
+ parentDir.mkdirs();
+ }
+ Files.writeString(file.toPath(), ascii);
+ printer().println("Diagram saved to: " + file.getAbsolutePath());
+ } else {
+ if (watch) {
+ clearScreen();
+ }
+ printer().println(ascii);
}
- ImageIO.write(image, "PNG", file);
- printer().println("Diagram saved to: " + file.getAbsolutePath());
} else {
- if (watch) {
- clearScreen();
+ RouteDiagramRenderer renderer = new RouteDiagramRenderer(
+ engine.getNodeWidth(), fontSize * RouteDiagramLayoutEngine.SCALE, engine.getNodeTextPadding(),
+ pid > 0 && metric);
+
+ BufferedImage image;
+ try {
+ image = renderer.renderDiagram(layoutRoutes, currentY, colors);
+ } catch (IllegalStateException e) {
+ printer().println(e.getMessage());
+ return 1;
+ }
+
+ if (output != null) {
+ File file = new File(output);
+ File parentDir = file.getParentFile();
+ if (parentDir != null) {
+ parentDir.mkdirs();
+ }
+ ImageIO.write(image, "PNG", file);
+ printer().println("Diagram saved to: " + file.getAbsolutePath());
+ } else {
+ if (watch) {
+ clearScreen();
+ }
+ doDisplayDiagram(image);
}
- doDisplayDiagram(image);
}
return 0;
@@ -319,6 +352,10 @@ public class CamelRouteDiagramAction extends ActionWatchCommand {
return RouteDiagramHelper.parseRoutes(jo);
}
+ private boolean isAsciiTheme() {
+ return "ascii".equalsIgnoreCase(theme);
+ }
+
static NodeLabelMode parseNodeLabelMode(String value) {
if (value == null || value.isBlank() || "code".equalsIgnoreCase(value)) {
return NodeLabelMode.CODE;