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 0a88f9900148 CAMEL-23807: render the route-diagram dev console with
the HTML web component
0a88f9900148 is described below
commit 0a88f9900148183a0d8c66c34de96e56dcbfa52e
Author: Ravi <[email protected]>
AuthorDate: Sun Jun 21 11:29:53 2026 +0530
CAMEL-23807: render the route-diagram dev console with the HTML web
component
The route-diagram developer console now renders its HTML output with the
interactive <camel-route-diagram> web component instead of a static base64
PNG image. The web component is fed by the sibling route-structure dev
console. Request ?format=png for the legacy inline image; ascii/unicode
themes are unchanged.
The web component JS now sets Accept: application/json explicitly so the
dev console content-negotiation returns structured data instead of plain
text.
Closes #24156
---
.../camel-diagram/src/main/docs/diagram.adoc | 7 +++++
.../apache/camel/diagram/DiagramDevConsole.java | 30 +++++++++++++++++++++-
.../resources/camel/diagram/camel-route-diagram.js | 6 ++++-
.../camel/diagram/DiagramDevConsoleTest.java | 16 +++++++++++-
4 files changed, 56 insertions(+), 3 deletions(-)
diff --git a/components/camel-diagram/src/main/docs/diagram.adoc
b/components/camel-diagram/src/main/docs/diagram.adoc
index a7cde4eb2f5e..414231173fd2 100644
--- a/components/camel-diagram/src/main/docs/diagram.adoc
+++ b/components/camel-diagram/src/main/docs/diagram.adoc
@@ -110,6 +110,13 @@ The diagram rendering is used by the `camel cmd
route-diagram` command in Camel
camel cmd route-diagram MyRoute.java
----
+=== Developer console
+
+When the developer console is enabled (for example with `camel run
--console`), the `route-diagram`
+console renders the running routes using the interactive
`<camel-route-diagram>` web component,
+fed by the `route-structure` console. Request `?format=png` to get the legacy
inline PNG image
+instead, or use the `ascii` / `unicode` themes for plain-text output.
+
== Color Themes
The following built-in themes are available:
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 78a3881ab81e..165eaa63f300 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
@@ -69,6 +69,12 @@ public class DiagramDevConsole extends AbstractDevConsole {
*/
public static final String MODE = "mode";
+ /**
+ * Output format for the HTML rendering: html (default, interactive web
component) or png (legacy inline image).
+ * Only applies to image themes; ascii/unicode themes always render as
text.
+ */
+ public static final String FORMAT = "format";
+
public DiagramDevConsole() {
super("camel", "route-diagram", "Route Diagram", "Visual route
diagrams");
}
@@ -87,6 +93,7 @@ public class DiagramDevConsole extends AbstractDevConsole {
String nodeLabel = (String) options.getOrDefault(NODE_LABEL,
RouteDiagramDumper.NodeLabelMode.CODE.name());
boolean metric = "true".equalsIgnoreCase((String)
options.getOrDefault(METRIC, "true"));
boolean refresh = "true".equalsIgnoreCase((String)
options.getOrDefault(AUTO_REFRESH, "true"));
+ String format = (String) options.getOrDefault(FORMAT, "html");
try {
RouteDiagramDumper dumper =
PluginHelper.getRouteDiagramDumper(getCamelContext());
@@ -97,7 +104,7 @@ public class DiagramDevConsole extends AbstractDevConsole {
RouteDiagramDumper.NodeLabelMode.valueOf(nodeLabel.toUpperCase()),
nodeWidth, isUnicodeTheme(theme));
sj.add(text);
- } else {
+ } else if ("png".equalsIgnoreCase(format)) {
BufferedImage image = dumper.dumpRoutesAsImage(filter,
RouteDiagramDumper.Theme.valueOf(theme.toUpperCase()),
metric,
RouteDiagramDumper.NodeLabelMode.valueOf(nodeLabel.toUpperCase()), nodeWidth,
fontSize);
@@ -110,6 +117,8 @@ public class DiagramDevConsole extends AbstractDevConsole {
}
html = "<html>\n" + html + "</html>\n";
sj.add(html);
+ } else {
+ sj.add(buildRouteWebComponentHtml(filter, refresh));
}
} catch (Exception e) {
// ignore
@@ -196,6 +205,25 @@ public class DiagramDevConsole extends AbstractDevConsole {
return root;
}
+ private static String buildRouteWebComponentHtml(String filter, boolean
refresh) {
+ String f = filter == null ? "*" : filter;
+ String refreshAttr = refresh ? " refresh=\"5000\"" : "";
+ // script path + route-structure src assume the dev console and static
resources share an origin
+ return "<html>\n"
+ + " <head>\n"
+ + " <script type=\"module\"
src=\"/camel/diagram/camel-route-diagram.js\"></script>\n"
+ + " </head>\n"
+ + " <body>\n"
+ + String.format(" <camel-route-diagram
src=\"route-structure\" filter=\"%s\"%s></camel-route-diagram>%n",
+ escapeAttr(f), refreshAttr)
+ + " </body>\n"
+ + "</html>\n";
+ }
+
+ private static String escapeAttr(String s) {
+ return s.replace("&", "&").replace("<", "<").replace(">",
">").replace("\"", """);
+ }
+
private static boolean isTextTheme(String theme) {
return "ascii".equalsIgnoreCase(theme) ||
"unicode".equalsIgnoreCase(theme);
}
diff --git
a/components/camel-diagram/src/main/resources/META-INF/resources/camel/diagram/camel-route-diagram.js
b/components/camel-diagram/src/main/resources/META-INF/resources/camel/diagram/camel-route-diagram.js
index 869f075288d1..665887f27ca9 100644
---
a/components/camel-diagram/src/main/resources/META-INF/resources/camel/diagram/camel-route-diagram.js
+++
b/components/camel-diagram/src/main/resources/META-INF/resources/camel/diagram/camel-route-diagram.js
@@ -372,7 +372,11 @@ class CamelRouteDiagram extends HTMLElement {
const url = new URL(src, location.href);
if (this.#filter) url.searchParams.set('filter', this.#filter);
url.searchParams.set('metric', 'true');
- const res = await fetch(url, { signal: this.#controller.signal });
+ // Ask for JSON explicitly; the dev console serves plain text for
a default */* Accept.
+ const res = await fetch(url, {
+ signal: this.#controller.signal,
+ headers: { 'Accept': 'application/json' },
+ });
if (!res.ok) {
this.#error = `HTTP ${res.status} ${res.statusText}`;
this.#render();
diff --git
a/components/camel-diagram/src/test/java/org/apache/camel/diagram/DiagramDevConsoleTest.java
b/components/camel-diagram/src/test/java/org/apache/camel/diagram/DiagramDevConsoleTest.java
index 681a48877ad1..524bc0e10ffc 100644
---
a/components/camel-diagram/src/test/java/org/apache/camel/diagram/DiagramDevConsoleTest.java
+++
b/components/camel-diagram/src/test/java/org/apache/camel/diagram/DiagramDevConsoleTest.java
@@ -17,6 +17,7 @@
package org.apache.camel.diagram;
import java.util.Base64;
+import java.util.Map;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.console.DevConsole;
@@ -68,9 +69,22 @@ class DiagramDevConsoleTest extends CamelTestSupport {
DevConsole console = resolveConsole();
String text = (String) console.call(DevConsole.MediaType.TEXT);
assertThat(text).isNotNull();
- // default theme renders HTML with inline image
+ assertThat(text).contains("<html>");
+ assertThat(text).contains("<camel-route-diagram");
+ assertThat(text).contains("src=\"route-structure\"");
+ assertThat(text).contains("camel-route-diagram.js");
+ assertThat(text).doesNotContain("data:image/png;base64,");
+ }
+
+ @Test
+ void testTextOutputPngFormat() {
+ System.setProperty("java.awt.headless", "true");
+ DevConsole console = resolveConsole();
+ String text = (String) console.call(DevConsole.MediaType.TEXT,
Map.of(DiagramDevConsole.FORMAT, "png"));
+ assertThat(text).isNotNull();
assertThat(text).contains("<html>");
assertThat(text).contains("data:image/png;base64,");
+ assertThat(text).doesNotContain("<camel-route-diagram");
}
@Test