@shibayama/pdgkit 0.1.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/dist/index.cjs ADDED
@@ -0,0 +1,3539 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/index.ts
31
+ var src_exports = {};
32
+ __export(src_exports, {
33
+ OP_TABLE: () => OP_TABLE,
34
+ PATTERN_LABEL: () => PATTERN_LABEL,
35
+ PATTERN_SOURCE: () => PATTERN_SOURCE,
36
+ SAMPLES: () => SAMPLES,
37
+ SAMPLE_ORDER: () => SAMPLE_ORDER,
38
+ VERSION: () => VERSION,
39
+ chooseLabelPlacement: () => chooseLabelPlacement,
40
+ computeContentBox: () => computeContentBox,
41
+ estimateTextWidth: () => estimateTextWidth,
42
+ installDomShim: () => installDomShim,
43
+ layout: () => layout,
44
+ loadAuthoringGuide: () => loadAuthoringGuide,
45
+ parse: () => parse,
46
+ refsToCsv: () => refsToCsv,
47
+ refsToMarkdown: () => refsToMarkdown,
48
+ render: () => render,
49
+ renderToJpeg: () => renderToJpeg,
50
+ renderToPdf: () => renderToPdf,
51
+ renderToPng: () => renderToPng,
52
+ renderToPptx: () => renderToPptx,
53
+ renderToSvg: () => renderToSvg,
54
+ serializeSvg: () => serializeSvg,
55
+ splitBilingual: () => splitBilingual,
56
+ stripComment: () => stripComment,
57
+ svgDisplayDimensions: () => svgDisplayDimensions,
58
+ toSvgString: () => toSvgString,
59
+ validate: () => validate
60
+ });
61
+ module.exports = __toCommonJS(src_exports);
62
+
63
+ // src/core/parser.ts
64
+ var ID_PATTERN = /[A-Za-z0-9_*]+/.source;
65
+ var OP_TABLE = [
66
+ { lit: "<->", kind: "bidir" },
67
+ { lit: "=>", kind: "thick" },
68
+ { lit: "->", kind: "arrow" },
69
+ { lit: "<-", kind: "arrow", reverse: true },
70
+ { lit: ".>", kind: "dashed-arrow" },
71
+ { lit: "..", kind: "dashed" },
72
+ { lit: "-", kind: "line" }
73
+ ];
74
+ var DEF_RE = new RegExp(`^(${ID_PATTERN})\\s*=(?!>)\\s*(.*)$`);
75
+ var CONN_RE = new RegExp(
76
+ `^(${ID_PATTERN})\\s+(<->|=>|->|<-|\\.>|\\.\\.|-)\\s+(${ID_PATTERN})\\s*(?::\\s*(.*))?$`
77
+ );
78
+ var CONT_RE = new RegExp(`^(${ID_PATTERN})\\s*:\\s*(.+)$`);
79
+ function parse(source) {
80
+ const doc = {
81
+ nodes: /* @__PURE__ */ new Map(),
82
+ containments: [],
83
+ edges: [],
84
+ diagnostics: [],
85
+ kind: "block"
86
+ };
87
+ const lines = source.split(/\r?\n/);
88
+ for (let i = 0; i < lines.length; i++) {
89
+ const raw = stripComment(lines[i]);
90
+ const line = raw.trim();
91
+ if (!line) continue;
92
+ if (handleDef(line, i + 1, doc)) continue;
93
+ if (handleConn(line, i + 1, doc)) continue;
94
+ if (handleCont(line, i + 1, doc)) continue;
95
+ doc.diagnostics.push({
96
+ severity: "error",
97
+ line: i + 1,
98
+ col: 1,
99
+ message: `\u69CB\u6587\u4E0D\u660E: "${line}"`
100
+ });
101
+ }
102
+ doc.kind = inferKind(doc);
103
+ return doc;
104
+ }
105
+ function handleDef(line, lineNum, doc) {
106
+ const m = line.match(DEF_RE);
107
+ if (!m) return false;
108
+ const id = m[1];
109
+ const tail = m[2];
110
+ const label = splitBilingual(tail);
111
+ const existing = doc.nodes.get(id);
112
+ if (existing) {
113
+ doc.diagnostics.push({
114
+ severity: "warning",
115
+ line: lineNum,
116
+ col: 1,
117
+ message: `\u7B26\u53F7 "${id}" \u306F\u518D\u5B9A\u7FA9\u3055\u308C\u307E\u3057\u305F`
118
+ });
119
+ }
120
+ doc.nodes.set(id, {
121
+ id,
122
+ label,
123
+ implicit: false
124
+ });
125
+ return true;
126
+ }
127
+ function handleConn(line, lineNum, doc) {
128
+ const m = line.match(CONN_RE);
129
+ if (!m) return false;
130
+ const opLit = m[2];
131
+ const entry = OP_TABLE.find((o) => o.lit === opLit);
132
+ const from = entry.reverse ? m[3] : m[1];
133
+ const to = entry.reverse ? m[1] : m[3];
134
+ const labelText = m[4] ?? "";
135
+ const edge = {
136
+ from,
137
+ to,
138
+ op: entry.kind,
139
+ label: labelText.trim() ? splitBilingual(labelText) : void 0,
140
+ line: lineNum
141
+ };
142
+ doc.edges.push(edge);
143
+ ensureNode(doc, from);
144
+ ensureNode(doc, to);
145
+ return true;
146
+ }
147
+ function handleCont(line, lineNum, doc) {
148
+ const m = line.match(CONT_RE);
149
+ if (!m) return false;
150
+ const parent = m[1];
151
+ const rest = m[2].trim();
152
+ const children = rest.split(/\s+/).filter(Boolean);
153
+ const idRe = new RegExp(`^${ID_PATTERN}$`);
154
+ for (const c of children) {
155
+ if (!idRe.test(c)) {
156
+ doc.diagnostics.push({
157
+ severity: "error",
158
+ line: lineNum,
159
+ col: 1,
160
+ message: `\u5305\u542B\u306E\u5B50\u3068\u3057\u3066\u4E0D\u6B63\u306A\u30C8\u30FC\u30AF\u30F3: "${c}"`
161
+ });
162
+ return true;
163
+ }
164
+ }
165
+ doc.containments.push({ parent, children, line: lineNum });
166
+ ensureNode(doc, parent);
167
+ for (const c of children) ensureNode(doc, c);
168
+ return true;
169
+ }
170
+ function ensureNode(doc, id) {
171
+ let n = doc.nodes.get(id);
172
+ if (n) return n;
173
+ n = { id, label: {}, implicit: true };
174
+ doc.nodes.set(id, n);
175
+ return n;
176
+ }
177
+ function stripComment(line) {
178
+ let inQuote = false;
179
+ for (let i = 0; i < line.length; i++) {
180
+ const c = line[i];
181
+ if (c === '"') inQuote = !inQuote;
182
+ else if (!inQuote && c === "#") return line.slice(0, i);
183
+ }
184
+ return line;
185
+ }
186
+ function splitBilingual(text) {
187
+ const s = text.trim();
188
+ if (!s) return {};
189
+ const slashIdx = findBilingualSeparator(s);
190
+ if (slashIdx === -1) {
191
+ return { ja: stripQuotes(s) };
192
+ }
193
+ return {
194
+ ja: stripQuotes(s.slice(0, slashIdx).trim()),
195
+ en: stripQuotes(s.slice(slashIdx + 1).trim())
196
+ };
197
+ }
198
+ function findBilingualSeparator(s) {
199
+ let inQuote = false;
200
+ for (let i = 0; i < s.length; i++) {
201
+ if (s[i] === '"') {
202
+ inQuote = !inQuote;
203
+ continue;
204
+ }
205
+ if (!inQuote && s[i] === "/" && isSpace(s[i - 1]) && isSpace(s[i + 1])) return i;
206
+ }
207
+ return -1;
208
+ }
209
+ function isSpace(ch) {
210
+ return ch === " " || ch === " ";
211
+ }
212
+ function stripQuotes(s) {
213
+ if (s.length >= 2 && s.startsWith('"') && s.endsWith('"')) {
214
+ return s.slice(1, -1);
215
+ }
216
+ return s;
217
+ }
218
+ function inferKind(doc) {
219
+ if (doc.containments.length > 0) return "block";
220
+ for (const n of doc.nodes.values()) {
221
+ const ja = n.label.ja ?? "";
222
+ const en = n.label.en ?? "";
223
+ if (ja.endsWith("?") || en.endsWith("?")) return "flow";
224
+ }
225
+ if (doc.nodes.has("*")) return "state";
226
+ const pairs = /* @__PURE__ */ new Set();
227
+ for (const e of doc.edges) {
228
+ if (e.op === "bidir") return "seq";
229
+ const fwd = `${e.from}|${e.to}`;
230
+ const rev = `${e.to}|${e.from}`;
231
+ if (pairs.has(rev)) return "seq";
232
+ pairs.add(fwd);
233
+ }
234
+ return "flow";
235
+ }
236
+
237
+ // src/core/layout.ts
238
+ var NODE_W = 36;
239
+ var NODE_H = 14;
240
+ var PAD = 8;
241
+ var GRID_GAP = 24;
242
+ var TITLE_H = 5;
243
+ var PARENT_EDGE_LANE_H = 14;
244
+ var MARGIN = 8;
245
+ var ROOT_GAP = MARGIN * 4;
246
+ var ROUTE_GAP = PAD * 1.5;
247
+ var OBSTACLE_PAD = 0.8;
248
+ var BORDER_CLEARANCE = 4;
249
+ var ARROW_TERMINAL_CLEARANCE = 4.6;
250
+ var THICK_ARROW_TERMINAL_CLEARANCE = 5.4;
251
+ var VERTICAL_PORT_RATIO = 0.25;
252
+ var PORT_STUB = 6;
253
+ var MAX_ROUTE_LANES = 18;
254
+ var EPS = 1e-3;
255
+ function layout(doc) {
256
+ switch (doc.kind) {
257
+ case "block":
258
+ return layoutBlock(doc);
259
+ case "flow":
260
+ return layoutFlow(doc);
261
+ case "state":
262
+ return layoutState(doc);
263
+ case "seq":
264
+ return layoutSeq(doc);
265
+ }
266
+ }
267
+ function layoutBlock(doc) {
268
+ const childMap = /* @__PURE__ */ new Map();
269
+ for (const c of doc.containments) childMap.set(c.parent, c.children);
270
+ const parentMap = /* @__PURE__ */ new Map();
271
+ for (const c of doc.containments) {
272
+ for (const child of c.children) parentMap.set(child, c.parent);
273
+ }
274
+ const childIds = /* @__PURE__ */ new Set();
275
+ for (const cs of childMap.values()) for (const c of cs) childIds.add(c);
276
+ const allIds = [...doc.nodes.keys()];
277
+ const roots = allIds.filter((id) => !childIds.has(id));
278
+ const placed = [];
279
+ const positions = /* @__PURE__ */ new Map();
280
+ function size(id) {
281
+ const children = childMap.get(id);
282
+ if (!children || children.length === 0) return { w: NODE_W, h: NODE_H };
283
+ const sizes = children.map(size);
284
+ if (arrangementOf(id) === "grid") {
285
+ const cols = gridColsOf(id, children);
286
+ const colW = Math.max(...sizes.map((s) => s.w));
287
+ const rowHeights = gridRowHeights(sizes, cols);
288
+ return {
289
+ w: cols * colW + 2 * PAD + (cols - 1) * GRID_GAP,
290
+ h: titleHeightOf(id) + rowHeights.reduce((sum, h) => sum + h, 0) + 2 * PAD + Math.max(0, rowHeights.length - 1) * GRID_GAP
291
+ };
292
+ }
293
+ const maxW = Math.max(...sizes.map((s) => s.w));
294
+ const totH = sizes.reduce((a, b) => a + b.h, 0);
295
+ return {
296
+ w: maxW + 2 * PAD,
297
+ h: titleHeightOf(id) + totH + 2 * PAD + (children.length - 1) * GRID_GAP
298
+ };
299
+ }
300
+ function arrangementOf(id) {
301
+ const children = childMap.get(id);
302
+ if (!children || children.length <= 2) return "stack";
303
+ if (hasParentToChildEdges(id, children)) return "grid";
304
+ return hasLinearChildFlow(children, doc.edges, childMap) ? "stack" : "grid";
305
+ }
306
+ function gridColsOf(id, children) {
307
+ if (hasParentToChildEdges(id, children)) return Math.min(children.length, 4);
308
+ if (children.length >= 7) return 4;
309
+ if (children.length >= 5) return 3;
310
+ return 2;
311
+ }
312
+ function gridRowHeights(sizes, cols) {
313
+ const rows = Math.ceil(sizes.length / cols);
314
+ const heights = [];
315
+ for (let row = 0; row < rows; row++) {
316
+ const rowSizes = sizes.slice(row * cols, row * cols + cols);
317
+ heights.push(Math.max(...rowSizes.map((s) => s.h)));
318
+ }
319
+ return heights;
320
+ }
321
+ function titleHeightOf(id) {
322
+ const children = childMap.get(id);
323
+ return children && hasParentToChildEdges(id, children) ? TITLE_H + PARENT_EDGE_LANE_H : TITLE_H;
324
+ }
325
+ function hasParentToChildEdges(id, children) {
326
+ return doc.edges.some((edge) => edge.from === id && children.some((child) => child === edge.to || containsDescendant(child, edge.to, childMap)));
327
+ }
328
+ function place(id, ox, oy) {
329
+ const s = size(id);
330
+ positions.set(id, { x: ox, y: oy, w: s.w, h: s.h });
331
+ const node = doc.nodes.get(id);
332
+ const children = childMap.get(id);
333
+ if (!children || children.length === 0) {
334
+ placed.push({
335
+ id,
336
+ x: ox,
337
+ y: oy,
338
+ w: s.w,
339
+ h: s.h,
340
+ label: node?.label ?? {},
341
+ shape: "box",
342
+ isContainer: false
343
+ });
344
+ return;
345
+ }
346
+ const sizes = children.map(size);
347
+ if (arrangementOf(id) === "grid") {
348
+ const cols = gridColsOf(id, children);
349
+ const colW = Math.max(...sizes.map((s2) => s2.w));
350
+ const rowHeights = gridRowHeights(sizes, cols);
351
+ const rowTops = [];
352
+ let rowTop = oy + titleHeightOf(id) + PAD;
353
+ for (let row = 0; row < rowHeights.length; row++) {
354
+ rowTops[row] = rowTop;
355
+ rowTop += rowHeights[row] + GRID_GAP;
356
+ }
357
+ for (let i = 0; i < children.length; i++) {
358
+ const r = Math.floor(i / cols);
359
+ const cc = i % cols;
360
+ const cx = ox + PAD + cc * (colW + GRID_GAP) + (colW - sizes[i].w) / 2;
361
+ const cy = rowTops[r] + (rowHeights[r] - sizes[i].h) / 2;
362
+ place(children[i], cx, cy);
363
+ }
364
+ alignGridRows(children, sizes, rowHeights, rowTops, cols, childMap, doc.edges, positions, placed);
365
+ } else {
366
+ const maxW = Math.max(...sizes.map((s2) => s2.w));
367
+ let yy = oy + titleHeightOf(id) + PAD;
368
+ for (let i = 0; i < children.length; i++) {
369
+ const cx = ox + PAD + (maxW - sizes[i].w) / 2;
370
+ place(children[i], cx, yy);
371
+ yy += sizes[i].h + GRID_GAP;
372
+ }
373
+ }
374
+ placed.push({
375
+ id,
376
+ x: ox,
377
+ y: oy,
378
+ w: s.w,
379
+ h: s.h,
380
+ label: node?.label ?? {},
381
+ shape: "box",
382
+ isContainer: true
383
+ });
384
+ }
385
+ let cur = MARGIN;
386
+ for (const r of roots) {
387
+ const previousIds = new Set(positions.keys());
388
+ place(r, cur, MARGIN);
389
+ const subtreeIds = collectSubtreeIds(r, childMap);
390
+ const dy = rootAlignmentDelta(r, subtreeIds, previousIds, doc.edges, positions);
391
+ if (Math.abs(dy) >= EPS) shiftSubtree(subtreeIds, dy, positions, placed);
392
+ const sz = size(r);
393
+ cur += sz.w + ROOT_GAP;
394
+ }
395
+ const edges = makeBlockEdges(doc.edges, positions, placed, parentMap);
396
+ placed.sort((a, b) => {
397
+ if (a.isContainer !== b.isContainer) return a.isContainer ? -1 : 1;
398
+ if (a.isContainer && b.isContainer) {
399
+ const depthDiff = depthOf(a.id, parentMap) - depthOf(b.id, parentMap);
400
+ if (depthDiff !== 0) return depthDiff;
401
+ return b.w * b.h - a.w * a.h;
402
+ }
403
+ return a.y - b.y || a.x - b.x;
404
+ });
405
+ const boxes = [...positions.values()];
406
+ const edgePoints = edges.flatMap((edge) => edge.points);
407
+ const maxBoxX = boxes.length ? Math.max(...boxes.map((b) => b.x + b.w)) : MARGIN;
408
+ const maxBoxY = boxes.length ? Math.max(...boxes.map((b) => b.y + b.h)) : MARGIN;
409
+ const maxEdgeX = edgePoints.length ? Math.max(...edgePoints.map(([x]) => x)) : MARGIN;
410
+ const maxEdgeY = edgePoints.length ? Math.max(...edgePoints.map(([, y]) => y)) : MARGIN;
411
+ const width = Math.max(maxBoxX, maxEdgeX) + MARGIN;
412
+ const height = Math.max(maxBoxY, maxEdgeY) + MARGIN;
413
+ return { nodes: placed, edges, width, height, kind: "block" };
414
+ }
415
+ function layoutFlow(doc) {
416
+ const ids = [...doc.nodes.keys()];
417
+ const { byRank } = computeRanks(doc);
418
+ function shapeOf(id) {
419
+ const n = doc.nodes.get(id);
420
+ const ja = n?.label.ja ?? "";
421
+ const en = n?.label.en ?? "";
422
+ if (ja.endsWith("?") || en.endsWith("?")) return "diamond";
423
+ const inc = doc.edges.filter((e) => e.to === id).length;
424
+ const out = doc.edges.filter((e) => e.from === id).length;
425
+ if (inc === 0 || out === 0) return "round";
426
+ return "box";
427
+ }
428
+ const V_GAP = 14;
429
+ const H_GAP = 10;
430
+ const positions = /* @__PURE__ */ new Map();
431
+ const sortedRanks = [...byRank.keys()].sort((a, b) => a - b);
432
+ let y = MARGIN;
433
+ let maxX = 0;
434
+ for (const r of sortedRanks) {
435
+ const lane = byRank.get(r);
436
+ const widths = lane.map((id) => shapeOf(id) === "diamond" ? NODE_W * 1.2 : NODE_W);
437
+ const totalW = widths.reduce((a, b) => a + b, 0) + (lane.length - 1) * H_GAP;
438
+ let x = MARGIN;
439
+ const canvasW = Math.max(totalW + 2 * MARGIN, 200);
440
+ x = (canvasW - totalW) / 2;
441
+ for (let i = 0; i < lane.length; i++) {
442
+ positions.set(lane[i], { x, y, w: widths[i], h: NODE_H });
443
+ x += widths[i] + H_GAP;
444
+ }
445
+ if (x > maxX) maxX = x;
446
+ y += NODE_H + V_GAP;
447
+ }
448
+ for (const id of ids) {
449
+ if (!positions.has(id)) {
450
+ positions.set(id, { x: MARGIN, y, w: NODE_W, h: NODE_H });
451
+ y += NODE_H + V_GAP;
452
+ }
453
+ }
454
+ const placed = [];
455
+ for (const [id, b] of positions) {
456
+ placed.push({
457
+ id,
458
+ ...b,
459
+ label: doc.nodes.get(id)?.label ?? {},
460
+ shape: shapeOf(id),
461
+ isContainer: false
462
+ });
463
+ }
464
+ const edges = makeEdges(doc.edges, positions);
465
+ return { nodes: placed, edges, width: maxX + MARGIN, height: y + MARGIN, kind: "flow" };
466
+ }
467
+ function layoutState(doc) {
468
+ const { byRank } = computeRanks(doc);
469
+ function shapeOf(id) {
470
+ if (id === "*") return "circle";
471
+ return "round";
472
+ }
473
+ const V_GAP = 14, H_GAP = 10;
474
+ const positions = /* @__PURE__ */ new Map();
475
+ const sortedRanks = [...byRank.keys()].sort((a, b) => a - b);
476
+ let y = MARGIN;
477
+ let maxX = 0;
478
+ for (const r of sortedRanks) {
479
+ const lane = byRank.get(r);
480
+ const widths = lane.map((id) => shapeOf(id) === "circle" ? 6 : NODE_W);
481
+ const heights = lane.map((id) => shapeOf(id) === "circle" ? 6 : NODE_H);
482
+ const totalW = widths.reduce((a, b) => a + b, 0) + (lane.length - 1) * H_GAP;
483
+ const canvasW = Math.max(totalW + 2 * MARGIN, 200);
484
+ let x = (canvasW - totalW) / 2;
485
+ for (let i = 0; i < lane.length; i++) {
486
+ positions.set(lane[i], { x, y: y + (NODE_H - heights[i]) / 2, w: widths[i], h: heights[i] });
487
+ x += widths[i] + H_GAP;
488
+ }
489
+ if (x > maxX) maxX = x;
490
+ y += NODE_H + V_GAP;
491
+ }
492
+ for (const id of doc.nodes.keys()) {
493
+ if (!positions.has(id)) {
494
+ positions.set(id, { x: MARGIN, y, w: NODE_W, h: NODE_H });
495
+ y += NODE_H + V_GAP;
496
+ }
497
+ }
498
+ const placed = [];
499
+ for (const [id, b] of positions) {
500
+ placed.push({
501
+ id,
502
+ ...b,
503
+ label: doc.nodes.get(id)?.label ?? {},
504
+ shape: shapeOf(id),
505
+ isContainer: false
506
+ });
507
+ }
508
+ const edges = makeEdges(doc.edges, positions);
509
+ return { nodes: placed, edges, width: maxX + MARGIN, height: y + MARGIN, kind: "state" };
510
+ }
511
+ function layoutSeq(doc) {
512
+ const seen = /* @__PURE__ */ new Set();
513
+ const actors = [];
514
+ for (const e of doc.edges) {
515
+ for (const id of [e.from, e.to]) {
516
+ if (!seen.has(id)) {
517
+ seen.add(id);
518
+ actors.push(id);
519
+ }
520
+ }
521
+ }
522
+ for (const id of doc.nodes.keys()) {
523
+ if (!seen.has(id)) {
524
+ seen.add(id);
525
+ actors.push(id);
526
+ }
527
+ }
528
+ const ACTOR_W = 40, ACTOR_H = 12, COL_GAP = 28, MSG_GAP = 12;
529
+ const xOf = /* @__PURE__ */ new Map();
530
+ const placed = [];
531
+ let x = MARGIN;
532
+ for (const id of actors) {
533
+ xOf.set(id, x + ACTOR_W / 2);
534
+ placed.push({
535
+ id,
536
+ x,
537
+ y: MARGIN,
538
+ w: ACTOR_W,
539
+ h: ACTOR_H,
540
+ label: doc.nodes.get(id)?.label ?? {},
541
+ shape: "actor",
542
+ isContainer: false
543
+ });
544
+ x += ACTOR_W + COL_GAP;
545
+ }
546
+ let y = MARGIN + ACTOR_H + MSG_GAP;
547
+ const msgEdges = [];
548
+ for (const e of doc.edges) {
549
+ const xa = xOf.get(e.from);
550
+ const xb = xOf.get(e.to);
551
+ if (xa === void 0 || xb === void 0) continue;
552
+ msgEdges.push({
553
+ from: e.from,
554
+ to: e.to,
555
+ points: [[xa, y], [xb, y]],
556
+ label: e.label,
557
+ op: e.op
558
+ });
559
+ y += MSG_GAP;
560
+ }
561
+ const lifelines = actors.map((id) => ({
562
+ from: id,
563
+ to: id,
564
+ points: [[xOf.get(id), MARGIN + ACTOR_H], [xOf.get(id), y + MSG_GAP]],
565
+ op: "dashed",
566
+ isLifeline: true
567
+ }));
568
+ return {
569
+ nodes: placed,
570
+ edges: [...lifelines, ...msgEdges],
571
+ width: x,
572
+ height: y + MARGIN * 2,
573
+ kind: "seq"
574
+ };
575
+ }
576
+ function computeRanks(doc) {
577
+ const ids = [...doc.nodes.keys()];
578
+ const incoming = /* @__PURE__ */ new Map();
579
+ for (const id of ids) incoming.set(id, 0);
580
+ for (const e of doc.edges) incoming.set(e.to, (incoming.get(e.to) ?? 0) + 1);
581
+ const outgoing = /* @__PURE__ */ new Map();
582
+ for (const e of doc.edges) {
583
+ if (!outgoing.has(e.from)) outgoing.set(e.from, []);
584
+ outgoing.get(e.from).push(e.to);
585
+ }
586
+ let sources = ids.filter((id) => incoming.get(id) === 0);
587
+ if (sources.length === 0) {
588
+ sources = ids.includes("*") ? ["*"] : ids.length ? [ids[0]] : [];
589
+ }
590
+ const rank = /* @__PURE__ */ new Map();
591
+ const visited = /* @__PURE__ */ new Set();
592
+ for (const s of sources) {
593
+ rank.set(s, 0);
594
+ visited.add(s);
595
+ }
596
+ let frontier = [...sources];
597
+ while (frontier.length) {
598
+ const next = [];
599
+ for (const n of frontier) {
600
+ const r = rank.get(n);
601
+ for (const m of outgoing.get(n) ?? []) {
602
+ if (!visited.has(m)) {
603
+ rank.set(m, r + 1);
604
+ visited.add(m);
605
+ next.push(m);
606
+ }
607
+ }
608
+ }
609
+ frontier = next;
610
+ }
611
+ let maxRank = 0;
612
+ for (const r of rank.values()) if (r > maxRank) maxRank = r;
613
+ for (const id of ids) {
614
+ if (!visited.has(id)) {
615
+ maxRank++;
616
+ rank.set(id, maxRank);
617
+ visited.add(id);
618
+ }
619
+ }
620
+ const byRank = /* @__PURE__ */ new Map();
621
+ for (const [id, r] of rank) {
622
+ if (!byRank.has(r)) byRank.set(r, []);
623
+ byRank.get(r).push(id);
624
+ }
625
+ return { byRank };
626
+ }
627
+ function makeEdges(srcEdges, positions) {
628
+ return srcEdges.map((e) => {
629
+ const a = positions.get(e.from);
630
+ const b = positions.get(e.to);
631
+ if (!a || !b) {
632
+ return { from: e.from, to: e.to, points: [], label: e.label, op: e.op };
633
+ }
634
+ return {
635
+ from: e.from,
636
+ to: e.to,
637
+ points: orthogonalRoute(a, b),
638
+ label: e.label,
639
+ op: e.op
640
+ };
641
+ });
642
+ }
643
+ function hasLinearChildFlow(children, edges, childMap) {
644
+ const childSet = new Set(children);
645
+ const pairs = /* @__PURE__ */ new Set();
646
+ const incoming = /* @__PURE__ */ new Map();
647
+ const outgoing = /* @__PURE__ */ new Map();
648
+ for (const child of children) {
649
+ incoming.set(child, 0);
650
+ outgoing.set(child, 0);
651
+ }
652
+ for (const edge of edges) {
653
+ const from = topChildFor(edge.from, childSet, childMap);
654
+ const to = topChildFor(edge.to, childSet, childMap);
655
+ if (!from || !to || from === to) continue;
656
+ const key = `${from}|${to}`;
657
+ if (pairs.has(key)) continue;
658
+ pairs.add(key);
659
+ outgoing.set(from, (outgoing.get(from) ?? 0) + 1);
660
+ incoming.set(to, (incoming.get(to) ?? 0) + 1);
661
+ }
662
+ if (pairs.size < children.length - 1) return false;
663
+ let starts = 0;
664
+ let ends = 0;
665
+ for (const child of children) {
666
+ const inc = incoming.get(child) ?? 0;
667
+ const out = outgoing.get(child) ?? 0;
668
+ if (inc > 1 || out > 1) return false;
669
+ if (inc === 0 && out === 1) starts++;
670
+ if (inc === 1 && out === 0) ends++;
671
+ }
672
+ return starts === 1 && ends === 1;
673
+ }
674
+ function topChildFor(id, childSet, childMap) {
675
+ if (childSet.has(id)) return id;
676
+ for (const child of childSet) {
677
+ if (containsDescendant(child, id, childMap)) return child;
678
+ }
679
+ return void 0;
680
+ }
681
+ function containsDescendant(ancestor, id, childMap) {
682
+ const children = childMap.get(ancestor);
683
+ if (!children) return false;
684
+ for (const child of children) {
685
+ if (child === id || containsDescendant(child, id, childMap)) return true;
686
+ }
687
+ return false;
688
+ }
689
+ function collectSubtreeIds(id, childMap) {
690
+ const ids = /* @__PURE__ */ new Set([id]);
691
+ for (const child of childMap.get(id) ?? []) {
692
+ for (const descendant of collectSubtreeIds(child, childMap)) ids.add(descendant);
693
+ }
694
+ return ids;
695
+ }
696
+ function alignGridRows(children, sizes, rowHeights, rowTops, cols, childMap, edges, positions, placed) {
697
+ const rows = Math.ceil(children.length / cols);
698
+ for (let row = 0; row < rows; row++) {
699
+ const rowChildren = children.slice(row * cols, row * cols + cols);
700
+ const rowTop = rowTops[row];
701
+ const rowH = rowHeights[row];
702
+ if (rowTop === void 0 || rowH === void 0) continue;
703
+ for (const child of rowChildren) {
704
+ const childIndex = children.indexOf(child);
705
+ const childSize = sizes[childIndex];
706
+ if (!childSize || childSize.h >= rowH - EPS) continue;
707
+ const subtreeIds = collectSubtreeIds(child, childMap);
708
+ const siblingIds = /* @__PURE__ */ new Set();
709
+ for (const sibling of rowChildren) {
710
+ if (sibling === child) continue;
711
+ for (const id of collectSubtreeIds(sibling, childMap)) siblingIds.add(id);
712
+ }
713
+ const deltas = [];
714
+ for (const edge of edges) {
715
+ const fromChild = subtreeIds.has(edge.from);
716
+ const toChild = subtreeIds.has(edge.to);
717
+ if (fromChild && siblingIds.has(edge.to)) {
718
+ addWeightedDelta(
719
+ deltas,
720
+ centerY(positions.get(edge.to)) - centerY(positions.get(edge.from)),
721
+ alignmentWeight(edge, false)
722
+ );
723
+ } else if (toChild && siblingIds.has(edge.from)) {
724
+ addWeightedDelta(
725
+ deltas,
726
+ centerY(positions.get(edge.from)) - centerY(positions.get(edge.to)),
727
+ alignmentWeight(edge, true)
728
+ );
729
+ }
730
+ }
731
+ if (deltas.length === 0) continue;
732
+ deltas.sort((a, b) => a - b);
733
+ const desired = deltas[Math.floor(deltas.length / 2)];
734
+ const box = positions.get(child);
735
+ if (!box) continue;
736
+ const minDy = rowTop - box.y;
737
+ const maxDy = rowTop + rowH - childSize.h - box.y;
738
+ const dy = Math.min(maxDy, Math.max(minDy, desired));
739
+ if (Math.abs(dy) >= EPS) shiftSubtree(subtreeIds, dy, positions, placed);
740
+ }
741
+ }
742
+ }
743
+ function rootAlignmentDelta(rootId, subtreeIds, previousIds, edges, positions) {
744
+ const deltas = [];
745
+ for (const edge of edges) {
746
+ const fromCurrent = subtreeIds.has(edge.from);
747
+ const toCurrent = subtreeIds.has(edge.to);
748
+ const fromPrevious = previousIds.has(edge.from);
749
+ const toPrevious = previousIds.has(edge.to);
750
+ if (fromCurrent && toPrevious) {
751
+ addWeightedDelta(
752
+ deltas,
753
+ centerY(positions.get(edge.to)) - centerY(positions.get(edge.from)),
754
+ alignmentWeight(edge, false)
755
+ );
756
+ } else if (toCurrent && fromPrevious) {
757
+ addWeightedDelta(
758
+ deltas,
759
+ centerY(positions.get(edge.from)) - centerY(positions.get(edge.to)),
760
+ alignmentWeight(edge, true)
761
+ );
762
+ }
763
+ }
764
+ if (deltas.length === 0) return 0;
765
+ deltas.sort((a, b) => a - b);
766
+ const desired = deltas[Math.floor(deltas.length / 2)];
767
+ const root = positions.get(rootId);
768
+ if (!root) return desired;
769
+ return Math.max(MARGIN - root.y, desired);
770
+ }
771
+ function addWeightedDelta(deltas, delta, weight) {
772
+ if (!Number.isFinite(delta)) return;
773
+ for (let i = 0; i < weight; i++) deltas.push(delta);
774
+ }
775
+ function alignmentWeight(edge, currentIsTarget) {
776
+ const feedback = edge.op === "dashed" || edge.op === "dashed-arrow";
777
+ return (feedback ? 1 : 3) + (currentIsTarget ? 1 : 0);
778
+ }
779
+ function centerY(box) {
780
+ return box ? box.y + box.h / 2 : Number.NaN;
781
+ }
782
+ function shiftSubtree(ids, dy, positions, placed) {
783
+ for (const id of ids) {
784
+ const box = positions.get(id);
785
+ if (box) box.y += dy;
786
+ }
787
+ for (const node of placed) {
788
+ if (ids.has(node.id)) node.y += dy;
789
+ }
790
+ }
791
+ function makeBlockEdges(srcEdges, positions, obstacles, parentMap) {
792
+ const containerIds = new Set(obstacles.filter((o) => o.isContainer).map((o) => o.id));
793
+ const plans = srcEdges.map((edge, index) => {
794
+ const a = positions.get(edge.from);
795
+ const b = positions.get(edge.to);
796
+ if (!a || !b) {
797
+ return { edge, index, endpointBoundaries: [], endpointInteriorBarriers: [] };
798
+ }
799
+ const routeA = routeEndpointBox(edge.from, edge.to, positions, parentMap, containerIds) ?? a;
800
+ const routeB = routeEndpointBox(edge.to, edge.from, positions, parentMap, containerIds) ?? b;
801
+ const endpointBoundaries = [
802
+ ...routeA === a ? [] : [a],
803
+ ...routeB === b ? [] : [b]
804
+ ];
805
+ const endpointInteriorBarriers = [
806
+ ...isExternalContainerEndpoint(edge.from, edge.to, parentMap, containerIds) ? [a] : [],
807
+ ...isExternalContainerEndpoint(edge.to, edge.from, parentMap, containerIds) ? [b] : []
808
+ ];
809
+ return {
810
+ edge,
811
+ index,
812
+ routeA,
813
+ routeB,
814
+ endpointBoundaries,
815
+ endpointInteriorBarriers,
816
+ bounds: commonRoutingBounds(edge.from, edge.to, parentMap, positions)
817
+ };
818
+ });
819
+ return routePlansSequentially(plans, obstacles, parentMap);
820
+ }
821
+ function routePlansSequentially(plans, baseObstacles, parentMap) {
822
+ const routed = [];
823
+ for (const plan of plans) {
824
+ const searchBox = routeSearchBox(plan);
825
+ const routedArrowGuards = routed.flatMap((edge, index) => arrowGuardObstaclesFor(edge, index));
826
+ const routedEdgeGuards = routed.flatMap((edge, index) => edgeGuardObstaclesFor(edge, index));
827
+ const extraArrowGuards = routedArrowGuards.filter((guard) => guard.edgeIndex !== plan.index && !sharesRouteEndpoint(guard, plan.edge) && (!searchBox || boxesOverlap(searchBox, guard)));
828
+ const extraEdgeGuards = routedEdgeGuards.filter((guard) => shouldUseEdgeGuard(guard, plan, searchBox));
829
+ routed.push(routeBlockPlan(plan, [...baseObstacles, ...extraArrowGuards, ...extraEdgeGuards], parentMap));
830
+ }
831
+ return routed;
832
+ }
833
+ function routeBlockPlan(plan, obstacles, parentMap) {
834
+ const { edge, routeA, routeB } = plan;
835
+ if (!routeA || !routeB) {
836
+ return { from: edge.from, to: edge.to, points: [], label: edge.label, op: edge.op };
837
+ }
838
+ return {
839
+ from: edge.from,
840
+ to: edge.to,
841
+ points: avoidObstaclesRoute(
842
+ routeA,
843
+ routeB,
844
+ edge.from,
845
+ edge.to,
846
+ obstacles,
847
+ parentMap,
848
+ plan.bounds,
849
+ plan.endpointBoundaries,
850
+ plan.endpointInteriorBarriers,
851
+ edge.op
852
+ ),
853
+ label: edge.label,
854
+ op: edge.op
855
+ };
856
+ }
857
+ function arrowGuardObstaclesFor(edge, edgeIndex) {
858
+ if (edge.isLifeline || edge.points.length < 2) return [];
859
+ const guards = [];
860
+ if (hasEndArrow(edge.op)) {
861
+ guards.push(makeArrowGuard(edge, edgeIndex, edge.points[edge.points.length - 1], "end"));
862
+ }
863
+ if (edge.op === "bidir") {
864
+ guards.push(makeArrowGuard(edge, edgeIndex, edge.points[0], "start"));
865
+ }
866
+ return guards;
867
+ }
868
+ function hasEndArrow(op) {
869
+ return op !== "line" && op !== "dashed";
870
+ }
871
+ function makeArrowGuard(edge, edgeIndex, tip, side) {
872
+ const half = edge.op === "thick" ? 5 : 4.2;
873
+ return {
874
+ id: `__arrow_guard_${edgeIndex}_${side}`,
875
+ x: tip[0] - half,
876
+ y: tip[1] - half,
877
+ w: half * 2,
878
+ h: half * 2,
879
+ isContainer: false,
880
+ edgeIndex,
881
+ edgeFrom: edge.from,
882
+ edgeTo: edge.to
883
+ };
884
+ }
885
+ function sharesRouteEndpoint(guard, edge) {
886
+ return guard.edgeFrom === edge.from || guard.edgeFrom === edge.to || guard.edgeTo === edge.from || guard.edgeTo === edge.to;
887
+ }
888
+ function edgeGuardObstaclesFor(edge, edgeIndex) {
889
+ if (edge.points.length < 2) return [];
890
+ const guards = [];
891
+ const clear = 2.2;
892
+ const trim = 1.2;
893
+ for (let i = 0; i < edge.points.length - 1; i++) {
894
+ const a = edge.points[i];
895
+ const b = edge.points[i + 1];
896
+ const len = segmentLength(a, b);
897
+ if (len <= trim * 2) continue;
898
+ if (Math.abs(a[0] - b[0]) < EPS) {
899
+ const y1 = Math.min(a[1], b[1]) + trim;
900
+ const y2 = Math.max(a[1], b[1]) - trim;
901
+ guards.push({
902
+ id: `__edge_guard_${edgeIndex}_${i}`,
903
+ x: a[0] - clear,
904
+ y: y1,
905
+ w: clear * 2,
906
+ h: y2 - y1,
907
+ isContainer: false,
908
+ edgeIndex,
909
+ edgeOp: edge.op,
910
+ orientation: "vertical"
911
+ });
912
+ } else if (Math.abs(a[1] - b[1]) < EPS) {
913
+ const x1 = Math.min(a[0], b[0]) + trim;
914
+ const x2 = Math.max(a[0], b[0]) - trim;
915
+ guards.push({
916
+ id: `__edge_guard_${edgeIndex}_${i}`,
917
+ x: x1,
918
+ y: a[1] - clear,
919
+ w: x2 - x1,
920
+ h: clear * 2,
921
+ isContainer: false,
922
+ edgeIndex,
923
+ edgeOp: edge.op,
924
+ orientation: "horizontal"
925
+ });
926
+ }
927
+ }
928
+ return guards;
929
+ }
930
+ function shouldUseEdgeGuard(guard, plan, searchBox) {
931
+ if (guard.edgeIndex === plan.index) return false;
932
+ return !searchBox || boxesOverlap(searchBox, guard);
933
+ }
934
+ function routeSearchBox(plan) {
935
+ if (!plan.routeA || !plan.routeB) return void 0;
936
+ const left = Math.min(plan.routeA.x, plan.routeB.x);
937
+ const top = Math.min(plan.routeA.y, plan.routeB.y);
938
+ const right = Math.max(plan.routeA.x + plan.routeA.w, plan.routeB.x + plan.routeB.w);
939
+ const bottom = Math.max(plan.routeA.y + plan.routeA.h, plan.routeB.y + plan.routeB.h);
940
+ const pad = ROUTE_GAP * 4;
941
+ const candidate = {
942
+ x: left - pad,
943
+ y: top - pad,
944
+ w: right - left + pad * 2,
945
+ h: bottom - top + pad * 2
946
+ };
947
+ if (!plan.bounds) return candidate;
948
+ return {
949
+ x: Math.max(candidate.x, plan.bounds.x),
950
+ y: Math.max(candidate.y, plan.bounds.y),
951
+ w: Math.min(candidate.x + candidate.w, plan.bounds.x + plan.bounds.w) - Math.max(candidate.x, plan.bounds.x),
952
+ h: Math.min(candidate.y + candidate.h, plan.bounds.y + plan.bounds.h) - Math.max(candidate.y, plan.bounds.y)
953
+ };
954
+ }
955
+ function boxesOverlap(a, b) {
956
+ return a.x < b.x + b.w && a.x + a.w > b.x && a.y < b.y + b.h && a.y + a.h > b.y;
957
+ }
958
+ function routeEndpointBox(id, otherId, positions, parentMap, containerIds) {
959
+ const container = positions.get(id);
960
+ const other = positions.get(otherId);
961
+ if (!container || !other) return void 0;
962
+ if (!isAncestorOf(id, otherId, parentMap)) {
963
+ if (!containerIds.has(id)) return void 0;
964
+ return container;
965
+ }
966
+ const left = container.x + PAD;
967
+ const right = container.x + container.w - PAD;
968
+ const top = container.y + TITLE_H + PAD / 2;
969
+ const x = Math.min(right, Math.max(left, other.x + other.w / 2));
970
+ return { x: x - 0.1, y: top - 0.1, w: 0.2, h: 0.2 };
971
+ }
972
+ function isExternalContainerEndpoint(id, otherId, parentMap, containerIds) {
973
+ return containerIds.has(id) && !isAncestorOf(id, otherId, parentMap);
974
+ }
975
+ function isAncestorOf(ancestor, id, parentMap) {
976
+ let cur = id;
977
+ const seen = /* @__PURE__ */ new Set();
978
+ while (parentMap.has(cur) && !seen.has(cur)) {
979
+ seen.add(cur);
980
+ cur = parentMap.get(cur);
981
+ if (cur === ancestor) return true;
982
+ }
983
+ return false;
984
+ }
985
+ function depthOf(id, parentMap) {
986
+ let depth = 0;
987
+ let cur = id;
988
+ const seen = /* @__PURE__ */ new Set();
989
+ while (parentMap.has(cur) && !seen.has(cur)) {
990
+ seen.add(cur);
991
+ cur = parentMap.get(cur);
992
+ depth++;
993
+ }
994
+ return depth;
995
+ }
996
+ function avoidObstaclesRoute(a, b, from, to, obstacles, parentMap, bounds, endpointBoundaries = [], endpointInteriorBarriers = [], op = "line", strictEdgeGuards = true) {
997
+ const routedObstacles = obstacles.filter((o) => o.id !== from && o.id !== to);
998
+ const leafObstacles = routedObstacles.filter((o) => !o.isContainer);
999
+ const hardLeafObstacles = leafObstacles.filter((o) => o.id.startsWith("__arrow_guard_"));
1000
+ const allowedContainers = allowedContainerIds(from, to, parentMap);
1001
+ const passableContainerObstacles = routedObstacles.filter((o) => o.isContainer && allowedContainers.has(o.id));
1002
+ const blockedContainerObstacles = routedObstacles.filter((o) => o.isContainer && !allowedContainers.has(o.id));
1003
+ const externalContainerObstacles = passableContainerObstacles.filter((o) => isAncestorOf(o.id, from, parentMap) !== isAncestorOf(o.id, to, parentMap));
1004
+ const boundaryObstacles = endpointBoundaries.map((box, index) => ({
1005
+ ...box,
1006
+ id: `__endpoint_boundary_${index}`,
1007
+ isContainer: true
1008
+ }));
1009
+ const realLeafObstacles = leafObstacles.filter((o) => !o.id.startsWith("__"));
1010
+ const edgeGuardObstacles = leafObstacles.filter(isEdgeGuardObstacle);
1011
+ const straight = preferredStraightRoute(a, b);
1012
+ if (straight && hasArrowTerminalClearance(straight, op) && (!bounds || routeFitsBounds(straight, bounds)) && !routeIntersectsAnyInterior(straight, realLeafObstacles, OBSTACLE_PAD) && !routeIntersectsAnyInterior(straight, hardLeafObstacles, OBSTACLE_PAD) && (!strictEdgeGuards || !routeSharesAnyEdgeGuardLane(straight, edgeGuardObstacles)) && !routeIntersectsAnyInterior(straight, blockedContainerObstacles, OBSTACLE_PAD) && !routeIntersectsAnyInterior(straight, endpointInteriorBarriers, 0) && !routeOverlapsAnyBorder(straight, [
1013
+ ...endpointBoundaries,
1014
+ ...passableContainerObstacles,
1015
+ ...blockedContainerObstacles
1016
+ ], BORDER_CLEARANCE)) {
1017
+ return straight;
1018
+ }
1019
+ const lanes = routeLanes(a, b, [...routedObstacles, ...boundaryObstacles], bounds);
1020
+ let best = orthogonalRoute(a, b);
1021
+ let bestScore = Number.POSITIVE_INFINITY;
1022
+ for (const start of portsOf(a)) {
1023
+ for (const end of portsOf(b)) {
1024
+ const sourcePort = start.point;
1025
+ const targetPort = end.point;
1026
+ const sp = portLeadPoint(start);
1027
+ const ep = portLeadPoint(end);
1028
+ const candidates = [
1029
+ [sourcePort, sp, [ep[0], sp[1]], ep, targetPort],
1030
+ [sourcePort, sp, [sp[0], ep[1]], ep, targetPort]
1031
+ ];
1032
+ if (Math.abs(sp[0] - ep[0]) < EPS || Math.abs(sp[1] - ep[1]) < EPS) {
1033
+ candidates.push([sourcePort, sp, ep, targetPort]);
1034
+ }
1035
+ for (const x of lanes.xs) {
1036
+ candidates.push([sourcePort, sp, [x, sp[1]], [x, ep[1]], ep, targetPort]);
1037
+ }
1038
+ for (const y of lanes.ys) {
1039
+ candidates.push([sourcePort, sp, [sp[0], y], [ep[0], y], ep, targetPort]);
1040
+ }
1041
+ for (const x of lanes.xs) {
1042
+ for (const y of lanes.ys) {
1043
+ candidates.push([sourcePort, sp, [x, sp[1]], [x, y], [ep[0], y], ep, targetPort]);
1044
+ candidates.push([sourcePort, sp, [sp[0], y], [x, y], [x, ep[1]], ep, targetPort]);
1045
+ }
1046
+ }
1047
+ for (const candidate of candidates) {
1048
+ const normalized = normalizeRoute(candidate);
1049
+ if (!isOrthogonalRoute(normalized)) continue;
1050
+ if (!hasArrowTerminalClearance(normalized, op)) continue;
1051
+ if (bounds && !routeFitsBounds(normalized, bounds)) continue;
1052
+ if (routeOverlapsAnyBorder(normalized, [a, b], 0)) continue;
1053
+ if (routeIntersectsAnyInterior(normalized, endpointInteriorBarriers, 0)) continue;
1054
+ if (routeIntersectsAnyInterior(normalized, realLeafObstacles, OBSTACLE_PAD)) continue;
1055
+ if (routeIntersectsAnyInterior(normalized, hardLeafObstacles, OBSTACLE_PAD)) continue;
1056
+ if (strictEdgeGuards && routeSharesAnyEdgeGuardLane(normalized, edgeGuardObstacles)) continue;
1057
+ if (routeIntersectsAnyInterior(normalized, blockedContainerObstacles, OBSTACLE_PAD)) continue;
1058
+ if (routeOverlapsAnyBorder(normalized, [
1059
+ ...endpointBoundaries,
1060
+ ...passableContainerObstacles,
1061
+ ...blockedContainerObstacles
1062
+ ], BORDER_CLEARANCE)) continue;
1063
+ const score = scoreRoute(
1064
+ normalized,
1065
+ a,
1066
+ b,
1067
+ leafObstacles,
1068
+ blockedContainerObstacles,
1069
+ passableContainerObstacles,
1070
+ externalContainerObstacles,
1071
+ endpointBoundaries
1072
+ ) + portPairPenalty(a, b, start.side, end.side, op) + start.offsetPenalty + end.offsetPenalty;
1073
+ if (score < bestScore) {
1074
+ best = normalized;
1075
+ bestScore = score;
1076
+ }
1077
+ }
1078
+ }
1079
+ }
1080
+ if (bestScore === Number.POSITIVE_INFINITY) {
1081
+ if (strictEdgeGuards && edgeGuardObstacles.length > 0) {
1082
+ return avoidObstaclesRoute(
1083
+ a,
1084
+ b,
1085
+ from,
1086
+ to,
1087
+ obstacles,
1088
+ parentMap,
1089
+ bounds,
1090
+ endpointBoundaries,
1091
+ endpointInteriorBarriers,
1092
+ op,
1093
+ false
1094
+ );
1095
+ }
1096
+ return orthogonalRoute(a, b);
1097
+ }
1098
+ return best;
1099
+ }
1100
+ function preferredStraightRoute(a, b) {
1101
+ const acx = a.x + a.w / 2;
1102
+ const acy = a.y + a.h / 2;
1103
+ const bcx = b.x + b.w / 2;
1104
+ const bcy = b.y + b.h / 2;
1105
+ if (Math.abs(acy - bcy) < EPS) {
1106
+ if (a.x + a.w <= b.x) return [[a.x + a.w, acy], [b.x, bcy]];
1107
+ if (b.x + b.w <= a.x) return [[a.x, acy], [b.x + b.w, bcy]];
1108
+ }
1109
+ if (Math.abs(acx - bcx) < EPS) {
1110
+ if (a.y + a.h <= b.y) return [[acx, a.y + a.h], [bcx, b.y]];
1111
+ if (b.y + b.h <= a.y) return [[acx, a.y], [bcx, b.y + b.h]];
1112
+ }
1113
+ return void 0;
1114
+ }
1115
+ function hasArrowTerminalClearance(points, op) {
1116
+ if (points.length < 2) return false;
1117
+ if (op !== "line" && op !== "dashed") {
1118
+ if (terminalSegmentLength(points, false) < arrowTerminalClearance(op)) return false;
1119
+ }
1120
+ if (op === "bidir") {
1121
+ if (terminalSegmentLength(points, true) < arrowTerminalClearance(op)) return false;
1122
+ }
1123
+ return true;
1124
+ }
1125
+ function terminalSegmentLength(points, atStart) {
1126
+ if (points.length < 2) return 0;
1127
+ const a = atStart ? points[0] : points[points.length - 1];
1128
+ const b = atStart ? points[1] : points[points.length - 2];
1129
+ return segmentLength(a, b);
1130
+ }
1131
+ function arrowTerminalClearance(op) {
1132
+ return op === "thick" ? THICK_ARROW_TERMINAL_CLEARANCE : ARROW_TERMINAL_CLEARANCE;
1133
+ }
1134
+ function routeIntersectsAnyInterior(points, boxes, pad) {
1135
+ for (let i = 0; i < points.length - 1; i++) {
1136
+ for (const box of boxes) {
1137
+ if (boxInteriorOverlap(points[i], points[i + 1], box, pad) > EPS) return true;
1138
+ }
1139
+ }
1140
+ return false;
1141
+ }
1142
+ function routeOverlapsAnyBorder(points, boxes, tolerance) {
1143
+ for (let i = 0; i < points.length - 1; i++) {
1144
+ for (const box of boxes) {
1145
+ if (boxBorderProximityOverlap(points[i], points[i + 1], box, tolerance) > EPS) return true;
1146
+ }
1147
+ }
1148
+ return false;
1149
+ }
1150
+ function routeSharesAnyEdgeGuardLane(points, guards) {
1151
+ for (let i = 0; i < points.length - 1; i++) {
1152
+ for (const guard of guards) {
1153
+ if (segmentSharesEdgeGuardLane(points[i], points[i + 1], guard)) return true;
1154
+ }
1155
+ }
1156
+ return false;
1157
+ }
1158
+ function segmentSharesEdgeGuardLane(a, b, guard) {
1159
+ const segmentVertical = Math.abs(a[0] - b[0]) < EPS;
1160
+ const segmentHorizontal = Math.abs(a[1] - b[1]) < EPS;
1161
+ if (segmentVertical && guard.orientation === "vertical") {
1162
+ const guardX = guard.x + guard.w / 2;
1163
+ if (Math.abs(a[0] - guardX) > guard.w / 2 + EPS) return false;
1164
+ return intervalOverlap(a[1], b[1], guard.y, guard.y + guard.h) > EPS;
1165
+ }
1166
+ if (segmentHorizontal && guard.orientation === "horizontal") {
1167
+ const guardY = guard.y + guard.h / 2;
1168
+ if (Math.abs(a[1] - guardY) > guard.h / 2 + EPS) return false;
1169
+ return intervalOverlap(a[0], b[0], guard.x, guard.x + guard.w) > EPS;
1170
+ }
1171
+ return false;
1172
+ }
1173
+ function allowedContainerIds(from, to, parentMap) {
1174
+ return /* @__PURE__ */ new Set([from, to, ...ancestorsOf(from, parentMap), ...ancestorsOf(to, parentMap)]);
1175
+ }
1176
+ function portsOf(box) {
1177
+ const cx = box.x + box.w / 2;
1178
+ const cy = box.y + box.h / 2;
1179
+ const xOffset = Math.min(box.w * 0.25, Math.max(3, box.w / 2 - 4));
1180
+ const yOffset = Math.min(box.h * 0.25, Math.max(2, box.h / 2 - 3));
1181
+ const sidePenalty = 9;
1182
+ return [
1183
+ { point: [box.x + box.w, cy], side: "right", offsetPenalty: 0 },
1184
+ { point: [box.x, cy], side: "left", offsetPenalty: 0 },
1185
+ { point: [cx, box.y + box.h], side: "bottom", offsetPenalty: 0 },
1186
+ { point: [cx, box.y], side: "top", offsetPenalty: 0 },
1187
+ { point: [box.x + box.w, cy - yOffset], side: "right", offsetPenalty: sidePenalty },
1188
+ { point: [box.x + box.w, cy + yOffset], side: "right", offsetPenalty: sidePenalty },
1189
+ { point: [box.x, cy - yOffset], side: "left", offsetPenalty: sidePenalty },
1190
+ { point: [box.x, cy + yOffset], side: "left", offsetPenalty: sidePenalty },
1191
+ { point: [cx - xOffset, box.y + box.h], side: "bottom", offsetPenalty: sidePenalty },
1192
+ { point: [cx + xOffset, box.y + box.h], side: "bottom", offsetPenalty: sidePenalty },
1193
+ { point: [cx - xOffset, box.y], side: "top", offsetPenalty: sidePenalty },
1194
+ { point: [cx + xOffset, box.y], side: "top", offsetPenalty: sidePenalty }
1195
+ ];
1196
+ }
1197
+ function portLeadPoint(port) {
1198
+ const [x, y] = port.point;
1199
+ switch (port.side) {
1200
+ case "right":
1201
+ return [x + PORT_STUB, y];
1202
+ case "left":
1203
+ return [x - PORT_STUB, y];
1204
+ case "bottom":
1205
+ return [x, y + PORT_STUB];
1206
+ case "top":
1207
+ return [x, y - PORT_STUB];
1208
+ }
1209
+ }
1210
+ function portPairPenalty(source, target, start, end, op) {
1211
+ const sx = source.x + source.w / 2;
1212
+ const sy = source.y + source.h / 2;
1213
+ const tx = target.x + target.w / 2;
1214
+ const ty = target.y + target.h / 2;
1215
+ const dx = tx - sx;
1216
+ const dy = ty - sy;
1217
+ if (op === "dashed") return dashedPortPairPenalty(start, end, dx, dy, source, target);
1218
+ return sourcePortPenalty(start, dx, dy, source) + targetPortPenalty(end, dx, dy, target);
1219
+ }
1220
+ function dashedPortPairPenalty(start, end, dx, dy, source, target) {
1221
+ if (Math.abs(dy) > Math.min(source.h, target.h) * 0.8) {
1222
+ const startDesired = dy > 0 ? "bottom" : "top";
1223
+ const endDesired = dy > 0 ? "top" : "bottom";
1224
+ return dashedSidePenalty(start, startDesired) + dashedSidePenalty(end, endDesired);
1225
+ }
1226
+ if (Math.abs(dx) > Math.min(source.w, target.w) * 0.8) {
1227
+ const startDesired = dx > 0 ? "right" : "left";
1228
+ const endDesired = dx > 0 ? "left" : "right";
1229
+ return dashedSidePenalty(start, startDesired) + dashedSidePenalty(end, endDesired);
1230
+ }
1231
+ return 0;
1232
+ }
1233
+ function dashedSidePenalty(side, desired) {
1234
+ if (side === desired) return 0;
1235
+ if (side === oppositeSide(desired)) return 1400;
1236
+ return 450;
1237
+ }
1238
+ function sourcePortPenalty(side, dx, dy, box) {
1239
+ if (Math.abs(dy) > box.h * 0.8 && Math.abs(dy) >= Math.abs(dx) * VERTICAL_PORT_RATIO) {
1240
+ const desired = dy > 0 ? "bottom" : "top";
1241
+ if (side === desired) return 0;
1242
+ return side === oppositeSide(desired) ? 800 : 180;
1243
+ }
1244
+ if (Math.abs(dx) > box.w * 0.5) {
1245
+ const desired = dx > 0 ? "right" : "left";
1246
+ if (side === desired) return 0;
1247
+ return side === oppositeSide(desired) ? 900 : 90;
1248
+ }
1249
+ if (Math.abs(dy) > box.h * 0.8) {
1250
+ const desired = dy > 0 ? "bottom" : "top";
1251
+ if (side === desired) return 0;
1252
+ return side === oppositeSide(desired) ? 600 : 120;
1253
+ }
1254
+ return 0;
1255
+ }
1256
+ function targetPortPenalty(side, dx, dy, box) {
1257
+ if (Math.abs(dy) > box.h * 0.8 && Math.abs(dy) >= Math.abs(dx) * VERTICAL_PORT_RATIO) {
1258
+ const desired = dy > 0 ? "top" : "bottom";
1259
+ if (side === desired) return 0;
1260
+ return side === oppositeSide(desired) ? 1400 : 360;
1261
+ }
1262
+ if (Math.abs(dx) > box.w * 0.5 && Math.abs(dx) >= Math.abs(dy) * 1.2) {
1263
+ const desired = dx > 0 ? "left" : "right";
1264
+ if (side === desired) return 0;
1265
+ return side === oppositeSide(desired) ? 1200 : 220;
1266
+ }
1267
+ if (Math.abs(dy) > box.h * 0.35) {
1268
+ const desired = dy > 0 ? "top" : "bottom";
1269
+ if (side === desired) return 0;
1270
+ return side === "left" || side === "right" ? 9e3 : 1200;
1271
+ }
1272
+ if (Math.abs(dx) > box.w * 0.5) {
1273
+ const desired = dx > 0 ? "left" : "right";
1274
+ if (side === desired) return 0;
1275
+ return side === oppositeSide(desired) ? 1e3 : 160;
1276
+ }
1277
+ return 0;
1278
+ }
1279
+ function oppositeSide(side) {
1280
+ switch (side) {
1281
+ case "right":
1282
+ return "left";
1283
+ case "left":
1284
+ return "right";
1285
+ case "bottom":
1286
+ return "top";
1287
+ case "top":
1288
+ return "bottom";
1289
+ }
1290
+ }
1291
+ function routeLanes(a, b, obstacles, bounds) {
1292
+ const boxes = [a, b, ...obstacles];
1293
+ const laneMargin = ROUTE_GAP * 2;
1294
+ const minX = bounds ? bounds.x : Math.max(MARGIN, Math.min(...boxes.map((o) => o.x)) - laneMargin);
1295
+ const maxX = bounds ? bounds.x + bounds.w : Math.max(...boxes.map((o) => o.x + o.w)) + laneMargin;
1296
+ const minY = bounds ? bounds.y : Math.max(MARGIN, Math.min(...boxes.map((o) => o.y)) - laneMargin);
1297
+ const maxY = bounds ? bounds.y + bounds.h : Math.max(...boxes.map((o) => o.y + o.h)) + laneMargin;
1298
+ const xs = [(a.x + a.w / 2 + b.x + b.w / 2) / 2];
1299
+ const ys = [(a.y + a.h / 2 + b.y + b.h / 2) / 2];
1300
+ for (const box of boxes) {
1301
+ for (const gap of [ROUTE_GAP, ROUTE_GAP * 1.5, ROUTE_GAP * 2]) {
1302
+ xs.push(box.x - gap, box.x + box.w + gap);
1303
+ ys.push(box.y - gap, box.y + box.h + gap);
1304
+ }
1305
+ }
1306
+ if (bounds) {
1307
+ xs.push(bounds.x, bounds.x + bounds.w);
1308
+ ys.push(bounds.y, bounds.y + bounds.h);
1309
+ }
1310
+ return {
1311
+ xs: limitRouteLanes(
1312
+ uniqueSorted(xs.filter((x) => x >= minX && x <= maxX)),
1313
+ (a.x + a.w / 2 + b.x + b.w / 2) / 2
1314
+ ),
1315
+ ys: limitRouteLanes(
1316
+ uniqueSorted(ys.filter((y) => y >= minY && y <= maxY)),
1317
+ (a.y + a.h / 2 + b.y + b.h / 2) / 2
1318
+ )
1319
+ };
1320
+ }
1321
+ function limitRouteLanes(values, anchor) {
1322
+ if (values.length <= MAX_ROUTE_LANES) return values;
1323
+ const keep = /* @__PURE__ */ new Set([values[0], values[values.length - 1]]);
1324
+ for (const value of [...values].sort((a, b) => Math.abs(a - anchor) - Math.abs(b - anchor))) {
1325
+ keep.add(value);
1326
+ if (keep.size >= MAX_ROUTE_LANES) break;
1327
+ }
1328
+ return [...keep].sort((a, b) => a - b);
1329
+ }
1330
+ function commonRoutingBounds(from, to, parentMap, positions) {
1331
+ const common = nearestCommonAncestor(from, to, parentMap);
1332
+ if (!common) return void 0;
1333
+ const box = positions.get(common);
1334
+ if (!box) return void 0;
1335
+ const inset = ROUTE_GAP / 2;
1336
+ const top = box.y + TITLE_H + PAD / 2;
1337
+ const bottom = box.y + box.h - inset;
1338
+ return {
1339
+ x: box.x + inset,
1340
+ y: top,
1341
+ w: Math.max(0, box.w - inset * 2),
1342
+ h: Math.max(0, bottom - top)
1343
+ };
1344
+ }
1345
+ function nearestCommonAncestor(from, to, parentMap) {
1346
+ const toAncestors = new Set(ancestorsOf(to, parentMap));
1347
+ return ancestorsOf(from, parentMap).find((id) => toAncestors.has(id));
1348
+ }
1349
+ function ancestorsOf(id, parentMap) {
1350
+ const out = [];
1351
+ let cur = id;
1352
+ const seen = /* @__PURE__ */ new Set();
1353
+ while (parentMap.has(cur) && !seen.has(cur)) {
1354
+ seen.add(cur);
1355
+ cur = parentMap.get(cur);
1356
+ out.push(cur);
1357
+ }
1358
+ return out;
1359
+ }
1360
+ function routeFitsBounds(points, bounds) {
1361
+ const right = bounds.x + bounds.w;
1362
+ const bottom = bounds.y + bounds.h;
1363
+ return points.every(([x, y]) => x >= bounds.x - EPS && x <= right + EPS && y >= bounds.y - EPS && y <= bottom + EPS);
1364
+ }
1365
+ function uniqueSorted(values) {
1366
+ const seen = /* @__PURE__ */ new Set();
1367
+ const out = [];
1368
+ for (const value of values) {
1369
+ const rounded = Math.round(value * 1e3) / 1e3;
1370
+ const key = rounded.toFixed(3);
1371
+ if (seen.has(key)) continue;
1372
+ seen.add(key);
1373
+ out.push(rounded);
1374
+ }
1375
+ return out.sort((a, b) => a - b);
1376
+ }
1377
+ function normalizeRoute(points) {
1378
+ const deduped = [];
1379
+ for (const point of points) {
1380
+ const prev = deduped[deduped.length - 1];
1381
+ if (!prev || Math.abs(prev[0] - point[0]) >= EPS || Math.abs(prev[1] - point[1]) >= EPS) {
1382
+ deduped.push(point);
1383
+ }
1384
+ }
1385
+ const out = [];
1386
+ for (const point of deduped) {
1387
+ out.push(point);
1388
+ while (out.length >= 3) {
1389
+ const a = out[out.length - 3];
1390
+ const b = out[out.length - 2];
1391
+ const c = out[out.length - 1];
1392
+ const sameX = Math.abs(a[0] - b[0]) < EPS && Math.abs(b[0] - c[0]) < EPS;
1393
+ const sameY = Math.abs(a[1] - b[1]) < EPS && Math.abs(b[1] - c[1]) < EPS;
1394
+ if (!sameX && !sameY) break;
1395
+ out.splice(out.length - 2, 1);
1396
+ }
1397
+ }
1398
+ return out;
1399
+ }
1400
+ function isOrthogonalRoute(points) {
1401
+ if (points.length < 2) return false;
1402
+ for (let i = 0; i < points.length - 1; i++) {
1403
+ const a = points[i];
1404
+ const b = points[i + 1];
1405
+ if (Math.abs(a[0] - b[0]) >= EPS && Math.abs(a[1] - b[1]) >= EPS) return false;
1406
+ }
1407
+ return true;
1408
+ }
1409
+ function scoreRoute(points, source, target, leafObstacles, blockedContainerObstacles, passableContainerObstacles, externalContainerObstacles, endpointBoundaries) {
1410
+ let score = 0;
1411
+ for (let i = 0; i < points.length - 1; i++) {
1412
+ const a = points[i];
1413
+ const b = points[i + 1];
1414
+ score += segmentLength(a, b);
1415
+ const sourceInterior = boxInteriorOverlap(a, b, source, 0);
1416
+ if (sourceInterior > 0) score += 22e4 + sourceInterior * 1500;
1417
+ const targetInterior = boxInteriorOverlap(a, b, target, 0);
1418
+ if (targetInterior > 0) score += 22e4 + targetInterior * 1500;
1419
+ const sourceBorder = boxBorderOverlap(a, b, source);
1420
+ if (sourceBorder > 0) score += 8e3 + sourceBorder * 120;
1421
+ const targetBorder = boxBorderOverlap(a, b, target);
1422
+ if (targetBorder > 0) score += 8e3 + targetBorder * 120;
1423
+ for (const boundary of endpointBoundaries) {
1424
+ const overlap = boxBorderOverlap(a, b, boundary);
1425
+ if (overlap > 0) score += 12e3 + overlap * 160;
1426
+ }
1427
+ for (const obstacle of leafObstacles) {
1428
+ const overlap = boxInteriorOverlap(a, b, obstacle, OBSTACLE_PAD);
1429
+ if (overlap > 0) score += leafObstaclePenalty(obstacle, a, b, overlap);
1430
+ }
1431
+ for (const obstacle of blockedContainerObstacles) {
1432
+ const overlap = boxInteriorOverlap(a, b, obstacle, OBSTACLE_PAD);
1433
+ if (overlap > 0) score += 18e4 + overlap * 1200;
1434
+ const border = boxBorderOverlap(a, b, obstacle);
1435
+ if (border > 0) score += 18e3 + border * 300;
1436
+ }
1437
+ for (const obstacle of passableContainerObstacles) {
1438
+ const titleOverlap = boxInteriorOverlap(a, b, containerTitleRoutingBand(obstacle), OBSTACLE_PAD);
1439
+ if (titleOverlap > 0) score += 14e3 + titleOverlap * 120;
1440
+ const overlap = boxBorderOverlap(a, b, obstacle);
1441
+ if (overlap > 0) score += 6e3 + overlap * 120;
1442
+ }
1443
+ for (const obstacle of externalContainerObstacles) {
1444
+ const overlap = boxInteriorOverlap(a, b, obstacle, 0);
1445
+ if (overlap > 0) score += overlap * 40;
1446
+ }
1447
+ }
1448
+ score += Math.max(0, points.length - 2) * 3;
1449
+ score += routeExcursionPenalty(points, source, target);
1450
+ return score;
1451
+ }
1452
+ function routeExcursionPenalty(points, source, target) {
1453
+ const pad = ROUTE_GAP * 5;
1454
+ const left = Math.min(source.x, target.x) - pad;
1455
+ const top = Math.min(source.y, target.y) - pad;
1456
+ const right = Math.max(source.x + source.w, target.x + target.w) + pad;
1457
+ const bottom = Math.max(source.y + source.h, target.y + target.h) + pad;
1458
+ let penalty = 0;
1459
+ for (const [x, y] of points) {
1460
+ if (x < left) penalty += (left - x) * 90;
1461
+ if (x > right) penalty += (x - right) * 90;
1462
+ if (y < top) penalty += (top - y) * 90;
1463
+ if (y > bottom) penalty += (y - bottom) * 90;
1464
+ }
1465
+ return penalty;
1466
+ }
1467
+ function leafObstaclePenalty(obstacle, a, b, overlap) {
1468
+ if (obstacle.id.startsWith("__arrow_guard_")) return 7e4 + overlap * 700;
1469
+ if (isEdgeGuardObstacle(obstacle)) return edgeGuardPenalty(obstacle, a, b, overlap);
1470
+ return 16e4 + overlap * 1200;
1471
+ }
1472
+ function edgeGuardPenalty(obstacle, a, b, overlap) {
1473
+ const segmentVertical = Math.abs(a[0] - b[0]) < EPS;
1474
+ const guardVertical = obstacle.orientation === "vertical";
1475
+ if (segmentVertical === guardVertical) return 85e3 + overlap * 1200;
1476
+ return 500 + overlap * 160;
1477
+ }
1478
+ function isEdgeGuardObstacle(obstacle) {
1479
+ return obstacle.id.startsWith("__edge_guard_") && "orientation" in obstacle && (obstacle.orientation === "vertical" || obstacle.orientation === "horizontal");
1480
+ }
1481
+ function containerTitleRoutingBand(container) {
1482
+ return {
1483
+ x: container.x,
1484
+ y: container.y,
1485
+ w: container.w,
1486
+ h: TITLE_H + PAD
1487
+ };
1488
+ }
1489
+ function segmentLength(a, b) {
1490
+ return Math.abs(a[0] - b[0]) + Math.abs(a[1] - b[1]);
1491
+ }
1492
+ function boxInteriorOverlap(a, b, box, pad) {
1493
+ const left = box.x - pad;
1494
+ const right = box.x + box.w + pad;
1495
+ const top = box.y - pad;
1496
+ const bottom = box.y + box.h + pad;
1497
+ if (Math.abs(a[0] - b[0]) < EPS) {
1498
+ const x = a[0];
1499
+ if (x <= left || x >= right) return 0;
1500
+ return intervalOverlap(a[1], b[1], top, bottom);
1501
+ }
1502
+ if (Math.abs(a[1] - b[1]) < EPS) {
1503
+ const y = a[1];
1504
+ if (y <= top || y >= bottom) return 0;
1505
+ return intervalOverlap(a[0], b[0], left, right);
1506
+ }
1507
+ return 0;
1508
+ }
1509
+ function boxBorderOverlap(a, b, box) {
1510
+ return boxBorderProximityOverlap(a, b, box, 0);
1511
+ }
1512
+ function boxBorderProximityOverlap(a, b, box, tolerance) {
1513
+ if (Math.abs(a[0] - b[0]) < EPS) {
1514
+ const x = a[0];
1515
+ if (Math.abs(x - box.x) > tolerance + EPS && Math.abs(x - (box.x + box.w)) > tolerance + EPS) return 0;
1516
+ return intervalOverlap(a[1], b[1], box.y, box.y + box.h);
1517
+ }
1518
+ if (Math.abs(a[1] - b[1]) < EPS) {
1519
+ const y = a[1];
1520
+ if (Math.abs(y - box.y) > tolerance + EPS && Math.abs(y - (box.y + box.h)) > tolerance + EPS) return 0;
1521
+ return intervalOverlap(a[0], b[0], box.x, box.x + box.w);
1522
+ }
1523
+ return 0;
1524
+ }
1525
+ function intervalOverlap(a1, a2, b1, b2) {
1526
+ const minA = Math.min(a1, a2);
1527
+ const maxA = Math.max(a1, a2);
1528
+ const minB = Math.min(b1, b2);
1529
+ const maxB = Math.max(b1, b2);
1530
+ return Math.max(0, Math.min(maxA, maxB) - Math.max(minA, minB));
1531
+ }
1532
+ function orthogonalRoute(a, b) {
1533
+ const ca = { x: a.x + a.w / 2, y: a.y + a.h / 2 };
1534
+ const cb = { x: b.x + b.w / 2, y: b.y + b.h / 2 };
1535
+ const dx = cb.x - ca.x;
1536
+ const dy = cb.y - ca.y;
1537
+ let pa, pb;
1538
+ const separatedHorizontally = a.x + a.w <= b.x || b.x + b.w <= a.x;
1539
+ const separatedVertically = a.y + a.h <= b.y || b.y + b.h <= a.y;
1540
+ const horizontal = separatedHorizontally ? true : separatedVertically ? false : Math.abs(dx) > Math.abs(dy);
1541
+ if (horizontal) {
1542
+ pa = [dx > 0 ? a.x + a.w : a.x, ca.y];
1543
+ pb = [dx > 0 ? b.x : b.x + b.w, cb.y];
1544
+ if (Math.abs(pa[1] - pb[1]) < 0.5) return [pa, pb];
1545
+ const midX = (pa[0] + pb[0]) / 2;
1546
+ return [pa, [midX, pa[1]], [midX, pb[1]], pb];
1547
+ } else {
1548
+ pa = [ca.x, dy > 0 ? a.y + a.h : a.y];
1549
+ pb = [cb.x, dy > 0 ? b.y : b.y + b.h];
1550
+ if (Math.abs(pa[0] - pb[0]) < 0.5) return [pa, pb];
1551
+ const midY = (pa[1] + pb[1]) / 2;
1552
+ return [pa, [pa[0], midY], [pb[0], midY], pb];
1553
+ }
1554
+ }
1555
+
1556
+ // src/core/render.ts
1557
+ var NS = "http://www.w3.org/2000/svg";
1558
+ var FONT_FAMILY = '"Hiragino Sans", "Yu Gothic", "Noto Sans CJK JP", sans-serif';
1559
+ var LABEL_OFFSET = 4;
1560
+ var LABEL_FONT_JA = 2.4;
1561
+ var LABEL_FONT_EN = 2.1;
1562
+ var LABEL_GAP = 0.6;
1563
+ var LABEL_OFFSET_STEPS = [0, 3.2, 6.4];
1564
+ var EPS2 = 1e-3;
1565
+ function render(laid, opts = {}) {
1566
+ const lang = opts.lang ?? "ja";
1567
+ const svg = el("svg", {
1568
+ xmlns: NS,
1569
+ viewBox: `0 0 ${laid.width} ${laid.height}`,
1570
+ "font-family": FONT_FAMILY,
1571
+ "shape-rendering": "geometricPrecision"
1572
+ });
1573
+ const containers = laid.nodes.filter((n) => n.isContainer);
1574
+ const leaves = laid.nodes.filter((n) => !n.isContainer);
1575
+ const labelBoxes = [];
1576
+ for (const c of containers) svg.appendChild(renderContainer(c, lang));
1577
+ for (const e of laid.edges) svg.appendChild(renderEdge(e, lang, laid, labelBoxes));
1578
+ for (const e of laid.edges) svg.appendChild(renderEdgeHeads(e));
1579
+ for (const n of leaves) svg.appendChild(renderNode(n, lang));
1580
+ return svg;
1581
+ }
1582
+ function renderContainer(n, lang) {
1583
+ const g = el("g");
1584
+ g.appendChild(el("rect", {
1585
+ x: n.x,
1586
+ y: n.y,
1587
+ width: n.w,
1588
+ height: n.h,
1589
+ fill: "white",
1590
+ stroke: "#000",
1591
+ "stroke-width": 0.3
1592
+ }));
1593
+ const labels = pickLabel(n.label, lang);
1594
+ if (labels.length || n.id) {
1595
+ const t = el("text", {
1596
+ x: n.x + 2,
1597
+ y: n.y + 3.5,
1598
+ "font-size": 2.6,
1599
+ fill: "#000"
1600
+ });
1601
+ t.textContent = (n.id ? n.id + " " : "") + (labels[0] ?? "");
1602
+ g.appendChild(t);
1603
+ }
1604
+ return g;
1605
+ }
1606
+ function renderNode(n, lang) {
1607
+ const g = el("g");
1608
+ g.appendChild(renderShape(n));
1609
+ const lines = [];
1610
+ if (n.id && n.id !== "*") lines.push(n.id);
1611
+ const labels = pickLabel(n.label, lang);
1612
+ for (const l of labels) lines.push(l);
1613
+ if (lines.length === 0) return g;
1614
+ const fontSize = 2.8;
1615
+ const lineH = fontSize * 1.2;
1616
+ const totalH = lines.length * lineH;
1617
+ const startY = n.y + n.h / 2 - totalH / 2 + lineH * 0.8;
1618
+ for (let i = 0; i < lines.length; i++) {
1619
+ const t = el("text", {
1620
+ x: n.x + n.w / 2,
1621
+ y: startY + i * lineH,
1622
+ "font-size": fontSize,
1623
+ fill: "#000",
1624
+ "text-anchor": "middle"
1625
+ });
1626
+ t.textContent = lines[i];
1627
+ g.appendChild(t);
1628
+ }
1629
+ return g;
1630
+ }
1631
+ function renderShape(n) {
1632
+ const stroke = "#000";
1633
+ const fill = "white";
1634
+ const sw = 0.4;
1635
+ switch (n.shape) {
1636
+ case "round":
1637
+ return el("rect", {
1638
+ x: n.x,
1639
+ y: n.y,
1640
+ width: n.w,
1641
+ height: n.h,
1642
+ rx: Math.min(n.w, n.h) / 2,
1643
+ ry: Math.min(n.w, n.h) / 2,
1644
+ fill,
1645
+ stroke,
1646
+ "stroke-width": sw
1647
+ });
1648
+ case "circle": {
1649
+ const r = Math.min(n.w, n.h) / 2;
1650
+ return el("circle", {
1651
+ cx: n.x + n.w / 2,
1652
+ cy: n.y + n.h / 2,
1653
+ r,
1654
+ fill: "#000",
1655
+ stroke
1656
+ });
1657
+ }
1658
+ case "diamond": {
1659
+ const cx = n.x + n.w / 2;
1660
+ const cy = n.y + n.h / 2;
1661
+ const pts = [[cx, n.y], [n.x + n.w, cy], [cx, n.y + n.h], [n.x, cy]].map((p) => p.join(",")).join(" ");
1662
+ return el("polygon", { points: pts, fill, stroke, "stroke-width": sw });
1663
+ }
1664
+ case "actor":
1665
+ case "box":
1666
+ default:
1667
+ return el("rect", {
1668
+ x: n.x,
1669
+ y: n.y,
1670
+ width: n.w,
1671
+ height: n.h,
1672
+ fill,
1673
+ stroke,
1674
+ "stroke-width": sw
1675
+ });
1676
+ }
1677
+ }
1678
+ function renderEdge(e, lang, laid, labelBoxes) {
1679
+ const g = el("g");
1680
+ if (e.points.length < 2) return g;
1681
+ const bodyPoints = edgeBodyPoints(e);
1682
+ if (bodyPoints.length < 2) return g;
1683
+ const d = bodyPoints.map((p, i) => i === 0 ? `M ${p[0]} ${p[1]}` : `L ${p[0]} ${p[1]}`).join(" ");
1684
+ const path = el("path", {
1685
+ d,
1686
+ fill: "none",
1687
+ stroke: "#000",
1688
+ "stroke-width": strokeWidth(e.op),
1689
+ "stroke-linecap": "butt",
1690
+ "stroke-linejoin": "miter"
1691
+ });
1692
+ const dash = strokeDash(e.op);
1693
+ if (dash) path.setAttribute("stroke-dasharray", dash);
1694
+ g.appendChild(path);
1695
+ const labels = pickLabel(e.label, lang);
1696
+ if (labels.length) {
1697
+ const ja = labels[0];
1698
+ const en = labels[1];
1699
+ const placement = chooseLabelPlacement(e, labels, laid, labelBoxes);
1700
+ labelBoxes.push(expandBox(placement.box, 0.8));
1701
+ const drawText = (text, x, y, fontSize, anchor, baseline) => {
1702
+ const t = el("text", {
1703
+ x,
1704
+ y,
1705
+ "font-size": fontSize,
1706
+ fill: "#000",
1707
+ "text-anchor": anchor,
1708
+ "dominant-baseline": baseline
1709
+ });
1710
+ t.textContent = text;
1711
+ g.appendChild(t);
1712
+ };
1713
+ drawText(ja, placement.ja.x, placement.ja.y, LABEL_FONT_JA, placement.anchor, placement.ja.baseline);
1714
+ if (en && placement.en) {
1715
+ drawText(en, placement.en.x, placement.en.y, LABEL_FONT_EN, placement.anchor, placement.en.baseline);
1716
+ }
1717
+ }
1718
+ return g;
1719
+ }
1720
+ function renderEdgeHeads(e) {
1721
+ const g = el("g");
1722
+ if (e.isLifeline) return g;
1723
+ const endHead = arrowHeadFor(e, false);
1724
+ const startHead = arrowHeadFor(e, true);
1725
+ if (endHead) g.appendChild(endHead);
1726
+ if (startHead) g.appendChild(startHead);
1727
+ return g;
1728
+ }
1729
+ function strokeWidth(op) {
1730
+ return op === "thick" ? 0.7 : 0.4;
1731
+ }
1732
+ function strokeDash(op) {
1733
+ return op === "dashed" || op === "dashed-arrow" ? "1.4 1.2" : null;
1734
+ }
1735
+ function edgeBodyPoints(e) {
1736
+ let points = e.points.map((p) => [p[0], p[1]]);
1737
+ if (e.op !== "line" && e.op !== "dashed") {
1738
+ points = trimPolyline(points, false, arrowLength(e.op));
1739
+ }
1740
+ if (e.op === "bidir") {
1741
+ points = trimPolyline(points, true, arrowLength(e.op));
1742
+ }
1743
+ return points;
1744
+ }
1745
+ function trimPolyline(points, atStart, amount) {
1746
+ if (points.length < 2 || amount <= 0) return points;
1747
+ const out = atStart ? [...points].reverse() : [...points];
1748
+ let remaining = amount;
1749
+ while (out.length >= 2 && remaining > 0) {
1750
+ const tip = out[out.length - 1];
1751
+ const prev = out[out.length - 2];
1752
+ const dx = prev[0] - tip[0];
1753
+ const dy = prev[1] - tip[1];
1754
+ const len = Math.hypot(dx, dy);
1755
+ if (len < EPS2) {
1756
+ out.pop();
1757
+ continue;
1758
+ }
1759
+ if (len > remaining) {
1760
+ out[out.length - 1] = [
1761
+ tip[0] + dx / len * remaining,
1762
+ tip[1] + dy / len * remaining
1763
+ ];
1764
+ remaining = 0;
1765
+ } else {
1766
+ out.pop();
1767
+ remaining -= len;
1768
+ }
1769
+ }
1770
+ return atStart ? out.reverse() : out;
1771
+ }
1772
+ function arrowLength(op) {
1773
+ return op === "thick" ? 3.4 : 2.8;
1774
+ }
1775
+ function arrowHeadFor(e, atStart) {
1776
+ if (e.points.length < 2) return null;
1777
+ if (atStart && e.op !== "bidir") return null;
1778
+ if (!atStart && (e.op === "line" || e.op === "dashed")) return null;
1779
+ const points = atStart ? e.points : [...e.points].reverse();
1780
+ return renderArrowHead(points[0], points[1], e.op === "thick");
1781
+ }
1782
+ function renderArrowHead(tip, next, bold) {
1783
+ const dx = next[0] - tip[0];
1784
+ const dy = next[1] - tip[1];
1785
+ const len = Math.hypot(dx, dy);
1786
+ if (len < 1e-3) return null;
1787
+ const ux = dx / len;
1788
+ const uy = dy / len;
1789
+ const arrowLen = bold ? 3.4 : 2.8;
1790
+ const halfW = bold ? 1.6 : 1.25;
1791
+ const bx = tip[0] + ux * arrowLen;
1792
+ const by = tip[1] + uy * arrowLen;
1793
+ const px = -uy;
1794
+ const py = ux;
1795
+ const pts = [
1796
+ tip,
1797
+ [bx + px * halfW, by + py * halfW],
1798
+ [bx - px * halfW, by - py * halfW]
1799
+ ].map((p) => p.join(",")).join(" ");
1800
+ return el("polygon", {
1801
+ points: pts,
1802
+ fill: "#000",
1803
+ stroke: "#000",
1804
+ "stroke-width": 0
1805
+ });
1806
+ }
1807
+ function el(tag, attrs = {}) {
1808
+ const node = document.createElementNS(NS, tag);
1809
+ for (const [k, v] of Object.entries(attrs)) node.setAttribute(k, String(v));
1810
+ return node;
1811
+ }
1812
+ function pickLabel(b, lang) {
1813
+ if (!b) return [];
1814
+ if (lang === "ja") return b.ja ? [b.ja] : b.en ? [b.en] : [];
1815
+ if (lang === "en") return b.en ? [b.en] : b.ja ? [b.ja] : [];
1816
+ const out = [];
1817
+ if (b.ja) out.push(b.ja);
1818
+ if (b.en) out.push(b.en);
1819
+ return out;
1820
+ }
1821
+ function chooseLabelPlacement(edge, labels, laid, occupiedLabels = []) {
1822
+ const candidates = [];
1823
+ const textW = Math.max(...labels.map((label, index) => estimateTextWidth(
1824
+ label,
1825
+ index === 0 ? LABEL_FONT_JA : LABEL_FONT_EN
1826
+ )));
1827
+ const textH = labels.length > 1 ? LABEL_FONT_JA + LABEL_FONT_EN + LABEL_GAP : LABEL_FONT_JA;
1828
+ for (let i = 0; i < edge.points.length - 1; i++) {
1829
+ const a = edge.points[i];
1830
+ const b = edge.points[i + 1];
1831
+ const dx = b[0] - a[0];
1832
+ const dy = b[1] - a[1];
1833
+ const len = Math.abs(dx) + Math.abs(dy);
1834
+ if (len < 8) continue;
1835
+ for (const t of [0.5, 0.35, 0.65, 0.22, 0.78]) {
1836
+ const x = a[0] + dx * t;
1837
+ const y = a[1] + dy * t;
1838
+ for (const offsetStep of LABEL_OFFSET_STEPS) {
1839
+ const offset = LABEL_OFFSET + offsetStep;
1840
+ const offsetPenalty = offsetStep * 9;
1841
+ if (Math.abs(dy) < EPS2) {
1842
+ candidates.push(makeHorizontalLabel(edge, laid, occupiedLabels, labels, textW, textH, a, b, x, y, "above", offset, offsetPenalty));
1843
+ candidates.push(makeHorizontalLabel(edge, laid, occupiedLabels, labels, textW, textH, a, b, x, y, "below", offset, offsetPenalty));
1844
+ } else if (Math.abs(dx) < EPS2) {
1845
+ candidates.push(makeVerticalLabel(edge, laid, occupiedLabels, labels, textW, textH, a, b, x, y, "right", offset, offsetPenalty));
1846
+ candidates.push(makeVerticalLabel(edge, laid, occupiedLabels, labels, textW, textH, a, b, x, y, "left", offset, offsetPenalty));
1847
+ }
1848
+ }
1849
+ }
1850
+ }
1851
+ if (candidates.length === 0) {
1852
+ const [a, b] = longestSegment(edge.points);
1853
+ const x = (a[0] + b[0]) / 2;
1854
+ const y = (a[1] + b[1]) / 2;
1855
+ return Math.abs(b[1] - a[1]) >= Math.abs(b[0] - a[0]) ? makeVerticalLabel(edge, laid, occupiedLabels, labels, textW, textH, a, b, x, y, "right", LABEL_OFFSET, 0) : makeHorizontalLabel(edge, laid, occupiedLabels, labels, textW, textH, a, b, x, y, "above", LABEL_OFFSET, 0);
1856
+ }
1857
+ candidates.sort((a, b) => a.score - b.score);
1858
+ return candidates[0];
1859
+ }
1860
+ function makeHorizontalLabel(edge, laid, occupiedLabels, labels, textW, textH, lineA, lineB, x, lineY, side, offset, offsetPenalty) {
1861
+ const top = side === "above" ? lineY - offset - textH : lineY + offset;
1862
+ const box = { x: x - textW / 2, y: top, w: textW, h: textH };
1863
+ let jaY;
1864
+ let enY;
1865
+ if (labels.length > 1) {
1866
+ jaY = top + LABEL_FONT_JA;
1867
+ enY = jaY + LABEL_GAP + LABEL_FONT_EN;
1868
+ } else {
1869
+ jaY = top + LABEL_FONT_JA;
1870
+ }
1871
+ const placement = {
1872
+ box,
1873
+ lineA,
1874
+ lineB,
1875
+ score: 0,
1876
+ vertical: false,
1877
+ anchor: "middle",
1878
+ ja: { x, y: jaY, baseline: "alphabetic" },
1879
+ en: enY === void 0 ? void 0 : { x, y: enY, baseline: "alphabetic" }
1880
+ };
1881
+ placement.score = labelPlacementScore(placement, edge, laid, occupiedLabels) + (side === "above" ? 0 : 8) + offsetPenalty;
1882
+ return placement;
1883
+ }
1884
+ function makeVerticalLabel(edge, laid, occupiedLabels, labels, textW, textH, lineA, lineB, lineX, y, side, offset, offsetPenalty) {
1885
+ const x = side === "right" ? lineX + offset : lineX - offset;
1886
+ const box = {
1887
+ x: side === "right" ? x : x - textW,
1888
+ y: y - textH / 2,
1889
+ w: textW,
1890
+ h: textH
1891
+ };
1892
+ const anchor = side === "right" ? "start" : "end";
1893
+ const jaY = labels.length > 1 ? y - LABEL_FONT_JA / 2 - LABEL_GAP / 2 : y;
1894
+ const enY = labels.length > 1 ? y + LABEL_FONT_EN / 2 + LABEL_GAP / 2 : void 0;
1895
+ const placement = {
1896
+ box,
1897
+ lineA,
1898
+ lineB,
1899
+ score: 0,
1900
+ vertical: true,
1901
+ anchor,
1902
+ ja: { x, y: jaY, baseline: "middle" },
1903
+ en: enY === void 0 ? void 0 : { x, y: enY, baseline: "middle" }
1904
+ };
1905
+ placement.score = labelPlacementScore(placement, edge, laid, occupiedLabels) + (side === "right" ? 0 : 8) + offsetPenalty;
1906
+ return placement;
1907
+ }
1908
+ function labelPlacementScore(placement, edge, laid, occupiedLabels) {
1909
+ let score = placement.vertical ? 5 : 0;
1910
+ const box = placement.box;
1911
+ if (box.x < 0) score += Math.abs(box.x) * 300;
1912
+ if (box.y < 0) score += Math.abs(box.y) * 300;
1913
+ if (box.x + box.w > laid.width) score += (box.x + box.w - laid.width) * 300;
1914
+ if (box.y + box.h > laid.height) score += (box.y + box.h - laid.height) * 300;
1915
+ for (const node of laid.nodes) {
1916
+ if (node.isContainer) {
1917
+ const title = { x: node.x, y: node.y, w: node.w, h: 6 };
1918
+ score += rectOverlapArea(box, title) * 900;
1919
+ score += rectBorderOverlapPenalty(box, node) * 1800;
1920
+ score += rectBorderBandOverlapArea(box, node, 1.8) * 9e3;
1921
+ } else {
1922
+ const bodyOverlap = rectOverlapArea(box, node);
1923
+ if (bodyOverlap > 0) score += 1e6 + bodyOverlap * 5e4;
1924
+ score += rectOverlapArea(box, expandBox(node, 1.8)) * 5200;
1925
+ }
1926
+ }
1927
+ for (const other of laid.edges) {
1928
+ for (let i = 0; i < other.points.length - 1; i++) {
1929
+ const a = other.points[i];
1930
+ const b = other.points[i + 1];
1931
+ const overlap = segmentIntersectsRect(a, b, expandBox(box, 0.8));
1932
+ if (!overlap) continue;
1933
+ score += other === edge ? 35 : 380;
1934
+ }
1935
+ }
1936
+ for (const occupied of occupiedLabels) {
1937
+ score += rectOverlapArea(box, occupied) * 18e3;
1938
+ }
1939
+ const segmentLen = Math.abs(placement.lineA[0] - placement.lineB[0]) + Math.abs(placement.lineA[1] - placement.lineB[1]);
1940
+ const labelSpan = placement.vertical ? box.h : box.w;
1941
+ score += Math.max(0, Math.max(36, labelSpan * 2.4) - segmentLen) * 80;
1942
+ const center = placement.vertical ? box.y + box.h / 2 : box.x + box.w / 2;
1943
+ const start = placement.vertical ? placement.lineA[1] : placement.lineA[0];
1944
+ const end = placement.vertical ? placement.lineB[1] : placement.lineB[0];
1945
+ const endpointDistance = Math.min(Math.abs(center - start), Math.abs(center - end));
1946
+ score += Math.max(0, 12 - endpointDistance) * 80;
1947
+ return score;
1948
+ }
1949
+ function estimateTextWidth(text, fontSize) {
1950
+ let width = 0;
1951
+ for (const char of text) {
1952
+ if (char === " ") width += fontSize * 0.35;
1953
+ else if (char.charCodeAt(0) <= 127) width += fontSize * 0.58;
1954
+ else width += fontSize;
1955
+ }
1956
+ return Math.max(fontSize * 2, width);
1957
+ }
1958
+ function longestSegment(pts) {
1959
+ let best = 0;
1960
+ let bestLen = -1;
1961
+ for (let i = 0; i < pts.length - 1; i++) {
1962
+ const dx = pts[i + 1][0] - pts[i][0];
1963
+ const dy = pts[i + 1][1] - pts[i][1];
1964
+ const len = dx * dx + dy * dy;
1965
+ if (len > bestLen) {
1966
+ bestLen = len;
1967
+ best = i;
1968
+ }
1969
+ }
1970
+ const a = pts[best];
1971
+ const b = pts[best + 1];
1972
+ return [a, b];
1973
+ }
1974
+ function expandBox(box, pad) {
1975
+ return { x: box.x - pad, y: box.y - pad, w: box.w + pad * 2, h: box.h + pad * 2 };
1976
+ }
1977
+ function rectOverlapArea(a, b) {
1978
+ const x = Math.max(0, Math.min(a.x + a.w, b.x + b.w) - Math.max(a.x, b.x));
1979
+ const y = Math.max(0, Math.min(a.y + a.h, b.y + b.h) - Math.max(a.y, b.y));
1980
+ return x * y;
1981
+ }
1982
+ function rectBorderOverlapPenalty(a, b) {
1983
+ const nearLeft = Math.abs(a.x - b.x) < 1 || Math.abs(a.x + a.w - b.x) < 1;
1984
+ const nearRight = Math.abs(a.x - (b.x + b.w)) < 1 || Math.abs(a.x + a.w - (b.x + b.w)) < 1;
1985
+ const nearTop = Math.abs(a.y - b.y) < 1 || Math.abs(a.y + a.h - b.y) < 1;
1986
+ const nearBottom = Math.abs(a.y - (b.y + b.h)) < 1 || Math.abs(a.y + a.h - (b.y + b.h)) < 1;
1987
+ return Number(nearLeft || nearRight || nearTop || nearBottom);
1988
+ }
1989
+ function rectBorderBandOverlapArea(a, b, band) {
1990
+ return rectOverlapArea(a, { x: b.x - band, y: b.y - band, w: band * 2, h: b.h + band * 2 }) + rectOverlapArea(a, { x: b.x + b.w - band, y: b.y - band, w: band * 2, h: b.h + band * 2 }) + rectOverlapArea(a, { x: b.x - band, y: b.y - band, w: b.w + band * 2, h: band * 2 }) + rectOverlapArea(a, { x: b.x - band, y: b.y + b.h - band, w: b.w + band * 2, h: band * 2 });
1991
+ }
1992
+ function segmentIntersectsRect(a, b, box) {
1993
+ const left = box.x;
1994
+ const right = box.x + box.w;
1995
+ const top = box.y;
1996
+ const bottom = box.y + box.h;
1997
+ if (Math.abs(a[0] - b[0]) < EPS2) {
1998
+ const x = a[0];
1999
+ if (x <= left || x >= right) return false;
2000
+ return intervalOverlap2(a[1], b[1], top, bottom) > EPS2;
2001
+ }
2002
+ if (Math.abs(a[1] - b[1]) < EPS2) {
2003
+ const y = a[1];
2004
+ if (y <= top || y >= bottom) return false;
2005
+ return intervalOverlap2(a[0], b[0], left, right) > EPS2;
2006
+ }
2007
+ return false;
2008
+ }
2009
+ function intervalOverlap2(a1, a2, b1, b2) {
2010
+ const minA = Math.min(a1, a2);
2011
+ const maxA = Math.max(a1, a2);
2012
+ const minB = Math.min(b1, b2);
2013
+ const maxB = Math.max(b1, b2);
2014
+ return Math.max(0, Math.min(maxA, maxB) - Math.max(minA, minB));
2015
+ }
2016
+
2017
+ // src/core/refs.ts
2018
+ function refsToMarkdown(doc) {
2019
+ const ids = sortedIds(doc);
2020
+ const lines = [];
2021
+ lines.push("## \u7B26\u53F7\u306E\u8AAC\u660E / Reference Signs");
2022
+ lines.push("");
2023
+ lines.push("| \u7B26\u53F7 | \u540D\u79F0(\u65E5\u672C\u8A9E) | Name (English) |");
2024
+ lines.push("|------|---------------|----------------|");
2025
+ for (const id of ids) {
2026
+ const n = doc.nodes.get(id);
2027
+ lines.push(`| ${id} | ${n.label.ja ?? ""} | ${n.label.en ?? ""} |`);
2028
+ }
2029
+ return lines.join("\n");
2030
+ }
2031
+ function refsToCsv(doc) {
2032
+ const ids = sortedIds(doc);
2033
+ const lines = ["id,ja,en"];
2034
+ for (const id of ids) {
2035
+ const n = doc.nodes.get(id);
2036
+ lines.push(`${csv(id)},${csv(n.label.ja ?? "")},${csv(n.label.en ?? "")}`);
2037
+ }
2038
+ return lines.join("\n");
2039
+ }
2040
+ function csv(s) {
2041
+ return /[",\n]/.test(s) ? '"' + s.replace(/"/g, '""') + '"' : s;
2042
+ }
2043
+ function sortedIds(doc) {
2044
+ const ids = [...doc.nodes.keys()].filter((id) => id !== "*");
2045
+ return ids.sort((a, b) => {
2046
+ const na = parseInt(a, 10);
2047
+ const nb = parseInt(b, 10);
2048
+ if (!isNaN(na) && !isNaN(nb)) return na - nb;
2049
+ return a.localeCompare(b);
2050
+ });
2051
+ }
2052
+
2053
+ // src/core/samples.ts
2054
+ var SAMPLE_ORDER = [
2055
+ "block",
2056
+ "system",
2057
+ "iot",
2058
+ "imagePipeline",
2059
+ "controlLoop",
2060
+ "flow",
2061
+ "state",
2062
+ "seq",
2063
+ "handshake"
2064
+ ];
2065
+ var SAMPLES = {
2066
+ block: {
2067
+ label: "\u30D6\u30ED\u30C3\u30AF\u56F3",
2068
+ hint: "\u300C:\u300D\u3092\u4F7F\u3046\u3068\u30D6\u30ED\u30C3\u30AF\u56F3\u306B\u306A\u308B",
2069
+ source: `# \u30D6\u30ED\u30C3\u30AF\u56F3(\u88C5\u7F6E\u30AF\u30EC\u30FC\u30E0\u7528)
2070
+ # \u30D2\u30F3\u30C8:\u300C:\u300D\u3067\u5305\u542B\u95A2\u4FC2\u3092\u66F8\u304F \u2192 \u30D6\u30ED\u30C3\u30AF\u56F3\u3068\u5224\u5B9A\u3055\u308C\u308B
2071
+
2072
+ 10 = \u5236\u5FA1\u88C5\u7F6E / control device
2073
+ 11 = CPU
2074
+ 12 = \u30E1\u30E2\u30EA / memory
2075
+ 13 = "I/O \u30A4\u30F3\u30BF\u30FC\u30D5\u30A7\u30FC\u30B9" / "I/O interface"
2076
+ 20 = \u5916\u90E8\u6A5F\u5668 / external device
2077
+
2078
+ 10 : 11 12 13
2079
+
2080
+ 11 - 12
2081
+ 11 - 13
2082
+ 13 -> 20 : \u4FE1\u53F7 / signal
2083
+ `
2084
+ },
2085
+ system: {
2086
+ label: "\u30B7\u30B9\u30C6\u30E0\u5168\u4F53",
2087
+ hint: "\u968E\u5C64\u69CB\u6210 + \u5916\u90E8\u63A5\u7D9A",
2088
+ source: `# \u30B7\u30B9\u30C6\u30E0\u5168\u4F53(\u968E\u5C64+\u5916\u90E8\u63A5\u7D9A)
2089
+ # \u30D2\u30F3\u30C8: \u5185\u90E8\u30D6\u30ED\u30C3\u30AF\u3068\u5916\u90E8\u88C5\u7F6E\u3092\u5206\u3051\u3066\u66F8\u304F\u3068\u3001\u7279\u8A31\u56F3\u9762\u3089\u3057\u3044\u69CB\u6210\u306B\u306A\u308B
2090
+
2091
+ 100 = \u30B7\u30B9\u30C6\u30E0\u672C\u4F53 / Main system
2092
+ 10 = \u5236\u5FA1\u90E8 / Control
2093
+ 20 = \u901A\u4FE1\u90E8 / Comm
2094
+ 11 = CPU
2095
+ 12 = \u30E1\u30E2\u30EA / memory
2096
+ 21 = \u7121\u7DDA\u90E8 / wireless
2097
+ 22 = \u6709\u7DDA\u90E8 / wired
2098
+ 30 = \u5916\u90E8\u30B5\u30FC\u30D0 / external server
2099
+ 40 = \u5916\u90E8\u7AEF\u672B / external terminal
2100
+
2101
+ 100 : 10 20
2102
+ 10 : 11 12
2103
+ 20 : 21 22
2104
+
2105
+ 21 .> 40 : \u7121\u7DDA / wireless
2106
+ 22 -> 30 : \u6709\u7DDA / wired
2107
+ 30 <-> 40 : \u901A\u4FE1 / comm
2108
+ `
2109
+ },
2110
+ iot: {
2111
+ label: "IoT/\u30AF\u30E9\u30A6\u30C9",
2112
+ hint: "\u30BB\u30F3\u30B5\u7AEF\u672B\u30FB\u30B2\u30FC\u30C8\u30A6\u30A7\u30A4\u30FB\u30AF\u30E9\u30A6\u30C9\u306E\u5178\u578B\u69CB\u6210",
2113
+ source: `# IoT/\u30AF\u30E9\u30A6\u30C9\u69CB\u6210
2114
+ # \u30D2\u30F3\u30C8: \u7AEF\u672B\u5185\u90E8\u306E\u69CB\u6210\u3068\u30AF\u30E9\u30A6\u30C9\u5074\u306E\u69CB\u6210\u3092\u5225\u30B3\u30F3\u30C6\u30CA\u306B\u3059\u308B
2115
+
2116
+ 100 = \u30BB\u30F3\u30B5\u7AEF\u672B / sensor terminal
2117
+ 10 = \u691C\u51FA\u90E8 / detector
2118
+ 11 = \u6E29\u5EA6\u30BB\u30F3\u30B5 / temperature sensor
2119
+ 12 = \u52A0\u901F\u5EA6\u30BB\u30F3\u30B5 / acceleration sensor
2120
+ 20 = \u51E6\u7406\u90E8 / processor
2121
+ 21 = \u53D6\u5F97\u90E8 / acquisition unit
2122
+ 22 = \u5224\u5B9A\u90E8 / determination unit
2123
+ 30 = \u901A\u4FE1\u90E8 / communication unit
2124
+ 200 = \u30B2\u30FC\u30C8\u30A6\u30A7\u30A4 / gateway
2125
+ 300 = \u30AF\u30E9\u30A6\u30C9\u30B5\u30FC\u30D0 / cloud server
2126
+ 310 = \u53D7\u4FE1\u90E8 / receiver
2127
+ 320 = \u89E3\u6790\u90E8 / analyzer
2128
+ 330 = \u8A18\u61B6\u90E8 / storage
2129
+
2130
+ 100 : 10 20 30
2131
+ 10 : 11 12
2132
+ 20 : 21 22
2133
+ 300 : 310 320 330
2134
+
2135
+ 10 -> 20 : \u30BB\u30F3\u30B5\u5024 / sensor value
2136
+ 20 -> 30 : \u9001\u4FE1\u30C7\u30FC\u30BF / transmission data
2137
+ 30 .> 200 : \u7121\u7DDA / wireless
2138
+ 200 -> 310 : \u4E2D\u7D99 / relay
2139
+ 310 -> 320 : \u30C7\u30FC\u30BF / data
2140
+ 320 -> 330 : \u7D50\u679C / result
2141
+ `
2142
+ },
2143
+ imagePipeline: {
2144
+ label: "\u753B\u50CF\u51E6\u7406",
2145
+ hint: "\u753B\u50CF\u5165\u529B\u304B\u3089\u5224\u5B9A\u7D50\u679C\u307E\u3067\u306E\u51E6\u7406\u30D1\u30A4\u30D7\u30E9\u30A4\u30F3",
2146
+ source: `# \u753B\u50CF\u51E6\u7406\u30D1\u30A4\u30D7\u30E9\u30A4\u30F3(\u65B9\u6CD5\u30AF\u30EC\u30FC\u30E0\u7528)
2147
+ # \u30D2\u30F3\u30C8: \u7247\u65B9\u5411\u306E\u63A5\u7D9A\u306E\u307F\u306A\u3089\u30D5\u30ED\u30FC\u30C1\u30E3\u30FC\u30C8\u3068\u3057\u3066\u63CF\u753B\u3055\u308C\u308B
2148
+
2149
+ S100 = \u753B\u50CF\u3092\u53D6\u5F97 / Acquire image
2150
+ S110 = \u524D\u51E6\u7406 / Preprocess
2151
+ S120 = \u7279\u5FB4\u91CF\u3092\u62BD\u51FA / Extract features
2152
+ S130 = \u6B20\u9665\u3042\u308A? / Defect?
2153
+ S140 = \u30A2\u30E9\u30FC\u30C8\u3092\u51FA\u529B / Output alert
2154
+ S150 = \u6B63\u5E38\u7D50\u679C\u3092\u8A18\u9332 / Record normal result
2155
+ S160 = \u7D42\u4E86 / End
2156
+
2157
+ S100 -> S110
2158
+ S110 -> S120
2159
+ S120 -> S130
2160
+ S130 -> S140 : Yes
2161
+ S130 -> S150 : No
2162
+ S140 -> S160
2163
+ S150 -> S160
2164
+ `
2165
+ },
2166
+ controlLoop: {
2167
+ label: "\u5236\u5FA1\u30EB\u30FC\u30D7",
2168
+ hint: "\u30BB\u30F3\u30B5\u30FB\u5236\u5FA1\u5668\u30FB\u30A2\u30AF\u30C1\u30E5\u30A8\u30FC\u30BF\u306E\u30D5\u30A3\u30FC\u30C9\u30D0\u30C3\u30AF\u69CB\u6210",
2169
+ source: `# \u5236\u5FA1\u30EB\u30FC\u30D7(\u88C5\u7F6E\u30AF\u30EC\u30FC\u30E0\u7528)
2170
+ # \u30D2\u30F3\u30C8: \u30EB\u30FC\u30D7\u3059\u308B\u69CB\u6210\u306F\u30011\u3064\u306E\u30B7\u30B9\u30C6\u30E0\u5185\u3067\u6642\u8A08\u56DE\u308A\u306B\u4E26\u3079\u308B\u3068\u8AAD\u307F\u3084\u3059\u3044
2171
+
2172
+ 100 = \u5236\u5FA1\u30B7\u30B9\u30C6\u30E0 / control system
2173
+ 10 = \u5236\u5FA1\u90E8 / controller
2174
+ 11 = \u76EE\u6A19\u5024\u53D6\u5F97\u90E8 / target acquisition unit
2175
+ 12 = \u504F\u5DEE\u7B97\u51FA\u90E8 / error calculator
2176
+ 13 = \u6307\u4EE4\u751F\u6210\u90E8 / command generator
2177
+ 20 = \u99C6\u52D5\u90E8 / driver
2178
+ 30 = \u30BB\u30F3\u30B5\u90E8 / sensor unit
2179
+ 40 = \u5BFE\u8C61\u88C5\u7F6E / controlled object
2180
+
2181
+ 100 : 30 10 40 20
2182
+ 10 : 11 12 13
2183
+
2184
+ 30 -> 12 : \u6E2C\u5B9A\u5024 / measured value
2185
+ 11 -> 12 : \u76EE\u6A19\u5024 / target value
2186
+ 12 -> 13 : \u504F\u5DEE / error
2187
+ 13 -> 20 : \u6307\u4EE4 / command
2188
+ 20 -> 40 : \u99C6\u52D5\u4FE1\u53F7 / drive signal
2189
+ 40 .> 30 : \u30D5\u30A3\u30FC\u30C9\u30D0\u30C3\u30AF / feedback
2190
+ `
2191
+ },
2192
+ flow: {
2193
+ label: "\u30D5\u30ED\u30FC\u30C1\u30E3\u30FC\u30C8",
2194
+ hint: "\u30E9\u30D9\u30EB\u306B\u300C?\u300D\u304C\u3042\u308B \u2192 \u30D5\u30ED\u30FC\u3068\u5224\u5B9A",
2195
+ source: `# \u30D5\u30ED\u30FC\u30C1\u30E3\u30FC\u30C8(\u65B9\u6CD5\u30AF\u30EC\u30FC\u30E0\u7528)
2196
+ # \u30D2\u30F3\u30C8:\u30E9\u30D9\u30EB\u672B\u5C3E\u306B\u300C?\u300D \u2192 \u83F1\u5F62(\u6761\u4EF6\u5206\u5C90)\u306B\u81EA\u52D5\u63A8\u8AD6
2197
+
2198
+ S100 = \u958B\u59CB / Start
2199
+ S110 = \u6761\u4EF6A? / "Condition A?"
2200
+ S120 = \u51E6\u7406X / Process X
2201
+ S130 = \u51E6\u7406Y / Process Y
2202
+ S140 = \u7D42\u4E86 / End
2203
+
2204
+ S100 -> S110
2205
+ S110 -> S120 : Yes
2206
+ S110 -> S130 : No
2207
+ S120 -> S140
2208
+ S130 -> S140
2209
+ `
2210
+ },
2211
+ state: {
2212
+ label: "\u72B6\u614B\u9077\u79FB\u56F3",
2213
+ hint: "\u300C*\u300D\u3092\u4F7F\u3046 \u2192 \u72B6\u614B\u9077\u79FB\u3068\u5224\u5B9A",
2214
+ source: `# \u72B6\u614B\u9077\u79FB\u56F3
2215
+ # \u30D2\u30F3\u30C8:\u300C*\u300D\u304C\u521D\u671F/\u7D42\u7AEF\u306E\u7B26\u53F7(\u9ED2\u4E38\u3067\u63CF\u753B)
2216
+
2217
+ S1 = \u5F85\u6A5F / Idle
2218
+ S2 = \u52D5\u4F5C\u4E2D / Running
2219
+ S3 = \u30A8\u30E9\u30FC / Error
2220
+
2221
+ * -> S1
2222
+ S1 -> S2 : \u8D77\u52D5 / start
2223
+ S2 -> S1 : \u505C\u6B62 / stop
2224
+ S2 -> S3 : \u7570\u5E38 / fault
2225
+ S3 -> S1 : \u30EA\u30BB\u30C3\u30C8 / reset
2226
+ S3 -> *
2227
+ `
2228
+ },
2229
+ seq: {
2230
+ label: "\u30B7\u30FC\u30B1\u30F3\u30B9\u56F3",
2231
+ hint: "\u300C:\u300D\u3082\u300C?\u300D\u3082\u300C*\u300D\u3082\u306A\u3044 \u2192 \u30B7\u30FC\u30B1\u30F3\u30B9",
2232
+ source: `# \u30B7\u30FC\u30B1\u30F3\u30B9\u56F3(\u30D7\u30ED\u30C8\u30B3\u30EB\u7CFB)
2233
+ # \u30D2\u30F3\u30C8:\u5305\u542B\u3082?\u3082*\u3082\u7121\u3044 \u2192 \u30B7\u30FC\u30B1\u30F3\u30B9\u56F3\u3068\u3057\u3066\u63CF\u753B
2234
+ # \u30A2\u30AF\u30BF\u306F\u767B\u5834\u9806\u306B\u5DE6\u304B\u3089\u4E26\u3076
2235
+
2236
+ 100 = \u30AF\u30E9\u30A4\u30A2\u30F3\u30C8 / client
2237
+ 200 = \u30B5\u30FC\u30D0 / server
2238
+
2239
+ 100 -> 200 : \u8A8D\u8A3C\u8981\u6C42 / auth request
2240
+ 200 -> 100 : \u30C8\u30FC\u30AF\u30F3 / token
2241
+ 100 -> 200 : \u30EA\u30BD\u30FC\u30B9\u8981\u6C42 / resource request
2242
+ 200 -> 100 : \u30EA\u30BD\u30FC\u30B9\u5FDC\u7B54 / resource response
2243
+ `
2244
+ },
2245
+ handshake: {
2246
+ label: "\u30CF\u30F3\u30C9\u30B7\u30A7\u30A4\u30AF",
2247
+ hint: "\u8981\u6C42/\u5FDC\u7B54/\u78BA\u7ACB/\u7D42\u4E86\u306E\u901A\u4FE1\u30B7\u30FC\u30B1\u30F3\u30B9",
2248
+ source: `# \u901A\u4FE1\u30CF\u30F3\u30C9\u30B7\u30A7\u30A4\u30AF(\u30B7\u30FC\u30B1\u30F3\u30B9\u56F3)
2249
+ # \u30D2\u30F3\u30C8: \u5F80\u5FA9\u30E1\u30C3\u30BB\u30FC\u30B8\u304C\u3042\u308B\u3068\u30B7\u30FC\u30B1\u30F3\u30B9\u56F3\u306B\u306A\u308B
2250
+
2251
+ 100 = \u7AEF\u672B / terminal
2252
+ 200 = \u30B5\u30FC\u30D0 / server
2253
+
2254
+ 100 -> 200 : \u63A5\u7D9A\u8981\u6C42 / connect request
2255
+ 200 -> 100 : \u5FDC\u7B54 / response
2256
+ 100 -> 200 : \u8A8D\u8A3C\u60C5\u5831 / credentials
2257
+ 200 -> 100 : \u8A8D\u8A3C\u7D50\u679C / auth result
2258
+ 100 <-> 200 : \u30C7\u30FC\u30BF\u901A\u4FE1 / data exchange
2259
+ 100 -> 200 : \u5207\u65AD\u8981\u6C42 / disconnect
2260
+ 200 -> 100 : \u5207\u65AD\u5B8C\u4E86 / disconnected
2261
+ `
2262
+ }
2263
+ };
2264
+
2265
+ // src/core/patterns.ts
2266
+ var PATTERN_LABEL = {
2267
+ cond: "\u6761\u4EF6\u5206\u5C90",
2268
+ container: "\u89AA\u5B50\u69CB\u6210",
2269
+ external: "\u89AA\u5B50+\u5916\u90E8",
2270
+ seq: "\u30B7\u30FC\u30B1\u30F3\u30B9",
2271
+ state: "\u72B6\u614B\u9077\u79FB",
2272
+ bidir: "\u53CC\u65B9\u5411\u901A\u4FE1",
2273
+ hierarchy: "3\u968E\u5C64\u69CB\u6210",
2274
+ pipeline: "N\u6BB5\u968E\u51E6\u7406",
2275
+ parallel: "\u4E26\u5217\u51E6\u7406",
2276
+ handshake: "\u901A\u4FE1\u30CF\u30F3\u30C9\u30B7\u30A7\u30A4\u30AF",
2277
+ state_with_cond: "\u72B6\u614B\u5224\u5B9A\u30D5\u30ED\u30FC",
2278
+ system: "\u30B7\u30B9\u30C6\u30E0\u5168\u4F53"
2279
+ };
2280
+ var PATTERN_SOURCE = {
2281
+ cond: `S100 = \u958B\u59CB / Start
2282
+ S110 = \u5224\u5B9A? / Decision?
2283
+ S120 = Yes\u51E6\u7406 / Yes Process
2284
+ S130 = No\u51E6\u7406 / No Process
2285
+ S140 = \u7D42\u4E86 / End
2286
+ S100 -> S110
2287
+ S110 -> S120 : Yes
2288
+ S110 -> S130 : No
2289
+ S120 -> S140
2290
+ S130 -> S140`,
2291
+ container: `10 = \u89AA\u88C5\u7F6E / Parent
2292
+ 11 = \u90E8\u54C11 / Part 1
2293
+ 12 = \u90E8\u54C12 / Part 2
2294
+ 13 = \u90E8\u54C13 / Part 3
2295
+ 10 : 11 12 13`,
2296
+ external: `10 = \u5236\u5FA1\u88C5\u7F6E / control device
2297
+ 11 = \u5165\u529B\u90E8 / input
2298
+ 12 = \u51E6\u7406\u90E8 / processor
2299
+ 20 = \u5916\u90E8\u6A5F\u5668 / external device
2300
+ 10 : 11 12
2301
+ 12 -> 20 : \u4FE1\u53F7 / signal`,
2302
+ seq: `100 = \u30AF\u30E9\u30A4\u30A2\u30F3\u30C8 / client
2303
+ 200 = \u30B5\u30FC\u30D0 / server
2304
+ 100 -> 200 : \u8981\u6C42 / request
2305
+ 200 -> 100 : \u5FDC\u7B54 / response
2306
+ 100 -> 200 : \u5207\u65AD / disconnect`,
2307
+ state: `S1 = \u5F85\u6A5F / Idle
2308
+ S2 = \u52D5\u4F5C\u4E2D / Running
2309
+ * -> S1
2310
+ S1 -> S2 : \u8D77\u52D5 / start
2311
+ S2 -> S1 : \u505C\u6B62 / stop`,
2312
+ bidir: `100 = \u7AEF\u672BA / Terminal A
2313
+ 200 = \u7AEF\u672BB / Terminal B
2314
+ 100 <-> 200 : \u901A\u4FE1 / comm`,
2315
+ hierarchy: `100 = \u30B7\u30B9\u30C6\u30E0 / System
2316
+ 10 = \u30B5\u30D6\u30B7\u30B9\u30C6\u30E0A / Subsystem A
2317
+ 20 = \u30B5\u30D6\u30B7\u30B9\u30C6\u30E0B / Subsystem B
2318
+ 11 = \u90E8\u54C1A1 / Part A1
2319
+ 12 = \u90E8\u54C1A2 / Part A2
2320
+ 21 = \u90E8\u54C1B1 / Part B1
2321
+ 22 = \u90E8\u54C1B2 / Part B2
2322
+ 100 : 10 20
2323
+ 10 : 11 12
2324
+ 20 : 21 22`,
2325
+ pipeline: `S100 = \u5165\u529B / Input
2326
+ S110 = \u524D\u51E6\u7406 / Preprocess
2327
+ S120 = \u4E3B\u51E6\u7406 / Main Process
2328
+ S130 = \u5F8C\u51E6\u7406 / Postprocess
2329
+ S140 = \u51FA\u529B / Output
2330
+ S100 -> S110
2331
+ S110 -> S120
2332
+ S120 -> S130
2333
+ S130 -> S140`,
2334
+ parallel: `S100 = \u958B\u59CB / Start
2335
+ S110 = \u5206\u5C90 / Branch
2336
+ S120 = \u7D4C\u8DEFA / Path A
2337
+ S130 = \u7D4C\u8DEFB / Path B
2338
+ S140 = \u7D4C\u8DEFC / Path C
2339
+ S150 = \u5408\u6D41 / Join
2340
+ S160 = \u7D42\u4E86 / End
2341
+ S100 -> S110
2342
+ S110 -> S120
2343
+ S110 -> S130
2344
+ S110 -> S140
2345
+ S120 -> S150
2346
+ S130 -> S150
2347
+ S140 -> S150
2348
+ S150 -> S160`,
2349
+ handshake: `100 = \u7AEF\u672B / Terminal
2350
+ 200 = \u30B5\u30FC\u30D0 / Server
2351
+ 100 -> 200 : SYN\u8981\u6C42 / SYN
2352
+ 200 -> 100 : SYN+ACK / SYN+ACK
2353
+ 100 -> 200 : ACK\u5FDC\u7B54 / ACK
2354
+ 100 <-> 200 : \u30C7\u30FC\u30BF\u4EA4\u63DB / data exchange
2355
+ 100 -> 200 : FIN / FIN
2356
+ 200 -> 100 : ACK / ACK`,
2357
+ state_with_cond: `S100 = \u5F85\u6A5F\u72B6\u614B / Idle state
2358
+ S110 = \u691C\u8A3C\u51E6\u7406 / Verify
2359
+ S120 = OK? / OK?
2360
+ S130 = \u5B8C\u4E86\u72B6\u614B / Done state
2361
+ S140 = \u5931\u6557\u72B6\u614B / Failed state
2362
+ S150 = \u518D\u8A66\u884C / Retry
2363
+ S100 -> S110 : \u8981\u6C42 / request
2364
+ S110 -> S120 : \u5224\u5B9A / check
2365
+ S120 -> S130 : OK
2366
+ S120 -> S140 : NG
2367
+ S140 -> S150
2368
+ S150 -> S100`,
2369
+ system: `100 = \u30B7\u30B9\u30C6\u30E0\u672C\u4F53 / Main system
2370
+ 10 = \u5236\u5FA1\u90E8 / Control
2371
+ 20 = \u901A\u4FE1\u90E8 / Comm
2372
+ 11 = CPU
2373
+ 12 = \u30E1\u30E2\u30EA / memory
2374
+ 21 = \u7121\u7DDA\u90E8 / wireless
2375
+ 22 = \u6709\u7DDA\u90E8 / wired
2376
+ 30 = \u5916\u90E8\u30B5\u30FC\u30D0 / external server
2377
+ 40 = \u5916\u90E8\u7AEF\u672B / external terminal
2378
+ 100 : 10 20
2379
+ 10 : 11 12
2380
+ 20 : 21 22
2381
+ 21 .> 40 : \u7121\u7DDA / wireless
2382
+ 22 -> 30 : \u6709\u7DDA / wired
2383
+ 30 <-> 40 : \u901A\u4FE1 / comm`
2384
+ };
2385
+
2386
+ // src/node/index.ts
2387
+ var import_node_fs2 = require("fs");
2388
+
2389
+ // src/node/dom.ts
2390
+ var SVG_NS = "http://www.w3.org/2000/svg";
2391
+ var ShimElement = class {
2392
+ constructor(namespaceURI, tagName) {
2393
+ this.namespaceURI = namespaceURI;
2394
+ this.tagName = tagName;
2395
+ }
2396
+ namespaceURI;
2397
+ tagName;
2398
+ nodeType = "element";
2399
+ attrs = /* @__PURE__ */ new Map();
2400
+ children = [];
2401
+ text = null;
2402
+ setAttribute(name, value) {
2403
+ this.attrs.set(name, String(value));
2404
+ }
2405
+ getAttribute(name) {
2406
+ return this.attrs.has(name) ? this.attrs.get(name) : null;
2407
+ }
2408
+ removeAttribute(name) {
2409
+ this.attrs.delete(name);
2410
+ }
2411
+ appendChild(child) {
2412
+ this.text = null;
2413
+ this.children.push(child);
2414
+ return child;
2415
+ }
2416
+ set textContent(value) {
2417
+ this.text = value == null ? "" : String(value);
2418
+ this.children = [];
2419
+ }
2420
+ get textContent() {
2421
+ if (this.text != null) return this.text;
2422
+ return this.children.map((c) => c.textContent).join("");
2423
+ }
2424
+ };
2425
+ var shimDocument = {
2426
+ createElementNS(ns, qualifiedName) {
2427
+ return new ShimElement(ns, qualifiedName);
2428
+ },
2429
+ createElement(tagName) {
2430
+ return new ShimElement(SVG_NS, tagName);
2431
+ }
2432
+ };
2433
+ var installed = false;
2434
+ function installDomShim() {
2435
+ if (installed) return;
2436
+ const existing = globalThis.document;
2437
+ if (!existing || typeof existing.createElementNS !== "function") {
2438
+ globalThis.document = shimDocument;
2439
+ }
2440
+ installed = true;
2441
+ }
2442
+ function withShimDocument(fn) {
2443
+ const g = globalThis;
2444
+ const prev = g.document;
2445
+ g.document = shimDocument;
2446
+ try {
2447
+ return fn();
2448
+ } finally {
2449
+ g.document = prev;
2450
+ }
2451
+ }
2452
+ function escapeAttr(value) {
2453
+ return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
2454
+ }
2455
+ function escapeText(value) {
2456
+ return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
2457
+ }
2458
+ function serializeSvg(node) {
2459
+ const attrs = [...node.attrs].map(([name, value]) => ` ${name}="${escapeAttr(value)}"`).join("");
2460
+ if (node.text != null) {
2461
+ return `<${node.tagName}${attrs}>${escapeText(node.text)}</${node.tagName}>`;
2462
+ }
2463
+ if (node.children.length === 0) {
2464
+ return `<${node.tagName}${attrs}/>`;
2465
+ }
2466
+ const inner = node.children.map(serializeSvg).join("");
2467
+ return `<${node.tagName}${attrs}>${inner}</${node.tagName}>`;
2468
+ }
2469
+
2470
+ // src/node/content-box.ts
2471
+ var DEFAULT_BLEED = 3;
2472
+ function num(node, name, fallback = 0) {
2473
+ const raw = node.getAttribute(name);
2474
+ if (raw == null) return fallback;
2475
+ const n = Number.parseFloat(raw);
2476
+ return Number.isFinite(n) ? n : fallback;
2477
+ }
2478
+ function parseNumbers(text) {
2479
+ const out = [];
2480
+ const re = /-?\d*\.?\d+(?:e[-+]?\d+)?/gi;
2481
+ let m;
2482
+ while ((m = re.exec(text)) !== null) out.push(Number.parseFloat(m[0]));
2483
+ return out;
2484
+ }
2485
+ function primitiveBounds(node) {
2486
+ switch (node.tagName) {
2487
+ case "rect": {
2488
+ const x = num(node, "x");
2489
+ const y = num(node, "y");
2490
+ const w = num(node, "width");
2491
+ const h = num(node, "height");
2492
+ if (w <= 0 && h <= 0) return null;
2493
+ return { minX: x, minY: y, maxX: x + w, maxY: y + h };
2494
+ }
2495
+ case "circle": {
2496
+ const cx = num(node, "cx");
2497
+ const cy = num(node, "cy");
2498
+ const r = num(node, "r");
2499
+ if (r <= 0) return null;
2500
+ return { minX: cx - r, minY: cy - r, maxX: cx + r, maxY: cy + r };
2501
+ }
2502
+ case "line": {
2503
+ const x1 = num(node, "x1");
2504
+ const y1 = num(node, "y1");
2505
+ const x2 = num(node, "x2");
2506
+ const y2 = num(node, "y2");
2507
+ return {
2508
+ minX: Math.min(x1, x2),
2509
+ minY: Math.min(y1, y2),
2510
+ maxX: Math.max(x1, x2),
2511
+ maxY: Math.max(y1, y2)
2512
+ };
2513
+ }
2514
+ case "polygon":
2515
+ case "polyline": {
2516
+ const nums = parseNumbers(node.getAttribute("points") ?? "");
2517
+ return boundsFromPairs(nums);
2518
+ }
2519
+ case "path": {
2520
+ const nums = parseNumbers(node.getAttribute("d") ?? "");
2521
+ return boundsFromPairs(nums);
2522
+ }
2523
+ case "text": {
2524
+ return textBounds(node);
2525
+ }
2526
+ default:
2527
+ return null;
2528
+ }
2529
+ }
2530
+ function boundsFromPairs(nums) {
2531
+ if (nums.length < 2) return null;
2532
+ let minX = Infinity;
2533
+ let minY = Infinity;
2534
+ let maxX = -Infinity;
2535
+ let maxY = -Infinity;
2536
+ for (let i = 0; i + 1 < nums.length; i += 2) {
2537
+ const x = nums[i];
2538
+ const y = nums[i + 1];
2539
+ if (x < minX) minX = x;
2540
+ if (y < minY) minY = y;
2541
+ if (x > maxX) maxX = x;
2542
+ if (y > maxY) maxY = y;
2543
+ }
2544
+ if (!Number.isFinite(minX)) return null;
2545
+ return { minX, minY, maxX, maxY };
2546
+ }
2547
+ function textBounds(node) {
2548
+ const text = node.textContent;
2549
+ if (!text) return null;
2550
+ const x = num(node, "x");
2551
+ const y = num(node, "y");
2552
+ const fontSize = num(node, "font-size", 2.8);
2553
+ const width = estimateTextWidth(text, fontSize);
2554
+ const anchor = node.getAttribute("text-anchor") ?? "start";
2555
+ let minX;
2556
+ if (anchor === "middle") minX = x - width / 2;
2557
+ else if (anchor === "end") minX = x - width;
2558
+ else minX = x;
2559
+ const maxX = minX + width;
2560
+ const baseline = node.getAttribute("dominant-baseline") ?? "alphabetic";
2561
+ let minY;
2562
+ let maxY;
2563
+ if (baseline === "middle" || baseline === "central") {
2564
+ minY = y - fontSize * 0.6;
2565
+ maxY = y + fontSize * 0.6;
2566
+ } else {
2567
+ minY = y - fontSize * 0.8;
2568
+ maxY = y + fontSize * 0.25;
2569
+ }
2570
+ return { minX, minY, maxX, maxY };
2571
+ }
2572
+ function collectBounds(node, acc) {
2573
+ const b = primitiveBounds(node);
2574
+ if (b) acc.push(b);
2575
+ for (const child of node.children) collectBounds(child, acc);
2576
+ }
2577
+ function unionAll(bounds) {
2578
+ if (bounds.length === 0) return null;
2579
+ let minX = Infinity;
2580
+ let minY = Infinity;
2581
+ let maxX = -Infinity;
2582
+ let maxY = -Infinity;
2583
+ for (const b of bounds) {
2584
+ if (b.minX < minX) minX = b.minX;
2585
+ if (b.minY < minY) minY = b.minY;
2586
+ if (b.maxX > maxX) maxX = b.maxX;
2587
+ if (b.maxY > maxY) maxY = b.maxY;
2588
+ }
2589
+ return { minX, minY, maxX, maxY };
2590
+ }
2591
+ function fallbackViewBox(root) {
2592
+ const nums = parseNumbers(root.getAttribute("viewBox") ?? "");
2593
+ if (nums.length === 4 && nums[2] > 0 && nums[3] > 0) {
2594
+ return { minX: nums[0], minY: nums[1], width: nums[2], height: nums[3] };
2595
+ }
2596
+ return { minX: 0, minY: 0, width: 210, height: 297 };
2597
+ }
2598
+ function computeContentBox(root, bleed = DEFAULT_BLEED) {
2599
+ const all = [];
2600
+ collectBounds(root, all);
2601
+ const merged = unionAll(all);
2602
+ if (!merged) return fallbackViewBox(root);
2603
+ const pad = Math.max(0, bleed);
2604
+ return {
2605
+ minX: merged.minX - pad,
2606
+ minY: merged.minY - pad,
2607
+ width: Math.max(1, merged.maxX - merged.minX + pad * 2),
2608
+ height: Math.max(1, merged.maxY - merged.minY + pad * 2)
2609
+ };
2610
+ }
2611
+ function svgDisplayDimensions(viewBox, targetSide = 1600) {
2612
+ const longest = Math.max(viewBox.width, viewBox.height, 1);
2613
+ const scale = Math.max(1, targetSide / longest);
2614
+ return {
2615
+ width: Math.max(1, Math.ceil(viewBox.width * scale)),
2616
+ height: Math.max(1, Math.ceil(viewBox.height * scale)),
2617
+ scale
2618
+ };
2619
+ }
2620
+ var RASTER_SCALE = 8;
2621
+ var RASTER_MAX_SIDE = 24e3;
2622
+ var RASTER_MAX_PIXELS = 1e8;
2623
+ function rasterDimensions(viewBox, scale = RASTER_SCALE) {
2624
+ const requested = Number.isFinite(scale) && scale > 0 ? scale : RASTER_SCALE;
2625
+ const sideScale = RASTER_MAX_SIDE / Math.max(viewBox.width, viewBox.height, 1);
2626
+ const pixelScale = Math.sqrt(RASTER_MAX_PIXELS / Math.max(viewBox.width * viewBox.height, 1));
2627
+ const capped = Math.min(requested, sideScale, pixelScale);
2628
+ return {
2629
+ width: Math.max(1, Math.ceil(viewBox.width * capped)),
2630
+ height: Math.max(1, Math.ceil(viewBox.height * capped)),
2631
+ scale: capped
2632
+ };
2633
+ }
2634
+
2635
+ // src/node/svg.ts
2636
+ var SVG_NS2 = "http://www.w3.org/2000/svg";
2637
+ var DEFAULT_BLEED2 = 3;
2638
+ var DEFAULT_TARGET_SIDE = 1600;
2639
+ function buildSvgModel(source, opts = {}) {
2640
+ const {
2641
+ lang = "ja",
2642
+ crop = true,
2643
+ bleed = DEFAULT_BLEED2,
2644
+ targetSide = DEFAULT_TARGET_SIDE
2645
+ } = opts;
2646
+ const doc = parse(source);
2647
+ const laid = layout(doc);
2648
+ const svgEl = withShimDocument(() => render(laid, { lang }));
2649
+ const viewBox = crop ? computeContentBox(svgEl, bleed) : { minX: 0, minY: 0, width: laid.width, height: laid.height };
2650
+ const display = svgDisplayDimensions(viewBox, targetSide);
2651
+ svgEl.setAttribute("xmlns", SVG_NS2);
2652
+ svgEl.setAttribute("version", "1.1");
2653
+ svgEl.setAttribute(
2654
+ "viewBox",
2655
+ `${viewBox.minX} ${viewBox.minY} ${viewBox.width} ${viewBox.height}`
2656
+ );
2657
+ svgEl.setAttribute("width", String(display.width));
2658
+ svgEl.setAttribute("height", String(display.height));
2659
+ svgEl.setAttribute("preserveAspectRatio", "xMidYMid meet");
2660
+ return { el: svgEl, kind: doc.kind, viewBox, width: display.width, height: display.height };
2661
+ }
2662
+ function renderToSvg(source, opts = {}) {
2663
+ const model = buildSvgModel(source, opts);
2664
+ const body = serializeSvg(model.el);
2665
+ const svg = opts.xmlDeclaration ?? true ? `<?xml version="1.0" encoding="UTF-8"?>
2666
+ ${body}` : body;
2667
+ return {
2668
+ svg,
2669
+ kind: model.kind,
2670
+ viewBox: model.viewBox,
2671
+ width: model.width,
2672
+ height: model.height
2673
+ };
2674
+ }
2675
+
2676
+ // src/node/assets.ts
2677
+ var import_node_fs = require("fs");
2678
+ var import_node_path = require("path");
2679
+ var import_node_url = require("url");
2680
+ var import_meta = {};
2681
+ var FONT_FAMILY_NAME = "IPAexGothic";
2682
+ var cachedPath = null;
2683
+ var cachedBuffer = null;
2684
+ function moduleDir() {
2685
+ let url = "";
2686
+ try {
2687
+ url = import_meta.url;
2688
+ } catch {
2689
+ }
2690
+ if (url) {
2691
+ try {
2692
+ return (0, import_node_path.dirname)((0, import_node_url.fileURLToPath)(url));
2693
+ } catch {
2694
+ }
2695
+ }
2696
+ if (typeof __dirname !== "undefined") return __dirname;
2697
+ return process.cwd();
2698
+ }
2699
+ function resolvePackageFile(...segments) {
2700
+ let dir = moduleDir();
2701
+ for (let i = 0; i < 8; i++) {
2702
+ const candidate = (0, import_node_path.join)(dir, ...segments);
2703
+ if ((0, import_node_fs.existsSync)(candidate)) return candidate;
2704
+ const parent = (0, import_node_path.dirname)(dir);
2705
+ if (parent === dir) break;
2706
+ dir = parent;
2707
+ }
2708
+ throw new Error(
2709
+ `pdgkit: bundled file ${segments.join("/")} not found. Ensure the package was installed with its data files intact.`
2710
+ );
2711
+ }
2712
+ function resolveFontPath() {
2713
+ if (cachedPath) return cachedPath;
2714
+ cachedPath = resolvePackageFile("assets", "ipaexg.ttf");
2715
+ return cachedPath;
2716
+ }
2717
+ function loadFontBuffer() {
2718
+ if (cachedBuffer) return cachedBuffer;
2719
+ cachedBuffer = (0, import_node_fs.readFileSync)(resolveFontPath());
2720
+ return cachedBuffer;
2721
+ }
2722
+ function loadFontBase64() {
2723
+ return loadFontBuffer().toString("base64");
2724
+ }
2725
+
2726
+ // src/node/validate.ts
2727
+ var KIND_LABEL_JA = {
2728
+ block: "\u30D6\u30ED\u30C3\u30AF\u56F3",
2729
+ flow: "\u30D5\u30ED\u30FC\u30C1\u30E3\u30FC\u30C8",
2730
+ state: "\u72B6\u614B\u9077\u79FB\u56F3",
2731
+ seq: "\u30B7\u30FC\u30B1\u30F3\u30B9\u56F3"
2732
+ };
2733
+ function commentOf(line) {
2734
+ let inQuote = false;
2735
+ for (let i = 0; i < line.length; i++) {
2736
+ const c = line[i];
2737
+ if (c === '"') inQuote = !inQuote;
2738
+ else if (!inQuote && c === "#") return line.slice(i + 1);
2739
+ }
2740
+ return null;
2741
+ }
2742
+ function uncommented(line) {
2743
+ let inQuote = false;
2744
+ for (let i = 0; i < line.length; i++) {
2745
+ const c = line[i];
2746
+ if (c === '"') inQuote = !inQuote;
2747
+ else if (!inQuote && c === "#") return line.slice(0, i);
2748
+ }
2749
+ return line;
2750
+ }
2751
+ var KIND_DIRECTIVE_RE = /^\s*!?\s*(?:pdgkit\s*:)?\s*kind\s*[:=]?\s*(block|flow|state|seq)\b/i;
2752
+ function findKindDirective(lines) {
2753
+ for (let i = 0; i < lines.length; i++) {
2754
+ const comment = commentOf(lines[i]);
2755
+ if (comment == null) continue;
2756
+ const m = comment.match(KIND_DIRECTIVE_RE);
2757
+ if (m) return { kind: m[1].toLowerCase(), line: i + 1 };
2758
+ }
2759
+ return null;
2760
+ }
2761
+ var SPACED_OP_RE = /(?:^|\s)(?:<->|=>|->|<-|\.>|\.\.|-)(?:\s|$)/g;
2762
+ var GLUED_OP_RE = /[A-Za-z0-9_*](?:<->|<-|->|=>|\.>|\.\.|-)[A-Za-z0-9_*]/;
2763
+ function countSpacedOps(text) {
2764
+ const matches = text.match(SPACED_OP_RE);
2765
+ return matches ? matches.length : 0;
2766
+ }
2767
+ function validate(source) {
2768
+ const doc = parse(source);
2769
+ const diagnostics = [...doc.diagnostics];
2770
+ const lines = source.split(/\r?\n/);
2771
+ const directive = findKindDirective(lines);
2772
+ const declaredKind = directive ? directive.kind : null;
2773
+ let kindMatches = null;
2774
+ if (directive) {
2775
+ kindMatches = directive.kind === doc.kind;
2776
+ if (!kindMatches) {
2777
+ diagnostics.push({
2778
+ severity: "error",
2779
+ line: directive.line,
2780
+ col: 1,
2781
+ message: `\u56F3\u7A2E\u30A2\u30B5\u30FC\u30B7\u30E7\u30F3\u4E0D\u4E00\u81F4: \u5BA3\u8A00=${KIND_LABEL_JA[directive.kind]}(${directive.kind}) \u3060\u304C\u63A8\u8AD6=${KIND_LABEL_JA[doc.kind]}(${doc.kind})\u3002\u69CB\u9020(\u5305\u542B\u300C:\u300D/ \u672B\u5C3E\u300C?\u300D/ \u7B26\u53F7\u300C*\u300D/ \u5F80\u5FA9\u30FB\u300C<->\u300D)\u3092\u898B\u76F4\u3059\u304B\u3001\u5BA3\u8A00\u3092\u4FEE\u6B63\u3057\u3066\u304F\u3060\u3055\u3044\u3002`
2782
+ });
2783
+ }
2784
+ }
2785
+ const unknownLines = new Set(
2786
+ doc.diagnostics.filter((d) => d.severity === "error" && d.message.startsWith("\u69CB\u6587\u4E0D\u660E")).map((d) => d.line)
2787
+ );
2788
+ for (const lineNum of unknownLines) {
2789
+ const code = uncommented(lines[lineNum - 1] ?? "").trim();
2790
+ if (!code) continue;
2791
+ if (countSpacedOps(code) >= 2) {
2792
+ diagnostics.push({
2793
+ severity: "info",
2794
+ line: lineNum,
2795
+ col: 1,
2796
+ message: "\u9023\u9396\u8A18\u6CD5(A -> B -> C)\u306F\u672A\u5BFE\u5FDC\u3067\u3059\u30021\u884C\u306B1\u63A5\u7D9A\u3067\u5206\u5272\u3057\u3066\u304F\u3060\u3055\u3044(\u4F8B: A -> B / B -> C)\u3002"
2797
+ });
2798
+ } else if (GLUED_OP_RE.test(code)) {
2799
+ diagnostics.push({
2800
+ severity: "info",
2801
+ line: lineNum,
2802
+ col: 1,
2803
+ message: '\u63A5\u7D9A\u6F14\u7B97\u5B50\u306E\u524D\u5F8C\u306B\u306F\u534A\u89D2\u30B9\u30DA\u30FC\u30B9\u304C\u5FC5\u8981\u3067\u3059(\u4F8B: "11-12" \u3067\u306F\u306A\u304F "11 - 12")\u3002'
2804
+ });
2805
+ }
2806
+ }
2807
+ diagnostics.sort((a, b) => a.line - b.line || a.col - b.col);
2808
+ const counts = {
2809
+ errors: diagnostics.filter((d) => d.severity === "error").length,
2810
+ warnings: diagnostics.filter((d) => d.severity === "warning").length,
2811
+ infos: diagnostics.filter((d) => d.severity === "info").length
2812
+ };
2813
+ return {
2814
+ ok: counts.errors === 0,
2815
+ kind: doc.kind,
2816
+ declaredKind,
2817
+ kindMatches,
2818
+ diagnostics,
2819
+ counts
2820
+ };
2821
+ }
2822
+
2823
+ // src/node/raster.ts
2824
+ var JPEG_QUALITY = 100;
2825
+ async function renderPixels(source, opts) {
2826
+ const { lang = "ja", scale = 8, bleed = 3 } = opts;
2827
+ const { svg, viewBox } = renderToSvg(source, { lang, crop: true, bleed });
2828
+ const dims = rasterDimensions(viewBox, scale);
2829
+ const { Resvg } = await import("@resvg/resvg-js");
2830
+ const resvg = new Resvg(svg, {
2831
+ background: "rgba(255,255,255,1)",
2832
+ fitTo: { mode: "width", value: dims.width },
2833
+ font: {
2834
+ fontFiles: [resolveFontPath()],
2835
+ loadSystemFonts: false,
2836
+ defaultFontFamily: FONT_FAMILY_NAME
2837
+ }
2838
+ });
2839
+ const rendered = resvg.render();
2840
+ const raw = {
2841
+ pixels: rendered.pixels,
2842
+ width: rendered.width,
2843
+ height: rendered.height
2844
+ };
2845
+ return { raw, png: rendered.asPng() };
2846
+ }
2847
+ async function renderToPng(source, opts = {}) {
2848
+ const { png } = await renderPixels(source, opts);
2849
+ return png;
2850
+ }
2851
+ async function renderToJpeg(source, opts = {}) {
2852
+ const { raw } = await renderPixels(source, opts);
2853
+ const jpeg = (await import("jpeg-js")).default;
2854
+ const encoded = jpeg.encode(
2855
+ { data: Buffer.from(raw.pixels), width: raw.width, height: raw.height },
2856
+ JPEG_QUALITY
2857
+ );
2858
+ return encoded.data;
2859
+ }
2860
+
2861
+ // src/node/ooxml.ts
2862
+ var PPTX_SLIDE_W = 12192e3;
2863
+ var PPTX_SLIDE_H = 6858e3;
2864
+ var PPTX_MARGIN = 457200;
2865
+ function fitRectIntoPage(sourceW, sourceH, pageW, pageH, margin = 10) {
2866
+ const usableW = Math.max(1, pageW - margin * 2);
2867
+ const usableH = Math.max(1, pageH - margin * 2);
2868
+ const scale = Math.min(usableW / sourceW, usableH / sourceH);
2869
+ const width = sourceW * scale;
2870
+ const height = sourceH * scale;
2871
+ return { x: (pageW - width) / 2, y: (pageH - height) / 2, width, height };
2872
+ }
2873
+ function fitRectIntoSlide(sourceW, sourceH, slideW = PPTX_SLIDE_W, slideH = PPTX_SLIDE_H, margin = PPTX_MARGIN) {
2874
+ return fitRectIntoPage(sourceW, sourceH, slideW, slideH, margin);
2875
+ }
2876
+ function buildPptxPackage(imagePng, imageW, imageH) {
2877
+ const fit = fitRectIntoSlide(imageW, imageH);
2878
+ const iso = (/* @__PURE__ */ new Date()).toISOString();
2879
+ return createStoredZip([
2880
+ ["[Content_Types].xml", contentTypesXml()],
2881
+ ["_rels/.rels", rootRelsXml()],
2882
+ ["docProps/app.xml", appPropsXml()],
2883
+ ["docProps/core.xml", corePropsXml(iso)],
2884
+ ["ppt/presentation.xml", presentationXml()],
2885
+ ["ppt/_rels/presentation.xml.rels", presentationRelsXml()],
2886
+ ["ppt/slides/slide1.xml", slideXml(fit)],
2887
+ ["ppt/slides/_rels/slide1.xml.rels", slideRelsXml()],
2888
+ ["ppt/slideMasters/slideMaster1.xml", slideMasterXml()],
2889
+ ["ppt/slideMasters/_rels/slideMaster1.xml.rels", slideMasterRelsXml()],
2890
+ ["ppt/slideLayouts/slideLayout1.xml", slideLayoutXml()],
2891
+ ["ppt/slideLayouts/_rels/slideLayout1.xml.rels", slideLayoutRelsXml()],
2892
+ ["ppt/theme/theme1.xml", themeXml()],
2893
+ ["ppt/media/image1.png", imagePng]
2894
+ ]);
2895
+ }
2896
+ function buildEditablePptxPackage(svgEl) {
2897
+ const viewBox = computeContentBox(svgEl);
2898
+ const fit = fitRectIntoSlide(viewBox.width, viewBox.height);
2899
+ const shapes = editablePptxShapes(svgEl, viewBox, fit);
2900
+ const iso = (/* @__PURE__ */ new Date()).toISOString();
2901
+ return createStoredZip([
2902
+ ["[Content_Types].xml", contentTypesXmlEditable()],
2903
+ ["_rels/.rels", rootRelsXml()],
2904
+ ["docProps/app.xml", appPropsXml()],
2905
+ ["docProps/core.xml", corePropsXml(iso)],
2906
+ ["ppt/presentation.xml", presentationXml()],
2907
+ ["ppt/_rels/presentation.xml.rels", presentationRelsXml()],
2908
+ ["ppt/slides/slide1.xml", editableSlideXml(shapes)],
2909
+ ["ppt/slides/_rels/slide1.xml.rels", editableSlideRelsXml()],
2910
+ ["ppt/slideMasters/slideMaster1.xml", slideMasterXml()],
2911
+ ["ppt/slideMasters/_rels/slideMaster1.xml.rels", slideMasterRelsXml()],
2912
+ ["ppt/slideLayouts/slideLayout1.xml", slideLayoutXml()],
2913
+ ["ppt/slideLayouts/_rels/slideLayout1.xml.rels", slideLayoutRelsXml()],
2914
+ ["ppt/theme/theme1.xml", themeXml()]
2915
+ ]);
2916
+ }
2917
+ function collectElements(root, tags) {
2918
+ const out = [];
2919
+ const walk = (node) => {
2920
+ if (tags.has(node.tagName.toLowerCase())) out.push(node);
2921
+ for (const child of node.children) walk(child);
2922
+ };
2923
+ walk(root);
2924
+ return out;
2925
+ }
2926
+ function editablePptxShapes(svgEl, viewBox, fit) {
2927
+ const ctx = {
2928
+ nextId: 2,
2929
+ tx: {
2930
+ minX: viewBox.minX,
2931
+ minY: viewBox.minY,
2932
+ scaleX: fit.width / viewBox.width,
2933
+ scaleY: fit.height / viewBox.height,
2934
+ fitX: fit.x,
2935
+ fitY: fit.y
2936
+ }
2937
+ };
2938
+ const out = [];
2939
+ const elements = collectElements(svgEl, /* @__PURE__ */ new Set(["rect", "circle", "polygon", "path", "text", "line", "polyline"]));
2940
+ for (const el2 of elements) {
2941
+ const tag = el2.tagName.toLowerCase();
2942
+ if (tag === "rect") out.push(...pptxRect(el2, ctx));
2943
+ else if (tag === "circle") out.push(...pptxCircle(el2, ctx));
2944
+ else if (tag === "polygon") out.push(...pptxPolygon(el2, ctx));
2945
+ else if (tag === "path") out.push(...pptxPath(el2, ctx));
2946
+ else if (tag === "line") out.push(...pptxLine(el2, ctx));
2947
+ else if (tag === "polyline") out.push(...pptxPolyline(el2, ctx));
2948
+ else if (tag === "text") out.push(...pptxText(el2, ctx));
2949
+ }
2950
+ return out.filter(Boolean).join("\n");
2951
+ }
2952
+ function pptxRect(el2, ctx) {
2953
+ const x = numAttr(el2, "x");
2954
+ const y = numAttr(el2, "y");
2955
+ const w = numAttr(el2, "width");
2956
+ const h = numAttr(el2, "height");
2957
+ if (w <= 0 || h <= 0) return [];
2958
+ const p = mapBox(ctx.tx, x, y, w, h);
2959
+ const rx = Math.max(numAttr(el2, "rx"), numAttr(el2, "ry"));
2960
+ return [shapeXml(
2961
+ ctx.nextId++,
2962
+ "rect",
2963
+ p.x,
2964
+ p.y,
2965
+ p.w,
2966
+ p.h,
2967
+ rx > 0 ? "roundRect" : "rect",
2968
+ fillXml(colorAttr(el2, "fill", "none")),
2969
+ lineXml(lineStyleFromElement(el2, ctx.tx))
2970
+ )];
2971
+ }
2972
+ function pptxCircle(el2, ctx) {
2973
+ const cx = numAttr(el2, "cx");
2974
+ const cy = numAttr(el2, "cy");
2975
+ const r = numAttr(el2, "r");
2976
+ if (r <= 0) return [];
2977
+ const p = mapBox(ctx.tx, cx - r, cy - r, r * 2, r * 2);
2978
+ return [shapeXml(
2979
+ ctx.nextId++,
2980
+ "circle",
2981
+ p.x,
2982
+ p.y,
2983
+ p.w,
2984
+ p.h,
2985
+ "ellipse",
2986
+ fillXml(colorAttr(el2, "fill", "#000")),
2987
+ lineXml(lineStyleFromElement(el2, ctx.tx))
2988
+ )];
2989
+ }
2990
+ function pptxPolygon(el2, ctx) {
2991
+ const points = parsePoints(el2.getAttribute("points"));
2992
+ if (points.length < 3) return [];
2993
+ return [freeformXml(
2994
+ ctx.nextId++,
2995
+ "polygon",
2996
+ points.map((point) => mapPoint(ctx.tx, point[0], point[1])),
2997
+ fillXml(colorAttr(el2, "fill", "none")),
2998
+ lineXml(lineStyleFromElement(el2, ctx.tx))
2999
+ )];
3000
+ }
3001
+ function pptxPath(el2, ctx) {
3002
+ const points = parsePathPoints(el2.getAttribute("d"));
3003
+ if (points.length < 2) return [];
3004
+ const line = lineStyleFromElement(el2, ctx.tx);
3005
+ const out = [];
3006
+ for (let i = 0; i < points.length - 1; i++) {
3007
+ const a = mapPoint(ctx.tx, points[i][0], points[i][1]);
3008
+ const b = mapPoint(ctx.tx, points[i + 1][0], points[i + 1][1]);
3009
+ out.push(connectorXml(ctx.nextId++, "line", a.x, a.y, b.x, b.y, line));
3010
+ }
3011
+ return out;
3012
+ }
3013
+ function pptxLine(el2, ctx) {
3014
+ const a = mapPoint(ctx.tx, numAttr(el2, "x1"), numAttr(el2, "y1"));
3015
+ const b = mapPoint(ctx.tx, numAttr(el2, "x2"), numAttr(el2, "y2"));
3016
+ return [connectorXml(ctx.nextId++, "line", a.x, a.y, b.x, b.y, lineStyleFromElement(el2, ctx.tx))];
3017
+ }
3018
+ function pptxPolyline(el2, ctx) {
3019
+ const points = parsePoints(el2.getAttribute("points"));
3020
+ if (points.length < 2) return [];
3021
+ const line = lineStyleFromElement(el2, ctx.tx);
3022
+ const out = [];
3023
+ for (let i = 0; i < points.length - 1; i++) {
3024
+ const a = mapPoint(ctx.tx, points[i][0], points[i][1]);
3025
+ const b = mapPoint(ctx.tx, points[i + 1][0], points[i + 1][1]);
3026
+ out.push(connectorXml(ctx.nextId++, "line", a.x, a.y, b.x, b.y, line));
3027
+ }
3028
+ return out;
3029
+ }
3030
+ function pptxText(el2, ctx) {
3031
+ const text = (el2.textContent ?? "").trim();
3032
+ if (!text) return [];
3033
+ const x = numAttr(el2, "x");
3034
+ const y = numAttr(el2, "y");
3035
+ const fontSize = numAttr(el2, "font-size", 2.8);
3036
+ const widthSvg = estimateSvgTextWidth(text, fontSize);
3037
+ const heightSvg = fontSize * 1.35;
3038
+ const anchor = el2.getAttribute("text-anchor") ?? "start";
3039
+ const baseline = el2.getAttribute("dominant-baseline") ?? "alphabetic";
3040
+ let left = x;
3041
+ if (anchor === "middle") left -= widthSvg / 2;
3042
+ else if (anchor === "end") left -= widthSvg;
3043
+ let top = y - fontSize * 0.95;
3044
+ if (baseline === "middle" || baseline === "central") top = y - heightSvg / 2;
3045
+ const p = mapBox(ctx.tx, left, top, widthSvg, heightSvg);
3046
+ const fontPt = Math.max(1, Math.round(fontSize * ctx.tx.scaleY / 12700 * 100) / 100);
3047
+ const align = anchor === "middle" ? "ctr" : anchor === "end" ? "r" : "l";
3048
+ return [textBoxXml(ctx.nextId++, p.x, p.y, p.w, p.h, text, fontPt, align, colorAttr(el2, "fill", "#000"))];
3049
+ }
3050
+ function mapPoint(tx, x, y) {
3051
+ return {
3052
+ x: Math.round(tx.fitX + (x - tx.minX) * tx.scaleX),
3053
+ y: Math.round(tx.fitY + (y - tx.minY) * tx.scaleY)
3054
+ };
3055
+ }
3056
+ function mapBox(tx, x, y, w, h) {
3057
+ const a = mapPoint(tx, x, y);
3058
+ return { x: a.x, y: a.y, w: Math.max(1, Math.round(w * tx.scaleX)), h: Math.max(1, Math.round(h * tx.scaleY)) };
3059
+ }
3060
+ function numAttr(el2, name, fallback = 0) {
3061
+ const n = Number.parseFloat(el2.getAttribute(name) ?? "");
3062
+ return Number.isFinite(n) ? n : fallback;
3063
+ }
3064
+ function colorAttr(el2, name, fallback) {
3065
+ const value = (el2.getAttribute(name) ?? fallback).trim();
3066
+ if (!value || value === "none" || value === "transparent") return "none";
3067
+ if (value.startsWith("#")) {
3068
+ const hex = value.slice(1);
3069
+ if (/^[0-9a-f]{3}$/i.test(hex)) return hex.split("").map((c) => c + c).join("").toUpperCase();
3070
+ if (/^[0-9a-f]{6}$/i.test(hex)) return hex.toUpperCase();
3071
+ }
3072
+ if (value.toLowerCase() === "white") return "FFFFFF";
3073
+ if (value.toLowerCase() === "black") return "000000";
3074
+ return fallback === "none" ? "none" : "000000";
3075
+ }
3076
+ function lineStyleFromElement(el2, tx) {
3077
+ const stroke = colorAttr(el2, "stroke", "none");
3078
+ if (stroke === "none") return null;
3079
+ const svgWidth = numAttr(el2, "stroke-width", 0.4);
3080
+ const width = Math.max(1270, Math.round(svgWidth * (tx.scaleX + tx.scaleY) / 2));
3081
+ return { color: stroke, width, dash: !!el2.getAttribute("stroke-dasharray") };
3082
+ }
3083
+ function parsePoints(raw) {
3084
+ if (!raw) return [];
3085
+ const nums = raw.trim().split(/[\s,]+/).map(Number).filter(Number.isFinite);
3086
+ const out = [];
3087
+ for (let i = 0; i + 1 < nums.length; i += 2) out.push([nums[i], nums[i + 1]]);
3088
+ return out;
3089
+ }
3090
+ function parsePathPoints(raw) {
3091
+ if (!raw) return [];
3092
+ const nums = raw.replace(/[MLZ]/gi, " ").trim().split(/[\s,]+/).map(Number).filter(Number.isFinite);
3093
+ const out = [];
3094
+ for (let i = 0; i + 1 < nums.length; i += 2) out.push([nums[i], nums[i + 1]]);
3095
+ return out;
3096
+ }
3097
+ function estimateSvgTextWidth(text, fontSize) {
3098
+ let width = 0;
3099
+ for (const char of text) {
3100
+ if (char === " ") width += fontSize * 0.35;
3101
+ else if (char.charCodeAt(0) <= 127) width += fontSize * 0.58;
3102
+ else width += fontSize;
3103
+ }
3104
+ return Math.max(fontSize * 2.2, width + fontSize * 0.8);
3105
+ }
3106
+ function shapeXml(id, name, x, y, w, h, preset, fill, line) {
3107
+ return `<p:sp>
3108
+ <p:nvSpPr><p:cNvPr id="${id}" name="${xmlAttr(name)}"/><p:cNvSpPr/><p:nvPr/></p:nvSpPr>
3109
+ <p:spPr><a:xfrm><a:off x="${x}" y="${y}"/><a:ext cx="${w}" cy="${h}"/></a:xfrm><a:prstGeom prst="${preset}"><a:avLst/></a:prstGeom>${fill}${line}</p:spPr>
3110
+ </p:sp>`;
3111
+ }
3112
+ function connectorXml(id, name, x1, y1, x2, y2, line) {
3113
+ const x = Math.min(x1, x2);
3114
+ const y = Math.min(y1, y2);
3115
+ const w = Math.max(1, Math.abs(x2 - x1));
3116
+ const h = Math.max(1, Math.abs(y2 - y1));
3117
+ const flipH = x2 < x1 ? ' flipH="1"' : "";
3118
+ const flipV = y2 < y1 ? ' flipV="1"' : "";
3119
+ return `<p:cxnSp>
3120
+ <p:nvCxnSpPr><p:cNvPr id="${id}" name="${xmlAttr(name)}"/><p:cNvCxnSpPr/><p:nvPr/></p:nvCxnSpPr>
3121
+ <p:spPr><a:xfrm${flipH}${flipV}><a:off x="${x}" y="${y}"/><a:ext cx="${w}" cy="${h}"/></a:xfrm><a:prstGeom prst="line"><a:avLst/></a:prstGeom>${lineXml(line)}</p:spPr>
3122
+ </p:cxnSp>`;
3123
+ }
3124
+ function freeformXml(id, name, points, fill, line) {
3125
+ const left = Math.min(...points.map((p) => p.x));
3126
+ const top = Math.min(...points.map((p) => p.y));
3127
+ const right = Math.max(...points.map((p) => p.x));
3128
+ const bottom = Math.max(...points.map((p) => p.y));
3129
+ const w = Math.max(1, right - left);
3130
+ const h = Math.max(1, bottom - top);
3131
+ const local = points.map((p) => ({ x: p.x - left, y: p.y - top }));
3132
+ const first = local[0];
3133
+ const rest = local.slice(1).map((p) => `<a:lnTo><a:pt x="${p.x}" y="${p.y}"/></a:lnTo>`).join("");
3134
+ return `<p:sp>
3135
+ <p:nvSpPr><p:cNvPr id="${id}" name="${xmlAttr(name)}"/><p:cNvSpPr/><p:nvPr/></p:nvSpPr>
3136
+ <p:spPr><a:xfrm><a:off x="${left}" y="${top}"/><a:ext cx="${w}" cy="${h}"/></a:xfrm><a:custGeom><a:avLst/><a:gdLst/><a:ahLst/><a:cxnLst/><a:rect l="l" t="t" r="r" b="b"/><a:pathLst><a:path w="${w}" h="${h}"><a:moveTo><a:pt x="${first.x}" y="${first.y}"/></a:moveTo>${rest}<a:close/></a:path></a:pathLst></a:custGeom>${fill}${line}</p:spPr>
3137
+ </p:sp>`;
3138
+ }
3139
+ function textBoxXml(id, x, y, w, h, text, fontPt, align, color) {
3140
+ const sz = Math.max(100, Math.round(fontPt * 100));
3141
+ return `<p:sp>
3142
+ <p:nvSpPr><p:cNvPr id="${id}" name="text"/><p:cNvSpPr txBox="1"/><p:nvPr/></p:nvSpPr>
3143
+ <p:spPr><a:xfrm><a:off x="${x}" y="${y}"/><a:ext cx="${w}" cy="${h}"/></a:xfrm><a:prstGeom prst="rect"><a:avLst/></a:prstGeom><a:noFill/><a:ln><a:noFill/></a:ln></p:spPr>
3144
+ <p:txBody><a:bodyPr wrap="none" anchor="ctr" lIns="0" tIns="0" rIns="0" bIns="0"><a:spAutoFit/></a:bodyPr><a:lstStyle/><a:p><a:pPr algn="${align}"/><a:r><a:rPr lang="ja-JP" sz="${sz}" dirty="0">${fillXml(color)}</a:rPr><a:t>${xmlText(text)}</a:t></a:r></a:p></p:txBody>
3145
+ </p:sp>`;
3146
+ }
3147
+ function fillXml(color) {
3148
+ return color === "none" ? "<a:noFill/>" : `<a:solidFill><a:srgbClr val="${xmlAttr(color)}"/></a:solidFill>`;
3149
+ }
3150
+ function lineXml(line) {
3151
+ if (!line) return "<a:ln><a:noFill/></a:ln>";
3152
+ const dash = line.dash ? '<a:prstDash val="dash"/>' : '<a:prstDash val="solid"/>';
3153
+ return `<a:ln w="${line.width}" cap="flat" cmpd="sng" algn="ctr"><a:solidFill><a:srgbClr val="${xmlAttr(line.color)}"/></a:solidFill>${dash}</a:ln>`;
3154
+ }
3155
+ function xmlText(text) {
3156
+ return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
3157
+ }
3158
+ function xmlAttr(text) {
3159
+ return xmlText(text).replace(/"/g, "&quot;");
3160
+ }
3161
+ function createStoredZip(entries) {
3162
+ const encoder = new TextEncoder();
3163
+ const files = entries.map(([name, data]) => {
3164
+ const bytes = typeof data === "string" ? encoder.encode(data) : data;
3165
+ return { name, nameBytes: encoder.encode(name), bytes, crc: crc32(bytes) };
3166
+ });
3167
+ const chunks = [];
3168
+ const central = [];
3169
+ let offset = 0;
3170
+ for (const file of files) {
3171
+ const local = zipLocalHeader(file.nameBytes, file.bytes, file.crc);
3172
+ chunks.push(local, file.nameBytes, file.bytes);
3173
+ central.push(zipCentralHeader(file.nameBytes, file.bytes, file.crc, offset), file.nameBytes);
3174
+ offset += local.length + file.nameBytes.length + file.bytes.length;
3175
+ }
3176
+ const centralOffset = offset;
3177
+ const centralSize = central.reduce((sum, chunk) => sum + chunk.length, 0);
3178
+ return concatBytes([...chunks, ...central, zipEndRecord(files.length, centralSize, centralOffset)]);
3179
+ }
3180
+ function zipLocalHeader(name, data, crc) {
3181
+ const out = new Uint8Array(30);
3182
+ const view = new DataView(out.buffer);
3183
+ view.setUint32(0, 67324752, true);
3184
+ view.setUint16(4, 20, true);
3185
+ view.setUint16(6, 0, true);
3186
+ view.setUint16(8, 0, true);
3187
+ view.setUint16(10, 0, true);
3188
+ view.setUint16(12, 0, true);
3189
+ view.setUint32(14, crc, true);
3190
+ view.setUint32(18, data.length, true);
3191
+ view.setUint32(22, data.length, true);
3192
+ view.setUint16(26, name.length, true);
3193
+ view.setUint16(28, 0, true);
3194
+ return out;
3195
+ }
3196
+ function zipCentralHeader(name, data, crc, offset) {
3197
+ const out = new Uint8Array(46);
3198
+ const view = new DataView(out.buffer);
3199
+ view.setUint32(0, 33639248, true);
3200
+ view.setUint16(4, 20, true);
3201
+ view.setUint16(6, 20, true);
3202
+ view.setUint16(8, 0, true);
3203
+ view.setUint16(10, 0, true);
3204
+ view.setUint16(12, 0, true);
3205
+ view.setUint16(14, 0, true);
3206
+ view.setUint32(16, crc, true);
3207
+ view.setUint32(20, data.length, true);
3208
+ view.setUint32(24, data.length, true);
3209
+ view.setUint16(28, name.length, true);
3210
+ view.setUint16(30, 0, true);
3211
+ view.setUint16(32, 0, true);
3212
+ view.setUint16(34, 0, true);
3213
+ view.setUint16(36, 0, true);
3214
+ view.setUint32(38, 0, true);
3215
+ view.setUint32(42, offset, true);
3216
+ return out;
3217
+ }
3218
+ function zipEndRecord(entries, centralSize, centralOffset) {
3219
+ const out = new Uint8Array(22);
3220
+ const view = new DataView(out.buffer);
3221
+ view.setUint32(0, 101010256, true);
3222
+ view.setUint16(4, 0, true);
3223
+ view.setUint16(6, 0, true);
3224
+ view.setUint16(8, entries, true);
3225
+ view.setUint16(10, entries, true);
3226
+ view.setUint32(12, centralSize, true);
3227
+ view.setUint32(16, centralOffset, true);
3228
+ view.setUint16(20, 0, true);
3229
+ return out;
3230
+ }
3231
+ function concatBytes(chunks) {
3232
+ const total = chunks.reduce((sum, chunk) => sum + chunk.length, 0);
3233
+ const out = new Uint8Array(total);
3234
+ let offset = 0;
3235
+ for (const chunk of chunks) {
3236
+ out.set(chunk, offset);
3237
+ offset += chunk.length;
3238
+ }
3239
+ return out;
3240
+ }
3241
+ function crc32(bytes) {
3242
+ let crc = 4294967295;
3243
+ for (const byte of bytes) {
3244
+ crc ^= byte;
3245
+ for (let i = 0; i < 8; i++) {
3246
+ crc = crc >>> 1 ^ 3988292384 & -(crc & 1);
3247
+ }
3248
+ }
3249
+ return (crc ^ 4294967295) >>> 0;
3250
+ }
3251
+ function contentTypesXml() {
3252
+ return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
3253
+ <Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">
3254
+ <Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>
3255
+ <Default Extension="xml" ContentType="application/xml"/>
3256
+ <Default Extension="png" ContentType="image/png"/>
3257
+ <Override PartName="/docProps/app.xml" ContentType="application/vnd.openxmlformats-officedocument.extended-properties+xml"/>
3258
+ <Override PartName="/docProps/core.xml" ContentType="application/vnd.openxmlformats-package.core-properties+xml"/>
3259
+ <Override PartName="/ppt/presentation.xml" ContentType="application/vnd.openxmlformats-officedocument.presentationml.presentation.main+xml"/>
3260
+ <Override PartName="/ppt/slides/slide1.xml" ContentType="application/vnd.openxmlformats-officedocument.presentationml.slide+xml"/>
3261
+ <Override PartName="/ppt/slideMasters/slideMaster1.xml" ContentType="application/vnd.openxmlformats-officedocument.presentationml.slideMaster+xml"/>
3262
+ <Override PartName="/ppt/slideLayouts/slideLayout1.xml" ContentType="application/vnd.openxmlformats-officedocument.presentationml.slideLayout+xml"/>
3263
+ <Override PartName="/ppt/theme/theme1.xml" ContentType="application/vnd.openxmlformats-officedocument.theme+xml"/>
3264
+ </Types>`;
3265
+ }
3266
+ function contentTypesXmlEditable() {
3267
+ return contentTypesXml().replace('<Default Extension="png" ContentType="image/png"/>\n', "");
3268
+ }
3269
+ function rootRelsXml() {
3270
+ return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
3271
+ <Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
3272
+ <Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="ppt/presentation.xml"/>
3273
+ <Relationship Id="rId2" Type="http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties" Target="docProps/core.xml"/>
3274
+ <Relationship Id="rId3" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties" Target="docProps/app.xml"/>
3275
+ </Relationships>`;
3276
+ }
3277
+ function appPropsXml() {
3278
+ return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
3279
+ <Properties xmlns="http://schemas.openxmlformats.org/officeDocument/2006/extended-properties" xmlns:vt="http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes">
3280
+ <Application>pdgkit</Application>
3281
+ <PresentationFormat>Widescreen</PresentationFormat>
3282
+ <Slides>1</Slides>
3283
+ <Notes>0</Notes>
3284
+ <HiddenSlides>0</HiddenSlides>
3285
+ </Properties>`;
3286
+ }
3287
+ function corePropsXml(iso) {
3288
+ return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
3289
+ <cp:coreProperties xmlns:cp="http://schemas.openxmlformats.org/package/2006/metadata/core-properties" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
3290
+ <dc:title>pdgkit figure</dc:title>
3291
+ <dc:creator>pdgkit</dc:creator>
3292
+ <cp:lastModifiedBy>pdgkit</cp:lastModifiedBy>
3293
+ <dcterms:created xsi:type="dcterms:W3CDTF">${iso}</dcterms:created>
3294
+ <dcterms:modified xsi:type="dcterms:W3CDTF">${iso}</dcterms:modified>
3295
+ </cp:coreProperties>`;
3296
+ }
3297
+ function presentationXml() {
3298
+ return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
3299
+ <p:presentation xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:p="http://schemas.openxmlformats.org/presentationml/2006/main">
3300
+ <p:sldMasterIdLst><p:sldMasterId id="2147483648" r:id="rId1"/></p:sldMasterIdLst>
3301
+ <p:sldIdLst><p:sldId id="256" r:id="rId2"/></p:sldIdLst>
3302
+ <p:sldSz cx="${PPTX_SLIDE_W}" cy="${PPTX_SLIDE_H}" type="screen16x9"/>
3303
+ <p:notesSz cx="6858000" cy="9144000"/>
3304
+ <p:defaultTextStyle/>
3305
+ </p:presentation>`;
3306
+ }
3307
+ function presentationRelsXml() {
3308
+ return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
3309
+ <Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
3310
+ <Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideMaster" Target="slideMasters/slideMaster1.xml"/>
3311
+ <Relationship Id="rId2" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/slide" Target="slides/slide1.xml"/>
3312
+ </Relationships>`;
3313
+ }
3314
+ function slideXml(fit) {
3315
+ return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
3316
+ <p:sld xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:p="http://schemas.openxmlformats.org/presentationml/2006/main">
3317
+ <p:cSld name="pdgkit">
3318
+ <p:bg><p:bgPr><a:solidFill><a:srgbClr val="FFFFFF"/></a:solidFill><a:effectLst/></p:bgPr></p:bg>
3319
+ <p:spTree>
3320
+ <p:nvGrpSpPr><p:cNvPr id="1" name=""/><p:cNvGrpSpPr/><p:nvPr/></p:nvGrpSpPr>
3321
+ <p:grpSpPr><a:xfrm><a:off x="0" y="0"/><a:ext cx="${PPTX_SLIDE_W}" cy="${PPTX_SLIDE_H}"/><a:chOff x="0" y="0"/><a:chExt cx="${PPTX_SLIDE_W}" cy="${PPTX_SLIDE_H}"/></a:xfrm></p:grpSpPr>
3322
+ <p:pic>
3323
+ <p:nvPicPr><p:cNvPr id="2" name="figure.png"/><p:cNvPicPr><a:picLocks noChangeAspect="1"/></p:cNvPicPr><p:nvPr/></p:nvPicPr>
3324
+ <p:blipFill><a:blip r:embed="rId1"/><a:stretch><a:fillRect/></a:stretch></p:blipFill>
3325
+ <p:spPr><a:xfrm><a:off x="${Math.round(fit.x)}" y="${Math.round(fit.y)}"/><a:ext cx="${Math.round(fit.width)}" cy="${Math.round(fit.height)}"/></a:xfrm><a:prstGeom prst="rect"><a:avLst/></a:prstGeom></p:spPr>
3326
+ </p:pic>
3327
+ </p:spTree>
3328
+ </p:cSld>
3329
+ <p:clrMapOvr><a:masterClrMapping/></p:clrMapOvr>
3330
+ </p:sld>`;
3331
+ }
3332
+ function slideRelsXml() {
3333
+ return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
3334
+ <Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
3335
+ <Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/image" Target="../media/image1.png"/>
3336
+ <Relationship Id="rId2" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideLayout" Target="../slideLayouts/slideLayout1.xml"/>
3337
+ </Relationships>`;
3338
+ }
3339
+ function editableSlideXml(shapes) {
3340
+ return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
3341
+ <p:sld xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:p="http://schemas.openxmlformats.org/presentationml/2006/main">
3342
+ <p:cSld name="pdgkit">
3343
+ <p:bg><p:bgPr><a:solidFill><a:srgbClr val="FFFFFF"/></a:solidFill><a:effectLst/></p:bgPr></p:bg>
3344
+ <p:spTree>
3345
+ <p:nvGrpSpPr><p:cNvPr id="1" name=""/><p:cNvGrpSpPr/><p:nvPr/></p:nvGrpSpPr>
3346
+ <p:grpSpPr><a:xfrm><a:off x="0" y="0"/><a:ext cx="${PPTX_SLIDE_W}" cy="${PPTX_SLIDE_H}"/><a:chOff x="0" y="0"/><a:chExt cx="${PPTX_SLIDE_W}" cy="${PPTX_SLIDE_H}"/></a:xfrm></p:grpSpPr>
3347
+ ${shapes}
3348
+ </p:spTree>
3349
+ </p:cSld>
3350
+ <p:clrMapOvr><a:masterClrMapping/></p:clrMapOvr>
3351
+ </p:sld>`;
3352
+ }
3353
+ function editableSlideRelsXml() {
3354
+ return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
3355
+ <Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
3356
+ <Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideLayout" Target="../slideLayouts/slideLayout1.xml"/>
3357
+ </Relationships>`;
3358
+ }
3359
+ function slideMasterXml() {
3360
+ return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
3361
+ <p:sldMaster xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:p="http://schemas.openxmlformats.org/presentationml/2006/main">
3362
+ <p:cSld><p:spTree><p:nvGrpSpPr><p:cNvPr id="1" name=""/><p:cNvGrpSpPr/><p:nvPr/></p:nvGrpSpPr><p:grpSpPr><a:xfrm><a:off x="0" y="0"/><a:ext cx="${PPTX_SLIDE_W}" cy="${PPTX_SLIDE_H}"/><a:chOff x="0" y="0"/><a:chExt cx="${PPTX_SLIDE_W}" cy="${PPTX_SLIDE_H}"/></a:xfrm></p:grpSpPr></p:spTree></p:cSld>
3363
+ <p:clrMap bg1="lt1" tx1="dk1" bg2="lt2" tx2="dk2" accent1="accent1" accent2="accent2" accent3="accent3" accent4="accent4" accent5="accent5" accent6="accent6" hlink="hlink" folHlink="folHlink"/>
3364
+ <p:sldLayoutIdLst><p:sldLayoutId id="2147483649" r:id="rId1"/></p:sldLayoutIdLst>
3365
+ <p:txStyles><p:titleStyle/><p:bodyStyle/><p:otherStyle/></p:txStyles>
3366
+ </p:sldMaster>`;
3367
+ }
3368
+ function slideMasterRelsXml() {
3369
+ return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
3370
+ <Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
3371
+ <Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideLayout" Target="../slideLayouts/slideLayout1.xml"/>
3372
+ <Relationship Id="rId2" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme" Target="../theme/theme1.xml"/>
3373
+ </Relationships>`;
3374
+ }
3375
+ function slideLayoutXml() {
3376
+ return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
3377
+ <p:sldLayout xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:p="http://schemas.openxmlformats.org/presentationml/2006/main" type="blank" preserve="1">
3378
+ <p:cSld name="Blank"><p:spTree><p:nvGrpSpPr><p:cNvPr id="1" name=""/><p:cNvGrpSpPr/><p:nvPr/></p:nvGrpSpPr><p:grpSpPr><a:xfrm><a:off x="0" y="0"/><a:ext cx="${PPTX_SLIDE_W}" cy="${PPTX_SLIDE_H}"/><a:chOff x="0" y="0"/><a:chExt cx="${PPTX_SLIDE_W}" cy="${PPTX_SLIDE_H}"/></a:xfrm></p:grpSpPr></p:spTree></p:cSld>
3379
+ <p:clrMapOvr><a:masterClrMapping/></p:clrMapOvr>
3380
+ </p:sldLayout>`;
3381
+ }
3382
+ function slideLayoutRelsXml() {
3383
+ return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
3384
+ <Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
3385
+ <Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideMaster" Target="../slideMasters/slideMaster1.xml"/>
3386
+ </Relationships>`;
3387
+ }
3388
+ function themeXml() {
3389
+ return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
3390
+ <a:theme xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" name="pdgkit">
3391
+ <a:themeElements>
3392
+ <a:clrScheme name="pdgkit"><a:dk1><a:srgbClr val="000000"/></a:dk1><a:lt1><a:srgbClr val="FFFFFF"/></a:lt1><a:dk2><a:srgbClr val="000000"/></a:dk2><a:lt2><a:srgbClr val="FFFFFF"/></a:lt2><a:accent1><a:srgbClr val="0B5FFF"/></a:accent1><a:accent2><a:srgbClr val="666666"/></a:accent2><a:accent3><a:srgbClr val="999999"/></a:accent3><a:accent4><a:srgbClr val="CCCCCC"/></a:accent4><a:accent5><a:srgbClr val="333333"/></a:accent5><a:accent6><a:srgbClr val="111111"/></a:accent6><a:hlink><a:srgbClr val="0B5FFF"/></a:hlink><a:folHlink><a:srgbClr val="0B5FFF"/></a:folHlink></a:clrScheme>
3393
+ <a:fontScheme name="pdgkit"><a:majorFont><a:latin typeface="Arial"/><a:ea typeface="Yu Gothic"/><a:cs typeface="Arial"/></a:majorFont><a:minorFont><a:latin typeface="Arial"/><a:ea typeface="Yu Gothic"/><a:cs typeface="Arial"/></a:minorFont></a:fontScheme>
3394
+ <a:fmtScheme name="pdgkit"><a:fillStyleLst><a:solidFill><a:schemeClr val="phClr"/></a:solidFill></a:fillStyleLst><a:lnStyleLst><a:ln w="9525" cap="flat" cmpd="sng" algn="ctr"><a:solidFill><a:schemeClr val="phClr"/></a:solidFill><a:prstDash val="solid"/></a:ln></a:lnStyleLst><a:effectStyleLst><a:effectStyle><a:effectLst/></a:effectStyle></a:effectStyleLst><a:bgFillStyleLst><a:solidFill><a:schemeClr val="phClr"/></a:solidFill></a:bgFillStyleLst></a:fmtScheme>
3395
+ </a:themeElements>
3396
+ <a:objectDefaults/>
3397
+ <a:extraClrSchemeLst/>
3398
+ </a:theme>`;
3399
+ }
3400
+
3401
+ // src/node/pdf.ts
3402
+ async function renderToPdf(source, opts = {}) {
3403
+ const { lang = "ja", bleed = 3, vector = true, scale = 8 } = opts;
3404
+ const { svg, viewBox } = renderToSvg(source, { lang, crop: true, bleed });
3405
+ if (vector) {
3406
+ try {
3407
+ return await renderVectorPdf(svg, viewBox);
3408
+ } catch {
3409
+ }
3410
+ }
3411
+ return await renderRasterPdf(source, viewBox, { lang, scale, bleed });
3412
+ }
3413
+ async function renderVectorPdf(svg, viewBox) {
3414
+ const { JSDOM } = await import("jsdom");
3415
+ const { jsPDF } = await import("jspdf");
3416
+ const { svg2pdf } = await import("svg2pdf.js");
3417
+ const dom = new JSDOM(svg, { contentType: "image/svg+xml" });
3418
+ patchSvgGeometry(dom.window);
3419
+ const svgEl = dom.window.document.documentElement;
3420
+ svgEl.setAttribute("font-family", FONT_FAMILY_NAME);
3421
+ for (const t of Array.from(dom.window.document.getElementsByTagName("text"))) {
3422
+ t.setAttribute("font-family", FONT_FAMILY_NAME);
3423
+ }
3424
+ const orientation = viewBox.width > viewBox.height ? "l" : "p";
3425
+ const pdf = new jsPDF({ unit: "mm", format: "a4", orientation });
3426
+ try {
3427
+ pdf.addFileToVFS(`${FONT_FAMILY_NAME}.ttf`, loadFontBase64());
3428
+ pdf.addFont(`${FONT_FAMILY_NAME}.ttf`, FONT_FAMILY_NAME, "normal");
3429
+ } catch {
3430
+ }
3431
+ const pageW = pdf.internal.pageSize.getWidth();
3432
+ const pageH = pdf.internal.pageSize.getHeight();
3433
+ const fit = fitRectIntoPage(viewBox.width, viewBox.height, pageW, pageH, 10);
3434
+ await svg2pdf(svgEl, pdf, {
3435
+ x: fit.x,
3436
+ y: fit.y,
3437
+ width: fit.width,
3438
+ height: fit.height
3439
+ });
3440
+ return new Uint8Array(pdf.output("arraybuffer"));
3441
+ }
3442
+ async function renderRasterPdf(source, viewBox, opts) {
3443
+ const { jsPDF } = await import("jspdf");
3444
+ const png = await renderToPng(source, opts);
3445
+ const orientation = viewBox.width > viewBox.height ? "l" : "p";
3446
+ const pdf = new jsPDF({ unit: "mm", format: "a4", orientation });
3447
+ const pageW = pdf.internal.pageSize.getWidth();
3448
+ const pageH = pdf.internal.pageSize.getHeight();
3449
+ const fit = fitRectIntoPage(viewBox.width, viewBox.height, pageW, pageH, 10);
3450
+ const dataUrl = "data:image/png;base64," + Buffer.from(png).toString("base64");
3451
+ pdf.addImage(dataUrl, "PNG", fit.x, fit.y, fit.width, fit.height);
3452
+ return new Uint8Array(pdf.output("arraybuffer"));
3453
+ }
3454
+ function patchSvgGeometry(win) {
3455
+ const proto = win.SVGElement?.prototype;
3456
+ if (!proto) return;
3457
+ const numOf = (el2, name, d = 0) => {
3458
+ const v = Number.parseFloat(el2.getAttribute?.(name) ?? "");
3459
+ return Number.isFinite(v) ? v : d;
3460
+ };
3461
+ proto.getBBox = function() {
3462
+ const tag = (this.tagName ?? "").toLowerCase();
3463
+ if (tag === "rect") {
3464
+ return { x: numOf(this, "x"), y: numOf(this, "y"), width: numOf(this, "width"), height: numOf(this, "height") };
3465
+ }
3466
+ if (tag === "circle") {
3467
+ const r = numOf(this, "r");
3468
+ return { x: numOf(this, "cx") - r, y: numOf(this, "cy") - r, width: 2 * r, height: 2 * r };
3469
+ }
3470
+ if (tag === "text") {
3471
+ const fs = numOf(this, "font-size", 2.8);
3472
+ return { x: numOf(this, "x"), y: numOf(this, "y") - fs, width: estimateTextWidth(this.textContent ?? "", fs), height: fs * 1.2 };
3473
+ }
3474
+ return { x: 0, y: 0, width: 0, height: 0 };
3475
+ };
3476
+ proto.getComputedTextLength = function() {
3477
+ const fs = numOf(this, "font-size", 2.8);
3478
+ return estimateTextWidth(this.textContent ?? "", fs);
3479
+ };
3480
+ for (const m of ["getCTM", "getScreenCTM"]) {
3481
+ if (typeof proto[m] !== "function") {
3482
+ proto[m] = function() {
3483
+ return null;
3484
+ };
3485
+ }
3486
+ }
3487
+ }
3488
+
3489
+ // src/node/pptx.ts
3490
+ async function renderToPptx(source, opts = {}) {
3491
+ const { lang = "ja", editable = false, scale = 8, bleed = 3 } = opts;
3492
+ if (editable) {
3493
+ const model = buildSvgModel(source, { lang, crop: true, bleed });
3494
+ return buildEditablePptxPackage(model.el);
3495
+ }
3496
+ const { viewBox } = buildSvgModel(source, { lang, crop: true, bleed });
3497
+ const dims = rasterDimensions(viewBox, scale);
3498
+ const png = await renderToPng(source, { lang, scale, bleed });
3499
+ return buildPptxPackage(png, dims.width, dims.height);
3500
+ }
3501
+
3502
+ // src/node/index.ts
3503
+ var VERSION = "0.1.0";
3504
+ function toSvgString(source, lang = "ja") {
3505
+ return renderToSvg(source, { lang }).svg;
3506
+ }
3507
+ function loadAuthoringGuide() {
3508
+ return (0, import_node_fs2.readFileSync)(resolvePackageFile("docs", "ai-authoring-guide.md"), "utf8");
3509
+ }
3510
+ // Annotate the CommonJS export names for ESM import in node:
3511
+ 0 && (module.exports = {
3512
+ OP_TABLE,
3513
+ PATTERN_LABEL,
3514
+ PATTERN_SOURCE,
3515
+ SAMPLES,
3516
+ SAMPLE_ORDER,
3517
+ VERSION,
3518
+ chooseLabelPlacement,
3519
+ computeContentBox,
3520
+ estimateTextWidth,
3521
+ installDomShim,
3522
+ layout,
3523
+ loadAuthoringGuide,
3524
+ parse,
3525
+ refsToCsv,
3526
+ refsToMarkdown,
3527
+ render,
3528
+ renderToJpeg,
3529
+ renderToPdf,
3530
+ renderToPng,
3531
+ renderToPptx,
3532
+ renderToSvg,
3533
+ serializeSvg,
3534
+ splitBilingual,
3535
+ stripComment,
3536
+ svgDisplayDimensions,
3537
+ toSvgString,
3538
+ validate
3539
+ });