html exporter fixed
This commit is contained in:
@@ -12,6 +12,8 @@ public class ExportOptions {
|
|||||||
boolean renderChoicesAsDiamonds = true;
|
boolean renderChoicesAsDiamonds = true;
|
||||||
@Builder.Default
|
@Builder.Default
|
||||||
boolean embedIdentifiers = false;
|
boolean embedIdentifiers = false;
|
||||||
|
@Builder.Default
|
||||||
|
boolean includeMetadataPane = true;
|
||||||
|
|
||||||
@Builder.Default
|
@Builder.Default
|
||||||
EnumFormat eventFormat = EnumFormat.fn;
|
EnumFormat eventFormat = EnumFormat.fn;
|
||||||
|
|||||||
@@ -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).")
|
@Option(names = {"--no-diamonds"}, description = "Disable rendering choice pseudostates as PlantUML diamonds (render them as normal state nodes instead).")
|
||||||
private boolean noDiamonds;
|
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
|
@Override
|
||||||
public Integer call() throws Exception {
|
public Integer call() throws Exception {
|
||||||
var out = spec.commandLine().getOut();
|
var out = spec.commandLine().getOut();
|
||||||
@@ -78,12 +81,17 @@ public class HtmlExporterCommand implements Callable<Integer> {
|
|||||||
List<String> formats = List.of("html", "json");
|
List<String> formats = List.of("html", "json");
|
||||||
List<String> profiles = activeProfiles != null ? activeProfiles : java.util.Collections.emptyList();
|
List<String> profiles = activeProfiles != null ? activeProfiles : java.util.Collections.emptyList();
|
||||||
|
|
||||||
|
if (noMetadataPane) {
|
||||||
|
System.setProperty("html.hideMetadataPane", "true");
|
||||||
|
}
|
||||||
|
|
||||||
if (jsonFile != null) {
|
if (jsonFile != null) {
|
||||||
exportService.runJsonExporter(jsonFile, outputDir, formats, profiles);
|
exportService.runJsonExporter(jsonFile, outputDir, formats, profiles);
|
||||||
} else {
|
} else {
|
||||||
boolean renderChoicesAsDiamonds = !noDiamonds;
|
boolean renderChoicesAsDiamonds = !noDiamonds;
|
||||||
exportService.runExporter(inputDir, outputDir, formats, renderChoicesAsDiamonds, profiles, flowsFile, machineFilter);
|
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."));
|
out.println(CommandLine.Help.Ansi.AUTO.string("%n@|bold,green SUCCESS:|@ Interactive documentation generated successfully."));
|
||||||
|
|
||||||
|
|||||||
@@ -56,12 +56,51 @@ public class HtmlExporter implements StateMachineExporter {
|
|||||||
// 5. Prepare Metadata JSON
|
// 5. Prepare Metadata JSON
|
||||||
String metadataJson = objectMapper.writeValueAsString(result);
|
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
|
// 6. Inject Data
|
||||||
return template
|
String finalHtml = template
|
||||||
.replace("{{MACHINE_NAME}}", result.getName())
|
.replace("{{MACHINE_NAME}}", result.getName())
|
||||||
.replace("{{SVG_CONTENT}}", decoratedSvg)
|
.replace("{{SVG_CONTENT}}", decoratedSvg)
|
||||||
.replace("{{METADATA_JSON}}", metadataJson);
|
.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) {
|
} catch (Exception e) {
|
||||||
log.error("Failed to export to HTML", e);
|
log.error("Failed to export to HTML", e);
|
||||||
throw new RuntimeException("HTML export failed", e);
|
throw new RuntimeException("HTML export failed", e);
|
||||||
|
|||||||
@@ -261,32 +261,10 @@
|
|||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
<div id="sidebar">
|
{{SIDEBAR_HTML}}
|
||||||
<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>
|
|
||||||
|
|
||||||
<div id="main">
|
<div id="main">
|
||||||
<button id="toggle-sidebar" aria-label="Toggle Sidebar" title="Toggle Sidebar">
|
{{TOGGLE_BUTTON_HTML}}
|
||||||
<svg viewBox="0 0 24 24"><path d="M4 6h16M4 12h16M4 18h16"></path></svg>
|
|
||||||
</button>
|
|
||||||
<div id="svg-container">
|
<div id="svg-container">
|
||||||
{{SVG_CONTENT}}
|
{{SVG_CONTENT}}
|
||||||
</div>
|
</div>
|
||||||
@@ -304,18 +282,21 @@
|
|||||||
const svg = document.querySelector('#svg-container svg');
|
const svg = document.querySelector('#svg-container svg');
|
||||||
panZoomInstance = svgPanZoom(svg, { zoomEnabled: true, controlIconsEnabled: true, fit: true, center: true });
|
panZoomInstance = svgPanZoom(svg, { zoomEnabled: true, controlIconsEnabled: true, fit: true, center: true });
|
||||||
|
|
||||||
document.getElementById('toggle-sidebar').addEventListener('click', () => {
|
const toggleSidebar = document.getElementById('toggle-sidebar');
|
||||||
const sidebar = document.getElementById('sidebar');
|
if (toggleSidebar) {
|
||||||
sidebar.classList.toggle('collapsed');
|
toggleSidebar.addEventListener('click', () => {
|
||||||
|
const sidebar = document.getElementById('sidebar');
|
||||||
|
if (sidebar) sidebar.classList.toggle('collapsed');
|
||||||
|
|
||||||
// Re-center SVG pan-zoom after transition
|
// Re-center SVG pan-zoom after transition
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if (panZoomInstance) {
|
if (panZoomInstance) {
|
||||||
panZoomInstance.resize();
|
panZoomInstance.resize();
|
||||||
panZoomInstance.center();
|
panZoomInstance.center();
|
||||||
}
|
}
|
||||||
}, 300);
|
}, 300);
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
setupTabs();
|
setupTabs();
|
||||||
populateSidebar();
|
populateSidebar();
|
||||||
@@ -325,8 +306,10 @@
|
|||||||
|
|
||||||
function setupTabs() {
|
function setupTabs() {
|
||||||
const flows = metadata.flows || [];
|
const flows = metadata.flows || [];
|
||||||
|
const tabsEl = document.querySelector('.tabs');
|
||||||
|
if (!tabsEl) return;
|
||||||
if (flows.length === 0) {
|
if (flows.length === 0) {
|
||||||
document.querySelector('.tabs').style.display = 'none';
|
tabsEl.style.display = 'none';
|
||||||
document.querySelector('.tab-content[data-tab="flows"]')?.remove();
|
document.querySelector('.tab-content[data-tab="flows"]')?.remove();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -342,6 +325,7 @@
|
|||||||
|
|
||||||
function populateSidebar() {
|
function populateSidebar() {
|
||||||
const list = document.getElementById('ep-list');
|
const list = document.getElementById('ep-list');
|
||||||
|
if (!list) return;
|
||||||
const entryPoints = metadata.metadata.entryPoints || [];
|
const entryPoints = metadata.metadata.entryPoints || [];
|
||||||
const machineEvents = new Set(metadata.transitions.map(t => t.event).filter(Boolean));
|
const machineEvents = new Set(metadata.transitions.map(t => t.event).filter(Boolean));
|
||||||
|
|
||||||
@@ -372,6 +356,7 @@
|
|||||||
|
|
||||||
function populateFlows() {
|
function populateFlows() {
|
||||||
const list = document.getElementById('flow-list');
|
const list = document.getElementById('flow-list');
|
||||||
|
if (!list) return;
|
||||||
const flows = metadata.flows || [];
|
const flows = metadata.flows || [];
|
||||||
|
|
||||||
if (flows.length === 0) {
|
if (flows.length === 0) {
|
||||||
|
|||||||
Reference in New Issue
Block a user