@zenuml/core 3.46.0 → 3.46.1
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/.claude/skills/dia-scoring/SKILL.md +139 -0
- package/.claude/skills/dia-scoring/agents/openai.yaml +7 -0
- package/.claude/skills/dia-scoring/references/selectors-and-keys.md +253 -0
- package/CLAUDE.md +1 -1
- package/bun.lock +25 -11
- package/cy/canonical-history.html +908 -0
- package/cy/compare-case.html +357 -0
- package/cy/compare-cases.js +824 -0
- package/cy/compare.html +35 -0
- package/cy/diff-algorithm.js +199 -0
- package/cy/element-report.html +705 -0
- package/cy/icons-test.html +29 -0
- package/cy/legacy-vs-html.html +291 -0
- package/cy/native-diff-ext/background.js +60 -0
- package/cy/native-diff-ext/bridge.js +26 -0
- package/cy/native-diff-ext/content.js +194 -0
- package/cy/parity-test.html +122 -0
- package/cy/return-in-nested-if.html +29 -0
- package/cy/svg-preview.html +56 -0
- package/cy/svg-test.html +21 -0
- package/cy/theme-default-test.html +28 -0
- package/dist/stats.html +1 -1
- package/dist/zenuml.esm.mjs +16352 -15223
- package/dist/zenuml.js +701 -575
- package/docs/superpowers/plans/2026-03-23-svg-parity-features.md +283 -0
- package/index.html +568 -73
- package/package.json +15 -4
- package/scripts/analyze-compare-case/collect-data.mjs +991 -0
- package/scripts/analyze-compare-case/config.mjs +102 -0
- package/scripts/analyze-compare-case/geometry.mjs +101 -0
- package/scripts/analyze-compare-case/native-diff.mjs +224 -0
- package/scripts/analyze-compare-case/output.mjs +74 -0
- package/scripts/analyze-compare-case/panel-diff.mjs +114 -0
- package/scripts/analyze-compare-case/report.mjs +157 -0
- package/scripts/analyze-compare-case/residual-scopes.mjs +325 -0
- package/scripts/analyze-compare-case/scoring.mjs +816 -0
- package/scripts/analyze-compare-case.mjs +149 -0
- package/scripts/snapshot-dual.js +34 -34
- package/skills/dia-scoring/SKILL.md +129 -0
- package/skills/dia-scoring/agents/openai.yaml +7 -0
- package/skills/dia-scoring/references/selectors-and-keys.md +253 -0
- package/test-setup.ts +8 -0
- package/types/index.d.ts +56 -0
- package/vite.config.ts +4 -0
- package/dist/10029-icon-service-Function-Apps-ObflOLuF.js +0 -5
- package/dist/Res_AWS-Identity-Access-Management_IAM-Access-Analyzer_48-BPq60XMY.js +0 -11
- package/dist/Res_AWS-Lambda_Lambda-Function_48-Co38UB_2.js +0 -12
- package/dist/Res_Amazon-EC2_Instance_48-CRaqbNUl.js +0 -12
- package/dist/Res_Amazon-Simple-Notification-Service_Topic_48-q13mxUeM.js +0 -11
- package/dist/Res_Amazon-Simple-Queue-Service_Queue_48-D2-8gbFw.js +0 -11
- package/dist/Robustness_Diagram_Boundary-nYnmTPs8.js +0 -10
- package/dist/Robustness_Diagram_Control-DLNLoMxd.js +0 -11
- package/dist/Robustness_Diagram_Entity-Be3kcbIE.js +0 -11
- package/dist/actor-BMj_HFpo.js +0 -11
- package/dist/database-BKHQQWQK.js +0 -8
package/index.html
CHANGED
|
@@ -26,21 +26,269 @@
|
|
|
26
26
|
rel="stylesheet"
|
|
27
27
|
href="/vendor/codemirror/material-darker.min.css"
|
|
28
28
|
/>
|
|
29
|
-
<title>ZenUML
|
|
29
|
+
<title>ZenUML Dev Workbench</title>
|
|
30
30
|
<style>
|
|
31
|
+
:root {
|
|
32
|
+
--bg: #0f172a;
|
|
33
|
+
--bg-alt: #111827;
|
|
34
|
+
--panel: #ffffff;
|
|
35
|
+
--panel-alt: #f8fafc;
|
|
36
|
+
--panel-muted: #eef2f7;
|
|
37
|
+
--ink: #0f172a;
|
|
38
|
+
--muted: #526072;
|
|
39
|
+
--line: #d7deea;
|
|
40
|
+
--line-strong: #c4cedd;
|
|
41
|
+
--accent: #0f766e;
|
|
42
|
+
--accent-strong: #115e59;
|
|
43
|
+
--danger: #991b1b;
|
|
44
|
+
--shadow: 0 18px 48px rgba(15, 23, 42, 0.14);
|
|
45
|
+
--radius-lg: 14px;
|
|
46
|
+
--radius-md: 10px;
|
|
47
|
+
--editor-width: 420px;
|
|
48
|
+
}
|
|
49
|
+
|
|
31
50
|
* {
|
|
51
|
+
box-sizing: border-box;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
html,
|
|
55
|
+
body {
|
|
56
|
+
margin: 0;
|
|
57
|
+
min-height: 100%;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
body {
|
|
32
61
|
font-family:
|
|
33
|
-
"
|
|
34
|
-
-apple-system,
|
|
35
|
-
BlinkMacSystemFont,
|
|
62
|
+
"SF Pro Text",
|
|
36
63
|
"Segoe UI",
|
|
37
|
-
|
|
64
|
+
system-ui,
|
|
38
65
|
sans-serif;
|
|
66
|
+
color: var(--ink);
|
|
67
|
+
background: linear-gradient(180deg, var(--bg) 0%, var(--bg-alt) 100%);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
a {
|
|
71
|
+
color: inherit;
|
|
39
72
|
}
|
|
40
73
|
|
|
41
74
|
code,
|
|
42
|
-
.CodeMirror
|
|
43
|
-
|
|
75
|
+
.CodeMirror,
|
|
76
|
+
.tool-chip,
|
|
77
|
+
.tool-button,
|
|
78
|
+
.tool-link,
|
|
79
|
+
.stats-text,
|
|
80
|
+
.toolbar-label {
|
|
81
|
+
font-family:
|
|
82
|
+
"IBM Plex Mono",
|
|
83
|
+
"SFMono-Regular",
|
|
84
|
+
"Menlo",
|
|
85
|
+
"Monaco",
|
|
86
|
+
"Consolas",
|
|
87
|
+
monospace;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
.app-shell {
|
|
91
|
+
min-height: 100vh;
|
|
92
|
+
display: grid;
|
|
93
|
+
grid-template-rows: auto 1fr;
|
|
94
|
+
gap: 12px;
|
|
95
|
+
padding: 14px;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
.toolbar {
|
|
99
|
+
display: grid;
|
|
100
|
+
grid-template-columns: minmax(0, 1fr) auto;
|
|
101
|
+
align-items: center;
|
|
102
|
+
gap: 16px;
|
|
103
|
+
padding: 12px 14px;
|
|
104
|
+
border: 1px solid rgba(255, 255, 255, 0.08);
|
|
105
|
+
border-radius: var(--radius-lg);
|
|
106
|
+
background: rgba(15, 23, 42, 0.9);
|
|
107
|
+
color: #e5edf8;
|
|
108
|
+
box-shadow: var(--shadow);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
.toolbar-title {
|
|
112
|
+
display: flex;
|
|
113
|
+
flex-wrap: wrap;
|
|
114
|
+
align-items: baseline;
|
|
115
|
+
gap: 12px;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
.toolbar-title strong {
|
|
119
|
+
font-size: 15px;
|
|
120
|
+
font-weight: 700;
|
|
121
|
+
letter-spacing: 0.01em;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
.toolbar-title span {
|
|
125
|
+
color: #9fb1c7;
|
|
126
|
+
font-size: 12px;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
.toolbar-actions {
|
|
130
|
+
display: flex;
|
|
131
|
+
align-items: center;
|
|
132
|
+
justify-content: flex-end;
|
|
133
|
+
flex-wrap: wrap;
|
|
134
|
+
gap: 8px;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
.toolbar-label {
|
|
138
|
+
color: #9fb1c7;
|
|
139
|
+
font-size: 11px;
|
|
140
|
+
letter-spacing: 0.08em;
|
|
141
|
+
text-transform: uppercase;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
.workspace {
|
|
145
|
+
min-height: 0;
|
|
146
|
+
display: grid;
|
|
147
|
+
grid-template-columns: minmax(280px, var(--editor-width)) 10px minmax(320px, 1fr) minmax(320px, 1fr);
|
|
148
|
+
gap: 12px;
|
|
149
|
+
align-items: stretch;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
.panel {
|
|
153
|
+
display: flex;
|
|
154
|
+
flex-direction: column;
|
|
155
|
+
min-height: 0;
|
|
156
|
+
min-width: 0;
|
|
157
|
+
overflow: hidden;
|
|
158
|
+
border-radius: var(--radius-lg);
|
|
159
|
+
border: 1px solid var(--line);
|
|
160
|
+
background: var(--panel);
|
|
161
|
+
box-shadow: var(--shadow);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
.workspace-resizer {
|
|
165
|
+
position: relative;
|
|
166
|
+
min-height: 0;
|
|
167
|
+
border-radius: 999px;
|
|
168
|
+
background: rgba(148, 163, 184, 0.28);
|
|
169
|
+
cursor: col-resize;
|
|
170
|
+
touch-action: none;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
.workspace-resizer::before {
|
|
174
|
+
content: "";
|
|
175
|
+
position: absolute;
|
|
176
|
+
inset: 0;
|
|
177
|
+
margin: auto;
|
|
178
|
+
width: 4px;
|
|
179
|
+
height: 56px;
|
|
180
|
+
border-radius: 999px;
|
|
181
|
+
background: rgba(15, 118, 110, 0.22);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
.workspace-resizer:hover,
|
|
185
|
+
.workspace.is-resizing .workspace-resizer {
|
|
186
|
+
background: rgba(15, 118, 110, 0.2);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
.workspace.is-resizing {
|
|
190
|
+
user-select: none;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
.panel-header {
|
|
194
|
+
display: flex;
|
|
195
|
+
align-items: center;
|
|
196
|
+
justify-content: space-between;
|
|
197
|
+
gap: 14px;
|
|
198
|
+
padding: 12px 14px;
|
|
199
|
+
border-bottom: 1px solid var(--line);
|
|
200
|
+
background: var(--panel-alt);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
.panel-title {
|
|
204
|
+
display: flex;
|
|
205
|
+
flex-direction: column;
|
|
206
|
+
gap: 4px;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
.panel-title h2,
|
|
210
|
+
.panel-title h3 {
|
|
211
|
+
margin: 0;
|
|
212
|
+
font-size: 14px;
|
|
213
|
+
letter-spacing: 0.01em;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
.panel-title p {
|
|
217
|
+
margin: 0;
|
|
218
|
+
color: var(--muted);
|
|
219
|
+
font-size: 12px;
|
|
220
|
+
line-height: 1.3;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
.panel-actions {
|
|
224
|
+
display: flex;
|
|
225
|
+
align-items: center;
|
|
226
|
+
flex-wrap: wrap;
|
|
227
|
+
justify-content: flex-end;
|
|
228
|
+
gap: 8px;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
.tool-button,
|
|
232
|
+
.tool-chip,
|
|
233
|
+
.tool-link {
|
|
234
|
+
display: inline-flex;
|
|
235
|
+
align-items: center;
|
|
236
|
+
justify-content: center;
|
|
237
|
+
appearance: none;
|
|
238
|
+
border: 1px solid var(--line);
|
|
239
|
+
border-radius: 8px;
|
|
240
|
+
padding: 8px 10px;
|
|
241
|
+
background: #fff;
|
|
242
|
+
color: var(--ink);
|
|
243
|
+
cursor: pointer;
|
|
244
|
+
text-decoration: none;
|
|
245
|
+
transition:
|
|
246
|
+
border-color 160ms ease,
|
|
247
|
+
background 160ms ease;
|
|
248
|
+
font-size: 11px;
|
|
249
|
+
letter-spacing: 0.03em;
|
|
250
|
+
white-space: nowrap;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
.tool-button:hover,
|
|
254
|
+
.tool-chip:hover,
|
|
255
|
+
.tool-link:hover {
|
|
256
|
+
border-color: var(--line-strong);
|
|
257
|
+
background: var(--panel-muted);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
.toolbar .tool-chip,
|
|
261
|
+
.toolbar .tool-link {
|
|
262
|
+
background: rgba(15, 23, 42, 0.55);
|
|
263
|
+
border-color: rgba(159, 177, 199, 0.25);
|
|
264
|
+
color: #e5edf8;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
.toolbar .tool-chip:hover,
|
|
268
|
+
.toolbar .tool-link:hover {
|
|
269
|
+
background: rgba(30, 41, 59, 0.95);
|
|
270
|
+
border-color: rgba(191, 209, 230, 0.4);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
.tool-chip.is-active {
|
|
274
|
+
background: var(--accent);
|
|
275
|
+
border-color: var(--accent-strong);
|
|
276
|
+
color: #f8fffe;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
.tool-button.primary {
|
|
280
|
+
background: var(--accent);
|
|
281
|
+
border-color: var(--accent-strong);
|
|
282
|
+
color: #f8fffe;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
.panel-body {
|
|
286
|
+
flex: 1;
|
|
287
|
+
min-height: 0;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
.editor-body {
|
|
291
|
+
background: #1d1d1b;
|
|
44
292
|
}
|
|
45
293
|
|
|
46
294
|
.CodeMirror {
|
|
@@ -58,83 +306,207 @@
|
|
|
58
306
|
border-left-width: 2px;
|
|
59
307
|
}
|
|
60
308
|
|
|
61
|
-
.
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
background
|
|
309
|
+
.preview-surface {
|
|
310
|
+
height: 100%;
|
|
311
|
+
overflow: auto;
|
|
312
|
+
padding: 12px;
|
|
313
|
+
background: var(--panel-muted);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
.html-surface {
|
|
317
|
+
position: relative;
|
|
66
318
|
}
|
|
67
319
|
|
|
68
|
-
.
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
320
|
+
.preview-stage {
|
|
321
|
+
min-height: 100%;
|
|
322
|
+
padding: 10px;
|
|
323
|
+
border-radius: var(--radius-md);
|
|
324
|
+
border: 1px solid var(--line);
|
|
325
|
+
background: #fff;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
.preview-stage pre {
|
|
329
|
+
margin: 0;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
.preview-stage svg {
|
|
333
|
+
display: block;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
.svg-stage {
|
|
337
|
+
display: block;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
#svg-container {
|
|
341
|
+
width: max-content;
|
|
342
|
+
margin: 0 auto;
|
|
343
|
+
min-width: 100%;
|
|
344
|
+
min-height: 100%;
|
|
345
|
+
display: grid;
|
|
346
|
+
place-items: center;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
#svg-container svg {
|
|
350
|
+
display: block;
|
|
351
|
+
box-shadow: 0 10px 28px rgba(15, 23, 42, 0.08);
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
.stats-text {
|
|
355
|
+
color: #9fb1c7;
|
|
356
|
+
font-size: 11px;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
.svg-error {
|
|
360
|
+
display: grid;
|
|
361
|
+
place-items: center;
|
|
362
|
+
min-height: 240px;
|
|
363
|
+
padding: 20px;
|
|
364
|
+
border-radius: var(--radius-md);
|
|
365
|
+
border: 1px dashed #d4b4b4;
|
|
366
|
+
color: var(--danger);
|
|
367
|
+
background: #fff7f7;
|
|
368
|
+
text-align: center;
|
|
369
|
+
line-height: 1.6;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
.workspace.mode-svg,
|
|
373
|
+
.workspace.mode-dom {
|
|
374
|
+
grid-template-columns: minmax(280px, var(--editor-width)) 10px minmax(720px, 1fr);
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
.workspace.mode-svg .html-panel,
|
|
378
|
+
.workspace.mode-dom .svg-panel {
|
|
379
|
+
display: none;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
@media (max-width: 1440px) {
|
|
383
|
+
.workspace,
|
|
384
|
+
.workspace.mode-svg,
|
|
385
|
+
.workspace.mode-dom {
|
|
386
|
+
grid-template-columns: minmax(280px, var(--editor-width)) 10px minmax(320px, 1fr);
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
.workspace.mode-split .svg-panel {
|
|
390
|
+
grid-column: 3;
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
@media (max-width: 1024px) {
|
|
395
|
+
.app-shell {
|
|
396
|
+
padding: 10px;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
.toolbar {
|
|
400
|
+
grid-template-columns: 1fr;
|
|
401
|
+
align-items: start;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
.workspace,
|
|
405
|
+
.workspace.mode-svg,
|
|
406
|
+
.workspace.mode-dom {
|
|
407
|
+
grid-template-columns: 1fr;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
.workspace-resizer {
|
|
411
|
+
display: none;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
.workspace.mode-split .svg-panel {
|
|
415
|
+
grid-column: auto;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
.panel {
|
|
419
|
+
min-height: 360px;
|
|
420
|
+
}
|
|
72
421
|
}
|
|
73
422
|
</style>
|
|
74
423
|
<script src="/vendor/tailwindcss/tailwindcss.js"></script>
|
|
75
424
|
</head>
|
|
76
|
-
<body
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
>
|
|
425
|
+
<body>
|
|
426
|
+
<div class="app-shell">
|
|
427
|
+
<header class="toolbar">
|
|
428
|
+
<div class="toolbar-title">
|
|
429
|
+
<strong>ZenUML Dev Workbench</strong>
|
|
430
|
+
<span>default to native SVG, switch to DOM or split only when needed</span>
|
|
431
|
+
</div>
|
|
432
|
+
<div class="toolbar-actions">
|
|
433
|
+
<span class="toolbar-label">View</span>
|
|
434
|
+
<button id="view-svg" class="tool-chip">SVG</button>
|
|
435
|
+
<button id="view-dom" class="tool-chip">DOM</button>
|
|
436
|
+
<button id="view-split" class="tool-chip">Split</button>
|
|
437
|
+
<button id="width-provider-toggle" class="tool-chip"></button>
|
|
438
|
+
<span id="svg-stats" class="stats-text">SVG: waiting</span>
|
|
439
|
+
<a class="tool-link" href="/cy/compare.html" target="_blank" rel="noreferrer">
|
|
440
|
+
Compare Cases
|
|
441
|
+
</a>
|
|
442
|
+
<a class="tool-link" href="/cy/svg-preview.html" target="_blank" rel="noreferrer">
|
|
443
|
+
SVG Only
|
|
444
|
+
</a>
|
|
445
|
+
</div>
|
|
446
|
+
</header>
|
|
447
|
+
|
|
448
|
+
<main class="workspace mode-svg" id="workspace">
|
|
449
|
+
<article class="panel editor-panel">
|
|
450
|
+
<div class="panel-header">
|
|
451
|
+
<div class="panel-title">
|
|
452
|
+
<h2>Editor</h2>
|
|
453
|
+
<p>Persisted in <code>localStorage</code>.</p>
|
|
454
|
+
</div>
|
|
455
|
+
<div class="panel-actions">
|
|
456
|
+
<button onclick="loadExample('basic')" class="tool-button">Basic</button>
|
|
457
|
+
<button onclick="loadExample('advanced')" class="tool-button">Advanced</button>
|
|
458
|
+
<button onclick="clearEditor()" class="tool-button">Clear</button>
|
|
459
|
+
<button onclick="exportDiagram()" class="tool-button primary">Export PNG</button>
|
|
460
|
+
</div>
|
|
461
|
+
</div>
|
|
462
|
+
<div class="panel-body editor-body">
|
|
463
|
+
<textarea id="text" style="display: none"></textarea>
|
|
464
|
+
</div>
|
|
465
|
+
</article>
|
|
466
|
+
|
|
84
467
|
<div
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
>
|
|
99
|
-
Advanced
|
|
100
|
-
</button>
|
|
101
|
-
<button
|
|
102
|
-
onclick="clearEditor()"
|
|
103
|
-
class="text-sm bg-gray-700 hover:bg-gray-600 px-3 py-1 rounded transition-colors"
|
|
104
|
-
>
|
|
105
|
-
Clear
|
|
106
|
-
</button>
|
|
107
|
-
<button
|
|
108
|
-
onclick="exportDiagram()"
|
|
109
|
-
class="text-sm bg-blue-600 hover:bg-blue-700 px-3 py-1 rounded transition-colors"
|
|
110
|
-
>
|
|
111
|
-
Export PNG
|
|
112
|
-
</button>
|
|
468
|
+
id="editor-resizer"
|
|
469
|
+
class="workspace-resizer"
|
|
470
|
+
role="separator"
|
|
471
|
+
aria-orientation="vertical"
|
|
472
|
+
aria-label="Resize editor"
|
|
473
|
+
></div>
|
|
474
|
+
|
|
475
|
+
<article class="panel html-panel">
|
|
476
|
+
<div class="panel-header">
|
|
477
|
+
<div class="panel-title">
|
|
478
|
+
<h3>DOM preview</h3>
|
|
479
|
+
<p><code>window.zenUml.render()</code> output.</p>
|
|
480
|
+
</div>
|
|
113
481
|
</div>
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
482
|
+
<div class="panel-body preview-surface html-surface">
|
|
483
|
+
<div class="preview-stage">
|
|
484
|
+
<pre class="zenuml"></pre>
|
|
485
|
+
</div>
|
|
486
|
+
</div>
|
|
487
|
+
</article>
|
|
119
488
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
489
|
+
<article class="panel svg-panel">
|
|
490
|
+
<div class="panel-header">
|
|
491
|
+
<div class="panel-title">
|
|
492
|
+
<h3>Native SVG preview</h3>
|
|
493
|
+
<p><code>renderToSvg()</code> output.</p>
|
|
494
|
+
</div>
|
|
495
|
+
</div>
|
|
496
|
+
<div class="panel-body preview-surface svg-surface">
|
|
497
|
+
<div class="preview-stage svg-stage">
|
|
498
|
+
<div id="svg-container"></div>
|
|
499
|
+
</div>
|
|
500
|
+
</div>
|
|
501
|
+
</article>
|
|
502
|
+
</main>
|
|
132
503
|
</div>
|
|
133
504
|
|
|
134
505
|
<script type="module">
|
|
135
506
|
import { waitUntil, debounce } from "./src/utils.ts";
|
|
136
507
|
import { createConfig } from "./src/config.ts";
|
|
137
508
|
import { toPng } from "html-to-image";
|
|
509
|
+
import { renderToSvg } from "./src/svg/renderToSvg.ts";
|
|
138
510
|
|
|
139
511
|
const editor = CodeMirror.fromTextArea(document.getElementById("text"), {
|
|
140
512
|
lineNumbers: true,
|
|
@@ -144,7 +516,41 @@
|
|
|
144
516
|
autofocus: true,
|
|
145
517
|
});
|
|
146
518
|
|
|
147
|
-
const
|
|
519
|
+
const svgContainer = document.getElementById("svg-container");
|
|
520
|
+
const svgStats = document.getElementById("svg-stats");
|
|
521
|
+
const workspace = document.getElementById("workspace");
|
|
522
|
+
const editorResizer = document.getElementById("editor-resizer");
|
|
523
|
+
const viewButtons = {
|
|
524
|
+
svg: document.getElementById("view-svg"),
|
|
525
|
+
dom: document.getElementById("view-dom"),
|
|
526
|
+
split: document.getElementById("view-split"),
|
|
527
|
+
};
|
|
528
|
+
const minEditorWidth = 280;
|
|
529
|
+
|
|
530
|
+
function clampEditorWidth(nextWidth) {
|
|
531
|
+
const maxEditorWidth = Math.max(minEditorWidth, Math.floor(window.innerWidth * 0.72));
|
|
532
|
+
return Math.min(Math.max(nextWidth, minEditorWidth), maxEditorWidth);
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
function setEditorWidth(nextWidth) {
|
|
536
|
+
const clampedWidth = clampEditorWidth(nextWidth);
|
|
537
|
+
document.documentElement.style.setProperty("--editor-width", `${clampedWidth}px`);
|
|
538
|
+
localStorage.setItem("zenuml-workbench-editor-width", String(clampedWidth));
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
function setViewMode(mode) {
|
|
542
|
+
const validMode = ["svg", "dom", "split"].includes(mode) ? mode : "svg";
|
|
543
|
+
workspace.classList.remove("mode-svg", "mode-dom", "mode-split");
|
|
544
|
+
workspace.classList.add(`mode-${validMode}`);
|
|
545
|
+
|
|
546
|
+
Object.entries(viewButtons).forEach(([key, button]) => {
|
|
547
|
+
button.classList.toggle("is-active", key === validMode);
|
|
548
|
+
});
|
|
549
|
+
|
|
550
|
+
localStorage.setItem("zenuml-workbench-view", validMode);
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
const updateHtmlPreview = debounce((content) => {
|
|
148
554
|
const config = createConfig({
|
|
149
555
|
onContentChange: (code) => editor.setValue(code),
|
|
150
556
|
});
|
|
@@ -154,15 +560,83 @@
|
|
|
154
560
|
.child({ name: "index.html" })
|
|
155
561
|
.debug("render resolved", r);
|
|
156
562
|
});
|
|
157
|
-
},
|
|
563
|
+
}, 400);
|
|
564
|
+
|
|
565
|
+
const updateSvgPreview = debounce((content) => {
|
|
566
|
+
try {
|
|
567
|
+
const t0 = performance.now();
|
|
568
|
+
const result = renderToSvg(content);
|
|
569
|
+
const dt = (performance.now() - t0).toFixed(1);
|
|
570
|
+
|
|
571
|
+
if (!result.svg || result.width === 0 || result.height === 0) {
|
|
572
|
+
svgContainer.innerHTML = `
|
|
573
|
+
<div class="svg-error">
|
|
574
|
+
Native SVG output is empty for the current input.
|
|
575
|
+
</div>
|
|
576
|
+
`;
|
|
577
|
+
svgStats.textContent = "SVG: empty";
|
|
578
|
+
return;
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
svgContainer.innerHTML = result.svg;
|
|
582
|
+
svgStats.textContent = `SVG ${result.width}x${result.height} | ${dt}ms`;
|
|
583
|
+
} catch (error) {
|
|
584
|
+
console.error("Failed to render native SVG preview:", error);
|
|
585
|
+
svgContainer.innerHTML = `
|
|
586
|
+
<div class="svg-error">
|
|
587
|
+
Native SVG preview failed. Check the console for details.
|
|
588
|
+
</div>
|
|
589
|
+
`;
|
|
590
|
+
svgStats.textContent = "SVG: error";
|
|
591
|
+
}
|
|
592
|
+
}, 180);
|
|
593
|
+
|
|
594
|
+
Object.entries(viewButtons).forEach(([mode, button]) => {
|
|
595
|
+
button.addEventListener("click", () => setViewMode(mode));
|
|
596
|
+
});
|
|
597
|
+
|
|
598
|
+
editorResizer.addEventListener("pointerdown", (event) => {
|
|
599
|
+
if (window.innerWidth <= 1024) {
|
|
600
|
+
return;
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
event.preventDefault();
|
|
604
|
+
workspace.classList.add("is-resizing");
|
|
605
|
+
editorResizer.setPointerCapture(event.pointerId);
|
|
606
|
+
|
|
607
|
+
const onPointerMove = (moveEvent) => {
|
|
608
|
+
setEditorWidth(moveEvent.clientX - workspace.getBoundingClientRect().left);
|
|
609
|
+
};
|
|
610
|
+
|
|
611
|
+
const stopResize = () => {
|
|
612
|
+
workspace.classList.remove("is-resizing");
|
|
613
|
+
editorResizer.removeEventListener("pointermove", onPointerMove);
|
|
614
|
+
editorResizer.removeEventListener("pointerup", stopResize);
|
|
615
|
+
editorResizer.removeEventListener("pointercancel", stopResize);
|
|
616
|
+
};
|
|
617
|
+
|
|
618
|
+
editorResizer.addEventListener("pointermove", onPointerMove);
|
|
619
|
+
editorResizer.addEventListener("pointerup", stopResize);
|
|
620
|
+
editorResizer.addEventListener("pointercancel", stopResize);
|
|
621
|
+
});
|
|
622
|
+
|
|
623
|
+
window.addEventListener("resize", () => {
|
|
624
|
+
const savedWidth = Number(localStorage.getItem("zenuml-workbench-editor-width"));
|
|
625
|
+
if (savedWidth) {
|
|
626
|
+
setEditorWidth(savedWidth);
|
|
627
|
+
}
|
|
628
|
+
});
|
|
158
629
|
|
|
159
630
|
editor.on("change", function (cm) {
|
|
631
|
+
const content = cm.getValue();
|
|
632
|
+
updateSvgPreview(content);
|
|
633
|
+
|
|
160
634
|
waitUntil(
|
|
161
635
|
() => window.zenUml,
|
|
162
636
|
() => {
|
|
163
|
-
|
|
637
|
+
updateHtmlPreview(content);
|
|
164
638
|
// Save to localStorage
|
|
165
|
-
localStorage.setItem("zenuml-cm-code",
|
|
639
|
+
localStorage.setItem("zenuml-cm-code", content);
|
|
166
640
|
},
|
|
167
641
|
);
|
|
168
642
|
});
|
|
@@ -226,6 +700,27 @@ WebApp --> Customer: Order confirmation`,
|
|
|
226
700
|
} else {
|
|
227
701
|
editor.setValue(examples.basic);
|
|
228
702
|
}
|
|
703
|
+
|
|
704
|
+
const savedEditorWidth = Number(localStorage.getItem("zenuml-workbench-editor-width"));
|
|
705
|
+
if (savedEditorWidth) {
|
|
706
|
+
setEditorWidth(savedEditorWidth);
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
setViewMode(localStorage.getItem("zenuml-workbench-view") || "svg");
|
|
710
|
+
</script>
|
|
711
|
+
<script type="module">
|
|
712
|
+
const params = new URLSearchParams(location.search);
|
|
713
|
+
const wp = params.get("WIDTH_PROVIDER") || import.meta.env.VITE_WIDTH_PROVIDER || "browser";
|
|
714
|
+
const toggle = document.getElementById("width-provider-toggle");
|
|
715
|
+
const isCanvas = wp === "canvas";
|
|
716
|
+
toggle.textContent = "WIDTH: " + wp;
|
|
717
|
+
toggle.classList.toggle("is-active", isCanvas);
|
|
718
|
+
toggle.title = "Click to switch to " + (isCanvas ? "browser" : "canvas");
|
|
719
|
+
toggle.addEventListener("click", () => {
|
|
720
|
+
const next = isCanvas ? "browser" : "canvas";
|
|
721
|
+
params.set("WIDTH_PROVIDER", next);
|
|
722
|
+
location.search = params.toString();
|
|
723
|
+
});
|
|
229
724
|
</script>
|
|
230
725
|
<script type="module" src="/src/main.tsx"></script>
|
|
231
726
|
</body>
|