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 2910bbde90403363e7d2a246662b4dde59d3246c Author: Claus Ibsen <[email protected]> AuthorDate: Tue May 12 14:57:33 2026 +0200 CAMEL-23485: camel-diagram - Support ascii/unicode themes in DiagramDevConsole Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> --- .../camel-diagram/src/main/docs/diagram.adoc | 1 + .../camel/diagram/DefaultRouteDiagramDumper.java | 8 ++-- .../apache/camel/diagram/DiagramDevConsole.java | 55 +++++++++++++++------- .../org/apache/camel/spi/RouteDiagramDumper.java | 14 +++++- 4 files changed, 57 insertions(+), 21 deletions(-) diff --git a/components/camel-diagram/src/main/docs/diagram.adoc b/components/camel-diagram/src/main/docs/diagram.adoc index 8792f2073f4d..23e545039c03 100644 --- a/components/camel-diagram/src/main/docs/diagram.adoc +++ b/components/camel-diagram/src/main/docs/diagram.adoc @@ -204,6 +204,7 @@ camel cmd route-diagram MyRoute.java --theme=unicode Example output: +[source,text] ---- route1 ┌──────────────────────┐ diff --git a/components/camel-diagram/src/main/java/org/apache/camel/diagram/DefaultRouteDiagramDumper.java b/components/camel-diagram/src/main/java/org/apache/camel/diagram/DefaultRouteDiagramDumper.java index 81ec387197d2..ba0d83311d38 100644 --- a/components/camel-diagram/src/main/java/org/apache/camel/diagram/DefaultRouteDiagramDumper.java +++ b/components/camel-diagram/src/main/java/org/apache/camel/diagram/DefaultRouteDiagramDumper.java @@ -118,12 +118,12 @@ public class DefaultRouteDiagramDumper extends ServiceSupport implements CamelCo @Override public String dumpRoutesAsAsciiArt( - String filter, RouteDiagramDumper.NodeLabelMode nodeLabel, int nodeWidth) { + String filter, RouteDiagramDumper.NodeLabelMode nodeLabel, int nodeWidth, boolean unicode) { DevConsole dc = getCamelContext().getCamelContextExtension().getContextPlugin(DevConsoleRegistry.class) .resolveById("route-structure"); JsonObject root = (JsonObject) dc.call(DevConsole.MediaType.JSON, Map.of("filter", filter)); var routes = RouteDiagramHelper.parseRoutes(root); - return renderAscii(routes, nodeWidth, nodeLabel.name()); + return renderAscii(routes, nodeWidth, nodeLabel.name(), unicode); } @Override @@ -163,7 +163,7 @@ public class DefaultRouteDiagramDumper extends ServiceSupport implements CamelCo } private static String renderAscii( - List<RouteDiagramLayoutEngine.RouteInfo> routes, int nodeWidth, String nodeLabel) { + List<RouteDiagramLayoutEngine.RouteInfo> routes, int nodeWidth, String nodeLabel, boolean unicode) { RouteDiagramLayoutEngine engine = new RouteDiagramLayoutEngine( nodeWidth, RouteDiagramLayoutEngine.DEFAULT_FONT_SIZE, RouteDiagramLayoutEngine.NodeLabelMode.valueOf(nodeLabel.toUpperCase())); @@ -177,7 +177,7 @@ public class DefaultRouteDiagramDumper extends ServiceSupport implements CamelCo } RouteDiagramAsciiRenderer renderer = new RouteDiagramAsciiRenderer( - nodeWidth * RouteDiagramLayoutEngine.SCALE); + nodeWidth * RouteDiagramLayoutEngine.SCALE, unicode); return renderer.renderDiagram(layoutRoutes, currentY); } diff --git a/components/camel-diagram/src/main/java/org/apache/camel/diagram/DiagramDevConsole.java b/components/camel-diagram/src/main/java/org/apache/camel/diagram/DiagramDevConsole.java index 74265961117b..e41c3b8cf15f 100644 --- a/components/camel-diagram/src/main/java/org/apache/camel/diagram/DiagramDevConsole.java +++ b/components/camel-diagram/src/main/java/org/apache/camel/diagram/DiagramDevConsole.java @@ -35,7 +35,7 @@ public class DiagramDevConsole extends AbstractDevConsole { public static final String FILTER = "filter"; /** - * Theme to use: dark or light + * Theme to use: dark, light, transparent, ascii, or unicode */ public static final String THEME = "theme"; @@ -84,18 +84,25 @@ public class DiagramDevConsole extends AbstractDevConsole { try { RouteDiagramDumper dumper = PluginHelper.getRouteDiagramDumper(getCamelContext()); - BufferedImage image = dumper.dumpRoutesAsImage(filter, RouteDiagramDumper.Theme.valueOf(theme.toUpperCase()), - metric, RouteDiagramDumper.NodeLabelMode.valueOf(nodeLabel.toUpperCase()), nodeWidth, fontSize); - String base64 = dumper.imageToBase64(image); - // For HTML embedding: - String html = String.format( - " <body>\n <img src=\"data:image/png;base64,%s\" alt=\"Route Diagram\">\n </body>\n", - base64); - if (refresh) { - html = "<head><meta http-equiv=\"refresh\" content=\"5\"></head>\n" + html; + if (isTextTheme(theme)) { + String text = dumper.dumpRoutesAsAsciiArt(filter, + RouteDiagramDumper.NodeLabelMode.valueOf(nodeLabel.toUpperCase()), + nodeWidth, isUnicodeTheme(theme)); + sj.add(text); + } else { + BufferedImage image = dumper.dumpRoutesAsImage(filter, + RouteDiagramDumper.Theme.valueOf(theme.toUpperCase()), + metric, RouteDiagramDumper.NodeLabelMode.valueOf(nodeLabel.toUpperCase()), nodeWidth, fontSize); + String base64 = dumper.imageToBase64(image); + String html = String.format( + " <body>\n <img src=\"data:image/png;base64,%s\" alt=\"Route Diagram\">\n </body>\n", + base64); + if (refresh) { + html = "<head><meta http-equiv=\"refresh\" content=\"5\"></head>\n" + html; + } + html = "<html>\n" + html + "</html>\n"; + sj.add(html); } - html = "<html>\n" + html + "</html>\n"; - sj.add(html); } catch (Exception e) { // ignore } @@ -117,10 +124,18 @@ public class DiagramDevConsole extends AbstractDevConsole { JsonObject root = new JsonObject(); try { RouteDiagramDumper dumper = PluginHelper.getRouteDiagramDumper(getCamelContext()); - BufferedImage image = dumper.dumpRoutesAsImage(filter, RouteDiagramDumper.Theme.valueOf(theme.toUpperCase()), - metric, RouteDiagramDumper.NodeLabelMode.valueOf(nodeLabel.toUpperCase()), nodeWidth, fontSize); - String base64 = dumper.imageToBase64(image); - root.put("image", base64); + if (isTextTheme(theme)) { + String text = dumper.dumpRoutesAsAsciiArt(filter, + RouteDiagramDumper.NodeLabelMode.valueOf(nodeLabel.toUpperCase()), + nodeWidth, isUnicodeTheme(theme)); + root.put("text", text); + } else { + BufferedImage image = dumper.dumpRoutesAsImage(filter, + RouteDiagramDumper.Theme.valueOf(theme.toUpperCase()), + metric, RouteDiagramDumper.NodeLabelMode.valueOf(nodeLabel.toUpperCase()), nodeWidth, fontSize); + String base64 = dumper.imageToBase64(image); + root.put("image", base64); + } } catch (Exception e) { // ignore } @@ -128,4 +143,12 @@ public class DiagramDevConsole extends AbstractDevConsole { return root; } + private static boolean isTextTheme(String theme) { + return "ascii".equalsIgnoreCase(theme) || "unicode".equalsIgnoreCase(theme); + } + + private static boolean isUnicodeTheme(String theme) { + return "unicode".equalsIgnoreCase(theme); + } + } diff --git a/core/camel-api/src/main/java/org/apache/camel/spi/RouteDiagramDumper.java b/core/camel-api/src/main/java/org/apache/camel/spi/RouteDiagramDumper.java index 79a6f842229f..1194e2e73f7d 100644 --- a/core/camel-api/src/main/java/org/apache/camel/spi/RouteDiagramDumper.java +++ b/core/camel-api/src/main/java/org/apache/camel/spi/RouteDiagramDumper.java @@ -95,7 +95,7 @@ public interface RouteDiagramDumper { * @param filter to filter routes */ default String dumpRoutesAsAsciiArt(String filter) { - return dumpRoutesAsAsciiArt(filter, NodeLabelMode.CODE, 180); + return dumpRoutesAsAsciiArt(filter, NodeLabelMode.CODE, 180, false); } /** @@ -106,6 +106,18 @@ public interface RouteDiagramDumper { * @param nodeWidth the width in pixels of the node boxes */ default String dumpRoutesAsAsciiArt(String filter, NodeLabelMode nodeLabel, int nodeWidth) { + return dumpRoutesAsAsciiArt(filter, nodeLabel, nodeWidth, false); + } + + /** + * Dumps the routes as ASCII art or Unicode box-drawing text + * + * @param filter to filter routes + * @param nodeLabel what information to display in the nodes + * @param nodeWidth the width in pixels of the node boxes + * @param unicode whether to use Unicode box-drawing characters + */ + default String dumpRoutesAsAsciiArt(String filter, NodeLabelMode nodeLabel, int nodeWidth, boolean unicode) { throw new UnsupportedOperationException(); }
