html exporter fixed

This commit is contained in:
2026-06-18 22:06:57 +02:00
parent 5894303510
commit d93d36e8ad
4 changed files with 72 additions and 38 deletions

View File

@@ -56,6 +56,9 @@ public class HtmlExporterCommand implements Callable<Integer> {
@Option(names = {"--no-diamonds"}, description = "Disable rendering choice pseudostates as PlantUML diamonds (render them as normal state nodes instead).")
private boolean noDiamonds;
@Option(names = {"--no-metadata-pane"}, description = "Disable rendering the left metadata pane (entry points, flows) in the HTML viewer.")
private boolean noMetadataPane;
@Override
public Integer call() throws Exception {
var out = spec.commandLine().getOut();
@@ -78,12 +81,17 @@ public class HtmlExporterCommand implements Callable<Integer> {
List<String> formats = List.of("html", "json");
List<String> profiles = activeProfiles != null ? activeProfiles : java.util.Collections.emptyList();
if (noMetadataPane) {
System.setProperty("html.hideMetadataPane", "true");
}
if (jsonFile != null) {
exportService.runJsonExporter(jsonFile, outputDir, formats, profiles);
} else {
boolean renderChoicesAsDiamonds = !noDiamonds;
exportService.runExporter(inputDir, outputDir, formats, renderChoicesAsDiamonds, profiles, flowsFile, machineFilter);
}
System.clearProperty("html.hideMetadataPane");
out.println(CommandLine.Help.Ansi.AUTO.string("%n@|bold,green SUCCESS:|@ Interactive documentation generated successfully."));

View File

@@ -56,12 +56,51 @@ public class HtmlExporter implements StateMachineExporter {
// 5. Prepare Metadata JSON
String metadataJson = objectMapper.writeValueAsString(result);
// Check if we should render metadata pane
boolean hideMetadataProp = Boolean.getBoolean("html.hideMetadataPane");
boolean hasEntryPoints = result.getMetadata() != null && result.getMetadata().getEntryPoints() != null && !result.getMetadata().getEntryPoints().isEmpty();
boolean hasFlows = result.getFlows() != null && !result.getFlows().isEmpty();
boolean shouldRenderPane = !hideMetadataProp && options.isIncludeMetadataPane() && (hasEntryPoints || hasFlows);
// 6. Inject Data
return template
String finalHtml = template
.replace("{{MACHINE_NAME}}", result.getName())
.replace("{{SVG_CONTENT}}", decoratedSvg)
.replace("{{METADATA_JSON}}", metadataJson);
if (shouldRenderPane) {
// If template.html uses {{SIDEBAR_HTML}} and {{TOGGLE_BUTTON_HTML}}, replace with actual HTML
finalHtml = finalHtml.replace("{{SIDEBAR_HTML}}", "<div id=\"sidebar\">\n" +
" <div class=\"sidebar-header\">\n" +
" <header>\n" +
" <h1>Explorer</h1>\n" +
" <p>{{MACHINE_NAME}}</p>\n" +
" </header>\n" +
" </div>\n" +
" \n" +
" <div class=\"tabs\">\n" +
" <div class=\"tab active\" data-tab=\"explorer\">Explorer</div>\n" +
" <div class=\"tab\" data-tab=\"flows\">Business Flows</div>\n" +
" </div>\n" +
"\n" +
" <div id=\"tab-explorer\" class=\"tab-content active\">\n" +
" <div id=\"ep-list\"></div>\n" +
" </div>\n" +
"\n" +
" <div id=\"tab-flows\" class=\"tab-content\">\n" +
" <div id=\"flow-list\"></div>\n" +
" </div>\n" +
"</div>").replace("{{MACHINE_NAME}}", result.getName());
finalHtml = finalHtml.replace("{{TOGGLE_BUTTON_HTML}}", " <button id=\"toggle-sidebar\" aria-label=\"Toggle Sidebar\" title=\"Toggle Sidebar\">\n" +
" <svg viewBox=\"0 0 24 24\"><path d=\"M4 6h16M4 12h16M4 18h16\"></path></svg>\n" +
" </button>");
} else {
finalHtml = finalHtml.replace("{{SIDEBAR_HTML}}", "").replace("{{TOGGLE_BUTTON_HTML}}", "");
}
return finalHtml;
} catch (Exception e) {
log.error("Failed to export to HTML", e);
throw new RuntimeException("HTML export failed", e);

View File

@@ -261,32 +261,10 @@
</head>
<body>
<div id="sidebar">
<div class="sidebar-header">
<header>
<h1>Explorer</h1>
<p>{{MACHINE_NAME}}</p>
</header>
</div>
<div class="tabs">
<div class="tab active" data-tab="explorer">Explorer</div>
<div class="tab" data-tab="flows">Business Flows</div>
</div>
<div id="tab-explorer" class="tab-content active">
<div id="ep-list"></div>
</div>
<div id="tab-flows" class="tab-content">
<div id="flow-list"></div>
</div>
</div>
{{SIDEBAR_HTML}}
<div id="main">
<button id="toggle-sidebar" aria-label="Toggle Sidebar" title="Toggle Sidebar">
<svg viewBox="0 0 24 24"><path d="M4 6h16M4 12h16M4 18h16"></path></svg>
</button>
{{TOGGLE_BUTTON_HTML}}
<div id="svg-container">
{{SVG_CONTENT}}
</div>
@@ -304,18 +282,21 @@
const svg = document.querySelector('#svg-container svg');
panZoomInstance = svgPanZoom(svg, { zoomEnabled: true, controlIconsEnabled: true, fit: true, center: true });
document.getElementById('toggle-sidebar').addEventListener('click', () => {
const sidebar = document.getElementById('sidebar');
sidebar.classList.toggle('collapsed');
// Re-center SVG pan-zoom after transition
setTimeout(() => {
if (panZoomInstance) {
panZoomInstance.resize();
panZoomInstance.center();
}
}, 300);
});
const toggleSidebar = document.getElementById('toggle-sidebar');
if (toggleSidebar) {
toggleSidebar.addEventListener('click', () => {
const sidebar = document.getElementById('sidebar');
if (sidebar) sidebar.classList.toggle('collapsed');
// Re-center SVG pan-zoom after transition
setTimeout(() => {
if (panZoomInstance) {
panZoomInstance.resize();
panZoomInstance.center();
}
}, 300);
});
}
setupTabs();
populateSidebar();
@@ -325,8 +306,10 @@
function setupTabs() {
const flows = metadata.flows || [];
const tabsEl = document.querySelector('.tabs');
if (!tabsEl) return;
if (flows.length === 0) {
document.querySelector('.tabs').style.display = 'none';
tabsEl.style.display = 'none';
document.querySelector('.tab-content[data-tab="flows"]')?.remove();
return;
}
@@ -342,6 +325,7 @@
function populateSidebar() {
const list = document.getElementById('ep-list');
if (!list) return;
const entryPoints = metadata.metadata.entryPoints || [];
const machineEvents = new Set(metadata.transitions.map(t => t.event).filter(Boolean));
@@ -372,6 +356,7 @@
function populateFlows() {
const list = document.getElementById('flow-list');
if (!list) return;
const flows = metadata.flows || [];
if (flows.length === 0) {