stickyflow 0.0.0-stage → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/CHANGELOG.md +157 -0
  2. package/LICENSE +202 -0
  3. package/README.md +167 -2
  4. package/dist/chunk-34PK7HEU.cjs +842 -0
  5. package/dist/chunk-34PK7HEU.cjs.map +1 -0
  6. package/dist/chunk-6JKNT2YC.js +4685 -0
  7. package/dist/chunk-6JKNT2YC.js.map +1 -0
  8. package/dist/chunk-6LQJUAD4.js +801 -0
  9. package/dist/chunk-6LQJUAD4.js.map +1 -0
  10. package/dist/chunk-IIOJFR73.cjs +3671 -0
  11. package/dist/chunk-IIOJFR73.cjs.map +1 -0
  12. package/dist/chunk-KFJIFHXG.js +8559 -0
  13. package/dist/chunk-KFJIFHXG.js.map +1 -0
  14. package/dist/chunk-N6WVHOUQ.js +3665 -0
  15. package/dist/chunk-N6WVHOUQ.js.map +1 -0
  16. package/dist/chunk-S5BVAAEF.cjs +8568 -0
  17. package/dist/chunk-S5BVAAEF.cjs.map +1 -0
  18. package/dist/chunk-YM3XFBI4.cjs +4814 -0
  19. package/dist/chunk-YM3XFBI4.cjs.map +1 -0
  20. package/dist/index.cjs +1167 -0
  21. package/dist/index.cjs.map +1 -0
  22. package/dist/index.d.cts +335 -0
  23. package/dist/index.d.ts +335 -0
  24. package/dist/index.js +1097 -0
  25. package/dist/index.js.map +1 -0
  26. package/dist/interaction/index.cjs +7358 -0
  27. package/dist/interaction/index.cjs.map +1 -0
  28. package/dist/interaction/index.d.cts +273 -0
  29. package/dist/interaction/index.d.ts +273 -0
  30. package/dist/interaction/index.js +7333 -0
  31. package/dist/interaction/index.js.map +1 -0
  32. package/dist/render-r1GYvjNm.d.cts +970 -0
  33. package/dist/render-r1GYvjNm.d.ts +970 -0
  34. package/dist/runtimeTypes-ByiafBwg.d.ts +239 -0
  35. package/dist/runtimeTypes-DwLeD-em.d.cts +239 -0
  36. package/dist/stickyflow-diagram.global.js +1442 -0
  37. package/dist/stickyflow-diagram.global.js.map +1 -0
  38. package/dist/types-T7W2yTQF.d.cts +116 -0
  39. package/dist/types-ndGA1kbl.d.ts +116 -0
  40. package/dist/viewer/index.cjs +31 -0
  41. package/dist/viewer/index.cjs.map +1 -0
  42. package/dist/viewer/index.d.cts +44 -0
  43. package/dist/viewer/index.d.ts +44 -0
  44. package/dist/viewer/index.js +6 -0
  45. package/dist/viewer/index.js.map +1 -0
  46. package/dist/web-component/index.cjs +144 -0
  47. package/dist/web-component/index.cjs.map +1 -0
  48. package/dist/web-component/index.d.cts +31 -0
  49. package/dist/web-component/index.d.ts +31 -0
  50. package/dist/web-component/index.js +141 -0
  51. package/dist/web-component/index.js.map +1 -0
  52. package/package.json +81 -4
