deepclause-pi 0.1.5 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +23 -0
- package/dist/diagram/extract.d.ts +5 -0
- package/dist/diagram/extract.js +701 -0
- package/dist/diagram/grade.d.ts +41 -0
- package/dist/diagram/grade.js +70 -0
- package/dist/diagram/validate.d.ts +36 -0
- package/dist/diagram/validate.js +148 -0
- package/dist/diagram/viewer.d.ts +30 -0
- package/dist/diagram/viewer.js +94 -0
- package/dist/diagram/workspace.d.ts +24 -0
- package/dist/diagram/workspace.js +106 -0
- package/dist/index.js +142 -2
- package/dist/model.d.ts +16 -0
- package/dist/model.js +28 -0
- package/docs/DIAGRAM_INTEGRATION_PROPOSAL.md +154 -0
- package/package.json +1 -1
- package/src/assets/AGENTS.md +16 -0
- package/src/assets/vendor/mermaid.min.js +3636 -0
- package/src/assets/viewer.template.html +319 -0
- package/src/diagram/extract.ts +721 -0
- package/src/diagram/grade.ts +104 -0
- package/src/diagram/validate.ts +188 -0
- package/src/diagram/viewer.ts +133 -0
- package/src/diagram/workspace.ts +109 -0
- package/src/index.ts +158 -2
- package/src/model.ts +46 -0
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
6
|
+
<meta http-equiv="Cache-Control" content="no-store" />
|
|
7
|
+
<title>DML flowcharts</title>
|
|
8
|
+
<style>
|
|
9
|
+
:root {
|
|
10
|
+
--bg: #f6f7f9; --panel: #ffffff; --line: #dfe3e8; --ink: #1f2933;
|
|
11
|
+
--muted: #6b7280; --accent: #0b7285; --accent-soft: #e0f7fa;
|
|
12
|
+
}
|
|
13
|
+
* { box-sizing: border-box; }
|
|
14
|
+
html, body { height: 100%; margin: 0; }
|
|
15
|
+
body {
|
|
16
|
+
font: 14px/1.45 system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
|
|
17
|
+
color: var(--ink); background: var(--bg);
|
|
18
|
+
display: grid; grid-template-columns: 250px 1fr; grid-template-rows: auto 1fr;
|
|
19
|
+
grid-template-areas: "head head" "aside main";
|
|
20
|
+
}
|
|
21
|
+
header {
|
|
22
|
+
grid-area: head; display: flex; align-items: center; gap: 12px;
|
|
23
|
+
padding: 10px 16px; background: var(--panel); border-bottom: 1px solid var(--line);
|
|
24
|
+
}
|
|
25
|
+
header h1 { font-size: 15px; margin: 0; font-weight: 650; }
|
|
26
|
+
header .sub { color: var(--muted); font-size: 12px; }
|
|
27
|
+
header .spacer, .toolbar .spacer, .tabs .spacer { flex: 1; }
|
|
28
|
+
button, select {
|
|
29
|
+
font: inherit; padding: 5px 10px; border: 1px solid var(--line);
|
|
30
|
+
background: var(--panel); border-radius: 7px; color: var(--ink); cursor: pointer;
|
|
31
|
+
}
|
|
32
|
+
button:hover { border-color: var(--accent); color: var(--accent); }
|
|
33
|
+
button.primary { background: var(--accent); color: #fff; border-color: var(--accent); }
|
|
34
|
+
aside {
|
|
35
|
+
grid-area: aside; border-right: 1px solid var(--line); background: var(--panel);
|
|
36
|
+
overflow: auto; padding: 8px;
|
|
37
|
+
}
|
|
38
|
+
aside .item {
|
|
39
|
+
display: block; width: 100%; text-align: left; padding: 8px 10px; margin: 2px 0;
|
|
40
|
+
border-radius: 8px; border: 1px solid transparent; background: transparent; cursor: pointer;
|
|
41
|
+
}
|
|
42
|
+
aside .item:hover { background: var(--bg); }
|
|
43
|
+
aside .item.active { background: var(--accent-soft); border-color: var(--accent); }
|
|
44
|
+
aside .item small { display: block; color: var(--muted); font-size: 11px; }
|
|
45
|
+
main { grid-area: main; display: flex; flex-direction: column; min-width: 0; min-height: 0; }
|
|
46
|
+
.toolbar {
|
|
47
|
+
display: flex; align-items: center; gap: 8px; padding: 8px 12px;
|
|
48
|
+
background: var(--panel); border-bottom: 1px solid var(--line);
|
|
49
|
+
}
|
|
50
|
+
.content { flex: 1; display: flex; min-height: 0; }
|
|
51
|
+
#preview {
|
|
52
|
+
flex: 1; overflow: auto; padding: 24px; background:
|
|
53
|
+
repeating-conic-gradient(#0000 0 25%, #00000005 0 50%) 0/24px 24px;
|
|
54
|
+
}
|
|
55
|
+
#preview svg { max-width: none; height: auto; }
|
|
56
|
+
|
|
57
|
+
/* --- split pane --- */
|
|
58
|
+
#side {
|
|
59
|
+
width: 42%; min-width: 260px; max-width: 72%;
|
|
60
|
+
border-left: 1px solid var(--line); background: var(--panel);
|
|
61
|
+
display: flex; flex-direction: column; min-height: 0;
|
|
62
|
+
resize: horizontal; overflow: hidden;
|
|
63
|
+
}
|
|
64
|
+
#side.hidden { display: none; }
|
|
65
|
+
.tabs {
|
|
66
|
+
display: flex; align-items: center; gap: 4px;
|
|
67
|
+
padding: 6px 8px; border-bottom: 1px solid var(--line);
|
|
68
|
+
}
|
|
69
|
+
.tabs .tab {
|
|
70
|
+
border: 1px solid transparent; background: transparent; padding: 4px 9px;
|
|
71
|
+
border-radius: 7px; color: var(--muted);
|
|
72
|
+
}
|
|
73
|
+
.tabs .tab.active { background: var(--accent-soft); border-color: var(--accent); color: var(--accent); }
|
|
74
|
+
.pane { flex: 1; min-height: 0; display: none; }
|
|
75
|
+
.pane.active { display: flex; flex-direction: column; }
|
|
76
|
+
#editor {
|
|
77
|
+
flex: 1; width: 100%; border: 0; resize: none; padding: 10px;
|
|
78
|
+
font: 12px/1.5 ui-monospace, SFMono-Regular, Menlo, monospace; color: var(--ink); background: var(--panel);
|
|
79
|
+
}
|
|
80
|
+
#editor:focus { outline: none; }
|
|
81
|
+
#dmlView {
|
|
82
|
+
flex: 1; margin: 0; overflow: auto; padding: 10px;
|
|
83
|
+
font: 12px/1.5 ui-monospace, SFMono-Regular, Menlo, monospace;
|
|
84
|
+
white-space: pre; tab-size: 2;
|
|
85
|
+
}
|
|
86
|
+
.pane .hint { padding: 6px 10px; color: var(--muted); font-size: 11px; border-bottom: 1px solid var(--line); }
|
|
87
|
+
.err {
|
|
88
|
+
display: none; margin: 0 0 16px; padding: 10px 12px; border-radius: 8px;
|
|
89
|
+
background: #fff1f2; border: 1px solid #f1aeb5; color: #9f1239; white-space: pre-wrap;
|
|
90
|
+
font: 12px/1.5 ui-monospace, Menlo, monospace;
|
|
91
|
+
}
|
|
92
|
+
.err.show { display: block; }
|
|
93
|
+
.empty { color: var(--muted); padding: 40px; text-align: center; }
|
|
94
|
+
</style>
|
|
95
|
+
</head>
|
|
96
|
+
<body>
|
|
97
|
+
<header>
|
|
98
|
+
<h1>DML flowcharts</h1>
|
|
99
|
+
<span class="sub">generated __GENERATED__</span>
|
|
100
|
+
<span class="spacer"></span>
|
|
101
|
+
<label class="sub">view
|
|
102
|
+
<select id="view" autocomplete="off"></select>
|
|
103
|
+
</label>
|
|
104
|
+
<label class="sub">theme
|
|
105
|
+
<select id="theme">
|
|
106
|
+
<option value="default">default</option>
|
|
107
|
+
<option value="forest">forest</option>
|
|
108
|
+
<option value="neutral">neutral</option>
|
|
109
|
+
<option value="dark">dark</option>
|
|
110
|
+
</select>
|
|
111
|
+
</label>
|
|
112
|
+
<button id="toggleSide">Source</button>
|
|
113
|
+
<button id="exportSvg">SVG</button>
|
|
114
|
+
<button class="primary" id="exportPng">PNG</button>
|
|
115
|
+
</header>
|
|
116
|
+
|
|
117
|
+
<aside id="list"></aside>
|
|
118
|
+
|
|
119
|
+
<main>
|
|
120
|
+
<div class="toolbar">
|
|
121
|
+
<strong id="title">—</strong>
|
|
122
|
+
<span class="sub" id="path"></span>
|
|
123
|
+
<span class="spacer"></span>
|
|
124
|
+
<button id="copy">Copy mermaid</button>
|
|
125
|
+
<button id="render">Re-render</button>
|
|
126
|
+
</div>
|
|
127
|
+
<div class="content">
|
|
128
|
+
<div id="preview"><div class="empty">Select a diagram</div></div>
|
|
129
|
+
<div id="side">
|
|
130
|
+
<div class="tabs">
|
|
131
|
+
<button class="tab" data-tab="mermaid">Mermaid</button>
|
|
132
|
+
<button class="tab active" data-tab="dml">DML</button>
|
|
133
|
+
<span class="spacer"></span>
|
|
134
|
+
<button id="collapseSide" title="Hide pane">›</button>
|
|
135
|
+
</div>
|
|
136
|
+
<div class="pane" id="paneMermaid">
|
|
137
|
+
<div class="hint">Edit the Mermaid, then Re-render (Ctrl/Cmd+Enter).</div>
|
|
138
|
+
<textarea id="editor" spellcheck="false"></textarea>
|
|
139
|
+
</div>
|
|
140
|
+
<div class="pane active" id="paneDml">
|
|
141
|
+
<pre id="dmlView"></pre>
|
|
142
|
+
</div>
|
|
143
|
+
</div>
|
|
144
|
+
</div>
|
|
145
|
+
</main>
|
|
146
|
+
|
|
147
|
+
<script>
|
|
148
|
+
const DIAGRAMS = __DIAGRAMS_JSON__;
|
|
149
|
+
</script>
|
|
150
|
+
<script>
|
|
151
|
+
function ensureMermaid(cb) {
|
|
152
|
+
if (window.mermaid) return cb();
|
|
153
|
+
const s = document.createElement('script');
|
|
154
|
+
s.src = 'https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.min.js';
|
|
155
|
+
s.onload = cb;
|
|
156
|
+
s.onerror = () => { document.getElementById('preview').innerHTML =
|
|
157
|
+
'<div class="err show">Mermaid failed to load (no vendor copy and no network).</div>'; };
|
|
158
|
+
document.head.appendChild(s);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const el = (id) => document.getElementById(id);
|
|
162
|
+
let current = -1;
|
|
163
|
+
let svgText = '';
|
|
164
|
+
let pendingView = null;
|
|
165
|
+
let viewInitialized = false;
|
|
166
|
+
|
|
167
|
+
function init() {
|
|
168
|
+
mermaid.initialize({ startOnLoad: false, securityLevel: 'loose', theme: el('theme').value,
|
|
169
|
+
flowchart: { htmlLabels: true, useMaxWidth: false } });
|
|
170
|
+
pendingView = new URLSearchParams(location.search).get('view');
|
|
171
|
+
buildList();
|
|
172
|
+
const wanted = decodeURIComponent(location.hash.replace(/^#/, ''));
|
|
173
|
+
const idx = DIAGRAMS.findIndex((d) => d.name === wanted);
|
|
174
|
+
if (DIAGRAMS.length) select(idx >= 0 ? idx : 0);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function setViewOptions(d) {
|
|
178
|
+
const sel = el('view');
|
|
179
|
+
const opts = [];
|
|
180
|
+
if (d.presentation) opts.push(['presentation', 'Presentation']);
|
|
181
|
+
if (d.specification) opts.push(['specification', 'Specification']);
|
|
182
|
+
opts.push(['flow', 'Flow']);
|
|
183
|
+
if (d.seq) opts.push(['sequence', 'Sequence']);
|
|
184
|
+
const available = opts.map(([v]) => v);
|
|
185
|
+
let wanted = viewInitialized ? sel.value : '';
|
|
186
|
+
if (pendingView && available.includes(pendingView)) wanted = pendingView;
|
|
187
|
+
pendingView = null;
|
|
188
|
+
if (!available.includes(wanted)) {
|
|
189
|
+
wanted = available.includes('presentation') ? 'presentation' : (available.includes('specification') ? 'specification' : 'flow');
|
|
190
|
+
}
|
|
191
|
+
sel.innerHTML = opts.map(([v, t]) => '<option value="' + v + '">' + t + '</option>').join('');
|
|
192
|
+
sel.value = wanted;
|
|
193
|
+
viewInitialized = true;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function buildList() {
|
|
197
|
+
const list = el('list');
|
|
198
|
+
list.innerHTML = '';
|
|
199
|
+
DIAGRAMS.forEach((d, i) => {
|
|
200
|
+
const b = document.createElement('button');
|
|
201
|
+
b.className = 'item' + (i === current ? ' active' : '');
|
|
202
|
+
b.innerHTML = '<span></span><small></small>';
|
|
203
|
+
b.firstChild.textContent = d.name;
|
|
204
|
+
b.lastChild.textContent = d.path;
|
|
205
|
+
b.onclick = () => select(i);
|
|
206
|
+
list.appendChild(b);
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
function codeFor(d) {
|
|
211
|
+
const v = el('view').value;
|
|
212
|
+
if (v === 'presentation' && d.presentation) return d.presentation;
|
|
213
|
+
if (v === 'specification' && d.specification) return d.specification;
|
|
214
|
+
if (v === 'sequence' && d.seq) return d.seq;
|
|
215
|
+
return d.flow;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
function select(i) {
|
|
219
|
+
current = i;
|
|
220
|
+
const d = DIAGRAMS[i];
|
|
221
|
+
location.hash = d.name;
|
|
222
|
+
el('title').textContent = d.name + '.dml';
|
|
223
|
+
el('path').textContent = d.path;
|
|
224
|
+
setViewOptions(d);
|
|
225
|
+
el('editor').value = codeFor(d);
|
|
226
|
+
el('dmlView').textContent = d.dml || '(DML source not embedded — rebuild the viewer)';
|
|
227
|
+
buildList();
|
|
228
|
+
render();
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
async function render() {
|
|
232
|
+
const code = el('editor').value;
|
|
233
|
+
try {
|
|
234
|
+
const out = await mermaid.render('g' + Date.now(), code);
|
|
235
|
+
svgText = out.svg;
|
|
236
|
+
el('preview').innerHTML = svgText;
|
|
237
|
+
} catch (e) {
|
|
238
|
+
svgText = '';
|
|
239
|
+
el('preview').innerHTML = '<div class="err show"></div>';
|
|
240
|
+
el('preview').querySelector('.err').textContent = String(e && e.message || e);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
function reinit() {
|
|
245
|
+
mermaid.initialize({ startOnLoad: false, securityLevel: 'loose', theme: el('theme').value,
|
|
246
|
+
flowchart: { htmlLabels: true, useMaxWidth: false } });
|
|
247
|
+
render();
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
function setTab(name) {
|
|
251
|
+
document.querySelectorAll('.tabs .tab').forEach((b) => b.classList.toggle('active', b.dataset.tab === name));
|
|
252
|
+
el('paneMermaid').classList.toggle('active', name === 'mermaid');
|
|
253
|
+
el('paneDml').classList.toggle('active', name === 'dml');
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
function download(blob, filename) {
|
|
257
|
+
const a = document.createElement('a');
|
|
258
|
+
a.href = URL.createObjectURL(blob);
|
|
259
|
+
a.download = filename;
|
|
260
|
+
a.click();
|
|
261
|
+
setTimeout(() => URL.revokeObjectURL(a.href), 1000);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
const currentName = () => (DIAGRAMS[current] ? DIAGRAMS[current].name : 'diagram');
|
|
265
|
+
|
|
266
|
+
function exportSvg() {
|
|
267
|
+
if (!svgText) return;
|
|
268
|
+
const clone = el('preview').querySelector('svg').cloneNode(true);
|
|
269
|
+
clone.setAttribute('xmlns', 'http://www.w3.org/2000/svg');
|
|
270
|
+
const data = new XMLSerializer().serializeToString(clone);
|
|
271
|
+
download(new Blob([data], { type: 'image/svg+xml;charset=utf-8' }), currentName() + '.svg');
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
async function exportPng() {
|
|
275
|
+
const svg = el('preview').querySelector('svg');
|
|
276
|
+
if (!svg) return;
|
|
277
|
+
const clone = svg.cloneNode(true);
|
|
278
|
+
clone.setAttribute('xmlns', 'http://www.w3.org/2000/svg');
|
|
279
|
+
const w = parseFloat(svg.getAttribute('width')) || svg.getBoundingClientRect().width;
|
|
280
|
+
const h = parseFloat(svg.getAttribute('height')) || svg.getBoundingClientRect().height;
|
|
281
|
+
clone.setAttribute('width', w); clone.setAttribute('height', h);
|
|
282
|
+
const data = new XMLSerializer().serializeToString(clone);
|
|
283
|
+
const url = 'data:image/svg+xml;base64,' + btoa(unescape(encodeURIComponent(data)));
|
|
284
|
+
const img = new Image();
|
|
285
|
+
await new Promise((res, rej) => { img.onload = res; img.onerror = rej; img.src = url; });
|
|
286
|
+
const scale = 2;
|
|
287
|
+
const canvas = document.createElement('canvas');
|
|
288
|
+
canvas.width = Math.ceil(w * scale); canvas.height = Math.ceil(h * scale);
|
|
289
|
+
const ctx = canvas.getContext('2d');
|
|
290
|
+
ctx.fillStyle = el('theme').value === 'dark' ? '#1e1e1e' : '#ffffff';
|
|
291
|
+
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
|
292
|
+
ctx.scale(scale, scale);
|
|
293
|
+
ctx.drawImage(img, 0, 0, w, h);
|
|
294
|
+
canvas.toBlob((b) => download(b, currentName() + '.png'), 'image/png');
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
el('theme').onchange = reinit;
|
|
298
|
+
el('view').onchange = () => { if (current >= 0) { el('editor').value = codeFor(DIAGRAMS[current]); render(); } };
|
|
299
|
+
el('toggleSide').onclick = () => el('side').classList.toggle('hidden');
|
|
300
|
+
el('collapseSide').onclick = () => el('side').classList.add('hidden');
|
|
301
|
+
document.querySelectorAll('.tabs .tab').forEach((b) => { b.onclick = () => setTab(b.dataset.tab); });
|
|
302
|
+
el('render').onclick = render;
|
|
303
|
+
el('exportSvg').onclick = exportSvg;
|
|
304
|
+
el('exportPng').onclick = exportPng;
|
|
305
|
+
el('copy').onclick = async () => {
|
|
306
|
+
const text = el('editor').value;
|
|
307
|
+
try { await navigator.clipboard.writeText(text); }
|
|
308
|
+
catch { el('editor').select(); document.execCommand('copy'); }
|
|
309
|
+
el('copy').textContent = 'Copied'; setTimeout(() => el('copy').textContent = 'Copy mermaid', 1200);
|
|
310
|
+
};
|
|
311
|
+
el('editor').addEventListener('keydown', (e) => {
|
|
312
|
+
if ((e.metaKey || e.ctrlKey) && e.key === 'Enter') { e.preventDefault(); render(); }
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
window.addEventListener('DOMContentLoaded', () => ensureMermaid(init));
|
|
316
|
+
</script>
|
|
317
|
+
<script src="vendor/mermaid.min.js"></script>
|
|
318
|
+
</body>
|
|
319
|
+
</html>
|