Skip to content

Commit 0e9458b

Browse files
committed
State Machine Webview Rendering
1 parent eb56383 commit 0e9458b

9 files changed

Lines changed: 106 additions & 10 deletions

File tree

client/src/extension.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ import { connectToPort, findJavaExecutable, getAvailablePort, killProcess } from
99
import { SERVER_JAR, DEBUG_MODE, DEBUG_PORT } from "./constants";
1010
import { LiquidJavaWebviewProvider } from "./webview/provider";
1111
import { LJDiagnostic } from "./types";
12-
import { createMermaidDiagram } from "./fsm";
12+
import { createMermaidDiagram } from "./webview/fsm";
13+
import { StateMachine } from "./types/fsm";
1314

1415
let serverProcess: child_process.ChildProcess;
1516
let client: LanguageClient;
@@ -20,6 +21,7 @@ let statusBarItem: vscode.StatusBarItem;
2021
let currentDiagnostics: LJDiagnostic[];
2122
let webviewProvider: LiquidJavaWebviewProvider;
2223
let currentFile: string | undefined;
24+
let currentStateMachine: StateMachine | undefined;
2325

2426
/**
2527
* Activates the LiquidJava extension
@@ -132,6 +134,7 @@ function initWebview(context: vscode.ExtensionContext) {
132134
if (message.type === "ready") {
133135
webviewProvider.sendMessage({ type: "file", file: currentFile });
134136
webviewProvider.sendMessage({ type: "diagnostics", diagnostics: currentDiagnostics });
137+
if (currentStateMachine) webviewProvider.sendMessage({ type: "fsm", sm: currentStateMachine });
135138
}
136139
})
137140
);
@@ -183,9 +186,9 @@ function initFileEvents(context: vscode.ExtensionContext) {
183186
async function requestStateMachine(document: vscode.TextDocument) {
184187
const sm: StateMachine = await client?.sendRequest("liquidjava/fsm", { uri: document.uri.toString() });
185188
if (!sm) return;
186-
187-
const diagram = createMermaidDiagram(sm);
188-
console.log(diagram);
189+
190+
webviewProvider?.sendMessage({ type: "fsm", sm });
191+
currentStateMachine = sm;
189192
}
190193

191194
/**

client/src/types/fsm.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
type StateMachine = {
1+
export type StateMachine = {
22
className: string;
33
initial: string;
44
states: string[];
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { StateMachine } from "../types/fsm";
12

23
/**
34
* Converts a StateMachine object to a Mermaid state diagram string
@@ -8,6 +9,9 @@ export function createMermaidDiagram(sm: StateMachine): string {
89
const lines: string[] = [];
910

1011
// header
12+
lines.push('---');
13+
lines.push(`title: ${sm.className}`);
14+
lines.push('---');
1115
lines.push('stateDiagram-v2');
1216

1317
// initial state

client/src/webview/html.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import * as vscode from "vscode";
22
import { getStyles } from "./styles";
33

4+
const MERMAID_CDN = "https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.esm.min.mjs";
5+
46
/**
57
* Generates the HTML content for the webview
68
* @param webview
@@ -18,12 +20,25 @@ export function getHtml(webview: vscode.Webview, extensionUri: vscode.Uri): stri
1820
<meta charset="utf-8">
1921
<meta
2022
http-equiv="Content-Security-Policy"
21-
content="default-src 'none'; style-src ${cspSource} 'unsafe-inline'; script-src 'nonce-${nonce}';"
23+
content="default-src 'none'; style-src ${cspSource} 'unsafe-inline'; script-src 'nonce-${nonce}' https://cdn.jsdelivr.net; connect-src https://cdn.jsdelivr.net;"
2224
>
2325
<style>${getStyles()}</style>
2426
</head>
2527
<body>
2628
<div id="root"></div>
29+
<script nonce="${nonce}" type="module">
30+
import mermaid from '${MERMAID_CDN}';
31+
mermaid.initialize({
32+
startOnLoad: false,
33+
theme: document.body.classList.contains('vscode-light') ? 'default' : 'dark',
34+
securityLevel: 'loose',
35+
flowchart: {
36+
useMaxWidth: true,
37+
htmlLabels: true
38+
}
39+
});
40+
window.mermaid = mermaid;
41+
</script>
2742
<script nonce="${nonce}" src="${scriptUri}"></script>
2843
</body>
2944
</html>

client/src/webview/main.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { getScript } from "./script";
22

3-
// Entry point for the webview
3+
// webview entry point
44

55
declare function acquireVsCodeApi(): any;
66
declare const document: any;
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { StateMachine } from "../../types/fsm";
2+
3+
export function getDiagramView(diagram: string, sm: StateMachine): string {
4+
return /*html*/`
5+
<div class="diagram-section">
6+
<div class="diagram-container">
7+
<pre class="mermaid">${diagram}</pre>
8+
</div>
9+
<div>
10+
<p><strong>States:</strong> ${sm.states.join(', ')}</p>
11+
<p><strong>Initial state:</strong> ${sm.initial}</p>
12+
<p><strong>Number of states:</strong> ${sm.states.length}</p>
13+
<p><strong>Number of transitions:</strong> ${sm.transitions.length}</p>
14+
</div>
15+
</div>
16+
`;
17+
}