@@ -0,0 +1,4685 @@
1
+ // src/model/chart/supportedTypes.ts
2
+ var CHART_TYPES = [
3
+ "bar",
4
+ "line",
5
+ "scatter",
6
+ "radar",
7
+ "radial",
8
+ "field",
9
+ "heatmap",
10
+ "boxplot",
11
+ "sankey",
12
+ "treemap",
13
+ "mekko",
14
+ "hexbin",
15
+ "pie",
16
+ "radialBar",
17
+ "polarArea",
18
+ "custom"
19
+ ];
20
+ function isChartType(value) {
21
+ return value !== void 0 && CHART_TYPES.includes(value);
22
+ }
23
+
24
+ // src/model/isometricObject/geometry.ts
25
+ var VISIBLE_ISOMETRIC_FACES = /* @__PURE__ */ new Set(["top", "front", "right"]);
26
+ var AXIS_ANGLE = Math.PI / 6;
27
+ var AXIS_COS = Math.cos(AXIS_ANGLE);
28
+ var AXIS_SIN = Math.sin(AXIS_ANGLE);
29
+ function isometricBounds(object) {
30
+ const width = Math.max(0, object.width);
31
+ const height = Math.max(0, object.height);
32
+ const depth = Math.max(0, object.depth);
33
+ return {
34
+ x: object.x,
35
+ y: object.y,
36
+ width: (width + depth) * AXIS_COS,
37
+ height: height + (width + depth) * AXIS_SIN
38
+ };
39
+ }
40
+ function isometricTopFaceCenter(object) {
41
+ const width = Math.max(0, object.width);
42
+ const depth = Math.max(0, object.depth);
43
+ return {
44
+ x: object.x + (width + depth) * AXIS_COS / 2,
45
+ y: object.y + (width + depth) * AXIS_SIN / 2
46
+ };
47
+ }
48
+ function faceCenter(corners) {
49
+ return {
50
+ x: (corners[0].x + corners[1].x + corners[2].x + corners[3].x) / 4,
51
+ y: (corners[0].y + corners[1].y + corners[2].y + corners[3].y) / 4
52
+ };
53
+ }
54
+ function isometricFaceCenter(object, face) {
55
+ return faceCenter(isometricFaces(object)[face]);
56
+ }
57
+ function isometricFaces(object) {
58
+ const { x, y } = object;
59
+ const width = Math.max(0, object.width);
60
+ const height = Math.max(0, object.height);
61
+ const depth = Math.max(0, object.depth);
62
+ const apex = { x: x + depth * AXIS_COS, y };
63
+ const toRight = { x: width * AXIS_COS, y: width * AXIS_SIN };
64
+ const toLeft = { x: -depth * AXIS_COS, y: depth * AXIS_SIN };
65
+ const down = { x: 0, y: height };
66
+ const right = { x: apex.x + toRight.x, y: apex.y + toRight.y };
67
+ const left = { x: apex.x + toLeft.x, y: apex.y + toLeft.y };
68
+ const center = { x: apex.x + toRight.x + toLeft.x, y: apex.y + toRight.y + toLeft.y };
69
+ const apexDown = { x: apex.x + down.x, y: apex.y + down.y };
70
+ const bottom = { x: center.x + down.x, y: center.y + down.y };
71
+ const bottomLeft = { x: left.x + down.x, y: left.y + down.y };
72
+ const bottomRight = { x: right.x + down.x, y: right.y + down.y };
73
+ return {
74
+ top: [apex, right, center, left],
75
+ bottom: [apexDown, bottomRight, bottom, bottomLeft],
76
+ front: [left, center, bottom, bottomLeft],
77
+ back: [apex, right, bottomRight, apexDown],
78
+ right: [center, right, bottomRight, bottom],
79
+ left: [apex, left, bottomLeft, apexDown]
80
+ };
81
+ }
82
+
83
+ // src/model/isometricObject/shading.ts
84
+ function clamp255(value) {
85
+ return Math.max(0, Math.min(255, Math.round(value)));
86
+ }
87
+ function hexToRgb(hex) {
88
+ const match = /^#?([0-9a-fA-F]{6})$/.exec(hex.trim());
89
+ if (!match) {
90
+ return null;
91
+ }
92
+ const n = Number.parseInt(match[1], 16);
93
+ return [n >> 16 & 255, n >> 8 & 255, n & 255];
94
+ }
95
+ function rgbToHex(r, g, b) {
96
+ return `#${[r, g, b].map((channel) => clamp255(channel).toString(16).padStart(2, "0")).join("")}`;
97
+ }
98
+ function multiply(hex, factor) {
99
+ const rgb = hexToRgb(hex);
100
+ if (!rgb) {
101
+ return hex;
102
+ }
103
+ const [r, g, b] = rgb;
104
+ return rgbToHex(r * factor, g * factor, b * factor);
105
+ }
106
+ function deriveFaceFills(fill) {
107
+ return {
108
+ topFill: fill,
109
+ frontFill: multiply(fill, 0.95),
110
+ sideFill: multiply(fill, 0.88)
111
+ };
112
+ }
113
+
114
+ // src/sflLanguage/lexing/cursor.ts
115
+ var SourceCursor = class {
116
+ constructor(input) {
117
+ this.input = input;
118
+ }
119
+ input;
120
+ offset = 0;
121
+ line = 1;
122
+ col = 1;
123
+ get done() {
124
+ return this.offset >= this.input.length;
125
+ }
126
+ peek(ahead = 0) {
127
+ return this.input[this.offset + ahead];
128
+ }
129
+ startsWith(value) {
130
+ return this.input.startsWith(value, this.offset);
131
+ }
132
+ position() {
133
+ return { line: this.line, col: this.col };
134
+ }
135
+ advance() {
136
+ const character = this.input[this.offset];
137
+ if (character === void 0) {
138
+ throw new Error("Cannot advance beyond the end of the SFL source.");
139
+ }
140
+ this.offset++;
141
+ if (character === "\n" || character === "\r" && this.peek() !== "\n") {
142
+ this.line++;
143
+ this.col = 1;
144
+ } else {
145
+ this.col++;
146
+ }
147
+ return character;
148
+ }
149
+ };
150
+
151
+ // src/sflLanguage/lexing/error.ts
152
+ var SflLexError = class extends Error {
153
+ constructor(message, line, col) {
154
+ super(`SFL lex error (${line}:${col}): ${message}`);
155
+ this.line = line;
156
+ this.col = col;
157
+ this.name = "SflLexError";
158
+ }
159
+ line;
160
+ col;
161
+ };
162
+
163
+ // src/sflLanguage/lexing/characters.ts
164
+ var isLetter = (character) => character !== void 0 && /^[A-Za-z_]$/.test(character);
165
+ var isDigit = (character) => character !== void 0 && character >= "0" && character <= "9";
166
+ var isIdentPart = (character) => character !== void 0 && /^[A-Za-z0-9_]$/.test(character);
167
+
168
+ // src/sflLanguage/lexing/tokenCreation.ts
169
+ function createToken(type, value, start, detail = {}) {
170
+ return {
171
+ type,
172
+ value,
173
+ line: start.line,
174
+ col: start.col,
175
+ endLine: start.line,
176
+ endCol: start.col,
177
+ ...detail
178
+ };
179
+ }
180
+ function withTokenEnd(token, cursor) {
181
+ return {
182
+ ...token,
183
+ endLine: cursor.line,
184
+ endCol: cursor.col
185
+ };
186
+ }
187
+
188
+ // src/sflLanguage/lexing/identifiers.ts
189
+ function startsIdentifier(cursor) {
190
+ return isLetter(cursor.peek());
191
+ }
192
+ function readIdentifier(cursor, start) {
193
+ let value = "";
194
+ while (isIdentPart(cursor.peek())) {
195
+ value += cursor.advance();
196
+ }
197
+ return createToken("ident", value, start);
198
+ }
199
+
200
+ // src/sflLanguage/lexing/lineBreak.ts
201
+ function isLineBreak(character) {
202
+ return character === "\r" || character === "\n";
203
+ }
204
+ function readLineBreak(cursor, start) {
205
+ const firstCharacter = cursor.advance();
206
+ if (firstCharacter === "\r" && cursor.peek() === "\n") {
207
+ cursor.advance();
208
+ }
209
+ return createToken("newline", "\n", start);
210
+ }
211
+
212
+ // src/sflLanguage/lexing/lineComment.ts
213
+ function startsLineComment(cursor) {
214
+ return cursor.peek() === "/" && cursor.peek(1) === "/";
215
+ }
216
+ function consumeLineComment(cursor) {
217
+ while (!cursor.done && cursor.peek() !== "\n" && cursor.peek() !== "\r") {
218
+ cursor.advance();
219
+ }
220
+ }
221
+
222
+ // src/sflLanguage/lexing/numbers.ts
223
+ function startsNumber(cursor) {
224
+ const character = cursor.peek();
225
+ return isDigit(character) || character === "-" && isDigit(cursor.peek(1));
226
+ }
227
+ function readNumber(cursor, start) {
228
+ let value = "";
229
+ if (cursor.peek() === "-") {
230
+ value += cursor.advance();
231
+ }
232
+ while (isDigit(cursor.peek())) {
233
+ value += cursor.advance();
234
+ }
235
+ if (cursor.peek() === "." && isDigit(cursor.peek(1))) {
236
+ value += cursor.advance();
237
+ while (isDigit(cursor.peek())) {
238
+ value += cursor.advance();
239
+ }
240
+ }
241
+ return createToken("number", value, start, { numberValue: Number(value) });
242
+ }
243
+
244
+ // src/sflLanguage/lexing/operators.ts
245
+ var OPERATORS = [
246
+ ["<->", "bind2"],
247
+ ["<-", "bind"],
248
+ ["->", "action"],
249
+ ["&&", "op"],
250
+ ["||", "op"],
251
+ [">=", "op"],
252
+ ["<=", "op"],
253
+ ["==", "op"],
254
+ ["!=", "op"],
255
+ ["+", "op"],
256
+ ["-", "op"],
257
+ ["*", "op"],
258
+ ["/", "op"],
259
+ [">", "op"],
260
+ ["<", "op"],
261
+ ["!", "op"]
262
+ ];
263
+ var startsNumber2 = (character) => character !== void 0 && /[\d.]/.test(character);
264
+ function readOperator(cursor, start) {
265
+ const match = OPERATORS.find(([text2]) => cursor.startsWith(text2));
266
+ if (!match) {
267
+ return null;
268
+ }
269
+ const [text, type] = match;
270
+ if (text === "-" && startsNumber2(cursor.peek(1))) {
271
+ return null;
272
+ }
273
+ for (let index = 0; index < text.length; index++) {
274
+ cursor.advance();
275
+ }
276
+ return createToken(type, text, start);
277
+ }
278
+
279
+ // src/sflLanguage/lexing/punctuation.ts
280
+ var PUNCTUATION = {
281
+ "{": "lbrace",
282
+ "}": "rbrace",
283
+ "[": "lbracket",
284
+ "]": "rbracket",
285
+ "(": "lparen",
286
+ ")": "rparen",
287
+ ":": "colon",
288
+ ",": "comma",
289
+ ";": "semi",
290
+ ".": "dot",
291
+ "&": "amp"
292
+ };
293
+ function readPunctuation(cursor, start) {
294
+ const character = cursor.peek();
295
+ const type = character ? PUNCTUATION[character] : void 0;
296
+ if (!character || !type || character === "&" && cursor.peek(1) === "&") {
297
+ return null;
298
+ }
299
+ cursor.advance();
300
+ return createToken(type, character, start);
301
+ }
302
+
303
+ // src/sflLanguage/lexing/spacing.ts
304
+ function isSpacing(character) {
305
+ return character === " " || character === " ";
306
+ }
307
+ function consumeSpacing(cursor) {
308
+ cursor.advance();
309
+ }
310
+
311
+ // src/sflLanguage/lexing/strings.ts
312
+ function indentationWidth(line) {
313
+ let width = 0;
314
+ while (line[width] === " " || line[width] === " ") {
315
+ width++;
316
+ }
317
+ return width;
318
+ }
319
+ function dedent(text) {
320
+ const lines = text.split("\n");
321
+ let margin = Infinity;
322
+ for (const line of lines) {
323
+ if (!line.trim()) {
324
+ continue;
325
+ }
326
+ margin = Math.min(margin, indentationWidth(line));
327
+ }
328
+ if (!Number.isFinite(margin) || margin === 0) {
329
+ return text;
330
+ }
331
+ return lines.map((line) => line.trim() ? line.slice(margin) : line).join("\n");
332
+ }
333
+ function readLongString(cursor, start) {
334
+ consumeLongStringDelimiter(cursor);
335
+ let body = "";
336
+ while (!cursor.done) {
337
+ if (cursor.startsWith('"""')) {
338
+ consumeLongStringDelimiter(cursor);
339
+ return dedent(body.replace(/^\r?\n/, "").replace(/\r?\n[ \t]*$/, ""));
340
+ }
341
+ body += cursor.advance();
342
+ }
343
+ throw new SflLexError("unterminated long string", start.line, start.col);
344
+ }
345
+ function consumeLongStringDelimiter(cursor) {
346
+ cursor.advance();
347
+ cursor.advance();
348
+ cursor.advance();
349
+ }
350
+ function readShortString(cursor, start) {
351
+ cursor.advance();
352
+ let value = "";
353
+ while (!cursor.done) {
354
+ const character = cursor.advance();
355
+ if (character === '"') {
356
+ return value;
357
+ }
358
+ if (character === "\n" || character === "\r") {
359
+ throw new SflLexError('newline in short string (use """ for multiline)', start.line, start.col);
360
+ }
361
+ value += character === "\\" ? readEscapedCharacter(cursor, start) : character;
362
+ }
363
+ throw new SflLexError("unterminated string", start.line, start.col);
364
+ }
365
+ function readEscapedCharacter(cursor, start) {
366
+ if (cursor.done) {
367
+ throw new SflLexError("unterminated escape sequence", start.line, start.col);
368
+ }
369
+ const escaped = cursor.advance();
370
+ switch (escaped) {
371
+ case '"':
372
+ return '"';
373
+ case "\\":
374
+ return "\\";
375
+ case "n":
376
+ return "\n";
377
+ case "t":
378
+ return " ";
379
+ default:
380
+ throw new SflLexError(`invalid escape '\\${escaped}'`, start.line, start.col);
381
+ }
382
+ }
383
+ function readString(cursor, start) {
384
+ return cursor.startsWith('"""') ? readLongString(cursor, start) : readShortString(cursor, start);
385
+ }
386
+ function readStringToken(cursor, start) {
387
+ return createToken("string", readString(cursor, start), start);
388
+ }
389
+
390
+ // src/sflLanguage/lexing/nextToken.ts
391
+ function readNextToken(cursor) {
392
+ const character = cursor.peek();
393
+ if (character === void 0) {
394
+ return null;
395
+ }
396
+ const start = cursor.position();
397
+ if (isLineBreak(character)) {
398
+ return readLineBreak(cursor, start);
399
+ }
400
+ if (consumeLexerTrivia(cursor, character)) {
401
+ return null;
402
+ }
403
+ const token = readSymbolToken(cursor, character, start) ?? readWordToken(cursor, start);
404
+ if (token) {
405
+ return token;
406
+ }
407
+ throw new SflLexError(`unexpected character '${character}'`, start.line, start.col);
408
+ }
409
+ function consumeLexerTrivia(cursor, character) {
410
+ if (isSpacing(character)) {
411
+ consumeSpacing(cursor);
412
+ return true;
413
+ }
414
+ if (startsLineComment(cursor)) {
415
+ consumeLineComment(cursor);
416
+ return true;
417
+ }
418
+ return false;
419
+ }
420
+ function readSymbolToken(cursor, character, start) {
421
+ const punctuation = readPunctuation(cursor, start);
422
+ if (punctuation) {
423
+ return punctuation;
424
+ }
425
+ if (character === '"') {
426
+ return readStringToken(cursor, start);
427
+ }
428
+ return readOperator(cursor, start);
429
+ }
430
+ function readWordToken(cursor, start) {
431
+ if (startsNumber(cursor)) {
432
+ return readNumber(cursor, start);
433
+ }
434
+ if (startsIdentifier(cursor)) {
435
+ return readIdentifier(cursor, start);
436
+ }
437
+ return null;
438
+ }
439
+
440
+ // src/sflLanguage/lexing/tokenizer.ts
441
+ function tokenizeSource(input) {
442
+ const cursor = new SourceCursor(input);
443
+ const tokens = [];
444
+ while (!cursor.done) {
445
+ const token = readNextToken(cursor);
446
+ if (token) {
447
+ tokens.push(withTokenEnd(token, cursor));
448
+ }
449
+ }
450
+ tokens.push(withTokenEnd(createToken("eof", "", cursor.position()), cursor));
451
+ return tokens;
452
+ }
453
+
454
+ // src/sflLanguage/parsing/error.ts
455
+ var SflParseError = class extends Error {
456
+ constructor(message, line, col) {
457
+ super(`SFL parse error (${line}:${col}): ${message}`);
458
+ this.line = line;
459
+ this.col = col;
460
+ this.name = "SflParseError";
461
+ }
462
+ line;
463
+ col;
464
+ };
465
+
466
+ // src/sflLanguage/parsing/cursor.ts
467
+ var TokenCursor = class {
468
+ constructor(tokens) {
469
+ this.tokens = tokens;
470
+ }
471
+ tokens;
472
+ position = 0;
473
+ peek(offset = 0) {
474
+ return this.tokens[Math.min(this.position + offset, this.tokens.length - 1)];
475
+ }
476
+ at(type) {
477
+ return this.peek().type === type;
478
+ }
479
+ next() {
480
+ return this.tokens[this.position++];
481
+ }
482
+ expect(type, description) {
483
+ const token = this.peek();
484
+ if (token.type !== type) {
485
+ throw new SflParseError(`expected ${description}, got '${token.value || token.type}'`, token.line, token.col);
486
+ }
487
+ return this.next();
488
+ }
489
+ expectNumber(description) {
490
+ const token = this.expect("number", description);
491
+ if (token.numberValue === void 0) {
492
+ throw new SflParseError(`expected ${description} to contain a numeric value`, token.line, token.col);
493
+ }
494
+ return token.numberValue;
495
+ }
496
+ skipNewlines() {
497
+ while (this.at("newline")) {
498
+ this.next();
499
+ }
500
+ }
501
+ };
502
+
503
+ // src/sflLanguage/ast.ts
504
+ var NAMED_BLOCK_KINDS = [
505
+ "Project",
506
+ "Model",
507
+ "Group",
508
+ "Entity",
509
+ "Container",
510
+ "Path",
511
+ "Chart",
512
+ "Widget",
513
+ "Shape",
514
+ "Text",
515
+ "Isometric",
516
+ "Link",
517
+ "Connection",
518
+ "Layout",
519
+ "Style",
520
+ "Shader",
521
+ "Axis",
522
+ "Series",
523
+ "Rule",
524
+ "Net",
525
+ "Symbol",
526
+ "Port",
527
+ "DataSource"
528
+ ];
529
+ var SIDES = ["top", "bottom", "left", "right"];
530
+ var isSide = (s) => SIDES.includes(s);
531
+ var isNamedBlockKind = (s) => NAMED_BLOCK_KINDS.includes(s);
532
+
533
+ // src/sflLanguage/parsing/values.ts
534
+ function parsePathSegments(cursor) {
535
+ const segments = [cursor.expect("ident", "path identifier").value];
536
+ while (cursor.at("dot")) {
537
+ cursor.next();
538
+ segments.push(cursor.expect("ident", 'identifier after "."').value);
539
+ }
540
+ return segments;
541
+ }
542
+ function parseEndpoint(cursor) {
543
+ const segments = parsePathSegments(cursor);
544
+ let side;
545
+ let fraction;
546
+ if (cursor.at("comma")) {
547
+ cursor.next();
548
+ const sideToken = cursor.expect("ident", 'side after ","');
549
+ if (!isSide(sideToken.value)) {
550
+ throw new SflParseError(
551
+ `expected a side (top/right/bottom/left), got '${sideToken.value}'`,
552
+ sideToken.line,
553
+ sideToken.col
554
+ );
555
+ }
556
+ side = sideToken.value;
557
+ if (cursor.at("lparen")) {
558
+ cursor.next();
559
+ fraction = cursor.expectNumber('fraction inside "("');
560
+ cursor.expect("rparen", "')'");
561
+ }
562
+ }
563
+ return { type: "endpoint", segments, side, fraction };
564
+ }
565
+ function parseList(cursor) {
566
+ cursor.expect("lbracket", "'['");
567
+ const values = [];
568
+ cursor.skipNewlines();
569
+ if (!cursor.at("rbracket")) {
570
+ values.push(parseValue(cursor, false));
571
+ cursor.skipNewlines();
572
+ while (cursor.at("comma")) {
573
+ cursor.next();
574
+ cursor.skipNewlines();
575
+ if (cursor.at("rbracket")) {
576
+ break;
577
+ }
578
+ values.push(parseValue(cursor, false));
579
+ cursor.skipNewlines();
580
+ }
581
+ }
582
+ cursor.expect("rbracket", "']'");
583
+ return { type: "list", values };
584
+ }
585
+ function parseValue(cursor, allowTuple = true) {
586
+ const token = cursor.peek();
587
+ switch (token.type) {
588
+ case "string":
589
+ cursor.next();
590
+ return { type: "string", value: token.value };
591
+ case "lbracket":
592
+ return parseList(cursor);
593
+ case "number": {
594
+ const firstValue = cursor.expectNumber("number");
595
+ if (!allowTuple || !cursor.at("comma")) {
596
+ return { type: "number", value: firstValue };
597
+ }
598
+ const values = [firstValue];
599
+ while (cursor.at("comma")) {
600
+ cursor.next();
601
+ values.push(cursor.expectNumber("number in tuple"));
602
+ }
603
+ return { type: "tuple", values };
604
+ }
605
+ case "ident":
606
+ return { type: "ident", segments: parsePathSegments(cursor) };
607
+ default:
608
+ throw new SflParseError(`expected a value, got '${token.value || token.type}'`, token.line, token.col);
609
+ }
610
+ }
611
+
612
+ // src/sflLanguage/parsing/expressions.ts
613
+ var COMPARISONS = /* @__PURE__ */ new Set([">", "<", ">=", "<=", "==", "!="]);
614
+ function atOperator(cursor, operators) {
615
+ const token = cursor.peek();
616
+ return token.type === "op" && operators.has(token.value);
617
+ }
618
+ function parseBinaryLevel(cursor, operators, parseOperand) {
619
+ let left = parseOperand(cursor);
620
+ while (atOperator(cursor, operators)) {
621
+ const operator = cursor.next().value;
622
+ left = { kind: "binary", operator, left, right: parseOperand(cursor) };
623
+ }
624
+ return left;
625
+ }
626
+ var OR = /* @__PURE__ */ new Set(["||"]);
627
+ var AND = /* @__PURE__ */ new Set(["&&"]);
628
+ var ADDITIVE = /* @__PURE__ */ new Set(["+", "-"]);
629
+ var MULTIPLICATIVE = /* @__PURE__ */ new Set(["*", "/"]);
630
+ function parseExpression(cursor) {
631
+ return parseBinaryLevel(cursor, OR, parseAnd);
632
+ }
633
+ function parseAnd(cursor) {
634
+ return parseBinaryLevel(cursor, AND, parseComparison);
635
+ }
636
+ function parseComparison(cursor) {
637
+ return parseBinaryLevel(cursor, COMPARISONS, parseAdditive);
638
+ }
639
+ function parseAdditive(cursor) {
640
+ return parseBinaryLevel(cursor, ADDITIVE, parseMultiplicative);
641
+ }
642
+ function parseMultiplicative(cursor) {
643
+ return parseBinaryLevel(cursor, MULTIPLICATIVE, parseUnary);
644
+ }
645
+ function parseUnary(cursor) {
646
+ const token = cursor.peek();
647
+ if (token.type === "op" && (token.value === "!" || token.value === "-")) {
648
+ cursor.next();
649
+ return { kind: "unary", operator: token.value, operand: parseUnary(cursor) };
650
+ }
651
+ return parsePrimary(cursor);
652
+ }
653
+ function parseCallArguments(cursor) {
654
+ cursor.expect("lparen", "'('");
655
+ const args = [];
656
+ cursor.skipNewlines();
657
+ while (!cursor.at("rparen")) {
658
+ args.push(parseExpression(cursor));
659
+ cursor.skipNewlines();
660
+ if (cursor.at("comma")) {
661
+ cursor.next();
662
+ cursor.skipNewlines();
663
+ } else if (!cursor.at("rparen")) {
664
+ const token = cursor.peek();
665
+ throw new SflParseError(`expected ',' or ')', got '${token.value || token.type}'`, token.line, token.col);
666
+ }
667
+ }
668
+ cursor.expect("rparen", "')'");
669
+ return args;
670
+ }
671
+ function parsePrimary(cursor) {
672
+ const token = cursor.peek();
673
+ switch (token.type) {
674
+ case "number":
675
+ return { kind: "number", value: cursor.expectNumber("number") };
676
+ case "string":
677
+ cursor.next();
678
+ return { kind: "string", value: token.value };
679
+ case "lparen": {
680
+ cursor.next();
681
+ const inner = parseExpression(cursor);
682
+ cursor.expect("rparen", "')'");
683
+ return inner;
684
+ }
685
+ case "ident": {
686
+ const path = parsePathSegments(cursor);
687
+ if (path.length === 1 && (path[0] === "true" || path[0] === "false")) {
688
+ return { kind: "boolean", value: path[0] === "true" };
689
+ }
690
+ return cursor.at("lparen") ? { kind: "call", callee: path, args: parseCallArguments(cursor) } : { kind: "ref", path };
691
+ }
692
+ default:
693
+ throw new SflParseError(`expected an expression, got '${token.value || token.type}'`, token.line, token.col);
694
+ }
695
+ }
696
+ var BINDING_OPERATORS = ["bind", "bind2", "action"];
697
+ function atBinding(cursor) {
698
+ return cursor.peek().type === "ident" && cursor.peek(1).type === "colon" && BINDING_OPERATORS.includes(cursor.peek(2).type);
699
+ }
700
+ function parseBinding(cursor) {
701
+ const key = cursor.expect("ident", "bound property");
702
+ cursor.expect("colon", "':'");
703
+ const operator = cursor.next().value;
704
+ const expr = parseExpression(cursor);
705
+ const next = cursor.peek();
706
+ if (!["newline", "semi", "rbrace", "eof"].includes(next.type)) {
707
+ throw new SflParseError(`unexpected '${next.value || next.type}' after binding`, next.line, next.col);
708
+ }
709
+ return { key: key.value, operator, expr, line: key.line, col: key.col };
710
+ }
711
+ function parseAssignment(cursor) {
712
+ const start = cursor.peek();
713
+ const target = parsePathSegments(cursor);
714
+ cursor.expect("bind", "'<-'");
715
+ const expr = parseExpression(cursor);
716
+ return { target, expr, line: start.line, col: start.col };
717
+ }
718
+ function atHandler(cursor) {
719
+ const token = cursor.peek();
720
+ return token.type === "ident" && /^on[A-Z]/.test(token.value) && ["colon", "lbrace"].includes(cursor.peek(1).type);
721
+ }
722
+ function parseHandler(cursor) {
723
+ const event = cursor.expect("ident", "event name");
724
+ const assignments = [];
725
+ if (cursor.at("colon")) {
726
+ cursor.next();
727
+ assignments.push(parseAssignment(cursor));
728
+ expectEnd(cursor, "handler");
729
+ } else {
730
+ cursor.expect("lbrace", "'{'");
731
+ cursor.skipNewlines();
732
+ while (!cursor.at("rbrace")) {
733
+ if (cursor.at("eof")) {
734
+ throw new SflParseError(`unterminated handler '${event.value}'`, event.line, event.col);
735
+ }
736
+ assignments.push(parseAssignment(cursor));
737
+ expectEnd(cursor, "assignment");
738
+ cursor.skipNewlines();
739
+ }
740
+ cursor.expect("rbrace", "'}'");
741
+ }
742
+ return { event: event.value, assignments, line: event.line, col: event.col };
743
+ }
744
+ function expectEnd(cursor, what) {
745
+ const next = cursor.peek();
746
+ if (!["newline", "semi", "rbrace", "eof"].includes(next.type)) {
747
+ throw new SflParseError(`unexpected '${next.value || next.type}' after ${what}`, next.line, next.col);
748
+ }
749
+ if (next.type === "semi") {
750
+ cursor.next();
751
+ }
752
+ }
753
+
754
+ // src/sflLanguage/parsing/properties.ts
755
+ var PropertyParser = class {
756
+ constructor(cursor) {
757
+ this.cursor = cursor;
758
+ }
759
+ cursor;
760
+ parseLayout() {
761
+ this.cursor.next();
762
+ return { properties: this.parsePropertyBlock("layout") };
763
+ }
764
+ parseStyle() {
765
+ this.cursor.next();
766
+ this.cursor.skipNewlines();
767
+ this.cursor.expect("lbrace", "'{'");
768
+ const properties = [];
769
+ const bindings = [];
770
+ this.cursor.skipNewlines();
771
+ while (!this.cursor.at("rbrace")) {
772
+ if (this.cursor.at("eof")) {
773
+ const token = this.cursor.peek();
774
+ throw new SflParseError("unterminated style block", token.line, token.col);
775
+ }
776
+ if (atBinding(this.cursor)) {
777
+ bindings.push(parseBinding(this.cursor));
778
+ if (this.cursor.at("newline") || this.cursor.at("semi")) {
779
+ this.cursor.next();
780
+ }
781
+ } else {
782
+ properties.push(this.parseProperty());
783
+ }
784
+ this.cursor.skipNewlines();
785
+ }
786
+ this.cursor.expect("rbrace", "'}'");
787
+ return bindings.length ? { properties, bindings } : { properties };
788
+ }
789
+ parseFields() {
790
+ this.cursor.next();
791
+ return this.parsePropertyBlock("fields");
792
+ }
793
+ parseProperty() {
794
+ const key = this.cursor.expect("ident", "property key").value;
795
+ this.cursor.expect("colon", "':'");
796
+ const value = key === "from" || key === "to" ? parseEndpoint(this.cursor) : parseValue(this.cursor);
797
+ this.consumeTerminator();
798
+ return { key, value };
799
+ }
800
+ parsePropertyBlock(kind) {
801
+ this.cursor.skipNewlines();
802
+ this.cursor.expect("lbrace", "'{'");
803
+ const properties = [];
804
+ this.cursor.skipNewlines();
805
+ while (!this.cursor.at("rbrace")) {
806
+ if (this.cursor.at("eof")) {
807
+ const token = this.cursor.peek();
808
+ throw new SflParseError(`unterminated ${kind} block`, token.line, token.col);
809
+ }
810
+ properties.push(this.parseProperty());
811
+ this.cursor.skipNewlines();
812
+ }
813
+ this.cursor.expect("rbrace", "'}'");
814
+ return properties;
815
+ }
816
+ consumeTerminator() {
817
+ if (this.cursor.at("newline") || this.cursor.at("semi")) {
818
+ this.cursor.next();
819
+ return;
820
+ }
821
+ if (this.cursor.at("rbrace") || this.cursor.at("eof")) {
822
+ return;
823
+ }
824
+ const token = this.cursor.peek();
825
+ throw new SflParseError(`unexpected '${token.value || token.type}' after property value`, token.line, token.col);
826
+ }
827
+ };
828
+
829
+ // src/sflLanguage/parsing/blocks.ts
830
+ var BlockParser = class {
831
+ constructor(cursor) {
832
+ this.cursor = cursor;
833
+ this.properties = new PropertyParser(cursor);
834
+ }
835
+ cursor;
836
+ properties;
837
+ parseBlock() {
838
+ const kind = this.cursor.expect("ident", "block kind");
839
+ if (!isNamedBlockKind(kind.value)) {
840
+ throw new SflParseError(`unknown block kind '${kind.value}'`, kind.line, kind.col);
841
+ }
842
+ const name = this.cursor.expect("ident", "block name");
843
+ const composition = this.parseComposition();
844
+ const entries = this.parseBlockEntries();
845
+ return { kind: kind.value, name: name.value, composition, entries };
846
+ }
847
+ parseComposition() {
848
+ if (!this.cursor.at("colon")) {
849
+ return [];
850
+ }
851
+ this.cursor.next();
852
+ const composition = [this.cursor.expect("ident", 'reference name after ":"').value];
853
+ while (this.cursor.at("amp")) {
854
+ this.cursor.next();
855
+ composition.push(this.cursor.expect("ident", 'reference name after "&"').value);
856
+ }
857
+ return composition;
858
+ }
859
+ parseBlockEntries() {
860
+ return this.parseEntries(() => this.parseBlockEntry());
861
+ }
862
+ parseDataEntries() {
863
+ return this.parseEntries(() => this.parseDataEntry());
864
+ }
865
+ parseEntries(parseEntry) {
866
+ this.cursor.skipNewlines();
867
+ this.cursor.expect("lbrace", "'{'");
868
+ const entries = [];
869
+ this.cursor.skipNewlines();
870
+ while (!this.cursor.at("rbrace")) {
871
+ if (this.cursor.at("eof")) {
872
+ const token = this.cursor.peek();
873
+ throw new SflParseError('unterminated block (missing "}")', token.line, token.col);
874
+ }
875
+ entries.push(parseEntry());
876
+ if (this.cursor.at("semi")) {
877
+ this.cursor.next();
878
+ }
879
+ this.cursor.skipNewlines();
880
+ }
881
+ this.cursor.expect("rbrace", "'}'");
882
+ return entries;
883
+ }
884
+ parseBlockEntry() {
885
+ const token = this.cursor.peek();
886
+ if (token.type !== "ident") {
887
+ throw new SflParseError(
888
+ `expected property, block, layout, or style \u2014 got '${token.value || token.type}'`,
889
+ token.line,
890
+ token.col
891
+ );
892
+ }
893
+ if (isNamedBlockKind(token.value) && this.cursor.peek(1).type === "ident") {
894
+ return { type: "block", block: this.parseBlock() };
895
+ }
896
+ if (token.value === "layout" && this.cursor.peek(1).type === "lbrace") {
897
+ return { type: "layout", layout: this.properties.parseLayout() };
898
+ }
899
+ if (token.value === "style" && this.cursor.peek(1).type === "lbrace") {
900
+ return { type: "style", style: this.properties.parseStyle() };
901
+ }
902
+ if (token.value === "fields" && this.cursor.peek(1).type === "lbrace") {
903
+ return { type: "fields", fields: this.properties.parseFields() };
904
+ }
905
+ if (atBinding(this.cursor)) {
906
+ return { type: "binding", binding: parseBinding(this.cursor) };
907
+ }
908
+ if (atHandler(this.cursor)) {
909
+ return { type: "handler", handler: parseHandler(this.cursor) };
910
+ }
911
+ if (token.value === "data" && this.cursor.peek(1).type === "lbrace") {
912
+ return { type: "data", data: this.parseDataBlock() };
913
+ }
914
+ return { type: "property", property: this.properties.parseProperty() };
915
+ }
916
+ parseDataEntry() {
917
+ const token = this.cursor.peek();
918
+ if (token.type === "ident" && !isNamedBlockKind(token.value) && this.looksLikeSeriesEntry()) {
919
+ return { type: "block", block: this.parseSeriesEntry() };
920
+ }
921
+ return this.parseBlockEntry();
922
+ }
923
+ parseDataBlock() {
924
+ this.cursor.next();
925
+ return { kind: "data", name: "", composition: [], entries: this.parseDataEntries() };
926
+ }
927
+ looksLikeSeriesEntry() {
928
+ if (this.cursor.peek(1).type === "lbrace") {
929
+ return true;
930
+ }
931
+ if (this.cursor.peek(1).type !== "colon") {
932
+ return false;
933
+ }
934
+ let offset = 2;
935
+ if (this.cursor.peek(offset).type !== "ident") {
936
+ return false;
937
+ }
938
+ offset++;
939
+ while (this.cursor.peek(offset).type === "amp") {
940
+ offset++;
941
+ if (this.cursor.peek(offset).type !== "ident") {
942
+ return false;
943
+ }
944
+ offset++;
945
+ }
946
+ return this.cursor.peek(offset).type === "lbrace";
947
+ }
948
+ parseSeriesEntry() {
949
+ const name = this.cursor.expect("ident", "series name");
950
+ const composition = this.parseComposition();
951
+ const entries = this.parseBlockEntries();
952
+ return { kind: "Series", name: name.value, composition, entries };
953
+ }
954
+ };
955
+
956
+ // src/sflLanguage/parsing/document.ts
957
+ var DocumentParser = class {
958
+ constructor(cursor) {
959
+ this.cursor = cursor;
960
+ this.blocks = new BlockParser(cursor);
961
+ }
962
+ cursor;
963
+ blocks;
964
+ parseDocument() {
965
+ this.cursor.skipNewlines();
966
+ const header = this.cursor.expect("ident", "'sfl' header");
967
+ if (header.value !== "sfl") {
968
+ throw new SflParseError(`document must start with 'sfl', got '${header.value}'`, header.line, header.col);
969
+ }
970
+ const version = this.cursor.expect("number", "version number");
971
+ this.cursor.skipNewlines();
972
+ const blocks = [];
973
+ while (!this.cursor.at("eof")) {
974
+ blocks.push(this.blocks.parseBlock());
975
+ this.cursor.skipNewlines();
976
+ }
977
+ return { version: version.value, blocks };
978
+ }
979
+ };
980
+
981
+ // src/sflLanguage/parsing/parser.ts
982
+ function parseSource(input) {
983
+ return new DocumentParser(new TokenCursor(tokenizeSource(input))).parseDocument();
984
+ }
985
+
986
+ // src/sflLanguage/emitting/blockEntries.ts
987
+ function collectBlockEntries(block2) {
988
+ const entries = {
989
+ properties: [],
990
+ blocks: [],
991
+ layouts: [],
992
+ styles: [],
993
+ dataBlocks: [],
994
+ fields: [],
995
+ bindings: [],
996
+ handlers: []
997
+ };
998
+ for (const entry of block2.entries) {
999
+ if (entry.type === "property") {
1000
+ entries.properties.push(entry.property);
1001
+ } else if (entry.type === "block") {
1002
+ entries.blocks.push(entry.block);
1003
+ } else if (entry.type === "layout") {
1004
+ entries.layouts.push(entry.layout);
1005
+ } else if (entry.type === "style") {
1006
+ entries.styles.push(entry.style);
1007
+ } else if (entry.type === "fields") {
1008
+ entries.fields.push(entry.fields);
1009
+ } else if (entry.type === "binding") {
1010
+ entries.bindings.push(entry.binding);
1011
+ } else if (entry.type === "handler") {
1012
+ entries.handlers.push(entry.handler);
1013
+ } else {
1014
+ entries.dataBlocks.push(entry.data);
1015
+ }
1016
+ }
1017
+ return entries;
1018
+ }
1019
+
1020
+ // src/sflLanguage/emitting/composition.ts
1021
+ function emitComposition(composition) {
1022
+ if (!composition.length) {
1023
+ return "";
1024
+ }
1025
+ return ` : ${composition.join(" & ")}`;
1026
+ }
1027
+
1028
+ // src/sflLanguage/emitting/values.ts
1029
+ function formatNumber(value) {
1030
+ if (Object.is(value, -0)) {
1031
+ return "0";
1032
+ }
1033
+ return String(value);
1034
+ }
1035
+ function emitString(value) {
1036
+ const escaped = value.replace(/\r\n?/g, "\n").replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\n").replace(/\t/g, "\\t");
1037
+ return `"${escaped}"`;
1038
+ }
1039
+ function emitValue(value) {
1040
+ switch (value.type) {
1041
+ case "string":
1042
+ return emitString(value.value);
1043
+ case "number":
1044
+ return formatNumber(value.value);
1045
+ case "tuple":
1046
+ return value.values.map(formatNumber).join(", ");
1047
+ case "list":
1048
+ return `[${value.values.map(emitValue).join(", ")}]`;
1049
+ case "ident":
1050
+ return value.segments.join(".");
1051
+ case "endpoint": {
1052
+ let output = value.segments.join(".");
1053
+ if (value.side) {
1054
+ output += `, ${value.side}`;
1055
+ if (value.fraction !== void 0) {
1056
+ output += ` (${formatNumber(value.fraction)})`;
1057
+ }
1058
+ }
1059
+ return output;
1060
+ }
1061
+ }
1062
+ }
1063
+ var emitProperty = (property) => `${property.key}: ${emitValue(property.value)}`;
1064
+
1065
+ // src/sflLanguage/emitting/expressions.ts
1066
+ var PRECEDENCE = {
1067
+ "||": 1,
1068
+ "&&": 2,
1069
+ "==": 3,
1070
+ "!=": 3,
1071
+ ">": 3,
1072
+ "<": 3,
1073
+ ">=": 3,
1074
+ "<=": 3,
1075
+ "+": 4,
1076
+ "-": 4,
1077
+ "*": 5,
1078
+ "/": 5
1079
+ };
1080
+ function emitExpression(expr, parentPrecedence = 0) {
1081
+ switch (expr.kind) {
1082
+ case "number":
1083
+ return String(expr.value);
1084
+ case "string":
1085
+ return emitValue({ type: "string", value: expr.value });
1086
+ case "boolean":
1087
+ return String(expr.value);
1088
+ case "ref":
1089
+ return expr.path.join(".");
1090
+ case "unary":
1091
+ return `${expr.operator}${emitExpression(expr.operand, 6)}`;
1092
+ case "call":
1093
+ return `${expr.callee.join(".")}(${expr.args.map((arg) => emitExpression(arg)).join(", ")})`;
1094
+ case "binary": {
1095
+ const precedence = PRECEDENCE[expr.operator];
1096
+ const text = `${emitExpression(expr.left, precedence)} ${expr.operator} ${emitExpression(expr.right, precedence + 1)}`;
1097
+ return precedence < parentPrecedence ? `(${text})` : text;
1098
+ }
1099
+ }
1100
+ }
1101
+ var emitAssignment = (assignment) => `${assignment.target.join(".")} <- ${emitExpression(assignment.expr)}`;
1102
+ function emitHandler(handler, pad) {
1103
+ if (handler.assignments.length === 1) {
1104
+ return `${pad}${handler.event}: ${emitAssignment(handler.assignments[0])}`;
1105
+ }
1106
+ const lines = handler.assignments.map((assignment) => `${pad} ${emitAssignment(assignment)}`);
1107
+ return [`${pad}${handler.event} {`, ...lines, `${pad}}`].join("\n");
1108
+ }
1109
+ var emitBinding = (binding) => `${binding.key}: ${binding.operator} ${emitExpression(binding.expr)}`;
1110
+
1111
+ // src/sflLanguage/emitting/propertyOrder.ts
1112
+ var LAYOUT_ORDER = ["position", "size", "rotation", "order"];
1113
+ var STYLE_ORDER = [
1114
+ "shape",
1115
+ "fill",
1116
+ "stroke",
1117
+ "strokeWidth",
1118
+ "strokeStyle",
1119
+ "textColor",
1120
+ "color",
1121
+ "fontSize",
1122
+ "fontWeight",
1123
+ "fontFamily",
1124
+ "textAlign",
1125
+ "verticalAlign",
1126
+ "cornerRadius",
1127
+ "dash",
1128
+ "shadow",
1129
+ "opacity",
1130
+ "icon",
1131
+ "route",
1132
+ "marker",
1133
+ "markerStart",
1134
+ "markerEnd",
1135
+ "asset"
1136
+ ];
1137
+ var alphabetical = (a, b) => a < b ? -1 : a > b ? 1 : 0;
1138
+ function compareProps(a, b) {
1139
+ const rank = (key) => key === "id" ? 0 : key === "kind" ? 1 : key === "text" ? 2 : 3;
1140
+ return rank(a) - rank(b) || alphabetical(a, b);
1141
+ }
1142
+ function compareByOrder(order, a, b) {
1143
+ const aIndex = order.indexOf(a);
1144
+ const bIndex = order.indexOf(b);
1145
+ const aRank = aIndex === -1 ? order.length : aIndex;
1146
+ const bRank = bIndex === -1 ? order.length : bIndex;
1147
+ return aRank - bRank || alphabetical(a, b);
1148
+ }
1149
+ var compareLayoutProps = (a, b) => compareByOrder(LAYOUT_ORDER, a, b);
1150
+ var compareStyleProps = (a, b) => compareByOrder(STYLE_ORDER, a, b);
1151
+
1152
+ // src/sflLanguage/emitting/propertyBlock.ts
1153
+ var INDENT = " ";
1154
+ function emitPropertyBlock(name, properties, depth, bindings = []) {
1155
+ const pad = INDENT.repeat(depth);
1156
+ const innerPad = INDENT.repeat(depth + 1);
1157
+ const compare = name === "layout" ? compareLayoutProps : compareStyleProps;
1158
+ const lines = [...properties].sort((left, right) => compare(left.key, right.key)).map((property) => `${innerPad}${emitProperty(property)}`);
1159
+ lines.push(...bindings.map((binding) => `${innerPad}${emitBinding(binding)}`));
1160
+ if (!lines.length) {
1161
+ return `${pad}${name} {
1162
+ ${pad}}`;
1163
+ }
1164
+ return `${pad}${name} {
1165
+ ${lines.join("\n")}
1166
+ ${pad}}`;
1167
+ }
1168
+
1169
+ // src/sflLanguage/emitting/blocks.ts
1170
+ var INDENT2 = " ";
1171
+ var COMPACT_MAX_PROPS = 3;
1172
+ function propertyComparator(kind) {
1173
+ if (kind === "Layout") {
1174
+ return compareLayoutProps;
1175
+ }
1176
+ if (kind === "Style") {
1177
+ return compareStyleProps;
1178
+ }
1179
+ return compareProps;
1180
+ }
1181
+ function joinSiblingBlocks(blocks, depth) {
1182
+ const pad = INDENT2.repeat(depth);
1183
+ const layouts = blocks.filter((block2) => block2.kind === "Layout");
1184
+ const styles = blocks.filter((block2) => block2.kind === "Style");
1185
+ const others = blocks.filter((block2) => block2.kind !== "Layout" && block2.kind !== "Style");
1186
+ const groups = [];
1187
+ if (layouts.length) {
1188
+ groups.push(`${pad}// Layout Definition
1189
+ ${layouts.map((block2) => emitBlock(block2, depth)).join("\n")}`);
1190
+ }
1191
+ if (styles.length) {
1192
+ groups.push(`${pad}// Style Definition
1193
+ ${styles.map((block2) => emitBlock(block2, depth)).join("\n")}`);
1194
+ }
1195
+ others.forEach((block2) => {
1196
+ groups.push(emitBlock(block2, depth));
1197
+ });
1198
+ return groups.join("\n\n");
1199
+ }
1200
+ function emitBlock(block2, depth) {
1201
+ const pad = INDENT2.repeat(depth);
1202
+ const header = `${block2.kind} ${block2.name}${emitComposition(block2.composition)} {`;
1203
+ const entries = collectBlockEntries(block2);
1204
+ const properties = orderedProperties(block2.kind, entries.properties);
1205
+ if (usesCompactBlockForm(block2.kind, entries, properties.length)) {
1206
+ return emitCompactBlock(pad, header, properties);
1207
+ }
1208
+ const body = emitExpandedBlockBody(entries, properties, depth);
1209
+ return emitExpandedBlock(pad, header, body);
1210
+ }
1211
+ function orderedProperties(kind, properties) {
1212
+ return [...properties].sort((left, right) => propertyComparator(kind)(left.key, right.key));
1213
+ }
1214
+ function usesCompactBlockForm(kind, entries, propertyCount) {
1215
+ const supportsCompactForm = kind === "Layout" || kind === "Style";
1216
+ const hasNestedContent = entries.blocks.length > 0 || entries.layouts.length > 0 || entries.styles.length > 0 || entries.dataBlocks.length > 0 || entries.fields.length > 0 || entries.bindings.length > 0 || entries.handlers.length > 0;
1217
+ return supportsCompactForm && !hasNestedContent && propertyCount <= COMPACT_MAX_PROPS;
1218
+ }
1219
+ function emitCompactBlock(pad, header, properties) {
1220
+ const body = properties.map(emitProperty).join("; ");
1221
+ return body ? `${pad}${header} ${body} }` : `${pad}${header} }`;
1222
+ }
1223
+ function emitExpandedBlockBody(entries, properties, depth) {
1224
+ const primaryEntries = emitPrimaryBlockEntries(entries, properties, depth);
1225
+ const propertyBlocks = emitAttachedPropertyBlocks(entries, depth);
1226
+ return [primaryEntries, propertyBlocks].filter(Boolean).join("\n");
1227
+ }
1228
+ function emitPrimaryBlockEntries(entries, properties, depth) {
1229
+ const innerPad = INDENT2.repeat(depth + 1);
1230
+ const propertyLines = [
1231
+ ...properties.map((property) => `${innerPad}${emitProperty(property)}`),
1232
+ ...entries.bindings.map((binding) => `${innerPad}${emitBinding(binding)}`),
1233
+ ...entries.handlers.map((handler) => emitHandler(handler, innerPad))
1234
+ ].join("\n");
1235
+ const nestedBlocks = joinSiblingBlocks(entries.blocks, depth + 1);
1236
+ return [propertyLines, nestedBlocks].filter(Boolean).join("\n\n");
1237
+ }
1238
+ function emitAttachedPropertyBlocks(entries, depth) {
1239
+ const fields = entries.fields.map((properties) => emitFieldsBlock(properties, depth + 1));
1240
+ const dataBlocks = entries.dataBlocks.map((data) => emitDataBlock(data, depth + 1));
1241
+ const layouts = entries.layouts.map((layout) => emitPropertyBlock("layout", layout.properties, depth + 1));
1242
+ const styles = entries.styles.map((style) => emitPropertyBlock("style", style.properties, depth + 1, style.bindings));
1243
+ return [...fields, ...dataBlocks, ...layouts, ...styles].join("\n");
1244
+ }
1245
+ function emitFieldsBlock(properties, depth) {
1246
+ const pad = INDENT2.repeat(depth);
1247
+ const lines = properties.map((property) => `${pad}${INDENT2}${emitProperty(property)}`);
1248
+ return lines.length ? `${pad}fields {
1249
+ ${lines.join("\n")}
1250
+ ${pad}}` : `${pad}fields {
1251
+ ${pad}}`;
1252
+ }
1253
+ function emitDataBlock(data, depth) {
1254
+ const pad = INDENT2.repeat(depth);
1255
+ const entries = collectBlockEntries(data);
1256
+ const properties = orderedProperties(data.kind, entries.properties);
1257
+ const body = emitExpandedBlockBody(entries, properties, depth);
1258
+ return emitExpandedBlock(pad, "data {", body);
1259
+ }
1260
+ function emitExpandedBlock(pad, header, body) {
1261
+ return body ? `${pad}${header}
1262
+ ${body}
1263
+ ${pad}}` : `${pad}${header}
1264
+ ${pad}}`;
1265
+ }
1266
+
1267
+ // src/sflLanguage/emitting/document.ts
1268
+ function emitDocument(doc) {
1269
+ const body = joinSiblingBlocks(doc.blocks, 0);
1270
+ const output = body ? `sfl ${doc.version}
1271
+
1272
+ ${body}` : `sfl ${doc.version}`;
1273
+ const trimmed = output.split("\n").map((line) => line.replace(/[ \t]+$/, "")).join("\n");
1274
+ return `${trimmed}
1275
+ `;
1276
+ }
1277
+
1278
+ // src/sflLanguage/canonicalize.ts
1279
+ function canonicalizeSource(sflText) {
1280
+ return emitDocument(parseSource(sflText));
1281
+ }
1282
+
1283
+ // src/sflSemantic/canvasObjects/artifacts/chart/types.ts
1284
+ function isChartObject(canvasObject) {
1285
+ return canvasObject.objectType === "chart" && canvasObject.chart !== void 0;
1286
+ }
1287
+
1288
+ // src/sflSemantic/canvasObjects/artifacts/isometric/types.ts
1289
+ function isIsometricObject(canvasObject) {
1290
+ return canvasObject.objectType === "isometric" && canvasObject.isometric !== void 0;
1291
+ }
1292
+
1293
+ // src/sflSemantic/canvasObjects/artifacts/path/types.ts
1294
+ function isPathObject(canvasObject) {
1295
+ return canvasObject.objectType === "path" && canvasObject.path !== void 0;
1296
+ }
1297
+
1298
+ // src/sflSemantic/canvasObjects/artifacts/swimlane/types.ts
1299
+ function isSwimlaneObject(canvasObject) {
1300
+ return canvasObject.objectType === "swimlane" && canvasObject.swimlane !== void 0;
1301
+ }
1302
+
1303
+ // src/sflSemantic/ast/valueAccess.ts
1304
+ function asString(value) {
1305
+ if (!value) {
1306
+ return void 0;
1307
+ }
1308
+ if (value.type === "string") {
1309
+ return value.value;
1310
+ }
1311
+ if (value.type === "ident") {
1312
+ return value.segments.join(".");
1313
+ }
1314
+ if (value.type === "number") {
1315
+ return String(value.value);
1316
+ }
1317
+ return void 0;
1318
+ }
1319
+ function asNumber(value) {
1320
+ return value?.type === "number" ? value.value : void 0;
1321
+ }
1322
+ function tupleValues(value) {
1323
+ return value?.type === "tuple" ? value.values : void 0;
1324
+ }
1325
+ function listNumbers(value) {
1326
+ return value?.type === "list" ? value.values.flatMap((item) => item.type === "number" ? [item.value] : []) : void 0;
1327
+ }
1328
+
1329
+ // src/sflSemantic/canvasObjects/artifacts/links/endpointReader.ts
1330
+ function namedPortReference(segments, context) {
1331
+ if (segments.length < 2) {
1332
+ return null;
1333
+ }
1334
+ const portName = segments[segments.length - 1];
1335
+ const canvasObjectName = segments[segments.length - 2];
1336
+ const canvasObjectId = context.idByName.get(canvasObjectName);
1337
+ const canvasObject = canvasObjectId ? context.canvasObjectById.get(canvasObjectId) : void 0;
1338
+ if (!canvasObject?.symbol?.ports?.some((port) => port.name === portName)) {
1339
+ return null;
1340
+ }
1341
+ return { id: canvasObjectId, port: portName };
1342
+ }
1343
+ function readLinkEndpoint(value, context) {
1344
+ if (value?.type !== "endpoint") {
1345
+ return {};
1346
+ }
1347
+ const namedPort = namedPortReference(value.segments, context);
1348
+ if (namedPort) {
1349
+ return namedPort;
1350
+ }
1351
+ const endpointName = value.segments[value.segments.length - 1];
1352
+ return {
1353
+ id: context.idByName.get(endpointName) ?? endpointName,
1354
+ side: value.side,
1355
+ fraction: value.fraction
1356
+ };
1357
+ }
1358
+
1359
+ // src/sflSemantic/ast/blockEntries.ts
1360
+ function directProperties(block2) {
1361
+ const properties = /* @__PURE__ */ new Map();
1362
+ for (const entry of block2.entries) {
1363
+ if (entry.type === "property") {
1364
+ properties.set(entry.property.key, entry.property.value);
1365
+ }
1366
+ }
1367
+ return properties;
1368
+ }
1369
+ function styleProperties(block2) {
1370
+ const properties = /* @__PURE__ */ new Map();
1371
+ for (const entry of block2.entries) {
1372
+ if (entry.type === "style") {
1373
+ for (const property of entry.style.properties) {
1374
+ properties.set(property.key, property.value);
1375
+ }
1376
+ }
1377
+ }
1378
+ return properties;
1379
+ }
1380
+ function layoutProperties(block2) {
1381
+ const properties = /* @__PURE__ */ new Map();
1382
+ for (const entry of block2.entries) {
1383
+ if (entry.type === "layout") {
1384
+ for (const property of entry.layout.properties) {
1385
+ properties.set(property.key, property.value);
1386
+ }
1387
+ }
1388
+ }
1389
+ return properties;
1390
+ }
1391
+ function childBlocks(block2) {
1392
+ return block2.entries.flatMap((entry) => entry.type === "block" ? [entry.block] : []);
1393
+ }
1394
+ function dataEntryBlock(block2) {
1395
+ for (const entry of block2.entries) {
1396
+ if (entry.type === "data") {
1397
+ return entry.data;
1398
+ }
1399
+ }
1400
+ return void 0;
1401
+ }
1402
+ function contentChildren(block2) {
1403
+ const data = dataEntryBlock(block2);
1404
+ return data ? [...childBlocks(block2), ...childBlocks(data)] : childBlocks(block2);
1405
+ }
1406
+ function contentProperty(block2, key) {
1407
+ const direct = directProperties(block2).get(key);
1408
+ if (direct !== void 0) {
1409
+ return direct;
1410
+ }
1411
+ const data = dataEntryBlock(block2);
1412
+ return data ? directProperties(data).get(key) : void 0;
1413
+ }
1414
+
1415
+ // src/sflSemantic/canvasObjects/processing/blockKinds.ts
1416
+ var CANVAS_OBJECT_BLOCK_KINDS = [
1417
+ "Entity",
1418
+ "Shape",
1419
+ "Text",
1420
+ "Container",
1421
+ "Path",
1422
+ "Chart",
1423
+ "Widget",
1424
+ "Isometric"
1425
+ ];
1426
+ var canvasObjectBlockKinds = new Set(CANVAS_OBJECT_BLOCK_KINDS);
1427
+ function isCanvasObjectBlockKind(kind) {
1428
+ return canvasObjectBlockKinds.has(kind);
1429
+ }
1430
+
1431
+ // src/sflSemantic/canvasObjects/artifacts/links/nameIndex.ts
1432
+ function indexLinkableNames(blocks, idByName) {
1433
+ for (const block2 of blocks) {
1434
+ if (isCanvasObjectBlockKind(block2.kind)) {
1435
+ idByName.set(block2.name, asString(directProperties(block2).get("id")) ?? block2.name);
1436
+ }
1437
+ indexLinkableNames(childBlocks(block2), idByName);
1438
+ }
1439
+ }
1440
+ function buildLinkNameIndex(model) {
1441
+ const idByName = /* @__PURE__ */ new Map();
1442
+ if (model) {
1443
+ indexLinkableNames(childBlocks(model), idByName);
1444
+ }
1445
+ return idByName;
1446
+ }
1447
+
1448
+ // src/sflSemantic/canvasObjects/artifacts/links/presentation.ts
1449
+ var STRUCTURAL_LINK_PROPERTIES = /* @__PURE__ */ new Set(["id", "from", "to", "text", "order", "segments"]);
1450
+ function readLinkPresentation(properties) {
1451
+ const presentation = {};
1452
+ for (const [key, value] of properties) {
1453
+ if (STRUCTURAL_LINK_PROPERTIES.has(key)) {
1454
+ continue;
1455
+ }
1456
+ const text = asString(value);
1457
+ if (text !== void 0) {
1458
+ presentation[key] = text;
1459
+ }
1460
+ }
1461
+ return presentation;
1462
+ }
1463
+
1464
+ // src/sflSemantic/canvasObjects/artifacts/connections/reader.ts
1465
+ var FACES = /* @__PURE__ */ new Set(["top", "bottom", "front", "back", "left", "right"]);
1466
+ function readFace(value) {
1467
+ return value !== void 0 && FACES.has(value) ? value : "front";
1468
+ }
1469
+ function readConnections(model, connectionBlocks, canvasObjects, context, sourceIndexes = []) {
1470
+ const endpointContext = {
1471
+ idByName: buildLinkNameIndex(model),
1472
+ canvasObjectById: new Map(canvasObjects.map((canvasObject) => [canvasObject.id, canvasObject]))
1473
+ };
1474
+ return connectionBlocks.map((block2, index) => {
1475
+ const properties = context.mergedProps(block2);
1476
+ return {
1477
+ id: asString(properties.get("id")) ?? block2.name,
1478
+ fromId: readLinkEndpoint(properties.get("from"), endpointContext).id,
1479
+ toId: readLinkEndpoint(properties.get("to"), endpointContext).id,
1480
+ fromFace: readFace(asString(properties.get("fromFace"))),
1481
+ toFace: readFace(asString(properties.get("toFace"))),
1482
+ order: asNumber(properties.get("order")),
1483
+ sourceIndex: sourceIndexes[index],
1484
+ props: readLinkPresentation(properties)
1485
+ };
1486
+ });
1487
+ }
1488
+
1489
+ // src/sflSemantic/canvasObjects/artifacts/links/blockReader.ts
1490
+ function readLinkBlock(block2, canvasObjectContext, endpointContext) {
1491
+ const properties = canvasObjectContext.mergedProps(block2);
1492
+ const from = readLinkEndpoint(properties.get("from"), endpointContext);
1493
+ const to = readLinkEndpoint(properties.get("to"), endpointContext);
1494
+ return {
1495
+ id: asString(properties.get("id")) ?? block2.name,
1496
+ fromId: from.id,
1497
+ toId: to.id,
1498
+ text: asString(properties.get("text")),
1499
+ fromSide: from.side,
1500
+ fromFraction: from.fraction,
1501
+ toSide: to.side,
1502
+ toFraction: to.fraction,
1503
+ fromPort: from.port,
1504
+ toPort: to.port,
1505
+ order: asNumber(properties.get("order")),
1506
+ segments: listNumbers(properties.get("segments")),
1507
+ props: readLinkPresentation(properties)
1508
+ };
1509
+ }
1510
+
1511
+ // src/sflSemantic/canvasObjects/artifacts/links/reader.ts
1512
+ function readLinks(model, linkBlocks, canvasObjects, context, sourceIndexes = []) {
1513
+ const endpointContext = {
1514
+ idByName: buildLinkNameIndex(model),
1515
+ canvasObjectById: new Map(canvasObjects.map((canvasObject) => [canvasObject.id, canvasObject]))
1516
+ };
1517
+ return linkBlocks.map((block2, index) => ({
1518
+ ...readLinkBlock(block2, context, endpointContext),
1519
+ sourceIndex: sourceIndexes[index]
1520
+ }));
1521
+ }
1522
+
1523
+ // src/sflSemantic/canvasObjects/artifacts/links/netExpansion.ts
1524
+ function netMembers(block2, context) {
1525
+ const membersValue = context.mergedProps(block2).get("members");
1526
+ return membersValue?.type === "list" ? membersValue.values.flatMap((value) => value.type === "ident" ? [value.segments] : []) : [];
1527
+ }
1528
+ function netLink(block2, from, to, index, text) {
1529
+ const entries = [
1530
+ { type: "property", property: { key: "from", value: { type: "endpoint", segments: from } } },
1531
+ { type: "property", property: { key: "to", value: { type: "endpoint", segments: to } } }
1532
+ ];
1533
+ if (text) {
1534
+ entries.push({
1535
+ type: "property",
1536
+ property: { key: "text", value: { type: "string", value: text } }
1537
+ });
1538
+ }
1539
+ return { kind: "Link", name: `${block2.name}_${index + 1}`, composition: [], entries };
1540
+ }
1541
+ function expandNetLinks(block2, context) {
1542
+ const members = netMembers(block2, context);
1543
+ if (members.length < 2) {
1544
+ return [];
1545
+ }
1546
+ const text = asString(context.mergedProps(block2).get("text"));
1547
+ return members.slice(1).map((member, index) => netLink(block2, members[0], member, index, text));
1548
+ }
1549
+
1550
+ // src/sflSemantic/canvasObjects/processing/layout/flow.ts
1551
+ function createFlowPositioner(block2, context) {
1552
+ const axis = asString(context.layoutProp(block2, "flow"));
1553
+ const gap = asNumber(context.layoutProp(block2, "gap")) ?? 0;
1554
+ let cursor = 0;
1555
+ return (size) => {
1556
+ if (axis !== "horizontal" && axis !== "vertical") {
1557
+ return void 0;
1558
+ }
1559
+ const position = axis === "horizontal" ? [cursor, 0] : [0, cursor];
1560
+ cursor += (axis === "horizontal" ? size[0] : size[1]) + gap;
1561
+ return position;
1562
+ };
1563
+ }
1564
+
1565
+ // src/sflSemantic/canvasObjects/artifacts/chart/values.ts
1566
+ function numberList(value) {
1567
+ if (value?.type !== "list") {
1568
+ return void 0;
1569
+ }
1570
+ const numbers = value.values.flatMap((item) => {
1571
+ const number = asNumber(item);
1572
+ return number === void 0 ? [] : [number];
1573
+ });
1574
+ return numbers.length > 0 ? numbers : void 0;
1575
+ }
1576
+ function stringList(value) {
1577
+ if (value?.type !== "list") {
1578
+ return void 0;
1579
+ }
1580
+ return value.values.flatMap((item) => {
1581
+ const text = asString(item);
1582
+ return text === void 0 ? [] : [text];
1583
+ });
1584
+ }
1585
+ function readBoolean(value) {
1586
+ const text = asString(value);
1587
+ if (text === void 0) {
1588
+ return void 0;
1589
+ }
1590
+ return text === "true";
1591
+ }
1592
+
1593
+ // src/sflSemantic/canvasObjects/artifacts/chart/axis.ts
1594
+ function readAxis(block2) {
1595
+ const properties = block2 ? directProperties(block2) : /* @__PURE__ */ new Map();
1596
+ return {
1597
+ min: asNumber(properties.get("min")) ?? 0,
1598
+ max: asNumber(properties.get("max")) ?? 10,
1599
+ step: asNumber(properties.get("step")) ?? 1,
1600
+ label: asString(properties.get("label")),
1601
+ unit: asString(properties.get("unit")),
1602
+ thresholds: numberList(properties.get("thresholds"))
1603
+ };
1604
+ }
1605
+
1606
+ // src/sflSemantic/canvasObjects/artifacts/chart/rules.ts
1607
+ function readChartRules(block2) {
1608
+ return contentChildren(block2).filter((child) => child.kind === "Rule").map((rule) => {
1609
+ const properties = directProperties(rule);
1610
+ return {
1611
+ id: rule.name,
1612
+ at: asNumber(properties.get("at")) ?? 0,
1613
+ text: asString(properties.get("text")),
1614
+ color: asString(properties.get("color")),
1615
+ strokeWidth: asNumber(properties.get("strokeWidth")),
1616
+ dash: numberList(properties.get("dash"))
1617
+ };
1618
+ });
1619
+ }
1620
+
1621
+ // src/sflSemantic/canvasObjects/artifacts/chart/seriesValues.ts
1622
+ function asComponents(value) {
1623
+ if (value.type !== "list" || value.values.length < 2) {
1624
+ return void 0;
1625
+ }
1626
+ const components = value.values.map(asNumber);
1627
+ return components.every((part) => part !== void 0) ? components : void 0;
1628
+ }
1629
+ function readSeriesValues(value) {
1630
+ if (value?.type !== "list") {
1631
+ return [];
1632
+ }
1633
+ const shape = firstSeriesValueShape(value.values);
1634
+ return shape === "tuple" ? readTupleSeriesValues(value.values) : readScalarSeriesValues(value.values);
1635
+ }
1636
+ function firstSeriesValueShape(values) {
1637
+ for (const value of values) {
1638
+ if (value.type === "number") {
1639
+ return "scalar";
1640
+ }
1641
+ if (asComponents(value)) {
1642
+ return "tuple";
1643
+ }
1644
+ }
1645
+ return void 0;
1646
+ }
1647
+ function readScalarSeriesValues(values) {
1648
+ return values.flatMap((value) => value.type === "number" ? [value.value] : []);
1649
+ }
1650
+ function readTupleSeriesValues(values) {
1651
+ return values.flatMap((value) => {
1652
+ const components = asComponents(value);
1653
+ return components ? [components] : [];
1654
+ });
1655
+ }
1656
+ function readSeriesErrors(value) {
1657
+ if (value?.type !== "list") {
1658
+ return void 0;
1659
+ }
1660
+ return value.values.map(readSeriesError);
1661
+ }
1662
+ function readSeriesError(value) {
1663
+ if (value.type === "ident" && value.segments.join(".") === "none") {
1664
+ return null;
1665
+ }
1666
+ if (value.type === "number") {
1667
+ return value.value;
1668
+ }
1669
+ return readSeriesErrorRange(value);
1670
+ }
1671
+ function readSeriesErrorRange(value) {
1672
+ if (value.type !== "list" || value.values.length < 2) {
1673
+ return null;
1674
+ }
1675
+ const low = asNumber(value.values[0]);
1676
+ const high = asNumber(value.values[1]);
1677
+ return low !== void 0 && high !== void 0 ? [low, high] : null;
1678
+ }
1679
+ function readSeriesColors(value) {
1680
+ if (value?.type !== "list") {
1681
+ return void 0;
1682
+ }
1683
+ return value.values.map(readSeriesColor);
1684
+ }
1685
+ function readSeriesColor(value) {
1686
+ if (value.type === "ident" && value.segments.join(".") === "none") {
1687
+ return null;
1688
+ }
1689
+ return asString(value) ?? null;
1690
+ }
1691
+ function readSeriesSymbol(value, symbolDefs) {
1692
+ if (value?.type === "list") {
1693
+ const points = value.values.flatMap(readSeriesSymbolPoint);
1694
+ return points.length > 0 ? { points } : void 0;
1695
+ }
1696
+ const name = asString(value);
1697
+ const definition = name ? symbolDefs.get(name) : void 0;
1698
+ if (!definition) {
1699
+ return void 0;
1700
+ }
1701
+ return { points: definition.points, normalized: true, tension: definition.tension };
1702
+ }
1703
+ function readSeriesSymbolPoint(value) {
1704
+ if (value.type !== "list" || value.values.length < 2) {
1705
+ return [];
1706
+ }
1707
+ const x = asNumber(value.values[0]);
1708
+ const y = asNumber(value.values[1]);
1709
+ return x === void 0 || y === void 0 ? [] : [[x, y]];
1710
+ }
1711
+
1712
+ // src/sflSemantic/canvasObjects/artifacts/chart/series.ts
1713
+ function readSeries(block2, symbolDefs, mergedProps) {
1714
+ const properties = mergedProps(block2);
1715
+ return {
1716
+ id: block2.name,
1717
+ label: asString(properties.get("label")) ?? block2.name,
1718
+ color: asString(properties.get("color")),
1719
+ colors: readSeriesColors(properties.get("colors")),
1720
+ opacity: asNumber(properties.get("opacity")),
1721
+ group: asString(properties.get("group")),
1722
+ values: readSeriesValues(properties.get("values")),
1723
+ error: readSeriesErrors(properties.get("error")),
1724
+ source: asString(properties.get("source")),
1725
+ target: asString(properties.get("target")),
1726
+ size: asNumber(properties.get("size")),
1727
+ symbol: readSeriesSymbol(properties.get("symbol"), symbolDefs),
1728
+ stroke: asString(properties.get("stroke")),
1729
+ strokeWidth: asNumber(properties.get("strokeWidth")),
1730
+ dash: numberList(properties.get("dash")),
1731
+ offset: asNumber(properties.get("offset")),
1732
+ labelOffsetX: asNumber(properties.get("labelOffsetX")),
1733
+ labelOffsetY: asNumber(properties.get("labelOffsetY"))
1734
+ };
1735
+ }
1736
+
1737
+ // src/sflSemantic/canvasObjects/artifacts/chart/zones.ts
1738
+ function readChartZones(value) {
1739
+ if (value?.type !== "list") {
1740
+ return void 0;
1741
+ }
1742
+ const zones = value.values.flatMap((item) => {
1743
+ if (item.type !== "list") {
1744
+ return [];
1745
+ }
1746
+ const label = asString(item.values[0]);
1747
+ if (label === void 0) {
1748
+ return [];
1749
+ }
1750
+ return [
1751
+ {
1752
+ label,
1753
+ color: asString(item.values[1]),
1754
+ offsetX: asNumber(item.values[2]),
1755
+ offsetY: asNumber(item.values[3])
1756
+ }
1757
+ ];
1758
+ });
1759
+ return zones.length > 0 ? zones : void 0;
1760
+ }
1761
+
1762
+ // src/sflSemantic/canvasObjects/artifacts/chart/reader.ts
1763
+ var STACKED_MODES = ["none", "true", "percent", "waterfall"];
1764
+ function asChartType(value) {
1765
+ return isChartType(value) ? value : "bar";
1766
+ }
1767
+ function asStackedMode(value) {
1768
+ return STACKED_MODES.find((mode) => mode === value);
1769
+ }
1770
+ function readChart(block2, properties, symbolDefs, mergedProps) {
1771
+ const children = contentChildren(block2);
1772
+ const axes = children.filter((child) => child.kind === "Axis");
1773
+ const stacked = asString(properties.get("stacked"));
1774
+ return {
1775
+ chartType: asChartType(asString(properties.get("type"))),
1776
+ categories: stringList(contentProperty(block2, "categories")),
1777
+ areaOpacity: asNumber(properties.get("areaOpacity")),
1778
+ stacked: asStackedMode(stacked),
1779
+ scale: asString(properties.get("scale")),
1780
+ interpolate: asString(properties.get("interpolate")) === "idw" ? "idw" : void 0,
1781
+ idwPower: asNumber(properties.get("idwPower")),
1782
+ isolines: numberList(properties.get("isolines")),
1783
+ zones: readChartZones(properties.get("zones")),
1784
+ boxWidth: asNumber(properties.get("boxWidth")),
1785
+ orientation: asString(properties.get("orientation")) === "horizontal" ? "horizontal" : void 0,
1786
+ lineTension: asNumber(properties.get("lineTension")),
1787
+ highlightPoints: numberList(properties.get("highlightPoints")),
1788
+ barCornerRadius: asNumber(properties.get("barCornerRadius")),
1789
+ showBoxplotPoints: readBoolean(properties.get("showBoxplotPoints")),
1790
+ barBorderColor: asString(properties.get("barBorderColor")),
1791
+ barBorderWidth: asNumber(properties.get("barBorderWidth")),
1792
+ barGroupGap: asNumber(properties.get("barGroupGap")),
1793
+ barGlow: asNumber(properties.get("barGlow")),
1794
+ barGap: asNumber(properties.get("barGap")),
1795
+ mekkoVaryHeight: readBoolean(properties.get("mekkoVaryHeight")),
1796
+ hexRadius: asNumber(properties.get("hexRadius")),
1797
+ innerRadius: asNumber(properties.get("innerRadius")),
1798
+ pieCornerRadius: asNumber(properties.get("pieCornerRadius")),
1799
+ pieSliceGap: asNumber(properties.get("pieSliceGap")),
1800
+ ringThickness: asNumber(properties.get("ringThickness")),
1801
+ ringGap: asNumber(properties.get("ringGap")),
1802
+ cellGap: asNumber(properties.get("cellGap")),
1803
+ showValues: readBoolean(properties.get("showValues")),
1804
+ squareCells: readBoolean(properties.get("squareCells")),
1805
+ headerColor: asString(properties.get("headerColor")),
1806
+ tileGradient: readBoolean(properties.get("tileGradient")),
1807
+ showColumnBackdrop: readBoolean(properties.get("showColumnBackdrop")),
1808
+ labelPosition: asString(properties.get("labelPosition")) === "inside" ? "inside" : void 0,
1809
+ labelOrientation: asString(properties.get("labelOrientation")) === "vertical" ? "vertical" : void 0,
1810
+ labelColor: asString(properties.get("labelColor")),
1811
+ linkGradient: readBoolean(properties.get("linkGradient")),
1812
+ connectPoints: readBoolean(properties.get("connectPoints")),
1813
+ reverseComparison: readBoolean(properties.get("reverseComparison")),
1814
+ axisX: readAxis(axes.find((axis) => axis.name === "x")),
1815
+ axisY: readAxis(axes.find((axis) => axis.name === "y" || axis.name === "value")),
1816
+ series: children.filter((child) => child.kind === "Series").map((child) => readSeries(child, symbolDefs, mergedProps)),
1817
+ rules: readChartRules(block2)
1818
+ };
1819
+ }
1820
+
1821
+ // src/sflSemantic/canvasObjects/artifacts/chart/resolver.ts
1822
+ function resolveChartObject(base, block2, properties, symbolDefinitions, mergedProperties) {
1823
+ return {
1824
+ ...base,
1825
+ objectType: "chart",
1826
+ chart: readChart(block2, properties, symbolDefinitions, mergedProperties)
1827
+ };
1828
+ }
1829
+
1830
+ // src/sflSemantic/canvasObjects/artifacts/isometric/reader.ts
1831
+ function readIsometric(properties, depth) {
1832
+ return {
1833
+ depth,
1834
+ icon: asString(properties.get("icon"))
1835
+ };
1836
+ }
1837
+
1838
+ // src/sflSemantic/canvasObjects/artifacts/isometric/resolver.ts
1839
+ function resolveIsometricObject(base, block2, properties, layoutProperty) {
1840
+ const size = tupleValues(layoutProperty(block2, "size"));
1841
+ return {
1842
+ ...base,
1843
+ objectType: "isometric",
1844
+ isometric: readIsometric(properties, size?.[2] ?? 24)
1845
+ };
1846
+ }
1847
+
1848
+ // src/sflSemantic/canvasObjects/artifacts/path/reader.ts
1849
+ function readPath(p, origin) {
1850
+ const pointsValue = p.get("points");
1851
+ const points = pointsValue?.type === "list" ? pointsValue.values.flatMap((value) => {
1852
+ if (value.type !== "list" || value.values.length < 2) {
1853
+ return [];
1854
+ }
1855
+ const x = asNumber(value.values[0]);
1856
+ const y = asNumber(value.values[1]);
1857
+ return x !== void 0 && y !== void 0 ? [{ x: origin.x + x, y: origin.y + y }] : [];
1858
+ }) : [];
1859
+ return {
1860
+ points,
1861
+ tension: asNumber(p.get("tension")),
1862
+ startMarker: asString(p.get("startMarker")),
1863
+ endMarker: asString(p.get("endMarker")),
1864
+ closed: asString(p.get("closed")) === "true",
1865
+ value: asNumber(p.get("value"))
1866
+ };
1867
+ }
1868
+
1869
+ // src/sflSemantic/canvasObjects/artifacts/path/resolver.ts
1870
+ function resolvePathObject(base, properties, origin) {
1871
+ return {
1872
+ ...base,
1873
+ objectType: "path",
1874
+ path: readPath(properties, origin)
1875
+ };
1876
+ }
1877
+
1878
+ // src/sflSemantic/canvasObjects/artifacts/swimlane/reader.ts
1879
+ function readSwimlane(p) {
1880
+ const orientation = asString(p.get("orientation")) === "vertical" ? "vertical" : "horizontal";
1881
+ const lanesValue = p.get("lanes");
1882
+ const lanes = lanesValue?.type === "list" ? lanesValue.values.flatMap((value, index) => {
1883
+ if (value.type !== "list") {
1884
+ return [];
1885
+ }
1886
+ const label = asString(value.values[0]);
1887
+ const size = asNumber(value.values[1]);
1888
+ if (label === void 0 || size === void 0) {
1889
+ return [];
1890
+ }
1891
+ const color = asString(value.values[2]);
1892
+ return [{ id: `lane-${index + 1}`, label, size, ...color ? { color } : {} }];
1893
+ }) : [];
1894
+ const swimlaneData = { orientation, lanes };
1895
+ return swimlaneData;
1896
+ }
1897
+
1898
+ // src/sflSemantic/canvasObjects/artifacts/swimlane/resolver.ts
1899
+ function resolveSwimlaneObject(base, properties) {
1900
+ return {
1901
+ ...base,
1902
+ objectType: "swimlane",
1903
+ swimlane: readSwimlane(properties)
1904
+ };
1905
+ }
1906
+
1907
+ // src/sflSemantic/canvasObjects/processing/classification.ts
1908
+ function declaredCanvasObjectKind(block2, properties) {
1909
+ if (block2.kind === "Path") {
1910
+ return "path";
1911
+ }
1912
+ if (block2.kind === "Chart") {
1913
+ return "chart";
1914
+ }
1915
+ if (block2.kind === "Widget") {
1916
+ return asString(properties.get("type")) ?? "widget";
1917
+ }
1918
+ if (block2.kind === "Text") {
1919
+ return "text";
1920
+ }
1921
+ if (block2.kind === "Isometric") {
1922
+ return "isometric";
1923
+ }
1924
+ return asString(properties.get("kind")) ?? "";
1925
+ }
1926
+ function canvasObjectTypeForKind(kind) {
1927
+ if (kind === "frame") {
1928
+ return "frame";
1929
+ }
1930
+ if (kind === "swimlane") {
1931
+ return "swimlane";
1932
+ }
1933
+ if (kind === "path") {
1934
+ return "path";
1935
+ }
1936
+ if (kind === "chart") {
1937
+ return "chart";
1938
+ }
1939
+ if (kind === "rollingNumber" || kind === "tickScale" || kind === "ruler") {
1940
+ return "widget";
1941
+ }
1942
+ if (kind === "isometric") {
1943
+ return "isometric";
1944
+ }
1945
+ return "shape";
1946
+ }
1947
+ function classifyCanvasObject(block2, properties, symbolDefinitions) {
1948
+ const kind = declaredCanvasObjectKind(block2, properties);
1949
+ const shapeReference = asString(properties.get("shape"));
1950
+ const symbol = shapeReference ? symbolDefinitions.get(shapeReference) : void 0;
1951
+ return {
1952
+ objectType: block2.kind === "Widget" ? "widget" : canvasObjectTypeForKind(kind),
1953
+ kind,
1954
+ shapeType: symbol ? "symbol" : shapeReference,
1955
+ symbol
1956
+ };
1957
+ }
1958
+
1959
+ // src/sflSemantic/canvasObjects/processing/layout/geometry.ts
1960
+ function readCanvasObjectGeometry(block2, origin, layoutProperty, positionOverride) {
1961
+ const position = positionOverride ?? tupleValues(layoutProperty(block2, "position")) ?? [0, 0];
1962
+ const size = tupleValues(layoutProperty(block2, "size")) ?? [0, 0];
1963
+ return {
1964
+ x: origin.x + (position[0] ?? 0),
1965
+ y: origin.y + (position[1] ?? 0),
1966
+ width: size[0] ?? 0,
1967
+ height: size[1] ?? 0,
1968
+ rotation: asNumber(layoutProperty(block2, "rotation")),
1969
+ order: asNumber(layoutProperty(block2, "order"))
1970
+ };
1971
+ }
1972
+
1973
+ // src/sflSemantic/canvasObjects/processing/presentation.ts
1974
+ var STRUCTURAL_PROPERTIES = /* @__PURE__ */ new Set(["id", "kind", "text", "shape", "parent", "order", "orientation", "lanes"]);
1975
+ function readCanvasObjectPresentation(properties) {
1976
+ const presentation = {};
1977
+ for (const [key, value] of properties) {
1978
+ if (STRUCTURAL_PROPERTIES.has(key)) {
1979
+ continue;
1980
+ }
1981
+ const text = asString(value);
1982
+ if (text !== void 0) {
1983
+ presentation[key] = text;
1984
+ }
1985
+ }
1986
+ return presentation;
1987
+ }
1988
+
1989
+ // src/sflSemantic/canvasObjects/processing/resolver.ts
1990
+ function resolveCanvasObject(block2, origin, context, frameId, positionOverride) {
1991
+ const { mergedProps, layoutProp, symbolDefs } = context;
1992
+ const properties = mergedProps(block2);
1993
+ const classification = classifyCanvasObject(block2, properties, symbolDefs);
1994
+ const geometry = readCanvasObjectGeometry(block2, origin, layoutProp, positionOverride);
1995
+ const base = {
1996
+ id: asString(properties.get("id")) ?? block2.name,
1997
+ kind: classification.kind,
1998
+ text: asString(properties.get("text")),
1999
+ ...geometry,
2000
+ parentFrameId: frameId ?? asString(properties.get("parent")),
2001
+ props: readCanvasObjectPresentation(properties)
2002
+ };
2003
+ switch (classification.objectType) {
2004
+ case "chart":
2005
+ return resolveChartObject(base, block2, properties, symbolDefs, mergedProps);
2006
+ case "isometric":
2007
+ return resolveIsometricObject(base, block2, properties, layoutProp);
2008
+ case "path":
2009
+ return resolvePathObject(base, properties, origin);
2010
+ case "swimlane":
2011
+ return resolveSwimlaneObject(base, properties);
2012
+ case "frame":
2013
+ return { ...base, objectType: "frame" };
2014
+ case "widget":
2015
+ return {
2016
+ ...base,
2017
+ objectType: "widget",
2018
+ widget: {
2019
+ type: asString(properties.get("type")) ?? "rollingNumber",
2020
+ value: asString(properties.get("value")) ?? "0",
2021
+ digits: asNumber(properties.get("digits")),
2022
+ duration: asNumber(properties.get("duration")),
2023
+ min: asNumber(properties.get("min")),
2024
+ max: asNumber(properties.get("max")),
2025
+ label: asString(properties.get("label"))
2026
+ }
2027
+ };
2028
+ case "shape":
2029
+ return {
2030
+ ...base,
2031
+ objectType: "shape",
2032
+ shapeType: classification.shapeType,
2033
+ symbol: classification.symbol
2034
+ };
2035
+ }
2036
+ }
2037
+
2038
+ // src/sflSemantic/groups/members.ts
2039
+ function directGroupMemberIds(block2) {
2040
+ return childBlocks(block2).filter((child) => isCanvasObjectBlockKind(child.kind)).map((child) => asString(directProperties(child).get("id")) ?? child.name);
2041
+ }
2042
+
2043
+ // src/sflSemantic/canvasTree/resolver.ts
2044
+ var CanvasTreeResolver = class {
2045
+ #context;
2046
+ #canvasObjects = [];
2047
+ #linkBlocks = [];
2048
+ #connectionBlocks = [];
2049
+ #linkSourceIndexes = [];
2050
+ #connectionSourceIndexes = [];
2051
+ #sequence = 0;
2052
+ constructor(resolution) {
2053
+ this.#context = resolution.canvasObjectResolutionContext;
2054
+ }
2055
+ resolve(model) {
2056
+ const groups = model ? this.#resolveChildren(model, { origin: { x: 0, y: 0 } }) : [];
2057
+ return {
2058
+ canvasObjects: this.#canvasObjects,
2059
+ groups,
2060
+ linkBlocks: this.#linkBlocks,
2061
+ connectionBlocks: this.#connectionBlocks,
2062
+ linkSourceIndexes: this.#linkSourceIndexes,
2063
+ connectionSourceIndexes: this.#connectionSourceIndexes
2064
+ };
2065
+ }
2066
+ #resolveChildren(block2, location) {
2067
+ const groups = [];
2068
+ const nextFlowPosition = createFlowPositioner(block2, this.#context);
2069
+ for (const child of childBlocks(block2)) {
2070
+ groups.push(...this.#resolveChild(child, location, nextFlowPosition));
2071
+ }
2072
+ return groups;
2073
+ }
2074
+ #resolveChild(child, location, nextFlowPosition) {
2075
+ const sourceIndex = this.#sequence++;
2076
+ if (child.kind === "Container") {
2077
+ return this.#resolveContainer(child, location, nextFlowPosition, sourceIndex);
2078
+ }
2079
+ if (isCanvasObjectBlockKind(child.kind)) {
2080
+ this.#resolveCanvasObject(child, location, nextFlowPosition, sourceIndex);
2081
+ return [];
2082
+ }
2083
+ switch (child.kind) {
2084
+ case "Group":
2085
+ return [this.#resolveGroup(child, location)];
2086
+ case "Link":
2087
+ this.#linkBlocks.push(child);
2088
+ this.#linkSourceIndexes.push(sourceIndex);
2089
+ return [];
2090
+ case "Connection":
2091
+ this.#connectionBlocks.push(child);
2092
+ this.#connectionSourceIndexes.push(sourceIndex);
2093
+ return [];
2094
+ case "Net": {
2095
+ const expanded = expandNetLinks(child, this.#context);
2096
+ this.#linkBlocks.push(...expanded);
2097
+ this.#linkSourceIndexes.push(...expanded.map(() => sourceIndex));
2098
+ return [];
2099
+ }
2100
+ default:
2101
+ return [];
2102
+ }
2103
+ }
2104
+ #resolveContainer(block2, location, nextFlowPosition, sourceIndex) {
2105
+ const canvasObject = this.#resolvePositionedCanvasObject(block2, location, nextFlowPosition, sourceIndex);
2106
+ this.#canvasObjects.push(canvasObject);
2107
+ return this.#resolveChildren(block2, {
2108
+ origin: { x: canvasObject.x, y: canvasObject.y },
2109
+ frameId: canvasObject.id
2110
+ });
2111
+ }
2112
+ #resolveCanvasObject(block2, location, nextFlowPosition, sourceIndex) {
2113
+ this.#canvasObjects.push(this.#resolvePositionedCanvasObject(block2, location, nextFlowPosition, sourceIndex));
2114
+ }
2115
+ #resolvePositionedCanvasObject(block2, location, nextFlowPosition, sourceIndex) {
2116
+ const size = tupleValues(this.#context.layoutProp(block2, "size")) ?? [0, 0];
2117
+ return {
2118
+ ...resolveCanvasObject(block2, location.origin, this.#context, location.frameId, nextFlowPosition(size)),
2119
+ sourceIndex
2120
+ };
2121
+ }
2122
+ #resolveGroup(block2, location) {
2123
+ const properties = this.#context.mergedProps(block2);
2124
+ return {
2125
+ id: asString(properties.get("id")) ?? block2.name,
2126
+ name: asString(properties.get("text")) ?? block2.name,
2127
+ color: asString(properties.get("color")),
2128
+ icon: asString(properties.get("icon")),
2129
+ canvasObjectIds: directGroupMemberIds(block2),
2130
+ subGroups: this.#resolveChildren(block2, location),
2131
+ parentFrameId: location.frameId ?? asString(properties.get("parent"))
2132
+ };
2133
+ }
2134
+ };
2135
+ function resolveCanvasTree(model, resolution) {
2136
+ return new CanvasTreeResolver(resolution).resolve(model);
2137
+ }
2138
+
2139
+ // src/sflSemantic/definitions/shaders.ts
2140
+ function readShaderDefinition(block2) {
2141
+ const properties = directProperties(block2);
2142
+ const program = asString(properties.get("program"));
2143
+ if (!program) {
2144
+ return void 0;
2145
+ }
2146
+ const parameters = {};
2147
+ for (const [key, value] of properties) {
2148
+ if (key === "program") {
2149
+ continue;
2150
+ }
2151
+ const number = asNumber(value);
2152
+ const text = asString(value);
2153
+ if (number !== void 0) {
2154
+ parameters[key] = number;
2155
+ } else if (text !== void 0) {
2156
+ parameters[key] = text === "true" ? true : text === "false" ? false : text;
2157
+ }
2158
+ }
2159
+ return { name: block2.name, program, parameters };
2160
+ }
2161
+
2162
+ // src/sflSemantic/definitions/symbols.ts
2163
+ function readSymbolPoints(block2) {
2164
+ const value = directProperties(block2).get("points");
2165
+ if (value?.type !== "list") {
2166
+ return [];
2167
+ }
2168
+ return value.values.flatMap((item) => {
2169
+ if (item.type !== "list" || item.values.length < 2) {
2170
+ return [];
2171
+ }
2172
+ const x = asNumber(item.values[0]);
2173
+ const y = asNumber(item.values[1]);
2174
+ return x === void 0 || y === void 0 ? [] : [[x, y]];
2175
+ });
2176
+ }
2177
+ function readSymbolPorts(block2) {
2178
+ const ports = childBlocks(block2).filter((child) => child.kind === "Port").flatMap((port) => {
2179
+ const position = tupleValues(directProperties(port).get("at"));
2180
+ if (typeof position?.[0] !== "number" || typeof position[1] !== "number") {
2181
+ return [];
2182
+ }
2183
+ return [{ name: port.name, at: [position[0], position[1]] }];
2184
+ });
2185
+ return ports.length > 0 ? ports : void 0;
2186
+ }
2187
+ function readSymbolDefinition(block2) {
2188
+ const points = readSymbolPoints(block2);
2189
+ if (points.length < 2) {
2190
+ return void 0;
2191
+ }
2192
+ return {
2193
+ name: block2.name,
2194
+ points,
2195
+ tension: asNumber(directProperties(block2).get("tension")),
2196
+ ports: readSymbolPorts(block2)
2197
+ };
2198
+ }
2199
+
2200
+ // src/sflSemantic/definitions/collection.ts
2201
+ function projectModel(block2) {
2202
+ return childBlocks(block2).find((child) => child.kind === "Model");
2203
+ }
2204
+ function collectCanvasDefinitions(sflDocument) {
2205
+ return {
2206
+ styles: collectStyleDefinitions(sflDocument),
2207
+ layouts: collectLayoutDefinitions(sflDocument),
2208
+ symbols: collectSymbolDefinitions(sflDocument),
2209
+ shaders: collectShaderDefinitions(sflDocument),
2210
+ model: findCanvasModel(sflDocument)
2211
+ };
2212
+ }
2213
+ function collectShaderDefinitions(sflDocument) {
2214
+ const shaders = /* @__PURE__ */ new Map();
2215
+ for (const block2 of sflDocument.blocks) {
2216
+ if (block2.kind !== "Shader") {
2217
+ continue;
2218
+ }
2219
+ const shader = readShaderDefinition(block2);
2220
+ if (shader) {
2221
+ shaders.set(block2.name, shader);
2222
+ }
2223
+ }
2224
+ return shaders;
2225
+ }
2226
+ function collectStyleDefinitions(sflDocument) {
2227
+ const styles = /* @__PURE__ */ new Map();
2228
+ for (const block2 of sflDocument.blocks) {
2229
+ if (block2.kind === "Style") {
2230
+ styles.set(block2.name, block2);
2231
+ }
2232
+ }
2233
+ return styles;
2234
+ }
2235
+ function collectLayoutDefinitions(sflDocument) {
2236
+ const layouts = /* @__PURE__ */ new Map();
2237
+ for (const block2 of sflDocument.blocks) {
2238
+ if (block2.kind === "Layout") {
2239
+ layouts.set(block2.name, block2);
2240
+ }
2241
+ }
2242
+ return layouts;
2243
+ }
2244
+ function collectSymbolDefinitions(sflDocument) {
2245
+ const symbols = /* @__PURE__ */ new Map();
2246
+ for (const block2 of sflDocument.blocks) {
2247
+ if (block2.kind !== "Symbol") {
2248
+ continue;
2249
+ }
2250
+ const symbol = readSymbolDefinition(block2);
2251
+ if (symbol) {
2252
+ symbols.set(block2.name, symbol);
2253
+ }
2254
+ }
2255
+ return symbols;
2256
+ }
2257
+ function findCanvasModel(sflDocument) {
2258
+ for (const block2 of sflDocument.blocks) {
2259
+ if (block2.kind === "Model") {
2260
+ return block2;
2261
+ }
2262
+ if (block2.kind === "Project") {
2263
+ const model = projectModel(block2);
2264
+ if (model) {
2265
+ return model;
2266
+ }
2267
+ }
2268
+ }
2269
+ return void 0;
2270
+ }
2271
+
2272
+ // src/sflSemantic/definitions/compositionReferences.ts
2273
+ function classifyCompositionReferences(composition, styleBlocks, layoutBlocks) {
2274
+ let styleRef;
2275
+ let layoutRef;
2276
+ for (const reference of composition) {
2277
+ if (styleBlocks.has(reference)) {
2278
+ styleRef = reference;
2279
+ } else if (layoutBlocks.has(reference)) {
2280
+ layoutRef = reference;
2281
+ }
2282
+ }
2283
+ return { styleRef, layoutRef };
2284
+ }
2285
+ function collectBlockDiagnostics(block2, styleBlocks, layoutBlocks, diagnostics) {
2286
+ for (const reference of block2.composition) {
2287
+ if (!styleBlocks.has(reference) && !layoutBlocks.has(reference)) {
2288
+ diagnostics.push({
2289
+ severity: "warning",
2290
+ code: "unresolved-style-or-layout",
2291
+ ownerId: block2.name,
2292
+ message: `"${reference}" is not a Style or Layout defined at the top level of the document \xC3\xA2\xE2\u201A\xAC\xE2\u20AC\x9D "${block2.name}" gets no shape, fill, or stroke from it.`
2293
+ });
2294
+ }
2295
+ }
2296
+ for (const child of childBlocks(block2)) {
2297
+ collectBlockDiagnostics(child, styleBlocks, layoutBlocks, diagnostics);
2298
+ }
2299
+ }
2300
+ function collectUnresolvedCompositionReferences(sflDocument, styleBlocks, layoutBlocks) {
2301
+ const diagnostics = [];
2302
+ for (const block2 of sflDocument.blocks) {
2303
+ collectBlockDiagnostics(block2, styleBlocks, layoutBlocks, diagnostics);
2304
+ }
2305
+ return diagnostics;
2306
+ }
2307
+
2308
+ // src/sflSemantic/definitions/inheritance.ts
2309
+ function resolveDefinitionInheritance(definitions, parentName) {
2310
+ return new DefinitionInheritanceResolver(definitions, parentName).resolveAll();
2311
+ }
2312
+ var DefinitionInheritanceResolver = class {
2313
+ #definitions;
2314
+ #parentName;
2315
+ #resolved = /* @__PURE__ */ new Map();
2316
+ constructor(definitions, parentName) {
2317
+ this.#definitions = definitions;
2318
+ this.#parentName = parentName;
2319
+ }
2320
+ resolveAll() {
2321
+ for (const name of this.#definitions.keys()) {
2322
+ this.#resolveDefinition(name, /* @__PURE__ */ new Set());
2323
+ }
2324
+ return this.#resolved;
2325
+ }
2326
+ #resolveDefinition(name, ancestry) {
2327
+ const cached = this.#resolved.get(name);
2328
+ if (cached) {
2329
+ return cached;
2330
+ }
2331
+ const block2 = this.#definitions.get(name);
2332
+ if (!block2 || ancestry.has(name)) {
2333
+ return /* @__PURE__ */ new Map();
2334
+ }
2335
+ const values = this.#inheritedProperties(block2, /* @__PURE__ */ new Set([...ancestry, name]));
2336
+ for (const [key, value] of directProperties(block2)) {
2337
+ values.set(key, value);
2338
+ }
2339
+ this.#resolved.set(name, values);
2340
+ return values;
2341
+ }
2342
+ #inheritedProperties(block2, ancestry) {
2343
+ const parent = this.#parentName(block2);
2344
+ return parent ? new Map(this.#resolveDefinition(parent, ancestry)) : /* @__PURE__ */ new Map();
2345
+ }
2346
+ };
2347
+
2348
+ // src/sflSemantic/definitions/propertyReaders.ts
2349
+ function createCanvasObjectPropertyReaders(definitions) {
2350
+ const { styles, layouts, symbols, shaders } = definitions;
2351
+ const references = (block2) => classifyCompositionReferences(block2.composition, styles, layouts);
2352
+ const resolvedStyles = resolveDefinitionInheritance(styles, (block2) => references(block2).styleRef);
2353
+ const resolvedLayouts = resolveDefinitionInheritance(layouts, (block2) => references(block2).layoutRef);
2354
+ const mergedProps = (block2) => {
2355
+ const merged = new Map(resolvedStyles.get(references(block2).styleRef ?? "") ?? []);
2356
+ for (const [key, value] of directProperties(block2)) {
2357
+ merged.set(key, value);
2358
+ }
2359
+ for (const [key, value] of styleProperties(block2)) {
2360
+ merged.set(key, value);
2361
+ }
2362
+ return merged;
2363
+ };
2364
+ const layoutProp = (block2, key) => {
2365
+ const localProperties = layoutProperties(block2);
2366
+ if (localProperties.has(key)) {
2367
+ return localProperties.get(key);
2368
+ }
2369
+ const reference = references(block2).layoutRef;
2370
+ return reference ? resolvedLayouts.get(reference)?.get(key) : void 0;
2371
+ };
2372
+ return { mergedProps, layoutProp, symbolDefs: symbols, shaderDefs: shaders };
2373
+ }
2374
+
2375
+ // src/sflSemantic/diagnostics/charts.ts
2376
+ var NATIVE_CIRCULAR_CHART_TYPES = /* @__PURE__ */ new Set(["radar", "radial"]);
2377
+ var OPTIONAL_CIRCULAR_GRID_CHART_TYPES = /* @__PURE__ */ new Set(["scatter", "custom", "hexbin"]);
2378
+ function collectUnsupportedChartTypes(doc) {
2379
+ const diagnostics = [];
2380
+ const visit = (block2) => {
2381
+ if (block2.kind === "Chart") {
2382
+ const declared = asString(directProperties(block2).get("type"));
2383
+ if (declared !== void 0 && !isChartType(declared)) {
2384
+ diagnostics.push({
2385
+ severity: "warning",
2386
+ code: "unsupported-chart-type",
2387
+ ownerId: block2.name,
2388
+ message: `Unsupported chart type '${declared}' \xC3\u0192\xC2\xA2\xC3\xA2\xE2\u20AC\u0161\xC2\xAC\xC3\xA2\xE2\u201A\xAC\xC2\x9D drawn as a plain series plot. Supported: ${CHART_TYPES.join(", ")}.`
2389
+ });
2390
+ }
2391
+ }
2392
+ for (const child of childBlocks(block2)) {
2393
+ visit(child);
2394
+ }
2395
+ };
2396
+ for (const block2 of doc.blocks) {
2397
+ visit(block2);
2398
+ }
2399
+ return diagnostics;
2400
+ }
2401
+ function collectEmptySeries(doc) {
2402
+ const diagnostics = [];
2403
+ const visit = (block2) => {
2404
+ if (block2.kind === "Chart") {
2405
+ for (const series of contentChildren(block2).filter((child) => child.kind === "Series")) {
2406
+ if (!directProperties(series).has("values")) {
2407
+ diagnostics.push({
2408
+ severity: "warning",
2409
+ code: "series-without-values",
2410
+ ownerId: series.name,
2411
+ message: `Series '${series.name}' has no values. If this was meant to be an Axis or Rule, it needs that keyword in front of the name \xC3\u0192\xC2\xA2\xC3\xA2\xE2\u20AC\u0161\xC2\xAC\xC3\xA2\xE2\u201A\xAC\xC2\x9D inside data{}, a bare name is read as a Series.`
2412
+ });
2413
+ }
2414
+ }
2415
+ }
2416
+ for (const child of childBlocks(block2)) {
2417
+ visit(child);
2418
+ }
2419
+ };
2420
+ for (const block2 of doc.blocks) {
2421
+ visit(block2);
2422
+ }
2423
+ return diagnostics;
2424
+ }
2425
+ function collectUnsupportedGridShapes(doc) {
2426
+ const diagnostics = [];
2427
+ const visit = (block2) => {
2428
+ if (block2.kind === "Chart") {
2429
+ const declared = asString(directProperties(block2).get("type"));
2430
+ const chartType = isChartType(declared) ? declared : "bar";
2431
+ const gridShape = asString(styleProperties(block2).get("gridShape"));
2432
+ if (gridShape === "circle" && !OPTIONAL_CIRCULAR_GRID_CHART_TYPES.has(chartType) && !NATIVE_CIRCULAR_CHART_TYPES.has(chartType)) {
2433
+ diagnostics.push({
2434
+ severity: "warning",
2435
+ code: "unsupported-grid-shape",
2436
+ ownerId: block2.name,
2437
+ message: `gridShape: circle isn't supported on '${chartType}' \xC3\u0192\xC2\xA2\xC3\xA2\xE2\u20AC\u0161\xC2\xAC\xC3\xA2\xE2\u201A\xAC\xC2\x9D drawn with the standard rectangular grid instead. Supported: ${[...OPTIONAL_CIRCULAR_GRID_CHART_TYPES].join(", ")}.`
2438
+ });
2439
+ }
2440
+ }
2441
+ for (const child of childBlocks(block2)) {
2442
+ visit(child);
2443
+ }
2444
+ };
2445
+ for (const block2 of doc.blocks) {
2446
+ visit(block2);
2447
+ }
2448
+ return diagnostics;
2449
+ }
2450
+
2451
+ // src/sflSemantic/diagnostics/widgets.ts
2452
+ function collectUnsupportedWidgetTypes(sflDocument) {
2453
+ const diagnostics = [];
2454
+ const visit = (block2) => {
2455
+ if (block2.kind === "Widget") {
2456
+ const type = asString(directProperties(block2).get("type")) ?? "rollingNumber";
2457
+ if (type !== "rollingNumber" && type !== "tickScale" && type !== "ruler") {
2458
+ diagnostics.push({
2459
+ severity: "warning",
2460
+ code: "unsupported-widget-type",
2461
+ ownerId: block2.name,
2462
+ message: `Unsupported widget type '${type}'. Supported: rollingNumber, tickScale, ruler.`
2463
+ });
2464
+ }
2465
+ }
2466
+ for (const child of childBlocks(block2)) {
2467
+ visit(child);
2468
+ }
2469
+ };
2470
+ for (const block2 of sflDocument.blocks) {
2471
+ visit(block2);
2472
+ }
2473
+ return diagnostics;
2474
+ }
2475
+
2476
+ // src/sflSemantic/canvasState/resolution.ts
2477
+ function createCanvasResolution(doc) {
2478
+ const definitions = collectCanvasDefinitions(doc);
2479
+ return {
2480
+ model: definitions.model,
2481
+ diagnostics: [
2482
+ ...collectUnresolvedCompositionReferences(doc, definitions.styles, definitions.layouts),
2483
+ ...collectUnsupportedChartTypes(doc),
2484
+ ...collectUnsupportedGridShapes(doc),
2485
+ ...collectEmptySeries(doc),
2486
+ ...collectUnsupportedWidgetTypes(doc)
2487
+ ],
2488
+ canvasObjectResolutionContext: createCanvasObjectPropertyReaders(definitions)
2489
+ };
2490
+ }
2491
+
2492
+ // src/sflSemantic/canvasState/resolver.ts
2493
+ function resolveCanvasState(document) {
2494
+ const resolution = createCanvasResolution(document);
2495
+ const { canvasObjects, groups, linkBlocks, connectionBlocks, linkSourceIndexes, connectionSourceIndexes } = resolveCanvasTree(resolution.model, resolution);
2496
+ const links = readLinks(
2497
+ resolution.model,
2498
+ linkBlocks,
2499
+ canvasObjects,
2500
+ resolution.canvasObjectResolutionContext,
2501
+ linkSourceIndexes
2502
+ );
2503
+ const connections = readConnections(
2504
+ resolution.model,
2505
+ connectionBlocks,
2506
+ canvasObjects,
2507
+ resolution.canvasObjectResolutionContext,
2508
+ connectionSourceIndexes
2509
+ );
2510
+ return {
2511
+ canvasObjects,
2512
+ groups,
2513
+ links,
2514
+ connections,
2515
+ diagnostics: resolution.diagnostics,
2516
+ shaders: [...resolution.canvasObjectResolutionContext.shaderDefs.values()]
2517
+ };
2518
+ }
2519
+
2520
+ // src/sflSemantic/sflToCanvasState.ts
2521
+ function sflToCanvasState(source) {
2522
+ return resolveCanvasState(parseSource(source));
2523
+ }
2524
+
2525
+ // src/svg/domContract/version.ts
2526
+ var DOM_CONTRACT_VERSION = 1;
2527
+
2528
+ // src/model/geometry/ports/rectangleCoordinates.ts
2529
+ var DEGREES_TO_RADIANS = Math.PI / 180;
2530
+ function rotatePoint(point, degrees) {
2531
+ const radians = degrees * DEGREES_TO_RADIANS;
2532
+ const cosine = Math.cos(radians);
2533
+ const sine = Math.sin(radians);
2534
+ return { x: point.x * cosine - point.y * sine, y: point.x * sine + point.y * cosine };
2535
+ }
2536
+ function rectangleCenter(rectangle) {
2537
+ return { x: rectangle.x + rectangle.width / 2, y: rectangle.y + rectangle.height / 2 };
2538
+ }
2539
+ function worldPointToRectangle(rectangle, worldPoint) {
2540
+ const center = rectangleCenter(rectangle);
2541
+ const pointRelativeToCenter = { x: worldPoint.x - center.x, y: worldPoint.y - center.y };
2542
+ return rectangle.rotation ? rotatePoint(pointRelativeToCenter, -rectangle.rotation) : pointRelativeToCenter;
2543
+ }
2544
+ function rectanglePointToWorld(rectangle, rectanglePoint) {
2545
+ const center = rectangleCenter(rectangle);
2546
+ const rotatedPoint = rectangle.rotation ? rotatePoint(rectanglePoint, rectangle.rotation) : rectanglePoint;
2547
+ return { x: center.x + rotatedPoint.x, y: center.y + rotatedPoint.y };
2548
+ }
2549
+
2550
+ // src/model/geometry/ports/coordinates.ts
2551
+ function portPointInRectangle(rectangle, port) {
2552
+ const offset = Math.max(0, Math.min(1, port.offset));
2553
+ const halfWidth = rectangle.width / 2;
2554
+ const halfHeight = rectangle.height / 2;
2555
+ switch (port.side) {
2556
+ case "top":
2557
+ return { x: (offset - 0.5) * rectangle.width, y: -halfHeight };
2558
+ case "right":
2559
+ return { x: halfWidth, y: (offset - 0.5) * rectangle.height };
2560
+ case "bottom":
2561
+ return { x: (offset - 0.5) * rectangle.width, y: halfHeight };
2562
+ case "left":
2563
+ return { x: -halfWidth, y: (offset - 0.5) * rectangle.height };
2564
+ }
2565
+ }
2566
+ function pointAtPort(rectangle, port) {
2567
+ return rectanglePointToWorld(rectangle, portPointInRectangle(rectangle, port));
2568
+ }
2569
+ function pointAtNormalizedPort(rectangle, normalizedPosition) {
2570
+ return rectanglePointToWorld(rectangle, {
2571
+ x: (normalizedPosition[0] - 0.5) * rectangle.width,
2572
+ y: (normalizedPosition[1] - 0.5) * rectangle.height
2573
+ });
2574
+ }
2575
+ function relativeOffsetOnRectangleSide(rectangle, side, worldPoint) {
2576
+ const rectanglePoint = worldPointToRectangle(rectangle, worldPoint);
2577
+ switch (side) {
2578
+ case "top":
2579
+ case "bottom":
2580
+ return (rectanglePoint.x + rectangle.width / 2) / rectangle.width;
2581
+ case "left":
2582
+ case "right":
2583
+ return (rectanglePoint.y + rectangle.height / 2) / rectangle.height;
2584
+ }
2585
+ }
2586
+
2587
+ // src/renderScene/scene/bounds.ts
2588
+ var EMPTY_BOUNDS = { minX: 0, minY: 0, maxX: 100, maxY: 100 };
2589
+ function boxBounds(frame, extra = 0) {
2590
+ return {
2591
+ minX: frame.x - extra,
2592
+ minY: frame.y - extra,
2593
+ maxX: frame.x + frame.width + extra,
2594
+ maxY: frame.y + frame.height + extra
2595
+ };
2596
+ }
2597
+ function rotatedBounds(frame, degrees, extra = 0) {
2598
+ if (!degrees) {
2599
+ return boxBounds(frame, extra);
2600
+ }
2601
+ return boundsAroundPoints(rotateFrameCorners(frame, degrees), extra);
2602
+ }
2603
+ function rotateFrameCorners(frame, degrees) {
2604
+ const centerX = frame.x + frame.width / 2;
2605
+ const centerY = frame.y + frame.height / 2;
2606
+ const angle = degrees * Math.PI / 180;
2607
+ const cos = Math.cos(angle);
2608
+ const sin = Math.sin(angle);
2609
+ return frameCorners(frame).map((point) => ({
2610
+ x: centerX + (point.x - centerX) * cos - (point.y - centerY) * sin,
2611
+ y: centerY + (point.x - centerX) * sin + (point.y - centerY) * cos
2612
+ }));
2613
+ }
2614
+ function frameCorners(frame) {
2615
+ return [
2616
+ { x: frame.x, y: frame.y },
2617
+ { x: frame.x + frame.width, y: frame.y },
2618
+ { x: frame.x + frame.width, y: frame.y + frame.height },
2619
+ { x: frame.x, y: frame.y + frame.height }
2620
+ ];
2621
+ }
2622
+ function boundsAroundPoints(points, extra) {
2623
+ return {
2624
+ minX: Math.min(...points.map((point) => point.x)) - extra,
2625
+ minY: Math.min(...points.map((point) => point.y)) - extra,
2626
+ maxX: Math.max(...points.map((point) => point.x)) + extra,
2627
+ maxY: Math.max(...points.map((point) => point.y)) + extra
2628
+ };
2629
+ }
2630
+
2631
+ // src/renderScene/scene/order.ts
2632
+ function stackRank(object) {
2633
+ const key = object.order_key ?? object.orderKey;
2634
+ if (!key) {
2635
+ return object.order ?? 0;
2636
+ }
2637
+ let rank = 0;
2638
+ for (let index = 0; index < Math.min(key.length, 8); index++) {
2639
+ rank = rank * 128 + key.charCodeAt(index);
2640
+ }
2641
+ return rank;
2642
+ }
2643
+ function stackSequence(object, fallback) {
2644
+ return object.sourceIndex ?? fallback;
2645
+ }
2646
+
2647
+ // src/renderScene/artifacts/link/roundedOrthogonalPathData.ts
2648
+ function roundedOrthogonalPathData(segments, radius) {
2649
+ if (segments.length === 0) {
2650
+ return "";
2651
+ }
2652
+ const raw = [segments[0].start, ...segments.map((segment) => segment.end)];
2653
+ const points = raw.filter(
2654
+ (point, index) => index === 0 || point.x !== raw[index - 1].x || point.y !== raw[index - 1].y
2655
+ );
2656
+ const corners = points.filter((point, index) => {
2657
+ if (index === 0 || index === points.length - 1) {
2658
+ return true;
2659
+ }
2660
+ const previous = points[index - 1];
2661
+ const next = points[index + 1];
2662
+ return !(previous.x === point.x && point.x === next.x || previous.y === point.y && point.y === next.y);
2663
+ });
2664
+ if (corners.length < 2) {
2665
+ return "";
2666
+ }
2667
+ let pathData = `M ${corners[0].x} ${corners[0].y}`;
2668
+ for (let index = 1; index < corners.length - 1; index++) {
2669
+ const previous = corners[index - 1];
2670
+ const corner = corners[index];
2671
+ const next = corners[index + 1];
2672
+ const incoming = Math.hypot(corner.x - previous.x, corner.y - previous.y);
2673
+ const outgoing = Math.hypot(next.x - corner.x, next.y - corner.y);
2674
+ if (incoming === 0 || outgoing === 0) {
2675
+ pathData += ` L ${corner.x} ${corner.y}`;
2676
+ continue;
2677
+ }
2678
+ const cornerRadius = Math.min(radius, incoming * 0.4, outgoing * 0.4);
2679
+ const before = {
2680
+ x: corner.x + (previous.x - corner.x) / incoming * cornerRadius,
2681
+ y: corner.y + (previous.y - corner.y) / incoming * cornerRadius
2682
+ };
2683
+ const after = {
2684
+ x: corner.x + (next.x - corner.x) / outgoing * cornerRadius,
2685
+ y: corner.y + (next.y - corner.y) / outgoing * cornerRadius
2686
+ };
2687
+ pathData += ` L ${before.x} ${before.y} Q ${corner.x} ${corner.y} ${after.x} ${after.y}`;
2688
+ }
2689
+ const last = corners[corners.length - 1];
2690
+ return `${pathData} L ${last.x} ${last.y}`;
2691
+ }
2692
+
2693
+ // src/renderScene/charts/propPath.ts
2694
+ var IDENT = /^[A-Za-z_][A-Za-z0-9_]*$/;
2695
+ function block(kind, id, property) {
2696
+ return IDENT.test(id) ? `${kind}[${id}].${property}` : void 0;
2697
+ }
2698
+ var CHART_TITLE_PROP = "text";
2699
+ function axisLabelProp(axis) {
2700
+ return `Axis[${axis}].label`;
2701
+ }
2702
+ function categoryProp(index) {
2703
+ return Number.isInteger(index) && index >= 0 ? `categories[${index}]` : void 0;
2704
+ }
2705
+ function seriesLabelProp(id) {
2706
+ return block("Series", id, "label");
2707
+ }
2708
+ function seriesColorProp(id) {
2709
+ return block("Series", id, "color");
2710
+ }
2711
+ function seriesLabelOffsetXProp(id) {
2712
+ return block("Series", id, "labelOffsetX");
2713
+ }
2714
+ function seriesLabelOffsetYProp(id) {
2715
+ return block("Series", id, "labelOffsetY");
2716
+ }
2717
+ function seriesValueProp(id, index) {
2718
+ return Number.isInteger(index) && index >= 0 ? block("Series", id, `values[${index}]`) : void 0;
2719
+ }
2720
+ function seriesValueColorProp(id, index) {
2721
+ return Number.isInteger(index) && index >= 0 ? block("Series", id, `colors[${index}]`) : void 0;
2722
+ }
2723
+ function ruleTextProp(id) {
2724
+ return block("Rule", id, "text");
2725
+ }
2726
+ function ruleAtProp(id) {
2727
+ return block("Rule", id, "at");
2728
+ }
2729
+
2730
+ // src/renderScene/scene/freeformPath.ts
2731
+ function freeformPathData(geometry) {
2732
+ if (geometry.points.length === 0) {
2733
+ return "";
2734
+ }
2735
+ if (geometry.points.length < 3 || geometry.tension <= 0) {
2736
+ return linearPathData(geometry);
2737
+ }
2738
+ return smoothPathData(geometry);
2739
+ }
2740
+ function linearPathData(geometry) {
2741
+ const [first, ...remaining] = geometry.points;
2742
+ const segments = remaining.map((point) => `L ${point.x} ${point.y}`).join(" ");
2743
+ return `M ${first.x} ${first.y} ${segments}${geometry.closed ? " Z" : ""}`;
2744
+ }
2745
+ function smoothPathData(geometry) {
2746
+ const first = geometry.points[0];
2747
+ const segmentCount = geometry.closed ? geometry.points.length : geometry.points.length - 1;
2748
+ const commands = [`M ${first.x} ${first.y}`];
2749
+ for (let index = 0; index < segmentCount; index++) {
2750
+ commands.push(curveSegmentCommand(geometry, index));
2751
+ }
2752
+ if (geometry.closed) {
2753
+ commands.push("Z");
2754
+ }
2755
+ return commands.join(" ");
2756
+ }
2757
+ function curveSegmentCommand(geometry, index) {
2758
+ const previous = freeformPointAt(geometry, index - 1);
2759
+ const start = freeformPointAt(geometry, index);
2760
+ const end = freeformPointAt(geometry, index + 1);
2761
+ const next = freeformPointAt(geometry, index + 2);
2762
+ const factor = geometry.tension / 6;
2763
+ const firstControl = { x: start.x + (end.x - previous.x) * factor, y: start.y + (end.y - previous.y) * factor };
2764
+ const secondControl = { x: end.x - (next.x - start.x) * factor, y: end.y - (next.y - start.y) * factor };
2765
+ return `C ${firstControl.x} ${firstControl.y} ${secondControl.x} ${secondControl.y} ${end.x} ${end.y}`;
2766
+ }
2767
+ function freeformPointAt(geometry, index) {
2768
+ if (geometry.closed) {
2769
+ return geometry.points[(index + geometry.points.length) % geometry.points.length];
2770
+ }
2771
+ return geometry.points[Math.max(0, Math.min(geometry.points.length - 1, index))];
2772
+ }
2773
+
2774
+ // src/renderScene/charts/primitives/radarShape.ts
2775
+ var START_ANGLE = -Math.PI / 2;
2776
+ var CURVED_TENSION = 0.5;
2777
+ var LABEL_FREE_MARGIN = 6;
2778
+ function radarLabelSpace(fontSize) {
2779
+ return Math.max(38, fontSize * 3.2);
2780
+ }
2781
+ function radarRadius(plotWidth, plotHeight, fontSize, hasLabels = true) {
2782
+ const margin = hasLabels ? radarLabelSpace(fontSize) : LABEL_FREE_MARGIN;
2783
+ return Math.max(8, Math.min(plotWidth, plotHeight) / 2 - margin);
2784
+ }
2785
+ function radarCategoryCount(configuredCount, dataCount) {
2786
+ return Math.max(3, configuredCount || dataCount);
2787
+ }
2788
+ function radarAngleAt(count, index) {
2789
+ return START_ANGLE + index * Math.PI * 2 / count;
2790
+ }
2791
+ function angleForAxisValue(min, max, value) {
2792
+ const ratio = max === min ? 0 : (value - min) / (max - min);
2793
+ return START_ANGLE + ratio * Math.PI * 2;
2794
+ }
2795
+ function radarPointAt(frame, index, pointRadius) {
2796
+ const angle = radarAngleAt(frame.count, index);
2797
+ return {
2798
+ x: frame.centerX + Math.cos(angle) * pointRadius,
2799
+ y: frame.centerY + Math.sin(angle) * pointRadius
2800
+ };
2801
+ }
2802
+ function radarRadiusForValue(frame, value) {
2803
+ const range = frame.max - frame.min;
2804
+ const ratio = range === 0 ? 0 : Math.max(0, Math.min(1, (value - frame.min) / range));
2805
+ return ratio * frame.radius;
2806
+ }
2807
+ function radarVertexPoint(frame, index, value) {
2808
+ return radarPointAt(frame, index, radarRadiusForValue(frame, value));
2809
+ }
2810
+ function radarValueAtPoint(frame, index, px, py) {
2811
+ const angle = radarAngleAt(frame.count, index);
2812
+ const projected = (px - frame.centerX) * Math.cos(angle) + (py - frame.centerY) * Math.sin(angle);
2813
+ const ratio = frame.radius === 0 ? 0 : Math.max(0, Math.min(1, projected / frame.radius));
2814
+ return frame.min + ratio * (frame.max - frame.min);
2815
+ }
2816
+ function radarSeriesPath(points, curved) {
2817
+ if (points.length === 0) {
2818
+ return "";
2819
+ }
2820
+ if (curved) {
2821
+ return freeformPathData({ points, tension: CURVED_TENSION, closed: true });
2822
+ }
2823
+ const commands = points.map((point, index) => `${index ? "L " : ""}${point.x} ${point.y}`);
2824
+ return `M ${commands.join(" ")} Z`;
2825
+ }
2826
+
2827
+ // src/svg/domContract/attributes.ts
2828
+ var SYMBOL_POINTS_ATTR = "data-sf-symbol-points";
2829
+ var SYMBOL_TENSION_ATTR = "data-sf-symbol-tension";
2830
+ var SYMBOL_BOUNDS_ATTR = "data-sf-symbol-bounds";
2831
+ var CORNER_RADIUS_ATTR = "data-sf-corner-radius";
2832
+ var SQUARE_CORNER_ATTR = "data-sf-square-corner";
2833
+ var PATH_POINTS_ATTR = "data-sf-path-points";
2834
+ var PATH_TENSION_ATTR = "data-sf-path-tension";
2835
+ var PATH_CLOSED_ATTR = "data-sf-path-closed";
2836
+ var PATH_POINT_INDEX_ATTR = "data-sf-path-point";
2837
+ var ISOMETRIC_SIZE_ATTR = "data-sf-isometric-size";
2838
+ var ISOMETRIC_LABEL_OFFSET_ATTR = "data-sf-isometric-label-offset";
2839
+
2840
+ // src/model/geometry/rectangle/edgePoint.ts
2841
+ function elementCenter(rect) {
2842
+ return { x: rect.x + rect.width / 2, y: rect.y + rect.height / 2 };
2843
+ }
2844
+ function edgePointToward(rect, other) {
2845
+ const center = elementCenter(rect);
2846
+ const otherCenter = elementCenter(other);
2847
+ const angle = Math.atan2(otherCenter.y - center.y, otherCenter.x - center.x);
2848
+ if (angle >= -Math.PI / 4 && angle < Math.PI / 4) {
2849
+ return { x: rect.x + rect.width, y: center.y };
2850
+ }
2851
+ if (angle >= Math.PI / 4 && angle < 3 * Math.PI / 4) {
2852
+ return { x: center.x, y: rect.y + rect.height };
2853
+ }
2854
+ if (angle >= -3 * Math.PI / 4 && angle < -Math.PI / 4) {
2855
+ return { x: center.x, y: rect.y };
2856
+ }
2857
+ return { x: rect.x, y: center.y };
2858
+ }
2859
+
2860
+ // src/renderScene/scene/tagging.ts
2861
+ function tag(nodes, role, id, kind, label, parentFrameId) {
2862
+ nodes.forEach((node, index) => {
2863
+ node.identity = {
2864
+ role,
2865
+ id,
2866
+ ...kind ? { kind } : {},
2867
+ ...index === 0 ? { primary: true } : {},
2868
+ ...index === 0 && label ? { label } : {},
2869
+ ...index === 0 && parentFrameId ? { parentFrameId } : {}
2870
+ };
2871
+ });
2872
+ return nodes;
2873
+ }
2874
+
2875
+ // src/renderScene/scene/numbers.ts
2876
+ function finite(value, fallback) {
2877
+ return typeof value === "number" && Number.isFinite(value) ? value : fallback;
2878
+ }
2879
+
2880
+ // src/renderScene/scene/textStyle.ts
2881
+ var DEFAULT_FONT = "Inter, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif";
2882
+
2883
+ // src/renderScene/scene/paint.ts
2884
+ function paint(value, diagnostics, ownerId) {
2885
+ if (!value) {
2886
+ return { kind: "none" };
2887
+ }
2888
+ if (typeof value === "string") {
2889
+ return { kind: "solid", color: value };
2890
+ }
2891
+ if (value.type === "gradient") {
2892
+ return {
2893
+ kind: "linear-gradient",
2894
+ angle: value.angle,
2895
+ stops: value.stops.map((stop) => ({ offset: stop.position, color: stop.color }))
2896
+ };
2897
+ }
2898
+ if (value.points.length === 0) {
2899
+ diagnostics.push({
2900
+ severity: "warning",
2901
+ code: "empty-mesh-gradient",
2902
+ ownerId,
2903
+ message: "Mesh gradient has no color points."
2904
+ });
2905
+ return { kind: "none" };
2906
+ }
2907
+ return {
2908
+ kind: "mesh-gradient",
2909
+ points: value.points.map((point) => ({ x: point.x, y: point.y, color: point.color }))
2910
+ };
2911
+ }
2912
+
2913
+ // src/renderScene/scene/roundedRectPath.ts
2914
+ function topRoundedRectPath(x, y, width, height, radius) {
2915
+ const clampedRadius = Math.max(0, Math.min(radius, width / 2, height));
2916
+ if (clampedRadius === 0) {
2917
+ return `M ${x} ${y} H ${x + width} V ${y + height} H ${x} Z`;
2918
+ }
2919
+ return `M ${x} ${y + clampedRadius} A ${clampedRadius} ${clampedRadius} 0 0 1 ${x + clampedRadius} ${y} H ${x + width - clampedRadius} A ${clampedRadius} ${clampedRadius} 0 0 1 ${x + width} ${y + clampedRadius} V ${y + height} H ${x} Z`;
2920
+ }
2921
+ function bottomRoundedRectPath(x, y, width, height, radius) {
2922
+ const clampedRadius = Math.max(0, Math.min(radius, width / 2, height));
2923
+ if (clampedRadius === 0) {
2924
+ return `M ${x} ${y} H ${x + width} V ${y + height} H ${x} Z`;
2925
+ }
2926
+ return `M ${x} ${y} H ${x + width} V ${y + height - clampedRadius} A ${clampedRadius} ${clampedRadius} 0 0 1 ${x + width - clampedRadius} ${y + height} H ${x + clampedRadius} A ${clampedRadius} ${clampedRadius} 0 0 1 ${x} ${y + height - clampedRadius} Z`;
2927
+ }
2928
+ function leftRoundedRectPath(x, y, width, height, radius) {
2929
+ const clampedRadius = Math.max(0, Math.min(radius, width / 2, height / 2));
2930
+ if (clampedRadius === 0) {
2931
+ return `M ${x} ${y} H ${x + width} V ${y + height} H ${x} Z`;
2932
+ }
2933
+ return `M ${x} ${y + clampedRadius} A ${clampedRadius} ${clampedRadius} 0 0 1 ${x + clampedRadius} ${y} H ${x + width} V ${y + height} H ${x + clampedRadius} A ${clampedRadius} ${clampedRadius} 0 0 1 ${x} ${y + height - clampedRadius} Z`;
2934
+ }
2935
+ function rightRoundedRectPath(x, y, width, height, radius) {
2936
+ const clampedRadius = Math.max(0, Math.min(radius, width / 2, height / 2));
2937
+ if (clampedRadius === 0) {
2938
+ return `M ${x} ${y} H ${x + width} V ${y + height} H ${x} Z`;
2939
+ }
2940
+ return `M ${x} ${y} H ${x + width - clampedRadius} A ${clampedRadius} ${clampedRadius} 0 0 1 ${x + width} ${y + clampedRadius} V ${y + height - clampedRadius} A ${clampedRadius} ${clampedRadius} 0 0 1 ${x + width - clampedRadius} ${y + height} H ${x} Z`;
2941
+ }
2942
+ function cornersRoundedRectPath(x, y, width, height, radii) {
2943
+ const max = Math.max(0, Math.min(width, height) / 2);
2944
+ const tl = Math.max(0, Math.min(radii.topLeft, max));
2945
+ const tr = Math.max(0, Math.min(radii.topRight, max));
2946
+ const br = Math.max(0, Math.min(radii.bottomRight, max));
2947
+ const bl = Math.max(0, Math.min(radii.bottomLeft, max));
2948
+ const arcTo = (r, toX, toY) => r > 0 ? `A ${r} ${r} 0 0 1 ${toX} ${toY} ` : "";
2949
+ return `M ${x + tl} ${y} H ${x + width - tr} ` + arcTo(tr, x + width, y + tr) + `V ${y + height - br} ` + arcTo(br, x + width - br, y + height) + `H ${x + bl} ` + arcTo(bl, x, y + height - bl) + `V ${y + tl} ` + arcTo(tl, x + tl, y) + "Z";
2950
+ }
2951
+
2952
+ // src/renderScene/scene/roundedPolygonPath.ts
2953
+ function distance(a, b) {
2954
+ return Math.hypot(b.x - a.x, b.y - a.y);
2955
+ }
2956
+ function toward(from, to, distanceFromStart) {
2957
+ const length = distance(from, to);
2958
+ if (length === 0) {
2959
+ return from;
2960
+ }
2961
+ const ratio = distanceFromStart / length;
2962
+ return {
2963
+ x: from.x + (to.x - from.x) * ratio,
2964
+ y: from.y + (to.y - from.y) * ratio
2965
+ };
2966
+ }
2967
+ function roundedCorner(previous, vertex, next, requestedRadius) {
2968
+ const radius = Math.max(0, Math.min(requestedRadius, distance(vertex, previous) / 2, distance(vertex, next) / 2));
2969
+ return {
2970
+ vertex,
2971
+ start: toward(vertex, previous, radius),
2972
+ end: toward(vertex, next, radius)
2973
+ };
2974
+ }
2975
+ function roundedPolygonPath(points, radius) {
2976
+ if (points.length < 3) {
2977
+ return "";
2978
+ }
2979
+ const radiusAt = (index) => typeof radius === "number" ? radius : radius[index] ?? 0;
2980
+ if (points.every((_, index) => radiusAt(index) <= 0)) {
2981
+ const [first2, ...rest] = points;
2982
+ return `M ${first2.x} ${first2.y} ${rest.map((point) => `L ${point.x} ${point.y}`).join(" ")} Z`;
2983
+ }
2984
+ const corners = points.map(
2985
+ (vertex, index) => roundedCorner(
2986
+ points[(index - 1 + points.length) % points.length],
2987
+ vertex,
2988
+ points[(index + 1) % points.length],
2989
+ radiusAt(index)
2990
+ )
2991
+ );
2992
+ const commands = [`M ${corners[0].end.x} ${corners[0].end.y}`];
2993
+ for (let index = 1; index < corners.length; index++) {
2994
+ const corner = corners[index];
2995
+ commands.push(
2996
+ `L ${corner.start.x} ${corner.start.y}`,
2997
+ `Q ${corner.vertex.x} ${corner.vertex.y} ${corner.end.x} ${corner.end.y}`
2998
+ );
2999
+ }
3000
+ const first = corners[0];
3001
+ commands.push(
3002
+ `L ${first.start.x} ${first.start.y}`,
3003
+ `Q ${first.vertex.x} ${first.vertex.y} ${first.end.x} ${first.end.y}`,
3004
+ "Z"
3005
+ );
3006
+ return commands.join(" ");
3007
+ }
3008
+
3009
+ // src/renderScene/scene/textLayout.ts
3010
+ var PLAIN_CHAR_FACTOR = 0.58;
3011
+ var BOLD_CHAR_FACTOR = 0.64;
3012
+ function wrapText(text, width, fontSize, metrics) {
3013
+ const maxChars = Math.max(1, Math.floor(width / Math.max(1, fontSize * PLAIN_CHAR_FACTOR)));
3014
+ const fits = metrics ? (line, word) => metrics.measure(line, false) + metrics.spaceWidth + metrics.measure(word, false) <= width : (line, word) => `${line} ${word}`.length <= maxChars;
3015
+ const result = [];
3016
+ for (const rawLine of text.split("\n")) {
3017
+ const words = rawLine.split(/\s+/).filter(Boolean);
3018
+ if (!words.length) {
3019
+ result.push("");
3020
+ continue;
3021
+ }
3022
+ let line = "";
3023
+ for (const word of words) {
3024
+ if (!line) {
3025
+ line = word;
3026
+ } else if (fits(line, word)) {
3027
+ line += ` ${word}`;
3028
+ } else {
3029
+ result.push(line);
3030
+ line = word;
3031
+ }
3032
+ }
3033
+ result.push(line);
3034
+ }
3035
+ return result;
3036
+ }
3037
+ function estimateTextWidth(text, fontSize, bold) {
3038
+ return text.length * fontSize * (bold ? BOLD_CHAR_FACTOR : PLAIN_CHAR_FACTOR);
3039
+ }
3040
+ function heuristicRichMeasure(fontSize) {
3041
+ return {
3042
+ measure: (text, bold) => estimateTextWidth(text, fontSize, bold),
3043
+ spaceWidth: fontSize * PLAIN_CHAR_FACTOR
3044
+ };
3045
+ }
3046
+
3047
+ // src/renderScene/artifacts/link/geometry/routing/sideNormal.ts
3048
+ function sideNormal(side) {
3049
+ switch (side) {
3050
+ case "top":
3051
+ return [0, -1];
3052
+ case "right":
3053
+ return [1, 0];
3054
+ case "bottom":
3055
+ return [0, 1];
3056
+ case "left":
3057
+ return [-1, 0];
3058
+ }
3059
+ }
3060
+
3061
+ // src/renderScene/artifacts/link/geometry/routing/cubic.ts
3062
+ function controlDistances(from, to, fromNormal, toNormal, options) {
3063
+ const minimum = options?.min;
3064
+ const maximum = options?.max;
3065
+ const perpendicularScale = options?.perpendicularScale;
3066
+ const deltaX = to.x - from.x;
3067
+ const deltaY = to.y - from.y;
3068
+ const alongFrom = Math.abs(deltaX * fromNormal[0] + deltaY * fromNormal[1]);
3069
+ const alongTo = Math.abs(deltaX * toNormal[0] + deltaY * toNormal[1]);
3070
+ const perpendicularFrom = Math.abs(deltaX * fromNormal[1] - deltaY * fromNormal[0]);
3071
+ const perpendicularTo = Math.abs(deltaX * toNormal[1] - deltaY * toNormal[0]);
3072
+ const parallel = fromNormal[0] === toNormal[0] && fromNormal[1] === toNormal[1] || fromNormal[0] === -toNormal[0] && fromNormal[1] === -toNormal[1];
3073
+ let fromDistance = Math.max(minimum, Math.min(maximum, Math.max(alongFrom, perpendicularFrom * perpendicularScale)));
3074
+ let toDistance = Math.max(minimum, Math.min(maximum, Math.max(alongTo, perpendicularTo * perpendicularScale)));
3075
+ if (parallel) {
3076
+ fromDistance = Math.min(maximum, fromDistance + perpendicularFrom * 0.75);
3077
+ toDistance = Math.min(maximum, toDistance + perpendicularTo * 0.75);
3078
+ }
3079
+ return { fromDistance, toDistance };
3080
+ }
3081
+ function routeCubic(from, to, fromSide, toSide, options) {
3082
+ const fromNormal = sideNormal(fromSide);
3083
+ const toNormal = sideNormal(toSide);
3084
+ const { fromDistance, toDistance } = controlDistances(from, to, fromNormal, toNormal, options);
3085
+ return {
3086
+ p0: [from.x, from.y],
3087
+ p1: [from.x + fromNormal[0] * fromDistance, from.y + fromNormal[1] * fromDistance],
3088
+ p2: [to.x + toNormal[0] * toDistance, to.y + toNormal[1] * toDistance],
3089
+ p3: [to.x, to.y]
3090
+ };
3091
+ }
3092
+
3093
+ // src/renderScene/artifacts/link/geometry/routing/curvedRoute.ts
3094
+ function calculateCurvedLinkRoute(from, to, fromSide, toSide) {
3095
+ const { p0, p1, p2, p3 } = routeCubic(from, to, fromSide, toSide, {
3096
+ min: 35,
3097
+ max: 90,
3098
+ perpendicularScale: 0.5
3099
+ });
3100
+ return { points: [...p0, ...p1, ...p2, ...p3], segments: [] };
3101
+ }
3102
+
3103
+ // src/renderScene/artifacts/link/geometry/routing/orthogonalPlan.ts
3104
+ function snapAlignedEndpoints(from, to, fromSide, toSide) {
3105
+ const threshold = 6;
3106
+ const verticalPair = ["top", "bottom"].includes(fromSide) && ["top", "bottom"].includes(toSide);
3107
+ const horizontalPair = ["left", "right"].includes(fromSide) && ["left", "right"].includes(toSide);
3108
+ if (verticalPair && Math.abs(to.x - from.x) < threshold) {
3109
+ return [from, { ...to, x: from.x }];
3110
+ }
3111
+ if (horizontalPair && Math.abs(to.y - from.y) < threshold) {
3112
+ return [from, { ...to, y: from.y }];
3113
+ }
3114
+ return [from, to];
3115
+ }
3116
+ function chooseInitialOrientation(from, to, endpointEdges) {
3117
+ if (endpointEdges?.toEdge) {
3118
+ return endpointEdges.toEdge === "left" || endpointEdges.toEdge === "right";
3119
+ }
3120
+ if (endpointEdges?.fromEdge) {
3121
+ return endpointEdges.fromEdge === "left" || endpointEdges.fromEdge === "right";
3122
+ }
3123
+ return Math.abs(to.x - from.x) > Math.abs(to.y - from.y);
3124
+ }
3125
+ function stepOutDistances(from, to, fromNormal, toNormal, horizontalFirst) {
3126
+ let fromDistance = 40;
3127
+ let toDistance = 40;
3128
+ const axis = horizontalFirst ? 0 : 1;
3129
+ const delta = horizontalFirst ? to.x - from.x : to.y - from.y;
3130
+ if (fromNormal[axis] !== 0 && fromNormal[axis] === -toNormal[axis]) {
3131
+ const gap = delta * fromNormal[axis];
3132
+ if (gap > 0 && gap < fromDistance + toDistance) {
3133
+ fromDistance = gap / 2;
3134
+ toDistance = gap / 2;
3135
+ }
3136
+ } else if (fromNormal[axis] === 0) {
3137
+ const gap = delta * -toNormal[axis];
3138
+ if (gap > 0 && gap < toDistance) {
3139
+ toDistance = gap;
3140
+ }
3141
+ }
3142
+ return [fromDistance, toDistance];
3143
+ }
3144
+ function chooseBend(route) {
3145
+ const { fromConnection, toConnection, to, horizontalFirst } = route;
3146
+ const fromStub = fromConnection.stub;
3147
+ const toStub = toConnection.stub;
3148
+ let x = (fromStub[0] + toStub[0]) / 2;
3149
+ let y = (fromStub[1] + toStub[1]) / 2;
3150
+ const fromHorizontal = isHorizontalSide(fromConnection.side);
3151
+ const toHorizontal = isHorizontalSide(toConnection.side);
3152
+ if (fromHorizontal !== toHorizontal) {
3153
+ if (horizontalFirst) {
3154
+ x = (fromStub[0] - to.x) * toConnection.normal[0] >= 0 ? fromStub[0] : toStub[0];
3155
+ } else {
3156
+ y = (fromStub[1] - to.y) * toConnection.normal[1] >= 0 ? fromStub[1] : toStub[1];
3157
+ }
3158
+ } else if (fromConnection.side === toConnection.side) {
3159
+ if (horizontalFirst) {
3160
+ x = fromConnection.normal[0] < 0 ? Math.min(fromStub[0], toStub[0]) : Math.max(fromStub[0], toStub[0]);
3161
+ } else {
3162
+ y = fromConnection.normal[1] < 0 ? Math.min(fromStub[1], toStub[1]) : Math.max(fromStub[1], toStub[1]);
3163
+ }
3164
+ }
3165
+ return [x, y];
3166
+ }
3167
+ function isHorizontalSide(side) {
3168
+ return side === "left" || side === "right";
3169
+ }
3170
+
3171
+ // src/renderScene/artifacts/link/geometry/routing/orthogonalPoints.ts
3172
+ function segmentMidpoints(points) {
3173
+ const midpoints = [];
3174
+ for (let index = 0; index < points.length - 2; index += 2) {
3175
+ midpoints.push({
3176
+ x: (points[index] + points[index + 2]) / 2,
3177
+ y: (points[index + 1] + points[index + 3]) / 2
3178
+ });
3179
+ }
3180
+ return midpoints;
3181
+ }
3182
+ function baseOrthogonalPoints(route, bend) {
3183
+ const { from, to, fromConnection, toConnection, horizontalFirst } = route;
3184
+ const fromStub = fromConnection.stub;
3185
+ const toStub = toConnection.stub;
3186
+ const points = [from.x, from.y, fromStub[0], fromStub[1]];
3187
+ if (horizontalFirst) {
3188
+ points.push(bend[0], fromStub[1], bend[0], toStub[1]);
3189
+ } else {
3190
+ points.push(fromStub[0], bend[1], toStub[0], bend[1]);
3191
+ }
3192
+ points.push(toStub[0], toStub[1], to.x, to.y);
3193
+ return points;
3194
+ }
3195
+ function movableInnerSegments(horizontalFirst, fromSide, toSide) {
3196
+ const fixedSides = horizontalFirst ? ["left", "right"] : ["top", "bottom"];
3197
+ return [!fixedSides.includes(fromSide), !fixedSides.includes(toSide)];
3198
+ }
3199
+ function offsetOrthogonalPoints(route, bend, segmentOffsets, movable) {
3200
+ const { from, to, fromConnection, toConnection, horizontalFirst } = route;
3201
+ const fromStub = fromConnection.stub;
3202
+ const toStub = toConnection.stub;
3203
+ const first = movable[0] ? segmentOffsets?.[1] || 0 : 0;
3204
+ const middle = segmentOffsets?.[2] || 0;
3205
+ const last = movable[1] ? segmentOffsets?.[3] || 0 : 0;
3206
+ if (horizontalFirst) {
3207
+ const fromY = fromStub[1] + first;
3208
+ const bendX = bend[0] + middle;
3209
+ const toY = toStub[1] + last;
3210
+ return [from.x, from.y, fromStub[0], fromY, bendX, fromY, bendX, toY, toStub[0], toY, to.x, to.y];
3211
+ }
3212
+ const fromX = fromStub[0] + first;
3213
+ const bendY = bend[1] + middle;
3214
+ const toX = toStub[0] + last;
3215
+ return [from.x, from.y, fromX, fromStub[1], fromX, bendY, toX, bendY, toX, toStub[1], to.x, to.y];
3216
+ }
3217
+
3218
+ // src/renderScene/artifacts/link/geometry/routing/orthogonalSegments.ts
3219
+ function describeSegments(points, originalMidpoints, canMoveFirstInner, canMoveLastInner) {
3220
+ const totalSegments = (points.length - 2) / 2;
3221
+ return originalMidpoints.map((originalMidpoint, segmentIndex) => {
3222
+ const offset = segmentIndex * 2;
3223
+ const start = { x: points[offset], y: points[offset + 1] };
3224
+ const end = { x: points[offset + 2], y: points[offset + 3] };
3225
+ const midpoint = { x: (start.x + end.x) / 2, y: (start.y + end.y) / 2 };
3226
+ const isInner = segmentIndex > 0 && segmentIndex < totalSegments - 1;
3227
+ const isMovableEdge = segmentIndex === 1 ? canMoveFirstInner : segmentIndex === totalSegments - 2 ? canMoveLastInner : true;
3228
+ const hasLength = Math.hypot(end.x - start.x, end.y - start.y) >= 1;
3229
+ return {
3230
+ start,
3231
+ end,
3232
+ midpoint,
3233
+ originalMidpoint,
3234
+ isHorizontal: Math.abs(end.x - start.x) > Math.abs(end.y - start.y),
3235
+ canMove: isInner && isMovableEdge && hasLength
3236
+ };
3237
+ });
3238
+ }
3239
+
3240
+ // src/renderScene/artifacts/link/geometry/routing/rounding.ts
3241
+ var CORNER_SAMPLE_COUNT = 8;
3242
+ function roundOrthogonalPoints(coordinates, maximumRadius) {
3243
+ if (coordinates.length < 6) {
3244
+ return coordinates;
3245
+ }
3246
+ const points = coordinatePoints(coordinates);
3247
+ const roundedPoints = [points[0]];
3248
+ for (let index = 1; index < points.length - 1; index++) {
3249
+ const corner = createRoundedCorner(points[index - 1], points[index], points[index + 1], maximumRadius);
3250
+ roundedPoints.push(...corner ? sampleRoundedCorner(corner) : [points[index]]);
3251
+ }
3252
+ roundedPoints.push(points[points.length - 1]);
3253
+ return roundedPoints.flatMap((point) => [point.x, point.y]);
3254
+ }
3255
+ function createRoundedCorner(previous, corner, next, maximumRadius) {
3256
+ const incoming = { x: corner.x - previous.x, y: corner.y - previous.y };
3257
+ const outgoing = { x: next.x - corner.x, y: next.y - corner.y };
3258
+ const incomingIsAxisAligned = incoming.x === 0 || incoming.y === 0;
3259
+ const outgoingIsAxisAligned = outgoing.x === 0 || outgoing.y === 0;
3260
+ const isStraight = incoming.x === 0 && outgoing.x === 0 || incoming.y === 0 && outgoing.y === 0;
3261
+ if (!incomingIsAxisAligned || !outgoingIsAxisAligned || isStraight) {
3262
+ return null;
3263
+ }
3264
+ const radius = Math.min(maximumRadius, axisLength(incoming) * 0.4, axisLength(outgoing) * 0.4);
3265
+ if (radius <= 0) {
3266
+ return null;
3267
+ }
3268
+ const incomingDirection = axisDirection(incoming);
3269
+ const outgoingDirection = axisDirection(outgoing);
3270
+ const entry = offsetPoint(corner, incomingDirection, -radius);
3271
+ const exit = offsetPoint(corner, outgoingDirection, radius);
3272
+ const center = offsetPoint(entry, outgoingDirection, radius);
3273
+ const startAngle = Math.atan2(entry.y - center.y, entry.x - center.x);
3274
+ const endAngle = Math.atan2(exit.y - center.y, exit.x - center.x);
3275
+ return { entry, exit, center, radius, startAngle, angleSweep: normalizedAngleSweep(endAngle - startAngle) };
3276
+ }
3277
+ function offsetPoint(point, direction, distance2) {
3278
+ return { x: point.x + direction.x * distance2, y: point.y + direction.y * distance2 };
3279
+ }
3280
+ function axisLength(vector) {
3281
+ return Math.abs(vector.x) + Math.abs(vector.y);
3282
+ }
3283
+ function axisDirection(vector) {
3284
+ return { x: vector.x !== 0 ? Math.sign(vector.x) : 0, y: vector.y !== 0 ? Math.sign(vector.y) : 0 };
3285
+ }
3286
+ function normalizedAngleSweep(angle) {
3287
+ if (angle > Math.PI) {
3288
+ return angle - 2 * Math.PI;
3289
+ }
3290
+ if (angle < -Math.PI) {
3291
+ return angle + 2 * Math.PI;
3292
+ }
3293
+ return angle;
3294
+ }
3295
+ function sampleRoundedCorner(corner) {
3296
+ const points = [corner.entry];
3297
+ for (let sampleIndex = 1; sampleIndex < CORNER_SAMPLE_COUNT - 1; sampleIndex++) {
3298
+ const progress = sampleIndex / (CORNER_SAMPLE_COUNT - 1);
3299
+ const angle = corner.startAngle + corner.angleSweep * progress;
3300
+ points.push({
3301
+ x: corner.center.x + Math.cos(angle) * corner.radius,
3302
+ y: corner.center.y + Math.sin(angle) * corner.radius
3303
+ });
3304
+ }
3305
+ points.push(corner.exit);
3306
+ return points;
3307
+ }
3308
+ function coordinatePoints(coordinates) {
3309
+ const points = [];
3310
+ for (let index = 0; index < coordinates.length; index += 2) {
3311
+ points.push({ x: coordinates[index], y: coordinates[index + 1] });
3312
+ }
3313
+ return points;
3314
+ }
3315
+
3316
+ // src/renderScene/artifacts/link/geometry/routing/orthogonal.ts
3317
+ function routeOrthogonal(request) {
3318
+ const routeDraft = createOrthogonalRouteDraft(request);
3319
+ const bend = chooseBend(routeDraft);
3320
+ const originalPoints = baseOrthogonalPoints(routeDraft, bend);
3321
+ const originalSegmentMidpoints = segmentMidpoints(originalPoints);
3322
+ const movableSegments = movableInnerSegments(routeDraft.horizontalFirst, request.fromSide, request.toSide);
3323
+ const points = offsetOrthogonalPoints(routeDraft, bend, request.segmentOffsets, movableSegments);
3324
+ const segments = describeSegments(points, originalSegmentMidpoints, movableSegments[0], movableSegments[1]);
3325
+ return { points: roundOrthogonalPoints(points, 10), segments };
3326
+ }
3327
+ function createOrthogonalRouteDraft(request) {
3328
+ const [alignedFrom, alignedTo] = snapAlignedEndpoints(request.from, request.to, request.fromSide, request.toSide);
3329
+ const fromNormal = sideNormal(request.fromSide);
3330
+ const toNormal = sideNormal(request.toSide);
3331
+ const horizontalFirst = chooseInitialOrientation(alignedFrom, alignedTo, request.endpointEdges);
3332
+ const [fromDistance, toDistance] = stepOutDistances(alignedFrom, alignedTo, fromNormal, toNormal, horizontalFirst);
3333
+ return {
3334
+ from: alignedFrom,
3335
+ to: alignedTo,
3336
+ fromConnection: createRouteConnection(alignedFrom, request.fromSide, fromNormal, fromDistance),
3337
+ toConnection: createRouteConnection(alignedTo, request.toSide, toNormal, toDistance),
3338
+ horizontalFirst
3339
+ };
3340
+ }
3341
+ function createRouteConnection(point, side, normal, distance2) {
3342
+ return { side, normal, stub: [point.x + normal[0] * distance2, point.y + normal[1] * distance2] };
3343
+ }
3344
+
3345
+ // src/renderScene/artifacts/link/geometry/routing/waypoints.ts
3346
+ var ORTHOGONAL_EPSILON = 0.5;
3347
+ function pathFromWaypoints(from, waypoints, to) {
3348
+ const corners = [from, ...waypoints, to];
3349
+ const points = [];
3350
+ for (const point of corners) {
3351
+ points.push(point.x, point.y);
3352
+ }
3353
+ const segments = [];
3354
+ for (let index = 0; index < points.length - 2; index += 2) {
3355
+ const start = { x: points[index], y: points[index + 1] };
3356
+ const end = { x: points[index + 2], y: points[index + 3] };
3357
+ const midpoint = { x: (start.x + end.x) / 2, y: (start.y + end.y) / 2 };
3358
+ segments.push({
3359
+ start,
3360
+ end,
3361
+ midpoint,
3362
+ originalMidpoint: midpoint,
3363
+ isHorizontal: Math.abs(end.x - start.x) > Math.abs(end.y - start.y),
3364
+ canMove: false
3365
+ });
3366
+ }
3367
+ return { points: roundOrthogonalPoints(points, 10), segments };
3368
+ }
3369
+ function isAxisAligned(segments) {
3370
+ return segments.every((segment) => {
3371
+ const dx = Math.abs(segment.end.x - segment.start.x);
3372
+ const dy = Math.abs(segment.end.y - segment.start.y);
3373
+ return dx <= ORTHOGONAL_EPSILON || dy <= ORTHOGONAL_EPSILON;
3374
+ });
3375
+ }
3376
+
3377
+ // src/renderScene/artifacts/link/geometry/routing/orthogonalRoute.ts
3378
+ function calculateOrthogonalLinkRoute(link, from, to, fromSide, toSide) {
3379
+ if (link.waypoints && link.waypoints.length > 0) {
3380
+ const waypointResult = pathFromWaypoints(from, link.waypoints, to);
3381
+ if (isAxisAligned(waypointResult.segments)) {
3382
+ return waypointResult;
3383
+ }
3384
+ }
3385
+ const endpointEdges = { fromEdge: fromSide, toEdge: toSide };
3386
+ const request = { from, to, fromSide, toSide, segmentOffsets: link.segmentOffsets, endpointEdges };
3387
+ const route = routeOrthogonal(request);
3388
+ if (isAxisAligned(route.segments)) {
3389
+ return route;
3390
+ }
3391
+ return routeOrthogonal({ ...request, segmentOffsets: void 0 });
3392
+ }
3393
+
3394
+ // src/renderScene/artifacts/link/geometry/routing/sides.ts
3395
+ function facingSide(from, to) {
3396
+ const angle = Math.atan2(to.y - from.y, to.x - from.x);
3397
+ if (angle >= -Math.PI / 4 && angle < Math.PI / 4) {
3398
+ return "right";
3399
+ }
3400
+ if (angle >= Math.PI / 4 && angle < 3 * Math.PI / 4) {
3401
+ return "bottom";
3402
+ }
3403
+ if (angle >= -3 * Math.PI / 4 && angle < -Math.PI / 4) {
3404
+ return "top";
3405
+ }
3406
+ return "left";
3407
+ }
3408
+ function connectionSides(link, from, to) {
3409
+ const fromSide = link.fromPoint?.kind === "side" ? link.fromPoint.side : facingSide(from, to);
3410
+ const toSide = link.toPoint?.kind === "side" ? link.toPoint.side : facingSide(to, from);
3411
+ return { fromSide, toSide };
3412
+ }
3413
+
3414
+ // src/renderScene/artifacts/link/geometry/routing/straightRoute.ts
3415
+ function calculateStraightLinkRoute(from, to) {
3416
+ return { points: [from.x, from.y, to.x, to.y], segments: [] };
3417
+ }
3418
+
3419
+ // src/renderScene/artifacts/link/geometry/routing.ts
3420
+ function calculateLinkRoute(link, effectiveFrom, effectiveTo, pathType) {
3421
+ const { fromSide, toSide } = connectionSides(link, effectiveFrom, effectiveTo);
3422
+ if (pathType === "curved") {
3423
+ return calculateCurvedLinkRoute(effectiveFrom, effectiveTo, fromSide, toSide);
3424
+ }
3425
+ if (pathType === "orthogonal") {
3426
+ return calculateOrthogonalLinkRoute(link, effectiveFrom, effectiveTo, fromSide, toSide);
3427
+ }
3428
+ return calculateStraightLinkRoute(effectiveFrom, effectiveTo);
3429
+ }
3430
+
3431
+ // src/renderScene/artifacts/shape/geometry/roundedForms.ts
3432
+ var KAPPA = 0.5523;
3433
+ var CYLINDER_CAP_OF_HEIGHT = 0.22;
3434
+ var CYLINDER_CAP_OF_WIDTH = 0.2;
3435
+ var PERSON_BODY_IN_HEADS = 2;
3436
+ var PERSON_NECK_IN_HEADS = 0.35;
3437
+ var PERSON_WIDTH_IN_HEADS = 2 * PERSON_BODY_IN_HEADS;
3438
+ var PERSON_HEIGHT_IN_HEADS = 2 + PERSON_NECK_IN_HEADS + PERSON_BODY_IN_HEADS;
3439
+ function cylinderPath(x, y, width, height) {
3440
+ const cap = Math.min(height * CYLINDER_CAP_OF_HEIGHT, width * CYLINDER_CAP_OF_WIDTH);
3441
+ const rx = width / 2;
3442
+ const cx = x + rx;
3443
+ const left = x;
3444
+ const right = x + width;
3445
+ const lidY = y + cap;
3446
+ const floorY = y + height - cap;
3447
+ const lid = `M ${left} ${lidY} C ${left} ${lidY + KAPPA * cap} ${cx - KAPPA * rx} ${lidY + cap} ${cx} ${lidY + cap} C ${cx + KAPPA * rx} ${lidY + cap} ${right} ${lidY + KAPPA * cap} ${right} ${lidY} C ${right} ${lidY - KAPPA * cap} ${cx + KAPPA * rx} ${y} ${cx} ${y} C ${cx - KAPPA * rx} ${y} ${left} ${lidY - KAPPA * cap} ${left} ${lidY} Z`;
3448
+ const wall = `M ${left} ${lidY} L ${left} ${floorY} C ${left} ${floorY + KAPPA * cap} ${cx - KAPPA * rx} ${y + height} ${cx} ${y + height} C ${cx + KAPPA * rx} ${y + height} ${right} ${floorY + KAPPA * cap} ${right} ${floorY} L ${right} ${lidY}`;
3449
+ return `${lid} ${wall}`;
3450
+ }
3451
+ function personFigure(x, y, width, height) {
3452
+ const scale = Math.min(width / PERSON_WIDTH_IN_HEADS, height / PERSON_HEIGHT_IN_HEADS);
3453
+ const head = scale;
3454
+ const body = scale * PERSON_BODY_IN_HEADS;
3455
+ const figureHeight = scale * PERSON_HEIGHT_IN_HEADS;
3456
+ const top = y + (height - figureHeight) / 2;
3457
+ return { head, body, centerX: x + width / 2, headCenterY: top + head, bottom: top + figureHeight };
3458
+ }
3459
+ function personLabelArea(x, y, width, height) {
3460
+ const { body, centerX, bottom } = personFigure(x, y, width, height);
3461
+ return { x: centerX - body, y: bottom - body, width: body * 2, height: body };
3462
+ }
3463
+ function personPath(x, y, width, height) {
3464
+ const { head, body, centerX, headCenterY, bottom } = personFigure(x, y, width, height);
3465
+ const skull = `M ${centerX} ${headCenterY - head} C ${centerX + KAPPA * head} ${headCenterY - head} ${centerX + head} ${headCenterY - KAPPA * head} ${centerX + head} ${headCenterY} C ${centerX + head} ${headCenterY + KAPPA * head} ${centerX + KAPPA * head} ${headCenterY + head} ${centerX} ${headCenterY + head} C ${centerX - KAPPA * head} ${headCenterY + head} ${centerX - head} ${headCenterY + KAPPA * head} ${centerX - head} ${headCenterY} C ${centerX - head} ${headCenterY - KAPPA * head} ${centerX - KAPPA * head} ${headCenterY - head} ${centerX} ${headCenterY - head} Z`;
3466
+ const dome = `M ${centerX - body} ${bottom} C ${centerX - body} ${bottom - KAPPA * body} ${centerX - KAPPA * body} ${bottom - body} ${centerX} ${bottom - body} C ${centerX + KAPPA * body} ${bottom - body} ${centerX + body} ${bottom - KAPPA * body} ${centerX + body} ${bottom} Z`;
3467
+ return `${skull} ${dome}`;
3468
+ }
3469
+
3470
+ // src/renderScene/charts/primitives/barLabel.ts
3471
+ var BAR_LABEL = { padX: 8, padY: 3, charWidth: 0.58, lineHeight: 1.2, gap: 4 };
3472
+ function formatBarLabel(value, template) {
3473
+ return (template || "{value}").replace("{value}", String(Math.round(value * 100) / 100));
3474
+ }
3475
+ function barLabelSize(text, fontSize, pill) {
3476
+ const padX = pill ? BAR_LABEL.padX : 0;
3477
+ const padY = pill ? BAR_LABEL.padY : 0;
3478
+ const lineHeight = fontSize * BAR_LABEL.lineHeight;
3479
+ return {
3480
+ width: text.length * fontSize * BAR_LABEL.charWidth + padX * 2,
3481
+ height: lineHeight + padY * 2,
3482
+ lineHeight,
3483
+ padX,
3484
+ padY
3485
+ };
3486
+ }
3487
+ function pillPath(x, y, width, height) {
3488
+ const r = height / 2;
3489
+ return `M ${x + r} ${y} H ${x + width - r} A ${r} ${r} 0 0 1 ${x + width - r} ${y + height} H ${x + r} A ${r} ${r} 0 0 1 ${x + r} ${y} Z`;
3490
+ }
3491
+
3492
+ // src/renderScene/charts/primitives/barShape.ts
3493
+ function barShapePath(x, y, width, height, radius) {
3494
+ const r = Math.max(0, Math.min(radius, width / 2, height));
3495
+ if (r === 0) {
3496
+ return `M ${x} ${y} H ${x + width} V ${y + height} H ${x} Z`;
3497
+ }
3498
+ return `M ${x} ${y + r} Q ${x} ${y} ${x + r} ${y} H ${x + width - r} Q ${x + width} ${y} ${x + width} ${y + r} V ${y + height} H ${x} Z`;
3499
+ }
3500
+ function roundedBarPath(x, y, width, height, radius) {
3501
+ const r = Math.max(0, Math.min(radius, width / 2, height / 2));
3502
+ if (r === 0) {
3503
+ return `M ${x} ${y} H ${x + width} V ${y + height} H ${x} Z`;
3504
+ }
3505
+ return `M ${x + r} ${y} H ${x + width - r} Q ${x + width} ${y} ${x + width} ${y + r} V ${y + height - r} Q ${x + width} ${y + height} ${x + width - r} ${y + height} H ${x + r} Q ${x} ${y + height} ${x} ${y + height - r} V ${y + r} Q ${x} ${y} ${x + r} ${y} Z`;
3506
+ }
3507
+ function barPath(x, y, width, height, radius, roundedBottom) {
3508
+ return roundedBottom ? roundedBarPath(x, y, width, height, radius) : barShapePath(x, y, width, height, radius);
3509
+ }
3510
+
3511
+ // src/renderScene/charts/primitives/linePath.ts
3512
+ function linePathData(points, tension) {
3513
+ if (tension > 0) {
3514
+ return freeformPathData({ points, tension, closed: false });
3515
+ }
3516
+ const commands = points.map((point, index) => `${index ? "L" : ""} ${point.x} ${point.y}`);
3517
+ return `M ${commands.join(" ")}`;
3518
+ }
3519
+
3520
+ // src/renderScene/charts/primitives/pieShape.ts
3521
+ function resolvePieRadii(plotWidth, plotHeight, configInnerRadius, configCornerRadius) {
3522
+ const rawOuterRadius = Math.max(1, Math.min(plotWidth, plotHeight) / 2 - 4);
3523
+ const rawInnerRadius = Math.max(0, Math.min(configInnerRadius ?? 0, rawOuterRadius - 1));
3524
+ const cornerRadius = Math.max(0, Math.min(configCornerRadius ?? 0, rawOuterRadius / 2));
3525
+ const outerRadius = Math.max(1, rawOuterRadius - cornerRadius);
3526
+ const innerRadius = rawInnerRadius > 0 ? Math.min(rawInnerRadius + cornerRadius, outerRadius - 1) : 0;
3527
+ return { outerRadius, innerRadius, cornerRadius };
3528
+ }
3529
+ function pieArcPoint(geometry, angle, radius) {
3530
+ return {
3531
+ x: geometry.centerX + radius * Math.cos(angle),
3532
+ y: geometry.centerY + radius * Math.sin(angle)
3533
+ };
3534
+ }
3535
+ function clipSegmentToRect(from, to, halfWidth, halfHeight) {
3536
+ const dx = to.x - from.x;
3537
+ const dy = to.y - from.y;
3538
+ let tMin = 0;
3539
+ let tMax = 1;
3540
+ if (Math.abs(dx) > 1e-6) {
3541
+ const tx1 = (to.x - halfWidth - from.x) / dx;
3542
+ const tx2 = (to.x + halfWidth - from.x) / dx;
3543
+ tMin = Math.max(tMin, Math.min(tx1, tx2));
3544
+ tMax = Math.min(tMax, Math.max(tx1, tx2));
3545
+ }
3546
+ if (Math.abs(dy) > 1e-6) {
3547
+ const ty1 = (to.y - halfHeight - from.y) / dy;
3548
+ const ty2 = (to.y + halfHeight - from.y) / dy;
3549
+ tMin = Math.max(tMin, Math.min(ty1, ty2));
3550
+ tMax = Math.min(tMax, Math.max(ty1, ty2));
3551
+ }
3552
+ const t = tMin <= tMax ? tMin : 1;
3553
+ return { x: from.x + t * dx, y: from.y + t * dy };
3554
+ }
3555
+ function largeArcFlag(a, b) {
3556
+ return Math.abs(b - a) > Math.PI ? 1 : 0;
3557
+ }
3558
+ function gapInsetAngle(halfGap, radius) {
3559
+ if (halfGap <= 0 || radius <= 0) {
3560
+ return 0;
3561
+ }
3562
+ return Math.asin(Math.min(1, halfGap / radius));
3563
+ }
3564
+ function insetAngles(angles, inset) {
3565
+ return { start: angles.start + inset, end: angles.end - inset };
3566
+ }
3567
+ function pieApexRadius(halfGap, sweep, outerRadius) {
3568
+ if (halfGap <= 0) {
3569
+ return 0;
3570
+ }
3571
+ const halfSweep = Math.min(Math.abs(sweep) / 2, Math.PI / 2);
3572
+ const denominator = Math.sin(halfSweep);
3573
+ return denominator <= 1e-6 ? outerRadius : Math.min(halfGap / denominator, outerRadius);
3574
+ }
3575
+ function apexSlicePath(geometry, outerAngles, apex) {
3576
+ const start = pieArcPoint(geometry, outerAngles.start, geometry.outerRadius);
3577
+ const end = pieArcPoint(geometry, outerAngles.end, geometry.outerRadius);
3578
+ return [
3579
+ `M ${apex.x} ${apex.y}`,
3580
+ `L ${start.x} ${start.y}`,
3581
+ `A ${geometry.outerRadius} ${geometry.outerRadius} 0 ${largeArcFlag(outerAngles.start, outerAngles.end)} 1 ${end.x} ${end.y}`,
3582
+ "Z"
3583
+ ].join(" ");
3584
+ }
3585
+ function donutSlicePath(geometry, innerRadius, outerAngles, innerAngles) {
3586
+ const outerStart = pieArcPoint(geometry, outerAngles.start, geometry.outerRadius);
3587
+ const outerEnd = pieArcPoint(geometry, outerAngles.end, geometry.outerRadius);
3588
+ const innerEnd = pieArcPoint(geometry, innerAngles.end, innerRadius);
3589
+ const innerStart = pieArcPoint(geometry, innerAngles.start, innerRadius);
3590
+ return [
3591
+ `M ${outerStart.x} ${outerStart.y}`,
3592
+ `A ${geometry.outerRadius} ${geometry.outerRadius} 0 ${largeArcFlag(outerAngles.start, outerAngles.end)} 1 ${outerEnd.x} ${outerEnd.y}`,
3593
+ `L ${innerEnd.x} ${innerEnd.y}`,
3594
+ `A ${innerRadius} ${innerRadius} 0 ${largeArcFlag(innerAngles.start, innerAngles.end)} 0 ${innerStart.x} ${innerStart.y}`,
3595
+ "Z"
3596
+ ].join(" ");
3597
+ }
3598
+ var CORNER_ANGLE_HEADROOM = 0.82;
3599
+ function pieFillet(geometry, circleRadius, filletCenterRadius, edgeAngle, cornerRadius, sign) {
3600
+ const ratio = Math.min(1, cornerRadius / filletCenterRadius);
3601
+ const angle = edgeAngle + sign * Math.asin(ratio);
3602
+ const d = Math.sqrt(Math.max(0, filletCenterRadius * filletCenterRadius - cornerRadius * cornerRadius));
3603
+ return {
3604
+ lineTangent: { x: geometry.centerX + d * Math.cos(edgeAngle), y: geometry.centerY + d * Math.sin(edgeAngle) },
3605
+ arcTangent: pieArcPoint(geometry, angle, circleRadius),
3606
+ angle
3607
+ };
3608
+ }
3609
+ function maxPieCornerRadius(outerRadius, innerRadius, sweep) {
3610
+ const halfSweep = Math.min(Math.abs(sweep) / 2, Math.PI / 2);
3611
+ const angleFactor = Math.sin(halfSweep * CORNER_ANGLE_HEADROOM);
3612
+ const outerLimit = outerRadius * angleFactor;
3613
+ const innerLimit = innerRadius > 0 ? innerRadius * angleFactor : Number.POSITIVE_INFINITY;
3614
+ const ringLimit = innerRadius > 0 ? (outerRadius - innerRadius) / 2 : outerRadius / 2;
3615
+ return Math.max(0, Math.min(outerLimit, innerLimit, ringLimit));
3616
+ }
3617
+ function maxApexCornerRadius(outerRadius, apexRadius, sweep) {
3618
+ const halfSweep = Math.min(Math.abs(sweep) / 2, Math.PI / 2);
3619
+ const angleFactor = Math.sin(halfSweep * CORNER_ANGLE_HEADROOM);
3620
+ const outerLimit = outerRadius * angleFactor;
3621
+ const depthLimit = (outerRadius * outerRadius - apexRadius * apexRadius) / (2 * outerRadius);
3622
+ return Math.max(0, Math.min(outerLimit, depthLimit));
3623
+ }
3624
+ function roundedApexSlicePath(geometry, outerAngles, cornerRadius, apex) {
3625
+ const { outerRadius } = geometry;
3626
+ const outerFilletCenterRadius = outerRadius - cornerRadius;
3627
+ const start = pieFillet(geometry, outerRadius, outerFilletCenterRadius, outerAngles.start, cornerRadius, 1);
3628
+ const end = pieFillet(geometry, outerRadius, outerFilletCenterRadius, outerAngles.end, cornerRadius, -1);
3629
+ return [
3630
+ `M ${apex.x} ${apex.y}`,
3631
+ `L ${start.lineTangent.x} ${start.lineTangent.y}`,
3632
+ `A ${cornerRadius} ${cornerRadius} 0 0 1 ${start.arcTangent.x} ${start.arcTangent.y}`,
3633
+ `A ${outerRadius} ${outerRadius} 0 ${largeArcFlag(start.angle, end.angle)} 1 ${end.arcTangent.x} ${end.arcTangent.y}`,
3634
+ `A ${cornerRadius} ${cornerRadius} 0 0 1 ${end.lineTangent.x} ${end.lineTangent.y}`,
3635
+ "Z"
3636
+ ].join(" ");
3637
+ }
3638
+ function roundedDonutSlicePath(geometry, innerRadius, outerAngles, innerAngles, cornerRadius) {
3639
+ const { outerRadius } = geometry;
3640
+ const outerFilletCenterRadius = outerRadius - cornerRadius;
3641
+ const outerStart = pieFillet(geometry, outerRadius, outerFilletCenterRadius, outerAngles.start, cornerRadius, 1);
3642
+ const outerEnd = pieFillet(geometry, outerRadius, outerFilletCenterRadius, outerAngles.end, cornerRadius, -1);
3643
+ const innerFilletCenterRadius = innerRadius + cornerRadius;
3644
+ const innerStart = pieFillet(geometry, innerRadius, innerFilletCenterRadius, innerAngles.start, cornerRadius, 1);
3645
+ const innerEnd = pieFillet(geometry, innerRadius, innerFilletCenterRadius, innerAngles.end, cornerRadius, -1);
3646
+ return [
3647
+ `M ${outerStart.lineTangent.x} ${outerStart.lineTangent.y}`,
3648
+ `A ${cornerRadius} ${cornerRadius} 0 0 1 ${outerStart.arcTangent.x} ${outerStart.arcTangent.y}`,
3649
+ `A ${outerRadius} ${outerRadius} 0 ${largeArcFlag(outerStart.angle, outerEnd.angle)} 1 ${outerEnd.arcTangent.x} ${outerEnd.arcTangent.y}`,
3650
+ `A ${cornerRadius} ${cornerRadius} 0 0 1 ${outerEnd.lineTangent.x} ${outerEnd.lineTangent.y}`,
3651
+ `L ${innerEnd.lineTangent.x} ${innerEnd.lineTangent.y}`,
3652
+ `A ${cornerRadius} ${cornerRadius} 0 0 1 ${innerEnd.arcTangent.x} ${innerEnd.arcTangent.y}`,
3653
+ `A ${innerRadius} ${innerRadius} 0 ${largeArcFlag(innerEnd.angle, innerStart.angle)} 0 ${innerStart.arcTangent.x} ${innerStart.arcTangent.y}`,
3654
+ `A ${cornerRadius} ${cornerRadius} 0 0 1 ${innerStart.lineTangent.x} ${innerStart.lineTangent.y}`,
3655
+ "Z"
3656
+ ].join(" ");
3657
+ }
3658
+ function pieUsesInnerArc(geometry, angles, gapPx) {
3659
+ const halfGap = Math.max(0, gapPx) / 2;
3660
+ const apexRadius = pieApexRadius(halfGap, angles.end - angles.start, geometry.outerRadius);
3661
+ return geometry.innerRadius > 0 && geometry.innerRadius >= apexRadius;
3662
+ }
3663
+ function createPieSlicePath(geometry, angles, cornerRadius = 0, gapPx = 0) {
3664
+ const halfGap = Math.max(0, gapPx) / 2;
3665
+ const outerAngles = insetAngles(angles, gapInsetAngle(halfGap, geometry.outerRadius));
3666
+ if (!pieUsesInnerArc(geometry, angles, gapPx)) {
3667
+ const apexRadius = pieApexRadius(halfGap, angles.end - angles.start, geometry.outerRadius);
3668
+ const apex = pieArcPoint(geometry, (angles.start + angles.end) / 2, apexRadius);
3669
+ return cornerRadius > 0 ? roundedApexSlicePath(geometry, outerAngles, cornerRadius, apex) : apexSlicePath(geometry, outerAngles, apex);
3670
+ }
3671
+ const innerAngles = insetAngles(angles, gapInsetAngle(halfGap, geometry.innerRadius));
3672
+ return cornerRadius > 0 ? roundedDonutSlicePath(geometry, geometry.innerRadius, outerAngles, innerAngles, cornerRadius) : donutSlicePath(geometry, geometry.innerRadius, outerAngles, innerAngles);
3673
+ }
3674
+
3675
+ // src/model/color/conversion.ts
3676
+ var hexToRgb2 = (hex) => {
3677
+ const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})?$/i.exec(hex);
3678
+ if (!result) {
3679
+ return { r: 255, g: 255, b: 255, a: 1 };
3680
+ }
3681
+ return {
3682
+ r: parseInt(result[1], 16),
3683
+ g: parseInt(result[2], 16),
3684
+ b: parseInt(result[3], 16),
3685
+ a: result[4] === void 0 ? 1 : parseInt(result[4], 16) / 255
3686
+ };
3687
+ };
3688
+ var rgbToHex2 = (r, g, b, a) => {
3689
+ const channel = (x) => {
3690
+ const hex = Math.max(0, Math.min(255, Math.round(x))).toString(16);
3691
+ return hex.length === 1 ? `0${hex}` : hex;
3692
+ };
3693
+ const alphaChannel = a === void 0 || a >= 1 ? "" : channel(a * 255);
3694
+ return `#${channel(r)}${channel(g)}${channel(b)}${alphaChannel}`;
3695
+ };
3696
+
3697
+ // src/model/color/interpolation.ts
3698
+ function mixHexColors(start, end, ratio) {
3699
+ const startRgb = hexToRgb2(start);
3700
+ const endRgb = hexToRgb2(end);
3701
+ const startAlpha = startRgb.a ?? 1;
3702
+ const endAlpha = endRgb.a ?? 1;
3703
+ return rgbToHex2(
3704
+ Math.round(startRgb.r + (endRgb.r - startRgb.r) * ratio),
3705
+ Math.round(startRgb.g + (endRgb.g - startRgb.g) * ratio),
3706
+ Math.round(startRgb.b + (endRgb.b - startRgb.b) * ratio),
3707
+ startAlpha + (endAlpha - startAlpha) * ratio
3708
+ );
3709
+ }
3710
+ function interpolateHexColorStops(stops, position) {
3711
+ const sortedStops = [...stops].sort((first2, second) => first2.position - second.position);
3712
+ const first = sortedStops[0];
3713
+ const last = sortedStops[sortedStops.length - 1];
3714
+ if (position <= first.position) {
3715
+ return first.color;
3716
+ }
3717
+ if (position >= last.position) {
3718
+ return last.color;
3719
+ }
3720
+ for (let index = 0; index < sortedStops.length - 1; index++) {
3721
+ const start = sortedStops[index];
3722
+ const end = sortedStops[index + 1];
3723
+ if (position >= start.position && position <= end.position) {
3724
+ const range = end.position - start.position;
3725
+ const ratio = range === 0 ? 0 : (position - start.position) / range;
3726
+ return mixHexColors(start.color, end.color, ratio);
3727
+ }
3728
+ }
3729
+ return last.color;
3730
+ }
3731
+
3732
+ // src/renderScene/charts/primitives/defaults.ts
3733
+ var CHART_COLORS = ["#7c3aed", "#0ea5e9", "#f97316", "#10b981", "#ec4899", "#eab308"];
3734
+ var DEFAULT_ZONE_FILL = "#94a3b8";
3735
+ var DEFAULT_HEATMAP_ZONES = [
3736
+ { label: "Low", color: "#ddd6fe" },
3737
+ { label: "Medium", color: "#a78bfa" },
3738
+ { label: "High", color: "#6d28d9" }
3739
+ ];
3740
+ var DEFAULT_HEX_RADIUS = 14;
3741
+
3742
+ // src/renderScene/charts/primitives/axis.ts
3743
+ function mapChartValue(value, axis, extent) {
3744
+ if (axis.max === axis.min) {
3745
+ return 0;
3746
+ }
3747
+ return (value - axis.min) / (axis.max - axis.min) * extent;
3748
+ }
3749
+ function chartTicks(axis) {
3750
+ if (!(axis.step > 0)) {
3751
+ return axis.min === axis.max ? [axis.min] : [axis.min, axis.max];
3752
+ }
3753
+ const ascending = axis.min <= axis.max;
3754
+ const step = ascending ? axis.step : -axis.step;
3755
+ const ticks = [];
3756
+ for (let value = axis.min; (ascending ? value <= axis.max + 1e-9 : value >= axis.max - 1e-9) && ticks.length < 1e3; value += step) {
3757
+ ticks.push(Math.round(value * 1e3) / 1e3);
3758
+ }
3759
+ return ticks;
3760
+ }
3761
+ function axisBands(axis) {
3762
+ const thresholds = [...axis.thresholds ?? []].filter((threshold) => threshold > axis.min && threshold < axis.max).sort((a, b) => a - b);
3763
+ return [axis.min, ...thresholds, axis.max];
3764
+ }
3765
+ function zoneColorAtValue(value, bands, zones) {
3766
+ const bandCount = Math.max(0, bands.length - 1);
3767
+ const stops = zones.slice(0, bandCount).map((zone, index) => ({
3768
+ position: bands[index + 1] ?? bands[bands.length - 1],
3769
+ color: zone.color ?? DEFAULT_ZONE_FILL
3770
+ }));
3771
+ if (!stops.length) {
3772
+ return DEFAULT_ZONE_FILL;
3773
+ }
3774
+ return interpolateHexColorStops(stops, value);
3775
+ }
3776
+
3777
+ // src/renderScene/charts/primitives/barCategories.ts
3778
+ function barCategoryCount(chart) {
3779
+ return Math.max(1, chart.config?.categories?.length ?? 0, ...chart.series.map((series) => series.values.length));
3780
+ }
3781
+
3782
+ // src/renderScene/charts/primitives/boxplot.ts
3783
+ function quantile(sorted, probability) {
3784
+ if (sorted.length === 1) {
3785
+ return sorted[0];
3786
+ }
3787
+ const position = (sorted.length - 1) * probability;
3788
+ const lowerIndex = Math.floor(position);
3789
+ const upperIndex = Math.ceil(position);
3790
+ if (lowerIndex === upperIndex) {
3791
+ return sorted[lowerIndex];
3792
+ }
3793
+ return sorted[lowerIndex] + (position - lowerIndex) * (sorted[upperIndex] - sorted[lowerIndex]);
3794
+ }
3795
+ function computeBoxplotStats(values) {
3796
+ if (!values?.length) {
3797
+ return null;
3798
+ }
3799
+ const sorted = [...values].sort((a, b) => a - b);
3800
+ return {
3801
+ min: sorted[0],
3802
+ q1: quantile(sorted, 0.25),
3803
+ median: quantile(sorted, 0.5),
3804
+ q3: quantile(sorted, 0.75),
3805
+ max: sorted[sorted.length - 1]
3806
+ };
3807
+ }
3808
+
3809
+ // src/renderScene/color.ts
3810
+ function hexToRgb3(hex) {
3811
+ if (hex?.[0] !== "#") {
3812
+ return void 0;
3813
+ }
3814
+ const channels = hex.length === 4 ? hex.slice(1).split("").map((channel) => parseInt(channel + channel, 16)) : [1, 3, 5].map((index) => parseInt(hex.slice(index, index + 2), 16));
3815
+ const [red, green, blue] = channels;
3816
+ return [red, green, blue].some((channel) => Number.isNaN(channel)) ? void 0 : [red, green, blue];
3817
+ }
3818
+ function colorWithAlpha(hex, alpha) {
3819
+ const rgb = hexToRgb3(hex);
3820
+ const clamped = Math.max(0, Math.min(1, alpha));
3821
+ if (!rgb) {
3822
+ return clamped <= 0 ? "transparent" : hex ?? "transparent";
3823
+ }
3824
+ const [red, green, blue] = rgb;
3825
+ return `rgba(${red}, ${green}, ${blue}, ${clamped})`;
3826
+ }
3827
+ function rgbToHex3(red, green, blue) {
3828
+ const channel = (value) => Math.round(Math.max(0, Math.min(255, value))).toString(16).padStart(2, "0");
3829
+ return `#${channel(red)}${channel(green)}${channel(blue)}`;
3830
+ }
3831
+ function rgbToHsl(red, green, blue) {
3832
+ const r = red / 255;
3833
+ const g = green / 255;
3834
+ const b = blue / 255;
3835
+ const max = Math.max(r, g, b);
3836
+ const min = Math.min(r, g, b);
3837
+ const lightness = (max + min) / 2;
3838
+ const delta = max - min;
3839
+ if (delta === 0) {
3840
+ return [0, 0, lightness];
3841
+ }
3842
+ const saturation = delta / (1 - Math.abs(2 * lightness - 1));
3843
+ let hue;
3844
+ if (max === r) {
3845
+ hue = (g - b) / delta % 6;
3846
+ } else if (max === g) {
3847
+ hue = (b - r) / delta + 2;
3848
+ } else {
3849
+ hue = (r - g) / delta + 4;
3850
+ }
3851
+ hue *= 60;
3852
+ if (hue < 0) {
3853
+ hue += 360;
3854
+ }
3855
+ return [hue, saturation, lightness];
3856
+ }
3857
+ function hslToRgb(hue, saturation, lightness) {
3858
+ const chroma = (1 - Math.abs(2 * lightness - 1)) * saturation;
3859
+ const x = chroma * (1 - Math.abs(hue / 60 % 2 - 1));
3860
+ const m = lightness - chroma / 2;
3861
+ let [r, g, b] = [0, 0, 0];
3862
+ if (hue < 60) {
3863
+ [r, g, b] = [chroma, x, 0];
3864
+ } else if (hue < 120) {
3865
+ [r, g, b] = [x, chroma, 0];
3866
+ } else if (hue < 180) {
3867
+ [r, g, b] = [0, chroma, x];
3868
+ } else if (hue < 240) {
3869
+ [r, g, b] = [0, x, chroma];
3870
+ } else if (hue < 300) {
3871
+ [r, g, b] = [x, 0, chroma];
3872
+ } else {
3873
+ [r, g, b] = [chroma, 0, x];
3874
+ }
3875
+ return [(r + m) * 255, (g + m) * 255, (b + m) * 255];
3876
+ }
3877
+ function relativeLuminance(red, green, blue) {
3878
+ const linear = (channel) => {
3879
+ const normalized = channel / 255;
3880
+ return normalized <= 0.03928 ? normalized / 12.92 : ((normalized + 0.055) / 1.055) ** 2.4;
3881
+ };
3882
+ return 0.2126 * linear(red) + 0.7152 * linear(green) + 0.0722 * linear(blue);
3883
+ }
3884
+ function contrastRatio(luminanceA, luminanceB) {
3885
+ const lighter = Math.max(luminanceA, luminanceB);
3886
+ const darker = Math.min(luminanceA, luminanceB);
3887
+ return (lighter + 0.05) / (darker + 0.05);
3888
+ }
3889
+ var SAME_HUE_LIGHTNESS_GAP = 0.35;
3890
+ var SAME_HUE_MIN_LIGHTNESS = 0.08;
3891
+ var SAME_HUE_MAX_LIGHTNESS = 0.22;
3892
+ var SAME_HUE_LIGHT_MIN_LIGHTNESS = 0.78;
3893
+ var SAME_HUE_LIGHT_MAX_LIGHTNESS = 0.92;
3894
+ var SAME_HUE_MIN_CONTRAST = 3;
3895
+ function contrastTextColor(hex) {
3896
+ const rgb = hexToRgb3(hex);
3897
+ if (!rgb) {
3898
+ return "#0f172a";
3899
+ }
3900
+ const [red, green, blue] = rgb;
3901
+ const fallback = (0.299 * red + 0.587 * green + 0.114 * blue) / 255 > 0.6 ? "#0f172a" : "#ffffff";
3902
+ const [hue, saturation, lightness] = rgbToHsl(red, green, blue);
3903
+ if (saturation === 0) {
3904
+ return fallback;
3905
+ }
3906
+ const targetLightness = lightness > 0.5 ? Math.max(SAME_HUE_MIN_LIGHTNESS, Math.min(lightness - SAME_HUE_LIGHTNESS_GAP, SAME_HUE_MAX_LIGHTNESS)) : Math.min(
3907
+ SAME_HUE_LIGHT_MAX_LIGHTNESS,
3908
+ Math.max(lightness + SAME_HUE_LIGHTNESS_GAP, SAME_HUE_LIGHT_MIN_LIGHTNESS)
3909
+ );
3910
+ const variant = hslToRgb(hue, saturation, targetLightness);
3911
+ const ratio = contrastRatio(relativeLuminance(red, green, blue), relativeLuminance(...variant));
3912
+ return ratio >= SAME_HUE_MIN_CONTRAST ? rgbToHex3(...variant) : fallback;
3913
+ }
3914
+ function sameHueContrastColor(hex, fallback) {
3915
+ const rgb = hexToRgb3(hex);
3916
+ if (!rgb) {
3917
+ return fallback;
3918
+ }
3919
+ const [red, green, blue] = rgb;
3920
+ const [hue, saturation, lightness] = rgbToHsl(red, green, blue);
3921
+ if (saturation === 0) {
3922
+ return fallback;
3923
+ }
3924
+ const targetLightness = lightness > 0.5 ? Math.max(SAME_HUE_MIN_LIGHTNESS, Math.min(lightness - SAME_HUE_LIGHTNESS_GAP, SAME_HUE_MAX_LIGHTNESS)) : Math.min(
3925
+ SAME_HUE_LIGHT_MAX_LIGHTNESS,
3926
+ Math.max(lightness + SAME_HUE_LIGHTNESS_GAP, SAME_HUE_LIGHT_MIN_LIGHTNESS)
3927
+ );
3928
+ return rgbToHex3(...hslToRgb(hue, saturation, targetLightness));
3929
+ }
3930
+ function isLightColor(hex) {
3931
+ const rgb = hexToRgb3(hex);
3932
+ return rgb ? rgbToHsl(...rgb)[2] > 0.5 : false;
3933
+ }
3934
+ function sameHueTint(hex, targetLightness) {
3935
+ const rgb = hexToRgb3(hex);
3936
+ if (!rgb) {
3937
+ return void 0;
3938
+ }
3939
+ const [hue, saturation] = rgbToHsl(...rgb);
3940
+ if (saturation === 0) {
3941
+ return void 0;
3942
+ }
3943
+ return rgbToHex3(...hslToRgb(hue, saturation, targetLightness));
3944
+ }
3945
+
3946
+ // src/renderScene/charts/primitives/colors.ts
3947
+ function representativeColor(value, fallback) {
3948
+ if (!value) {
3949
+ return fallback;
3950
+ }
3951
+ if (typeof value === "string") {
3952
+ return value;
3953
+ }
3954
+ if (value.type === "gradient") {
3955
+ return value.stops[0]?.color ?? fallback;
3956
+ }
3957
+ return value.points[0]?.color ?? fallback;
3958
+ }
3959
+
3960
+ // src/renderScene/charts/primitives/errors.ts
3961
+ var ERROR_CAP_HALF = 5;
3962
+ function errorBarRange(value, error) {
3963
+ return Array.isArray(error) ? [value - error[0], value + error[1]] : [value - error, value + error];
3964
+ }
3965
+ function createVerticalErrorWhiskerNode(whisker) {
3966
+ return {
3967
+ type: "path",
3968
+ id: whisker.id,
3969
+ order: whisker.order,
3970
+ bounds: whisker.bounds,
3971
+ d: verticalErrorWhiskerPath(whisker.geometry),
3972
+ detail: {
3973
+ of: "chartValue",
3974
+ series: whisker.valueReference.seriesId,
3975
+ valueIndex: whisker.valueReference.valueIndex
3976
+ },
3977
+ style: { fill: { kind: "none" }, stroke: { kind: "solid", color: whisker.color }, strokeWidth: 1 }
3978
+ };
3979
+ }
3980
+ function verticalErrorWhiskerPath(geometry) {
3981
+ const { centerX, startY, endY } = geometry;
3982
+ const capStartX = centerX - ERROR_CAP_HALF;
3983
+ const capEndX = centerX + ERROR_CAP_HALF;
3984
+ return `M ${centerX} ${startY} L ${centerX} ${endY} M ${capStartX} ${startY} L ${capEndX} ${startY} M ${capStartX} ${endY} L ${capEndX} ${endY}`;
3985
+ }
3986
+
3987
+ // src/renderScene/charts/primitives/format.ts
3988
+ function formatFieldValue(value) {
3989
+ return Number.isInteger(value) ? String(value) : value.toFixed(2).replace(/\.?0+$/, "");
3990
+ }
3991
+
3992
+ // src/renderScene/charts/primitives/horizontalBars.ts
3993
+ var CATEGORY_LABEL_ROW = 18;
3994
+ var settings = (chart) => chart.config;
3995
+ function isHorizontalBar(chart) {
3996
+ return chart.chartType === "bar" && settings(chart)?.orientation === "horizontal";
3997
+ }
3998
+ function horizontalRows(chart, plot) {
3999
+ const count = barCategoryCount({ series: chart.series, config: settings(chart) });
4000
+ const slotHeight = plot.height / count;
4001
+ const groupGap = Math.min(Math.max(0, finite(settings(chart)?.barGroupGap, 8)), Math.max(0, slotHeight - 1));
4002
+ const labelRow = chart.categoryAlign === "left" ? CATEGORY_LABEL_ROW : 0;
4003
+ const available = Math.max(1, slotHeight - groupGap - labelRow);
4004
+ const thickness = finite(settings(chart)?.barThickness, 0);
4005
+ const barsHeight = thickness > 0 ? Math.min(available, thickness) : available;
4006
+ return { count, slotHeight, groupGap, labelRow, barsHeight, offset: (available - barsHeight) / 2 };
4007
+ }
4008
+ function rowTop(rows, plot, index) {
4009
+ return plot.y + index * rows.slotHeight + rows.groupGap / 2 + rows.offset;
4010
+ }
4011
+
4012
+ // src/renderScene/charts/primitives/segments.ts
4013
+ function segmentedBarPath(frame, orientation, count, gap, radius, fraction, snap) {
4014
+ const horizontal = orientation === "horizontal";
4015
+ const length = horizontal ? frame.width : frame.height;
4016
+ const segment = Math.max(1, (length - gap * (count - 1)) / count);
4017
+ const filled = Math.min(count, Math.max(0, fraction * count));
4018
+ const units = snap ? Math.round(filled) : filled;
4019
+ const parts = [];
4020
+ for (let index = 0; index < count; index++) {
4021
+ const visible = Math.min(1, Math.max(0, units - index)) * segment;
4022
+ if (visible < 0.5) {
4023
+ continue;
4024
+ }
4025
+ const offset = index * (segment + gap);
4026
+ parts.push(
4027
+ horizontal ? roundedBarPath(frame.x + offset, frame.y, visible, frame.height, radius) : roundedBarPath(frame.x, frame.y + frame.height - offset - visible, frame.width, visible, radius)
4028
+ );
4029
+ }
4030
+ return parts.join(" ");
4031
+ }
4032
+
4033
+ // src/renderScene/charts/primitives/shadow.ts
4034
+ function resolveChartShadow(shadow) {
4035
+ if (!shadow?.enabled) {
4036
+ return void 0;
4037
+ }
4038
+ const angle = finite(shadow.angle, 45) * Math.PI / 180;
4039
+ const distance2 = finite(shadow.distance, 5);
4040
+ return {
4041
+ color: shadow.color ?? "#000000",
4042
+ opacity: finite(shadow.opacity, 0.25),
4043
+ blur: finite(shadow.blur, 10),
4044
+ dx: Math.cos(angle) * distance2,
4045
+ dy: Math.sin(angle) * distance2
4046
+ };
4047
+ }
4048
+
4049
+ // src/renderScene/charts/primitives/stacking.ts
4050
+ function stackedSegment(chart, mode, categoryIndex, seriesIndex, plotHeight) {
4051
+ const value = chart.series[seriesIndex].values[categoryIndex] ?? 0;
4052
+ if (mode === "waterfall") {
4053
+ return waterfallSegment(chart, categoryIndex, seriesIndex, plotHeight, value);
4054
+ }
4055
+ if (mode === "percent") {
4056
+ return percentSegment(chart, categoryIndex, seriesIndex, plotHeight, value);
4057
+ }
4058
+ return cumulativeSegment(chart, categoryIndex, seriesIndex, plotHeight, value);
4059
+ }
4060
+ function waterfallSegment(chart, categoryIndex, seriesIndex, plotHeight, value) {
4061
+ let running = 0;
4062
+ for (let index = 0; index < categoryIndex; index++) {
4063
+ running += chart.series[seriesIndex].values[index] ?? 0;
4064
+ }
4065
+ return mappedSegment(value, running, running + value, chart, plotHeight);
4066
+ }
4067
+ function percentSegment(chart, categoryIndex, seriesIndex, plotHeight, value) {
4068
+ const total = chart.series.reduce((sum, series) => sum + Math.abs(series.values[categoryIndex] ?? 0), 0) || 1;
4069
+ let cumulative = 0;
4070
+ for (let index = 0; index < seriesIndex; index++) {
4071
+ cumulative += Math.abs(chart.series[index].values[categoryIndex] ?? 0);
4072
+ }
4073
+ const magnitude = Math.abs(value);
4074
+ return {
4075
+ value,
4076
+ pixelStart: cumulative / total * plotHeight,
4077
+ pixelEnd: (cumulative + magnitude) / total * plotHeight
4078
+ };
4079
+ }
4080
+ function cumulativeSegment(chart, categoryIndex, seriesIndex, plotHeight, value) {
4081
+ let cumulative = 0;
4082
+ for (let index = 0; index < seriesIndex; index++) {
4083
+ cumulative += chart.series[index].values[categoryIndex] ?? 0;
4084
+ }
4085
+ return mappedSegment(value, cumulative, cumulative + value, chart, plotHeight);
4086
+ }
4087
+ function mappedSegment(value, start, end, chart, plotHeight) {
4088
+ return {
4089
+ value,
4090
+ pixelStart: mapChartValue(start, chart.axisY, plotHeight),
4091
+ pixelEnd: mapChartValue(end, chart.axisY, plotHeight)
4092
+ };
4093
+ }
4094
+
4095
+ // src/renderScene/charts/primitives/text.ts
4096
+ function chartText(options) {
4097
+ const { chart, frame } = options;
4098
+ return {
4099
+ type: "text",
4100
+ id: options.id,
4101
+ order: options.order,
4102
+ bounds: options.bounds,
4103
+ ...frame,
4104
+ lines: [options.text],
4105
+ fontFamily: (options.role === "axis" ? chart.axisFontFamily : void 0) ?? chart.fontFamily ?? DEFAULT_FONT,
4106
+ fontSize: Math.max(
4107
+ 9,
4108
+ finite(options.role === "axis" ? chart.axisFontSize ?? chart.fontSize : chart.fontSize, 12)
4109
+ ),
4110
+ fontWeight: "normal",
4111
+ color: (options.role === "axis" ? chart.axisLabelColor : options.role === "legend" ? chart.legendColor : void 0) ?? chart.textColor ?? "#0f172a",
4112
+ align: options.align ?? "left",
4113
+ verticalAlign: "top",
4114
+ lineHeight: 1.2,
4115
+ rotation: options.rotation,
4116
+ rotationCenter: options.rotation ? { x: frame.x, y: frame.y } : void 0
4117
+ };
4118
+ }
4119
+
4120
+ // src/renderScene/charts/renderers/pie/geometry.ts
4121
+ function createPieGeometry(context) {
4122
+ const { chart, plot } = context;
4123
+ const resolved = resolvePieRadii(plot.width, plot.height, chart.config?.innerRadius, chart.config?.pieCornerRadius);
4124
+ const outside = chart.config?.pieLabelPosition === "outside" && chart.showAxisLabels !== false;
4125
+ const room = outside ? chart.config?.pieLabelContent === "inline" ? 0.6 : 0.7 : 1;
4126
+ const outerRadius = resolved.outerRadius * room;
4127
+ const innerRadius = resolved.innerRadius * room;
4128
+ const cornerRadius = resolved.cornerRadius;
4129
+ return {
4130
+ centerX: plot.x + plot.width / 2,
4131
+ centerY: plot.y + plot.height / 2,
4132
+ outerRadius,
4133
+ innerRadius,
4134
+ cornerRadius,
4135
+ total: chart.series.reduce((sum, series) => sum + Math.max(0, finite(series.values[0], 0)), 0) || 1
4136
+ };
4137
+ }
4138
+
4139
+ // src/renderScene/charts/renderers/pie/labels.ts
4140
+ var MIN_LABEL_SWEEP = 14 * Math.PI / 180;
4141
+ var LABEL_WIDTH = 80;
4142
+ var LABEL_RADIUS_FRACTION = 0.62;
4143
+ var OUTSIDE_GAP = 14;
4144
+ var LEADER_INSET = 3;
4145
+ var OUTSIDE_TINT_LIGHTNESS = 0.32;
4146
+ var OUTSIDE_FALLBACK_COLOR = "#334155";
4147
+ function renderPieLabels(context, geometry, marks) {
4148
+ if (context.chart.showAxisLabels === false) {
4149
+ return [];
4150
+ }
4151
+ return marks.filter((mark) => mark.end - mark.start >= MIN_LABEL_SWEEP).flatMap((mark) => createPieLabel(context, geometry, mark));
4152
+ }
4153
+ function labelTextPair(context, mark, point, color, fontSize, percent, outside, midAngle) {
4154
+ const { chart, bounds, nextOrder } = context;
4155
+ const lineHeight = fontSize * 1.2;
4156
+ const content = chart.config?.pieLabelContent ?? "both";
4157
+ const width = content === "inline" ? LABEL_WIDTH * 1.9 : LABEL_WIDTH;
4158
+ const base = {
4159
+ bounds,
4160
+ x: point.x - width / 2,
4161
+ width,
4162
+ height: lineHeight,
4163
+ fontFamily: chart.fontFamily ?? DEFAULT_FONT,
4164
+ fontSize,
4165
+ align: "center",
4166
+ verticalAlign: "middle",
4167
+ lineHeight: 1.2,
4168
+ color
4169
+ };
4170
+ const detail = (extra) => ({
4171
+ of: "chartSeries",
4172
+ series: mark.seriesId,
4173
+ mark: "pie-label",
4174
+ pulled: outside || void 0,
4175
+ midAngle,
4176
+ ...extra
4177
+ });
4178
+ const nameText = {
4179
+ type: "text",
4180
+ id: `chart-pie-label-name-${chart.id}-${mark.seriesId}`,
4181
+ order: nextOrder(),
4182
+ ...base,
4183
+ y: content === "name" ? point.y - lineHeight / 2 : point.y - lineHeight,
4184
+ lines: [content === "inline" ? `${mark.label}, ${percent}%` : mark.label],
4185
+ fontWeight: "bold",
4186
+ detail: detail({}),
4187
+ prop: seriesLabelProp(mark.seriesId)
4188
+ };
4189
+ const valueText = {
4190
+ type: "text",
4191
+ id: `chart-pie-label-value-${chart.id}-${mark.seriesId}`,
4192
+ order: nextOrder(),
4193
+ ...base,
4194
+ y: content === "percent" ? point.y - lineHeight / 2 : point.y,
4195
+ lines: [`${percent}%`],
4196
+ fontWeight: "normal",
4197
+ detail: detail({}),
4198
+ prop: seriesValueProp(mark.seriesId, 0),
4199
+ propKind: "value"
4200
+ };
4201
+ if (content === "inline" || content === "name") {
4202
+ return [nameText];
4203
+ }
4204
+ return content === "percent" ? [valueText] : [nameText, valueText];
4205
+ }
4206
+ function createTick(context, mark, tickStart, tickEnd, color, outside) {
4207
+ const { chart, bounds, nextOrder } = context;
4208
+ return {
4209
+ type: "path",
4210
+ id: `chart-pie-label-tick-${chart.id}-${mark.seriesId}`,
4211
+ order: nextOrder(),
4212
+ bounds,
4213
+ d: `M ${tickStart.x} ${tickStart.y} L ${tickEnd.x} ${tickEnd.y}`,
4214
+ detail: { of: "chartSeries", series: mark.seriesId, mark: "pie-label-tick" },
4215
+ style: outside ? { stroke: { kind: "solid", color }, strokeWidth: 1.5 } : { stroke: { kind: "none" } }
4216
+ };
4217
+ }
4218
+ function createPieLabel(context, base, mark) {
4219
+ const config = context.chart.config;
4220
+ const geometry = { ...base, centerX: base.centerX + mark.shift.dx, centerY: base.centerY + mark.shift.dy };
4221
+ const mid = (mark.start + mark.end) / 2;
4222
+ const fontSize = Math.max(9, finite(context.chart.fontSize, 12));
4223
+ const percent = Math.round(mark.share * 100);
4224
+ const lineHeight = fontSize * 1.2;
4225
+ const tickStart = pieArcPoint(geometry, mid, geometry.outerRadius + LEADER_INSET);
4226
+ const ringThickness = geometry.outerRadius - geometry.innerRadius;
4227
+ const tooThin = ringThickness < lineHeight * 2 + 6;
4228
+ const hasManualOffset = mark.labelOffsetX !== 0 || mark.labelOffsetY !== 0;
4229
+ const position = config?.pieLabelPosition ?? "auto";
4230
+ const outside = hasManualOffset || position === "outside" || position === "auto" && tooThin;
4231
+ if (outside) {
4232
+ const point2 = hasManualOffset ? addOffset(pieArcPoint(geometry, mid, geometry.outerRadius), mark.labelOffsetX, mark.labelOffsetY) : pieArcPoint(
4233
+ geometry,
4234
+ mid,
4235
+ geometry.outerRadius + OUTSIDE_GAP + (position === "outside" ? Math.abs(Math.cos(mid)) * (config?.pieLabelContent === "inline" ? LABEL_WIDTH * 0.95 : LABEL_WIDTH / 2) : 0)
4236
+ );
4237
+ const tickEnd = clipSegmentToRect(tickStart, point2, LABEL_WIDTH / 2, lineHeight);
4238
+ const color2 = config?.pieLabelColor ?? sameHueTint(representativeColor(mark.color, OUTSIDE_FALLBACK_COLOR), OUTSIDE_TINT_LIGHTNESS) ?? OUTSIDE_FALLBACK_COLOR;
4239
+ const tick2 = createTick(context, mark, tickStart, tickEnd, color2, true);
4240
+ return [tick2, ...labelTextPair(context, mark, point2, color2, fontSize, percent, true, mid)];
4241
+ }
4242
+ const labelRadius = geometry.innerRadius + ringThickness * LABEL_RADIUS_FRACTION;
4243
+ const point = pieArcPoint(geometry, mid, labelRadius);
4244
+ const sliceColor = representativeColor(mark.color, "#0f172a");
4245
+ const color = config?.pieLabelColor ?? sameHueContrastColor(sliceColor, contrastTextColor(sliceColor));
4246
+ const tick = createTick(context, mark, tickStart, tickStart, color, false);
4247
+ return [tick, ...labelTextPair(context, mark, point, color, fontSize, percent, false, mid)];
4248
+ }
4249
+ function addOffset(point, dx, dy) {
4250
+ return { x: point.x + dx, y: point.y + dy };
4251
+ }
4252
+
4253
+ // src/renderScene/artifacts/link/geometry/label/pathMetrics.ts
4254
+ var lerpPoint = (a, b, t) => ({
4255
+ x: a.x + (b.x - a.x) * t,
4256
+ y: a.y + (b.y - a.y) * t
4257
+ });
4258
+ function cubicBezierPoint(p0, p1, p2, p3, t) {
4259
+ const mt = 1 - t;
4260
+ return {
4261
+ x: mt * mt * mt * p0.x + 3 * mt * mt * t * p1.x + 3 * mt * t * t * p2.x + t * t * t * p3.x,
4262
+ y: mt * mt * mt * p0.y + 3 * mt * mt * t * p1.y + 3 * mt * t * t * p2.y + t * t * t * p3.y
4263
+ };
4264
+ }
4265
+ function splitCubicBezier(p0, p1, p2, p3, t) {
4266
+ const a = lerpPoint(p0, p1, t);
4267
+ const b = lerpPoint(p1, p2, t);
4268
+ const c = lerpPoint(p2, p3, t);
4269
+ const d = lerpPoint(a, b, t);
4270
+ const e = lerpPoint(b, c, t);
4271
+ const f = lerpPoint(d, e, t);
4272
+ return { left: [p0, a, d, f], right: [f, e, c, p3] };
4273
+ }
4274
+ function pointAtArcLength(points, targetLength) {
4275
+ const lastIndex = points.length - 2;
4276
+ if (targetLength <= 0) {
4277
+ return { point: { x: points[0], y: points[1] }, segmentIndex: 0 };
4278
+ }
4279
+ let accumulated = 0;
4280
+ for (let index = 0; index < lastIndex; index += 2) {
4281
+ const start = { x: points[index], y: points[index + 1] };
4282
+ const end = { x: points[index + 2], y: points[index + 3] };
4283
+ const length = Math.hypot(end.x - start.x, end.y - start.y);
4284
+ if (accumulated + length >= targetLength) {
4285
+ const ratio = length === 0 ? 0 : (targetLength - accumulated) / length;
4286
+ return { point: lerpPoint(start, end, ratio), segmentIndex: index };
4287
+ }
4288
+ accumulated += length;
4289
+ }
4290
+ return { point: { x: points[lastIndex], y: points[lastIndex + 1] }, segmentIndex: lastIndex };
4291
+ }
4292
+ function polylineLength(points) {
4293
+ let total = 0;
4294
+ for (let index = 0; index < points.length - 2; index += 2) {
4295
+ total += Math.hypot(points[index + 2] - points[index], points[index + 3] - points[index + 1]);
4296
+ }
4297
+ return total;
4298
+ }
4299
+ function bezierArcLength(p0, p1, p2, p3, samples = 40) {
4300
+ let total = 0;
4301
+ let previous = p0;
4302
+ for (let index = 1; index <= samples; index++) {
4303
+ const point = cubicBezierPoint(p0, p1, p2, p3, index / samples);
4304
+ total += Math.hypot(point.x - previous.x, point.y - previous.y);
4305
+ previous = point;
4306
+ }
4307
+ return total;
4308
+ }
4309
+
4310
+ // src/renderScene/artifacts/link/geometry/label/curvedGap.ts
4311
+ var SAMPLES = 40;
4312
+ function sampledArcLengths(points) {
4313
+ const lengths = [0];
4314
+ let previous = points[0];
4315
+ for (let index = 1; index <= SAMPLES; index++) {
4316
+ const point = cubicBezierPoint(...points, index / SAMPLES);
4317
+ lengths.push(lengths[index - 1] + Math.hypot(point.x - previous.x, point.y - previous.y));
4318
+ previous = point;
4319
+ }
4320
+ return lengths;
4321
+ }
4322
+ function parameterAtLength(lengths, target) {
4323
+ const total = lengths[SAMPLES];
4324
+ if (target <= 0) {
4325
+ return 0;
4326
+ }
4327
+ if (target >= total) {
4328
+ return 1;
4329
+ }
4330
+ for (let index = 1; index <= SAMPLES; index++) {
4331
+ if (lengths[index] >= target) {
4332
+ const fraction = (target - lengths[index - 1]) / (lengths[index] - lengths[index - 1] || 1);
4333
+ return (index - 1 + fraction) / SAMPLES;
4334
+ }
4335
+ }
4336
+ return 1;
4337
+ }
4338
+ function projectedGapHalfLength(points, clearance, maximum) {
4339
+ const [p0, p1, p2, p3] = points;
4340
+ const dx = 0.75 * (p1.x - p0.x) + 1.5 * (p2.x - p1.x) + 0.75 * (p3.x - p2.x);
4341
+ const dy = 0.75 * (p1.y - p0.y) + 1.5 * (p2.y - p1.y) + 0.75 * (p3.y - p2.y);
4342
+ const length = Math.hypot(dx, dy);
4343
+ const ux = length === 0 ? 1 : Math.abs(dx) / length;
4344
+ const uy = length === 0 ? 0 : Math.abs(dy) / length;
4345
+ return Math.min(clearance.halfWidth * ux + clearance.halfHeight * uy, maximum);
4346
+ }
4347
+ function splitCurvedPath(flatPoints, clearance) {
4348
+ if (flatPoints.length < 8) {
4349
+ return null;
4350
+ }
4351
+ const points = curvedControlPoints(flatPoints);
4352
+ const lengths = sampledArcLengths(points);
4353
+ const total = lengths[SAMPLES];
4354
+ if (total === 0) {
4355
+ return null;
4356
+ }
4357
+ const half = total / 2;
4358
+ const maximumGap = Math.max(0, half - clearance.minimumVisibleStub);
4359
+ const gapHalfLength = projectedGapHalfLength(points, clearance, maximumGap);
4360
+ const beforeParameter = parameterAtLength(lengths, half - gapHalfLength);
4361
+ const afterParameter = parameterAtLength(lengths, half + gapHalfLength);
4362
+ return {
4363
+ before: curvedPathBeforeGap(points, beforeParameter),
4364
+ after: curvedPathAfterGap(points, afterParameter),
4365
+ bezier: true
4366
+ };
4367
+ }
4368
+ function curvedControlPoints(flatPoints) {
4369
+ return [
4370
+ { x: flatPoints[0], y: flatPoints[1] },
4371
+ { x: flatPoints[2], y: flatPoints[3] },
4372
+ { x: flatPoints[4], y: flatPoints[5] },
4373
+ { x: flatPoints[6], y: flatPoints[7] }
4374
+ ];
4375
+ }
4376
+ function curvedPathBeforeGap(points, parameter) {
4377
+ if (parameter <= 0.01) {
4378
+ return null;
4379
+ }
4380
+ return splitCubicBezier(...points, parameter).left.flatMap((point) => [point.x, point.y]);
4381
+ }
4382
+ function curvedPathAfterGap(points, parameter) {
4383
+ if (parameter >= 0.99) {
4384
+ return null;
4385
+ }
4386
+ return splitCubicBezier(...points, parameter).right.flatMap((point) => [point.x, point.y]);
4387
+ }
4388
+
4389
+ // src/renderScene/artifacts/link/geometry/label/polylineGap.ts
4390
+ function splitPolylinePath(points, clearance) {
4391
+ if (points.length < 4) {
4392
+ return null;
4393
+ }
4394
+ const total = polylineLength(points);
4395
+ if (total === 0) {
4396
+ return null;
4397
+ }
4398
+ const placement = polylineGapPlacement(points, total, clearance);
4399
+ return {
4400
+ before: polylineBeforeGap(points, placement.beforeLength),
4401
+ after: polylineAfterGap(points, placement.afterLength, total),
4402
+ bezier: false
4403
+ };
4404
+ }
4405
+ function polylineGapPlacement(points, totalLength, clearance) {
4406
+ const half = totalLength / 2;
4407
+ const midpoint = pointAtArcLength(points, half);
4408
+ const start = { x: points[midpoint.segmentIndex], y: points[midpoint.segmentIndex + 1] };
4409
+ const end = { x: points[midpoint.segmentIndex + 2], y: points[midpoint.segmentIndex + 3] };
4410
+ const gapHalfLength = projectedPolylineGap(start, end, clearance, half);
4411
+ return { beforeLength: half - gapHalfLength, afterLength: half + gapHalfLength };
4412
+ }
4413
+ function projectedPolylineGap(start, end, clearance, halfLength) {
4414
+ const segmentLength = Math.hypot(end.x - start.x, end.y - start.y);
4415
+ const horizontalShare = segmentLength === 0 ? 1 : Math.abs(end.x - start.x) / segmentLength;
4416
+ const verticalShare = segmentLength === 0 ? 0 : Math.abs(end.y - start.y) / segmentLength;
4417
+ const projectedClearance = clearance.halfWidth * horizontalShare + clearance.halfHeight * verticalShare;
4418
+ return Math.min(projectedClearance, Math.max(0, halfLength - clearance.minimumVisibleStub));
4419
+ }
4420
+ function polylineBeforeGap(points, splitLength) {
4421
+ if (splitLength <= 0.5) {
4422
+ return null;
4423
+ }
4424
+ const split = pointAtArcLength(points, splitLength);
4425
+ return [...points.slice(0, split.segmentIndex + 2), split.point.x, split.point.y];
4426
+ }
4427
+ function polylineAfterGap(points, splitLength, totalLength) {
4428
+ if (splitLength >= totalLength - 0.5) {
4429
+ return null;
4430
+ }
4431
+ const split = pointAtArcLength(points, splitLength);
4432
+ return [split.point.x, split.point.y, ...points.slice(split.segmentIndex + 2)];
4433
+ }
4434
+
4435
+ // src/renderScene/artifacts/link/geometry/label/gap.ts
4436
+ var LABEL_GAP_PADDING = 3;
4437
+ var MIN_VISIBLE_STUB = 10;
4438
+ function bezierPoints(points) {
4439
+ return [
4440
+ { x: points[0], y: points[1] },
4441
+ { x: points[2], y: points[3] },
4442
+ { x: points[4], y: points[5] },
4443
+ { x: points[6], y: points[7] }
4444
+ ];
4445
+ }
4446
+ function pathLength(points, bezier) {
4447
+ if (bezier) {
4448
+ return bezierArcLength(...bezierPoints(points));
4449
+ }
4450
+ return polylineLength(points);
4451
+ }
4452
+ function splitPathAroundLabel(points, pathType, gapWidth, gapHeight) {
4453
+ const clearance = {
4454
+ halfWidth: (gapWidth + LABEL_GAP_PADDING) / 2,
4455
+ halfHeight: (gapHeight + LABEL_GAP_PADDING) / 2,
4456
+ minimumVisibleStub: MIN_VISIBLE_STUB
4457
+ };
4458
+ if (pathType === "curved") {
4459
+ return splitCurvedPath(points, clearance);
4460
+ }
4461
+ return splitPolylinePath(points, clearance);
4462
+ }
4463
+ function labelGapDash(points, pathType, gapWidth, gapHeight) {
4464
+ const split = splitPathAroundLabel(points, pathType, gapWidth, gapHeight);
4465
+ if (!split || !split.before && !split.after) {
4466
+ return null;
4467
+ }
4468
+ const totalLength = pathLength(points, split.bezier);
4469
+ const dashBefore = split.before ? pathLength(split.before, split.bezier) : 0;
4470
+ const afterLength = split.after ? pathLength(split.after, split.bezier) : 0;
4471
+ const gapLength = Math.max(0, totalLength - dashBefore - afterLength);
4472
+ if (gapLength <= 0) {
4473
+ return null;
4474
+ }
4475
+ return { dashBefore, gapLength };
4476
+ }
4477
+
4478
+ // src/renderScene/artifacts/link/geometry/markers/midpoint.ts
4479
+ function curvedMidpoint(points) {
4480
+ const [x0, y0, x1, y1, x2, y2, x3, y3] = points;
4481
+ const t = 0.5;
4482
+ const mt = 1 - t;
4483
+ return {
4484
+ x: mt * mt * mt * x0 + 3 * mt * mt * t * x1 + 3 * mt * t * t * x2 + t * t * t * x3,
4485
+ y: mt * mt * mt * y0 + 3 * mt * mt * t * y1 + 3 * mt * t * t * y2 + t * t * t * y3
4486
+ };
4487
+ }
4488
+ function orthogonalMidpoint(points, fallback) {
4489
+ let totalLength = 0;
4490
+ const segments = [];
4491
+ for (let index = 0; index < points.length - 2; index += 2) {
4492
+ const start = { x: points[index], y: points[index + 1] };
4493
+ const end = { x: points[index + 2], y: points[index + 3] };
4494
+ const length = Math.sqrt((end.x - start.x) ** 2 + (end.y - start.y) ** 2);
4495
+ totalLength += length;
4496
+ segments.push({ start, end, length });
4497
+ }
4498
+ const halfLength = totalLength / 2;
4499
+ let accumulatedLength = 0;
4500
+ for (const segment of segments) {
4501
+ if (accumulatedLength + segment.length >= halfLength) {
4502
+ const ratio = (halfLength - accumulatedLength) / segment.length;
4503
+ return {
4504
+ x: segment.start.x + ratio * (segment.end.x - segment.start.x),
4505
+ y: segment.start.y + ratio * (segment.end.y - segment.start.y)
4506
+ };
4507
+ }
4508
+ accumulatedLength += segment.length;
4509
+ }
4510
+ return fallback;
4511
+ }
4512
+ function getLineMidpoint(points, pathType, effectiveFrom, effectiveTo) {
4513
+ if (pathType === "curved") {
4514
+ return curvedMidpoint(points);
4515
+ }
4516
+ const fallback = {
4517
+ x: (effectiveFrom.x + effectiveTo.x) / 2,
4518
+ y: (effectiveFrom.y + effectiveTo.y) / 2
4519
+ };
4520
+ if (pathType === "orthogonal") {
4521
+ return orthogonalMidpoint(points, fallback);
4522
+ }
4523
+ return fallback;
4524
+ }
4525
+
4526
+ // src/renderScene/artifacts/shape/geometry/cloud.ts
4527
+ var CLOUD_LOBES = [
4528
+ [30, 64, 24],
4529
+ [52, 40, 22],
4530
+ [86, 32, 28],
4531
+ [118, 42, 24],
4532
+ [132, 64, 24]
4533
+ ];
4534
+ var QUARTER_TURN = Math.PI / 2;
4535
+ var FULL_TURN = 4 * QUARTER_TURN;
4536
+ var BOTTOM_ANGLE = Math.PI / 2;
4537
+ var EPSILON = 1e-9;
4538
+ function pointOnLobe([centerX, centerY, radius], angle) {
4539
+ return [centerX + radius * Math.cos(angle), centerY + radius * Math.sin(angle)];
4540
+ }
4541
+ function angleOnLobe([centerX, centerY], point) {
4542
+ return Math.atan2(point[1] - centerY, point[0] - centerX);
4543
+ }
4544
+ function outerCusp(first, second, centroid) {
4545
+ const dx = second[0] - first[0];
4546
+ const dy = second[1] - first[1];
4547
+ const distance2 = Math.hypot(dx, dy);
4548
+ const along = (first[2] ** 2 - second[2] ** 2 + distance2 ** 2) / (2 * distance2);
4549
+ const across = Math.sqrt(Math.max(0, first[2] ** 2 - along ** 2));
4550
+ const footX = first[0] + along * dx / distance2;
4551
+ const footY = first[1] + along * dy / distance2;
4552
+ const candidates = [
4553
+ [footX - across * dy / distance2, footY + across * dx / distance2],
4554
+ [footX + across * dy / distance2, footY - across * dx / distance2]
4555
+ ];
4556
+ const fromCentroid = (point) => Math.hypot(point[0] - centroid[0], point[1] - centroid[1]);
4557
+ return fromCentroid(candidates[0]) > fromCentroid(candidates[1]) ? candidates[0] : candidates[1];
4558
+ }
4559
+ function lobeArcs(lobes) {
4560
+ const centroid = [
4561
+ lobes.reduce((sum, lobe) => sum + lobe[0], 0) / lobes.length,
4562
+ lobes.reduce((sum, lobe) => sum + lobe[1], 0) / lobes.length
4563
+ ];
4564
+ return lobes.map((lobe, index) => {
4565
+ const from = index === 0 ? BOTTOM_ANGLE : angleOnLobe(lobe, outerCusp(lobes[index - 1], lobe, centroid));
4566
+ const isLast = index === lobes.length - 1;
4567
+ let to = isLast ? BOTTOM_ANGLE : angleOnLobe(lobe, outerCusp(lobe, lobes[index + 1], centroid));
4568
+ while (to <= from) {
4569
+ to += FULL_TURN;
4570
+ }
4571
+ return { lobe, from, to };
4572
+ });
4573
+ }
4574
+ function quarterStops(from, to) {
4575
+ const stops = [];
4576
+ for (let turn = Math.floor(from / QUARTER_TURN) + 1; turn * QUARTER_TURN < to - EPSILON; turn++) {
4577
+ stops.push(turn * QUARTER_TURN);
4578
+ }
4579
+ return [...stops, to];
4580
+ }
4581
+ function arcCurves({ lobe, from, to }) {
4582
+ const start = pointOnLobe(lobe, from);
4583
+ const curves = [];
4584
+ let angle = from;
4585
+ for (const stop of quarterStops(from, to)) {
4586
+ const reach = 4 / 3 * Math.tan((stop - angle) / 4) * lobe[2];
4587
+ const begin = pointOnLobe(lobe, angle);
4588
+ const end = pointOnLobe(lobe, stop);
4589
+ curves.push([
4590
+ [begin[0] - reach * Math.sin(angle), begin[1] + reach * Math.cos(angle)],
4591
+ [end[0] + reach * Math.sin(stop), end[1] - reach * Math.cos(stop)],
4592
+ end
4593
+ ]);
4594
+ angle = stop;
4595
+ }
4596
+ return { start, curves };
4597
+ }
4598
+ function normalizedCloudOutline() {
4599
+ const arcs = lobeArcs(CLOUD_LOBES).map(arcCurves);
4600
+ const start = arcs[0].start;
4601
+ const curves = arcs.flatMap((arc) => arc.curves);
4602
+ const anchors = [start, ...curves.map((curve) => curve[2])];
4603
+ const minX = Math.min(...anchors.map((point) => point[0]));
4604
+ const maxX = Math.max(...anchors.map((point) => point[0]));
4605
+ const minY = Math.min(...anchors.map((point) => point[1]));
4606
+ const maxY = Math.max(...anchors.map((point) => point[1]));
4607
+ const unit = (point) => [(point[0] - minX) / (maxX - minX), (point[1] - minY) / (maxY - minY)];
4608
+ return {
4609
+ start: unit(start),
4610
+ curves: curves.map(([first, second, end]) => [unit(first), unit(second), unit(end)])
4611
+ };
4612
+ }
4613
+ var CLOUD_OUTLINE = normalizedCloudOutline();
4614
+ function cloudPath(x, y, width, height) {
4615
+ const at = ([u, v]) => `${x + u * width} ${y + v * height}`;
4616
+ const curves = CLOUD_OUTLINE.curves.map(([first, second, end]) => `C ${at(first)} ${at(second)} ${at(end)}`);
4617
+ return `M ${at(CLOUD_OUTLINE.start)} ${curves.join(" ")} Z`;
4618
+ }
4619
+
4620
+ // src/renderScene/artifacts/shape/geometry/pathCalculation.ts
4621
+ var polygonPath = (points) => points.length < 4 ? "" : `M ${points[0]} ${points[1]} ${points.slice(2).reduce((s, n, i) => s + (i % 2 ? ` ${n}` : ` L ${n}`), "")} Z`;
4622
+ var DOCUMENT_WAVE_AMPLITUDE = 0.1;
4623
+ var QUARTER_SINE_SLOPE = Math.PI / 6;
4624
+ var NOTE_FOLD_RATIO = 0.25;
4625
+ var PREDEFINED_PROCESS_BAR_RATIO = 0.15;
4626
+ function pillCornerRadius(width, height) {
4627
+ return Math.min(width, height) / 2;
4628
+ }
4629
+ function localShapePath(type, x, y, width, height) {
4630
+ const p = (...coords) => polygonPath(coords);
4631
+ switch (type) {
4632
+ case "diamond":
4633
+ return p(x + width / 2, y, x + width, y + height / 2, x + width / 2, y + height, x, y + height / 2);
4634
+ case "triangle":
4635
+ return p(x + width / 2, y, x, y + height, x + width, y + height);
4636
+ case "parallelogram":
4637
+ return p(x + width * 0.15, y, x + width, y, x + width * 0.85, y + height, x, y + height);
4638
+ case "trapezoid":
4639
+ return p(x + width * 0.2, y, x + width * 0.8, y, x + width, y + height, x, y + height);
4640
+ case "hexagon": {
4641
+ const cut = Math.min(width, height) / 4;
4642
+ return p(
4643
+ x + cut,
4644
+ y,
4645
+ x + width - cut,
4646
+ y,
4647
+ x + width,
4648
+ y + height / 2,
4649
+ x + width - cut,
4650
+ y + height,
4651
+ x + cut,
4652
+ y + height,
4653
+ x,
4654
+ y + height / 2
4655
+ );
4656
+ }
4657
+ case "document": {
4658
+ const amplitude = height * DOCUMENT_WAVE_AMPLITUDE;
4659
+ const mid = y + height - amplitude;
4660
+ const quarter = width / 4;
4661
+ const slope = amplitude * QUARTER_SINE_SLOPE;
4662
+ return `M ${x} ${y} L ${x + width} ${y} L ${x + width} ${mid} C ${x + width - quarter / 3} ${mid - slope} ${x + width - quarter * 2 / 3} ${mid - amplitude} ${x + width - quarter} ${mid - amplitude} C ${x + width - quarter - quarter / 3} ${mid - amplitude} ${x + width / 2 + quarter / 3} ${mid - slope} ${x + width / 2} ${mid} C ${x + width / 2 - quarter / 3} ${mid + slope} ${x + width / 2 - quarter * 2 / 3} ${mid + amplitude} ${x + quarter} ${mid + amplitude} C ${x + quarter - quarter / 3} ${mid + amplitude} ${x + quarter / 3} ${mid + slope} ${x} ${mid} Z`;
4663
+ }
4664
+ case "note": {
4665
+ const fold = Math.min(width, height) * NOTE_FOLD_RATIO;
4666
+ return `M ${x} ${y} L ${x + width - fold} ${y} L ${x + width} ${y + fold} L ${x + width} ${y + height} L ${x} ${y + height} Z M ${x + width} ${y + fold} L ${x + width - fold} ${y + fold} L ${x + width - fold} ${y}`;
4667
+ }
4668
+ case "cloud":
4669
+ return cloudPath(x, y, width, height);
4670
+ case "cylinder":
4671
+ return cylinderPath(x, y, width, height);
4672
+ case "person":
4673
+ return personPath(x, y, width, height);
4674
+ case "predefined_process": {
4675
+ const bar = Math.min(width, height) * PREDEFINED_PROCESS_BAR_RATIO;
4676
+ return `M ${x} ${y} L ${x + width} ${y} L ${x + width} ${y + height} L ${x} ${y + height} Z M ${x + bar} ${y} L ${x + bar} ${y + height} M ${x + width - bar} ${y} L ${x + width - bar} ${y + height}`;
4677
+ }
4678
+ default:
4679
+ return null;
4680
+ }
4681
+ }
4682
+
4683
+ export { AXIS_COS, AXIS_SIN, BAR_LABEL, CATEGORY_LABEL_ROW, CHART_COLORS, CHART_TITLE_PROP, CHART_TYPES, CORNER_RADIUS_ATTR, DEFAULT_FONT, DEFAULT_HEATMAP_ZONES, DEFAULT_HEX_RADIUS, DEFAULT_ZONE_FILL, DOM_CONTRACT_VERSION, EMPTY_BOUNDS, ISOMETRIC_LABEL_OFFSET_ATTR, ISOMETRIC_SIZE_ATTR, LABEL_WIDTH, LEADER_INSET, PATH_CLOSED_ATTR, PATH_POINTS_ATTR, PATH_POINT_INDEX_ATTR, PATH_TENSION_ATTR, SQUARE_CORNER_ATTR, SYMBOL_BOUNDS_ATTR, SYMBOL_POINTS_ATTR, SYMBOL_TENSION_ATTR, VISIBLE_ISOMETRIC_FACES, angleForAxisValue, axisBands, axisLabelProp, barCategoryCount, barLabelSize, barPath, barShapePath, bottomRoundedRectPath, boxBounds, calculateLinkRoute, canonicalizeSource, categoryProp, chartText, chartTicks, clipSegmentToRect, colorWithAlpha, computeBoxplotStats, contrastTextColor, cornersRoundedRectPath, createPieGeometry, createPieSlicePath, createVerticalErrorWhiskerNode, deriveFaceFills, edgePointToward, elementCenter, errorBarRange, estimateTextWidth, finite, formatBarLabel, formatFieldValue, freeformPathData, gapInsetAngle, getLineMidpoint, heuristicRichMeasure, horizontalRows, interpolateHexColorStops, isChartObject, isHorizontalBar, isIsometricObject, isLightColor, isPathObject, isSwimlaneObject, isometricBounds, isometricFaceCenter, isometricFaces, isometricTopFaceCenter, labelGapDash, leftRoundedRectPath, linePathData, localShapePath, mapChartValue, maxApexCornerRadius, maxPieCornerRadius, paint, parseSource, personLabelArea, pieApexRadius, pieArcPoint, pieUsesInnerArc, pillCornerRadius, pillPath, pointAtNormalizedPort, pointAtPort, radarCategoryCount, radarPointAt, radarRadius, radarRadiusForValue, radarSeriesPath, radarValueAtPoint, radarVertexPoint, relativeOffsetOnRectangleSide, renderPieLabels, representativeColor, resolveChartShadow, resolvePieRadii, rightRoundedRectPath, rotatedBounds, roundedBarPath, roundedOrthogonalPathData, roundedPolygonPath, rowTop, ruleAtProp, ruleTextProp, sameHueContrastColor, sameHueTint, segmentedBarPath, seriesColorProp, seriesLabelOffsetXProp, seriesLabelOffsetYProp, seriesLabelProp, seriesValueColorProp, seriesValueProp, sflToCanvasState, stackRank, stackSequence, stackedSegment, tag, tokenizeSource, topRoundedRectPath, wrapText, zoneColorAtValue };
4684
+ //# sourceMappingURL=chunk-6JKNT2YC.js.map
4685
+ //# sourceMappingURL=chunk-6JKNT2YC.js.map