client/src/webview/script.ts

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ import { getCorrectView } from "./renderers/correct";
44
import { getLoadingView } from "./renderers/loading";
55
import { getErrorsView } from "./renderers/diagnostics/errors";
66
import { getWarningsView } from "./renderers/diagnostics/warnings";
7+
import { getDiagramView } from "./renderers/diagram";
8+
import { StateMachine } from "../types/fsm";
9+
import { createMermaidDiagram } from "./fsm";
710

811
/**
912
* Initializes the webview script
@@ -18,6 +21,7 @@ export function getScript(vscode: any, document: any, window: any) {
1821
let showAllDiagnostics = false;
1922
let currentFile: string | undefined;
2023
let expandedErrors = new Set<number>();
24+
let stateMachine = '';
2125

2226
// initial state
2327
root.innerHTML = getLoadingView();
@@ -103,13 +107,37 @@ export function getScript(vscode: any, document: any, window: any) {
103107
} else if (msg.type === 'file') {
104108
currentFile = msg.file;
105109
if (!showAllDiagnostics) updateView();
110+
} else if (msg.type === 'fsm') {
111+
const sm = msg.sm as StateMachine;
112+
const diagram = createMermaidDiagram(sm);
113+
stateMachine = diagram ? getDiagramView(diagram, sm) : '';
114+
updateView();
106115
}
107-
});
116+
});
117+
118+
async function renderMermaidDiagram() {
119+
const mermaid = (window as any).mermaid;
120+
if (!mermaid) return;
121+
122+
const mermaidElements = document.querySelectorAll('.mermaid');
123+
if (mermaidElements.length === 0) return;
124+
125+
try {
126+
await mermaid.run({ nodes: mermaidElements });
127+
} catch (e) {
128+
console.error('Failed to render Mermaid diagram:', e);
129+
}
130+
}
108131

109132
function updateView() {
110133
let mainView = fileErrors.length > 0 ? getErrorsView(fileErrors, showAllDiagnostics, currentFile, expandedErrors) : getCorrectView(showAllDiagnostics);
111134
let warningsView = fileWarnings.length > 0 ? getWarningsView(fileWarnings, showAllDiagnostics, currentFile) : '';
112-
root.innerHTML = mainView + warningsView;
135+
root.innerHTML = mainView + warningsView + stateMachine;
136+
137+
// re-render mermaid diagram after DOM update
138+
if (stateMachine) {
139+
renderMermaidDiagram();
140+
}
113141
}
114142
}
115143

client/src/webview/styles.ts

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ export function getStyles(): string {
3030
overflow-x: auto;
3131
}
3232
strong {
33-
display: block;
33+
display: inline;
3434
margin-bottom: 0.5rem;
3535
}
3636
.container {
@@ -241,5 +241,30 @@ export function getStyles(): string {
241241
font-family: var(--vscode-editor-font-family);
242242
font-size: 0.9em;
243243
}
244+
.diagram-section {
245+
margin-bottom: 1.5rem;
246+
padding-bottom: 1rem;
247+
border-bottom: 1px solid var(--vscode-panel-border);
248+
}
249+
.diagram-section h2 {
250+
margin-bottom: 0.5rem;
251+
}
252+
.diagram-container {
253+
background-color: var(--vscode-editor-background);
254+
border-radius: 4px;
255+
padding: 1rem;
256+
overflow-x: auto;
257+
}
258+
.diagram-container .mermaid {
259+
display: flex;
260+
justify-content: center;
261+
}
262+
.diagram-container .mermaid svg {
263+
max-width: 100%;
264+
height: auto;
265+
}
266+
.mermaid .statediagramTitleText {
267+
font-size: 22px!important;
268+
}
244269
`;
245270
}

server/src/main/java/fsm/StateMachineParser.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,10 @@ public static StateMachine parse(String uri) {
4343

4444
// extract class name and states
4545
List<String> states = getStates(ctType);
46+
if (states == null || states.isEmpty()) {
47+
return null;
48+
}
49+
4650
String className = getClassName(ctType);
4751

4852
// extract initial state and transitions

0 commit comments

Comments
 (0)