@diagrammo/dgmo 0.92.1 → 0.94.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 (174) hide show
  1. package/dist/advanced.d.ts +103 -38
  2. package/dist/advanced.js +457 -423
  3. package/dist/block.d.ts +2 -2
  4. package/dist/block.js +75 -70
  5. package/dist/{chart-meta-BBqLnv5q.d.ts → chart-meta-BbjsRY7o.d.ts} +2 -2
  6. package/dist/chart-meta.d.ts +3 -3
  7. package/dist/chart-meta.js +25 -19
  8. package/dist/{chart-types-CbXlZFLd.d.ts → chart-types-Cppnp31G.d.ts} +173 -4
  9. package/dist/{charts-d3-QUUDVETP.js → charts-d3-SZV2SFTM.js} +7 -6
  10. package/dist/{chunk-ADNIO3XY.js → chunk-2FDQEQID.js} +5 -5
  11. package/dist/{chunk-QUGC7GWB.js → chunk-2HSHWPTO.js} +15 -15
  12. package/dist/{chunk-L5NW3PVL.js → chunk-2I4UQG7W.js} +1 -1
  13. package/dist/{chunk-2YAFDND3.js → chunk-2MS623GW.js} +4 -6
  14. package/dist/{chunk-73EYNV4O.js → chunk-2XMTZ5JR.js} +4 -6
  15. package/dist/{chunk-ZJAK6C7Y.js → chunk-3PP746YM.js} +6 -1
  16. package/dist/{chunk-MDVJYRJT.js → chunk-3TWBMU7K.js} +22 -22
  17. package/dist/{chunk-FTUMRSGT.js → chunk-3ZP3QC47.js} +8 -288
  18. package/dist/{chunk-J2E7EUER.js → chunk-4EX4OKGZ.js} +86 -23
  19. package/dist/{chunk-Y2V5AEVG.js → chunk-4SP6OUO7.js} +1952 -1824
  20. package/dist/{chunk-HIWHIYLD.js → chunk-4TEPDQHJ.js} +19 -19
  21. package/dist/{chunk-DEFBUZWV.js → chunk-5HAYT43L.js} +70 -54
  22. package/dist/{chunk-6WVX4KNP.js → chunk-5ZUZZDW5.js} +97 -297
  23. package/dist/{chunk-X5BUX2GK.js → chunk-6JCTW74W.js} +7 -5
  24. package/dist/{chunk-Q6W2K33N.js → chunk-6XLMTAEX.js} +1 -1
  25. package/dist/{chunk-ONYO5WJW.js → chunk-7IOZ6JE4.js} +21 -21
  26. package/dist/chunk-7SXV7NUS.js +416 -0
  27. package/dist/{chunk-NPWVOFB3.js → chunk-7WEYXYLC.js} +1 -1
  28. package/dist/{chunk-GE7ICBX6.js → chunk-AM42TLSZ.js} +102 -97
  29. package/dist/{chunk-WLLO3PZ3.js → chunk-AYUSD4Z6.js} +45 -19
  30. package/dist/{chunk-45QUTYPD.js → chunk-B25IWGDH.js} +69 -20
  31. package/dist/{chunk-JKST4RTT.js → chunk-BH2SF4TT.js} +6 -6
  32. package/dist/{chunk-6OIMUSDX.js → chunk-C6G36F5S.js} +9 -9
  33. package/dist/{chunk-DRFGIOZ6.js → chunk-CIHHTS5B.js} +4 -6
  34. package/dist/chunk-CYTFO2LF.js +209 -0
  35. package/dist/{chunk-Q2UFOKPB.js → chunk-DDIOWNHS.js} +22 -22
  36. package/dist/{chunk-Q6ZQX2HX.js → chunk-DLJJOCIT.js} +4 -6
  37. package/dist/{chunk-26BBJUBW.js → chunk-DNM3XZCK.js} +4 -6
  38. package/dist/{chunk-AUS7V54P.js → chunk-ERT7XDQW.js} +1 -1
  39. package/dist/{chunk-7ENDOHAA.js → chunk-FMAVHTYB.js} +919 -927
  40. package/dist/{chunk-URQIWIKX.js → chunk-FWVE373X.js} +4 -4
  41. package/dist/{chunk-VOR32OHN.js → chunk-G3HZ4PGE.js} +13 -15
  42. package/dist/{chunk-RBFJT23E.js → chunk-GWQRTA5W.js} +6 -6
  43. package/dist/chunk-GXCDD6NG.js +24 -0
  44. package/dist/{chunk-3PFMGNOM.js → chunk-HHVIE45A.js} +26 -11
  45. package/dist/{chunk-MXAP37W6.js → chunk-HI6R27YA.js} +16 -7
  46. package/dist/{chunk-QQPNCXAO.js → chunk-I37S3EZE.js} +18 -18
  47. package/dist/{chunk-YITHYQCL.js → chunk-IGJEKO2P.js} +8 -8
  48. package/dist/{chunk-BZX7QCXJ.js → chunk-IHN6WDEY.js} +15 -15
  49. package/dist/{chunk-QT3KDDXK.js → chunk-IN7XI6NO.js} +0 -7
  50. package/dist/{chunk-4QLGGQNO.js → chunk-IZHGT5HD.js} +44 -18
  51. package/dist/{chunk-5SSTVFPX.js → chunk-JT2BG7LM.js} +5 -7
  52. package/dist/{chunk-2BJW7FSF.js → chunk-KBEWVCK3.js} +1 -1
  53. package/dist/{chunk-DUPZ2BBY.js → chunk-KBHT5XEY.js} +143 -6
  54. package/dist/{chunk-V4D2ZQBE.js → chunk-KRPLP5LP.js} +6 -1
  55. package/dist/{chunk-QKY6W4NA.js → chunk-KSZB7TIY.js} +25 -25
  56. package/dist/{chunk-6ZL2XYM7.js → chunk-MLY5ALOV.js} +6 -6
  57. package/dist/{chunk-VSOZZYLK.js → chunk-NERQSSYD.js} +20 -9
  58. package/dist/{chunk-OQW4ETJX.js → chunk-NM5V5EKY.js} +6 -1
  59. package/dist/{chunk-EBSFSOJC.js → chunk-NOQB4KMV.js} +22 -22
  60. package/dist/{chunk-6BNNLVAR.js → chunk-NPDVWFAQ.js} +6 -1
  61. package/dist/{chunk-FSJ3ZA6N.js → chunk-OSSTOSXU.js} +12 -12
  62. package/dist/chunk-P7TRFL4P.js +54 -0
  63. package/dist/{chunk-A4PNWAUW.js → chunk-PGSGJLUR.js} +6 -1
  64. package/dist/{chunk-S7W75MIM.js → chunk-QKPOIJBT.js} +23 -5
  65. package/dist/{chunk-YO6AQU4A.js → chunk-QPQWY5RU.js} +12 -12
  66. package/dist/{chunk-NREU4JFE.js → chunk-RBVFNUAL.js} +6 -8
  67. package/dist/{chunk-5EBYPETE.js → chunk-RESEJSCV.js} +3 -5
  68. package/dist/{chunk-BRSM6Z34.js → chunk-RITTLIDJ.js} +14 -14
  69. package/dist/{chunk-MX4SZLSV.js → chunk-S5Y3OO3V.js} +16 -8
  70. package/dist/{chunk-O5WUZGPV.js → chunk-TASRGDYP.js} +18 -18
  71. package/dist/{chunk-HXULJZA2.js → chunk-TDAGJJPZ.js} +6 -1
  72. package/dist/chunk-UCHZJJIT.js +421 -0
  73. package/dist/{chunk-42UJ23H3.js → chunk-UIEKW4SG.js} +1 -0
  74. package/dist/{chunk-WFA6IULY.js → chunk-URRAWU5D.js} +5 -0
  75. package/dist/{chunk-IGKFOHBG.js → chunk-VBMRZCMQ.js} +13 -13
  76. package/dist/{chunk-JRWUHLRP.js → chunk-VDY2GR6U.js} +51 -8
  77. package/dist/{chunk-EJS3K44P.js → chunk-VGAMBOOC.js} +5 -3
  78. package/dist/{chunk-B5PEKSRH.js → chunk-VSAKEMDD.js} +6 -1
  79. package/dist/chunk-WTTIODDV.js +9 -0
  80. package/dist/{chunk-JLU52ZDT.js → chunk-XJQ6Z3FO.js} +10 -10
  81. package/dist/{chunk-KTGZDMDX.js → chunk-XKCRKD2B.js} +140 -53
  82. package/dist/{chunk-LRP5FXST.js → chunk-YQMIKX54.js} +61 -24
  83. package/dist/{chunk-H5UEVW6Q.js → chunk-ZD3QCNB7.js} +1 -1
  84. package/dist/clock.js +168 -0
  85. package/dist/completion-registry.js +3 -1
  86. package/dist/countdown.js +36 -395
  87. package/dist/{dgmo-router-4UAPPIKN.js → dgmo-router-E5O7RJSW.js} +50 -43
  88. package/dist/{dimensions-SCNVYLRF.js → dimensions-DS6FWLRY.js} +3 -3
  89. package/dist/{d3-BThYBTQk.d.ts → export-context-Ceh3wr0E.d.ts} +2 -8
  90. package/dist/{flowchart-parser-NA75YQ77.js → flowchart-parser-NZXASOZG.js} +8 -3
  91. package/dist/{flowchart-renderer-6MGGX4RC.js → flowchart-renderer-PTCRIANF.js} +17 -19
  92. package/dist/index.d.ts +8 -7
  93. package/dist/index.js +80 -74
  94. package/dist/{layout-MBVBAMPA.js → layout-3TSVAGWH.js} +2 -2
  95. package/dist/layout-ISSBWBH7.js +24 -0
  96. package/dist/layout-IT4ZTV6C.js +24 -0
  97. package/dist/{layout-BNLFQHSB.js → layout-U3AH6M7J.js} +9 -10
  98. package/dist/layout-VC3MXRB4.js +29 -0
  99. package/dist/layout-VDCJOZSH.js +11 -0
  100. package/dist/{layout-search-PMYL3VWS.js → layout-search-BBX2BMEF.js} +11 -12
  101. package/dist/{palettes-IN7ZFRJM.js → palettes-4Y7CL2QL.js} +4 -2
  102. package/dist/{parser-QYISOAGR.js → parser-4RFY7D52.js} +8 -3
  103. package/dist/{parser-3MK56JZC.js → parser-4Y6PODW6.js} +2 -2
  104. package/dist/{parser-GX5YLRIP.js → parser-7QKQ4P4P.js} +8 -3
  105. package/dist/{parser-2NWH5JUS.js → parser-A3AN76RQ.js} +2 -1
  106. package/dist/{parser-LCJ76UXS.js → parser-BT4NI6JY.js} +5 -1
  107. package/dist/{parser-FXDS4JDK.js → parser-F7NDRPMA.js} +2 -2
  108. package/dist/{parser-BES6LV3G.js → parser-GDLKUBTO.js} +5 -1
  109. package/dist/{parser-SEUTXN6U.js → parser-KJPPUN4D.js} +2 -2
  110. package/dist/{parser-SIKL4PLF.js → parser-KMKVFET2.js} +5 -1
  111. package/dist/{parser-5LW5JDYG.js → parser-NL73FSFI.js} +5 -1
  112. package/dist/{parser-VA7ZQQTA.js → parser-NUU3DVW4.js} +5 -1
  113. package/dist/{parser-XZUKNZPB.js → parser-R3LPEQIT.js} +7 -1
  114. package/dist/{parser-X23GRTQJ.js → parser-RCIEKN7Y.js} +5 -1
  115. package/dist/{parser-L6RWDXQN.js → parser-TAYRTCPR.js} +8 -3
  116. package/dist/redraw-AHW7EUUF.js +1698 -0
  117. package/dist/redraw-STZ4WJAI.js +73 -0
  118. package/dist/{renderer-LM4ICK6I.js → renderer-2LYX6NHN.js} +4 -5
  119. package/dist/{renderer-NHTR7IRR.js → renderer-3NLDX7DQ.js} +4 -5
  120. package/dist/{renderer-TO2NVJP3.js → renderer-4SE7R22T.js} +50 -45
  121. package/dist/{renderer-3IHQI4A4.js → renderer-53B523PM.js} +11 -10
  122. package/dist/{renderer-3JY6JAQJ.js → renderer-7OGGV327.js} +5 -6
  123. package/dist/{renderer-2FVUPBGV.js → renderer-AM7P4LN7.js} +1 -1
  124. package/dist/{renderer-5VKPB4LL.js → renderer-BXD5KVSR.js} +5 -5
  125. package/dist/{renderer-OAR5VPH4.js → renderer-DAZWHNLI.js} +2 -2
  126. package/dist/{renderer-WIU5BJUK.js → renderer-DDWDCM2K.js} +2 -2
  127. package/dist/{renderer-3QAFDXEY.js → renderer-FMTLSOCX.js} +16 -12
  128. package/dist/{renderer-JEJT3YET.js → renderer-HX6ICPDX.js} +2 -3
  129. package/dist/{renderer-NDA2FFYB.js → renderer-ISQJ2ELA.js} +5 -5
  130. package/dist/{renderer-XGG2UNXT.js → renderer-IXBOT6DU.js} +3 -4
  131. package/dist/{renderer-S3YCLJKL.js → renderer-J3GPDK73.js} +20 -22
  132. package/dist/{renderer-DVNMCYHZ.js → renderer-J7ZSTJUE.js} +15 -15
  133. package/dist/{renderer-ALWVK3EJ.js → renderer-JJ4QW3SK.js} +6 -6
  134. package/dist/{renderer-RUSFL7WU.js → renderer-JYHA6R2D.js} +8 -7
  135. package/dist/{renderer-5TZVFQ7K.js → renderer-KJ3V5BYP.js} +7 -8
  136. package/dist/{renderer-RSK6QFWN.js → renderer-LNP23NSM.js} +7 -9
  137. package/dist/{renderer-ZHGHTSNG.js → renderer-M5DUMITP.js} +13 -12
  138. package/dist/{renderer-O6DY46AV.js → renderer-MWZC7TMD.js} +7 -7
  139. package/dist/{renderer-DC7C5Q3G.js → renderer-NOYLNVTI.js} +2 -2
  140. package/dist/{renderer-ISSDGK54.js → renderer-O6RP52CQ.js} +3 -3
  141. package/dist/{renderer-ZGY7P4BM.js → renderer-OUDHH5MV.js} +19 -22
  142. package/dist/{renderer-QGIHLZOJ.js → renderer-PHLCYVSN.js} +9 -10
  143. package/dist/{renderer-VPIELEE5.js → renderer-QTPWRRJ7.js} +8 -9
  144. package/dist/{renderer-LQP3AGEJ.js → renderer-RUSQJFFC.js} +3 -4
  145. package/dist/{renderer-MM27BSBO.js → renderer-TINHO2IO.js} +3 -4
  146. package/dist/{renderer-7ZZG5HR3.js → renderer-UTEEJDGB.js} +9 -9
  147. package/dist/{renderer-J5FKF423.js → renderer-W4MUMXJE.js} +7 -4
  148. package/dist/{renderer-XO57LTU4.js → renderer-WPN2SXGT.js} +5 -5
  149. package/dist/{renderer-ZZ6MWVB7.js → renderer-XCLARXCK.js} +4 -4
  150. package/dist/{renderer-J666LZXJ.js → renderer-XOWDERU2.js} +8 -8
  151. package/dist/{state-parser-TIR2ORJY.js → state-parser-KPVW5CRI.js} +8 -3
  152. package/dist/{state-renderer-G6SUXU4J.js → state-renderer-ZVEI265C.js} +18 -21
  153. package/dist/{tag-groups-DsoVpAY-.d.ts → tag-groups-DydewK3D.d.ts} +2 -173
  154. package/dist/{themes-DRNjxX7Q.d.ts → themes-DzIH3Hjv.d.ts} +3 -2
  155. package/docs/ai-integration.md +1 -1
  156. package/docs/language-reference.md +23 -2
  157. package/gallery/fixtures/whiteboard.dgmo +3 -0
  158. package/package.json +2 -6
  159. package/dist/chunk-3Y63RFJL.js +0 -75
  160. package/dist/chunk-5DH3KYFH.js +0 -20
  161. package/dist/chunk-O753JUEZ.js +0 -67
  162. package/dist/chunk-RJNZHFNG.js +0 -51
  163. package/dist/chunk-Z6DXWULM.js +0 -23
  164. package/dist/layout-5USEVQBY.js +0 -25
  165. package/dist/layout-J3AGFZZ7.js +0 -10
  166. package/dist/layout-S3RLVPH4.js +0 -25
  167. package/dist/layout-WRG4APSJ.js +0 -32
  168. package/dist/{chunk-DGUGE67L.js → chunk-CDYV46JE.js} +3 -3
  169. package/dist/{chunk-TKAB3FTK.js → chunk-DRQSB72S.js} +11 -11
  170. package/dist/{chunk-ZKP77MB3.js → chunk-FVTYJUXR.js} +5 -5
  171. package/dist/{chunk-MA42PIGJ.js → chunk-H5NVCOEJ.js} +4 -4
  172. package/dist/{chunk-BEA4D7OS.js → chunk-IXHWS2HI.js} +3 -3
  173. package/dist/{chunk-SOHZDW6N.js → chunk-UXNA2H2V.js} +3 -3
  174. package/dist/{chunk-NO3UX6AP.js → chunk-V2NQV7VL.js} +4 -4
@@ -0,0 +1,416 @@
1
+ import {
2
+ normalizeName
3
+ } from "./chunk-NOTA6TEI.js";
4
+ import {
5
+ NOTE_GAP,
6
+ noteBoxSize,
7
+ noteConnectorPoints
8
+ } from "./chunk-VDY2GR6U.js";
9
+ import {
10
+ extractColor,
11
+ measureIndent
12
+ } from "./chunk-TIIZIAGS.js";
13
+ import {
14
+ makeDgmoError,
15
+ suggest
16
+ } from "./chunk-BYU54C3S.js";
17
+
18
+ // src/utils/notes/parse.ts
19
+ function parseNoteHeader(rest) {
20
+ const t = rest.trim();
21
+ const quoted = t.match(/^"([^"]+)"\s*(.*)$/);
22
+ if (quoted) {
23
+ return { ref: quoted[1].trim(), inlineBody: quoted[2].trim() };
24
+ }
25
+ const m = t.match(/^(\S+)\s*(.*)$/);
26
+ if (m) {
27
+ return { ref: m[1], inlineBody: m[2].trim() };
28
+ }
29
+ return { ref: t, inlineBody: "" };
30
+ }
31
+ function collectNoteBody(lines, noteIndex, noteIndent, inlineBody) {
32
+ const parts = [];
33
+ if (inlineBody) parts.push(inlineBody);
34
+ let lastIndex = noteIndex;
35
+ let endLineNumber = noteIndex + 1;
36
+ for (let j = noteIndex + 1; j < lines.length; j++) {
37
+ const raw = lines[j];
38
+ const trimmed = raw.trim();
39
+ if (!trimmed) break;
40
+ if (measureIndent(raw) <= noteIndent) break;
41
+ parts.push(trimmed);
42
+ lastIndex = j;
43
+ endLineNumber = j + 1;
44
+ }
45
+ return { body: parts.join("\n"), endLineNumber, lastIndex };
46
+ }
47
+ function tryCollectNote(lines, index, indent, palette, diagnostics) {
48
+ const trimmed = lines[index].trim();
49
+ const m = trimmed.match(/^note\s+(.+)$/i);
50
+ if (!m || /^note\s+->/i.test(trimmed)) return null;
51
+ const lineNumber = index + 1;
52
+ const { label: headerNoColor, color } = extractColor(
53
+ m[1],
54
+ palette,
55
+ diagnostics,
56
+ lineNumber
57
+ );
58
+ const { ref, inlineBody } = parseNoteHeader(headerNoColor);
59
+ const collected = collectNoteBody(lines, index, indent, inlineBody);
60
+ if (!collected.body.trim()) {
61
+ diagnostics.push(
62
+ makeDgmoError(
63
+ lineNumber,
64
+ `Note on "${ref}" has no text \u2014 ignored.`,
65
+ "warning"
66
+ )
67
+ );
68
+ return { lastIndex: collected.lastIndex };
69
+ }
70
+ return {
71
+ note: {
72
+ ref,
73
+ body: collected.body,
74
+ ...color && { color },
75
+ lineNumber,
76
+ endLineNumber: collected.endLineNumber
77
+ },
78
+ lastIndex: collected.lastIndex
79
+ };
80
+ }
81
+
82
+ // src/utils/notes/resolve.ts
83
+ function resolveNotes(notes, targets, diagnostics) {
84
+ const byNodeId = /* @__PURE__ */ new Map();
85
+ if (notes.length === 0) return byNodeId;
86
+ const byNormLabel = /* @__PURE__ */ new Map();
87
+ for (const t of targets) {
88
+ const key = normalizeName(t.label);
89
+ const arr = byNormLabel.get(key);
90
+ if (arr) arr.push(t);
91
+ else byNormLabel.set(key, [t]);
92
+ }
93
+ for (const note of notes) {
94
+ const matches = byNormLabel.get(normalizeName(note.ref));
95
+ if (!matches || matches.length === 0) {
96
+ if (diagnostics) {
97
+ const hint = suggest(
98
+ note.ref,
99
+ targets.map((t) => t.label)
100
+ );
101
+ diagnostics.push(
102
+ makeDgmoError(
103
+ note.lineNumber,
104
+ `Note references unknown node id "${note.ref}".${hint ? " " + hint : ""}`,
105
+ "error"
106
+ )
107
+ );
108
+ }
109
+ continue;
110
+ }
111
+ const target = matches[0];
112
+ if (matches.length > 1 && diagnostics) {
113
+ diagnostics.push(
114
+ makeDgmoError(
115
+ note.lineNumber,
116
+ `Note ref "${note.ref}" matches ${matches.length} nodes; attaching to the first.`,
117
+ "warning"
118
+ )
119
+ );
120
+ }
121
+ if (byNodeId.has(target.id)) {
122
+ if (diagnostics) {
123
+ diagnostics.push(
124
+ makeDgmoError(
125
+ note.lineNumber,
126
+ `Multiple notes on node "${target.label}"; keeping the first.`,
127
+ "warning"
128
+ )
129
+ );
130
+ }
131
+ continue;
132
+ }
133
+ byNodeId.set(target.id, note);
134
+ }
135
+ return byNodeId;
136
+ }
137
+
138
+ // src/utils/notes/place.ts
139
+ var NOTE_CLEAR = 14;
140
+ var intersects = (a, b, pad) => !(a.right + pad <= b.left || b.right + pad <= a.left || a.bottom + pad <= b.top || b.bottom + pad <= a.top);
141
+ var CONNECTOR_CLEAR = 4;
142
+ var EDGE_CLEAR = 6;
143
+ var SLIDE_STEP = 10;
144
+ var SEARCH_REACH = 480;
145
+ var segmentCrosses = (x1, y1, x2, y2, r) => {
146
+ const dx = x2 - x1;
147
+ const dy = y2 - y1;
148
+ const p = [-dx, dx, -dy, dy];
149
+ const q = [x1 - r.left, r.right - x1, y1 - r.top, r.bottom - y1];
150
+ let t0 = 0;
151
+ let t1 = 1;
152
+ for (let i = 0; i < 4; i++) {
153
+ const pi = p[i];
154
+ const qi = q[i];
155
+ if (pi === 0) {
156
+ if (qi <= 0) return false;
157
+ continue;
158
+ }
159
+ const t = qi / pi;
160
+ if (pi < 0) t0 = Math.max(t0, t);
161
+ else t1 = Math.min(t1, t);
162
+ }
163
+ return t0 < t1;
164
+ };
165
+ function placeNotes(obstacles, requests, direction, edges = []) {
166
+ const placements = /* @__PURE__ */ new Map();
167
+ const occupied = obstacles.map((p) => ({
168
+ left: p.x - p.width / 2,
169
+ top: p.y - p.height / 2,
170
+ right: p.x + p.width / 2,
171
+ bottom: p.y + p.height / 2
172
+ }));
173
+ for (const req of requests) {
174
+ if (req.collapsed) {
175
+ placements.set(req.key, {
176
+ collapsed: true,
177
+ side: "right",
178
+ x: 0,
179
+ y: 0
180
+ });
181
+ continue;
182
+ }
183
+ const p = req.node;
184
+ const cx = p.x;
185
+ const cy = p.y;
186
+ const nodeLeft = cx - p.width / 2;
187
+ const nodeRight = cx + p.width / 2;
188
+ const nodeTop = cy - p.height / 2;
189
+ const nodeBottom = cy + p.height / 2;
190
+ const { noteW, noteH } = req;
191
+ const rectFor = (side) => {
192
+ switch (side) {
193
+ case "right": {
194
+ const left = nodeRight + NOTE_GAP;
195
+ const top = cy - noteH / 2;
196
+ return { left, top, right: left + noteW, bottom: top + noteH };
197
+ }
198
+ case "left": {
199
+ const right = nodeLeft - NOTE_GAP;
200
+ const top = cy - noteH / 2;
201
+ return { left: right - noteW, top, right, bottom: top + noteH };
202
+ }
203
+ case "below": {
204
+ const left = cx - noteW / 2;
205
+ const top = nodeBottom + NOTE_GAP;
206
+ return { left, top, right: left + noteW, bottom: top + noteH };
207
+ }
208
+ case "above":
209
+ default: {
210
+ const left = cx - noteW / 2;
211
+ const bottom = nodeTop - NOTE_GAP;
212
+ return { left, top: bottom - noteH, right: left + noteW, bottom };
213
+ }
214
+ }
215
+ };
216
+ const order = direction === "LR" ? ["below", "above", "right", "left"] : ["right", "left", "below", "above"];
217
+ const anchor = {
218
+ left: nodeLeft,
219
+ top: nodeTop,
220
+ right: nodeRight,
221
+ bottom: nodeBottom
222
+ };
223
+ const isAnchor = (o) => o.left === anchor.left && o.top === anchor.top && o.right === anchor.right && o.bottom === anchor.bottom;
224
+ const connector = (rect, side) => {
225
+ const [x1, y1, x2, y2] = noteConnectorPoints(p, {
226
+ x: rect.left - cx,
227
+ y: rect.top - cy,
228
+ width: noteW,
229
+ height: noteH,
230
+ side
231
+ });
232
+ return [cx + x1, cy + y1, cx + x2, cy + y2];
233
+ };
234
+ const onEdge = (rect) => {
235
+ const padded = {
236
+ left: rect.left - EDGE_CLEAR,
237
+ top: rect.top - EDGE_CLEAR,
238
+ right: rect.right + EDGE_CLEAR,
239
+ bottom: rect.bottom + EDGE_CLEAR
240
+ };
241
+ return edges.some(
242
+ (path) => path.some(
243
+ (pt, i) => i > 0 && segmentCrosses(path[i - 1].x, path[i - 1].y, pt.x, pt.y, padded)
244
+ )
245
+ );
246
+ };
247
+ const isClear = (rect, side, avoidEdges) => {
248
+ if (occupied.some((o) => intersects(rect, o, NOTE_CLEAR))) return false;
249
+ if (avoidEdges && onEdge(rect)) return false;
250
+ const [x1, y1, x2, y2] = connector(rect, side);
251
+ return !occupied.some(
252
+ (o) => !isAnchor(o) && segmentCrosses(x1, y1, x2, y2, {
253
+ left: o.left - CONNECTOR_CLEAR,
254
+ top: o.top - CONNECTOR_CLEAR,
255
+ right: o.right + CONNECTOR_CLEAR,
256
+ bottom: o.bottom + CONNECTOR_CLEAR
257
+ })
258
+ );
259
+ };
260
+ const search = (avoidEdges) => {
261
+ let chosen2 = null;
262
+ for (const side of order.slice(0, 2)) {
263
+ const rect = rectFor(side);
264
+ if (isClear(rect, side, avoidEdges)) {
265
+ chosen2 = { rect, side };
266
+ break;
267
+ }
268
+ }
269
+ if (chosen2) return chosen2;
270
+ let bestLen = Infinity;
271
+ for (const side of order) {
272
+ const axisIsY = side === "above" || side === "below";
273
+ const outward = side === "below" || side === "right" ? 1 : -1;
274
+ const reach = axisIsY ? noteW / 2 : SEARCH_REACH;
275
+ const slides = [0];
276
+ for (let s = SLIDE_STEP; s <= reach; s += SLIDE_STEP) {
277
+ slides.push(s, -s);
278
+ }
279
+ const base = rectFor(side);
280
+ for (let out = 0; out <= SEARCH_REACH; out += NOTE_CLEAR) {
281
+ for (const slide of slides) {
282
+ const dx = axisIsY ? slide : out * outward;
283
+ const dy = axisIsY ? out * outward : slide;
284
+ const rect = {
285
+ left: base.left + dx,
286
+ top: base.top + dy,
287
+ right: base.right + dx,
288
+ bottom: base.bottom + dy
289
+ };
290
+ if (!isClear(rect, side, avoidEdges)) continue;
291
+ const [x1, y1, x2, y2] = connector(rect, side);
292
+ const len = Math.hypot(x2 - x1, y2 - y1);
293
+ if (len < bestLen) {
294
+ bestLen = len;
295
+ chosen2 = { rect, side };
296
+ }
297
+ }
298
+ }
299
+ }
300
+ return chosen2;
301
+ };
302
+ let chosen = search(true) ?? search(false);
303
+ if (!chosen) {
304
+ const side = order[0];
305
+ let rect = rectFor(side);
306
+ const axisIsY = side === "above" || side === "below";
307
+ const outward = side === "below" || side === "right" ? 1 : -1;
308
+ for (let guard = 0; guard < 50; guard++) {
309
+ const blockers = occupied.filter(
310
+ (o) => intersects(rect, o, NOTE_CLEAR)
311
+ );
312
+ if (blockers.length === 0) break;
313
+ if (axisIsY && outward > 0) {
314
+ const top = Math.max(...blockers.map((b) => b.bottom)) + NOTE_CLEAR;
315
+ rect = { ...rect, top, bottom: top + noteH };
316
+ } else if (axisIsY) {
317
+ const bottom = Math.min(...blockers.map((b) => b.top)) - NOTE_CLEAR;
318
+ rect = { ...rect, bottom, top: bottom - noteH };
319
+ } else if (outward > 0) {
320
+ const left = Math.max(...blockers.map((b) => b.right)) + NOTE_CLEAR;
321
+ rect = { ...rect, left, right: left + noteW };
322
+ } else {
323
+ const right = Math.min(...blockers.map((b) => b.left)) - NOTE_CLEAR;
324
+ rect = { ...rect, right, left: right - noteW };
325
+ }
326
+ }
327
+ chosen = { rect, side };
328
+ }
329
+ occupied.push(chosen.rect);
330
+ placements.set(req.key, {
331
+ collapsed: false,
332
+ side: chosen.side,
333
+ x: chosen.rect.left - p.x,
334
+ y: chosen.rect.top - p.y
335
+ });
336
+ }
337
+ return placements;
338
+ }
339
+
340
+ // src/utils/notes/build.ts
341
+ function buildPlacedNotes(anchors, noteByNode, direction, collapsedNotes) {
342
+ const placed = /* @__PURE__ */ new Map();
343
+ if (noteByNode.size === 0) return placed;
344
+ const geoms = /* @__PURE__ */ new Map();
345
+ const requests = [];
346
+ for (const a of anchors) {
347
+ const note = noteByNode.get(a.id);
348
+ if (!note) continue;
349
+ const size = noteBoxSize(note.body);
350
+ geoms.set(a.id, {
351
+ noteW: size.width,
352
+ noteH: size.height,
353
+ ...note.color && { color: note.color },
354
+ lines: size.lines,
355
+ lineNumber: note.lineNumber,
356
+ endLineNumber: note.endLineNumber
357
+ });
358
+ requests.push({
359
+ key: a.id,
360
+ node: { x: a.x, y: a.y, width: a.width, height: a.height },
361
+ noteW: size.width,
362
+ noteH: size.height,
363
+ collapsed: collapsedNotes?.has(note.lineNumber) ?? false
364
+ });
365
+ }
366
+ const placements = placeNotes(
367
+ anchors.map((a) => ({ x: a.x, y: a.y, width: a.width, height: a.height })),
368
+ requests,
369
+ direction
370
+ );
371
+ for (const [id, g] of geoms) {
372
+ const p = placements.get(id);
373
+ placed.set(
374
+ id,
375
+ p.collapsed ? {
376
+ x: 0,
377
+ y: 0,
378
+ width: 0,
379
+ height: 0,
380
+ side: "right",
381
+ ...g.color && { color: g.color },
382
+ lines: [],
383
+ lineNumber: g.lineNumber,
384
+ endLineNumber: g.endLineNumber,
385
+ collapsed: true
386
+ } : {
387
+ x: p.x,
388
+ y: p.y,
389
+ width: g.noteW,
390
+ height: g.noteH,
391
+ side: p.side,
392
+ ...g.color && { color: g.color },
393
+ lines: g.lines,
394
+ lineNumber: g.lineNumber,
395
+ endLineNumber: g.endLineNumber
396
+ }
397
+ );
398
+ }
399
+ return placed;
400
+ }
401
+
402
+ // src/utils/notes/bounds.ts
403
+ function noteCanvasShift(minX, minY, margin = 20) {
404
+ return {
405
+ shiftX: minX < 0 ? margin - minX : 0,
406
+ shiftY: minY < 0 ? margin - minY : 0
407
+ };
408
+ }
409
+
410
+ export {
411
+ tryCollectNote,
412
+ resolveNotes,
413
+ placeNotes,
414
+ buildPlacedNotes,
415
+ noteCanvasShift
416
+ };
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  stripInlineMarkdown
3
- } from "./chunk-S7W75MIM.js";
3
+ } from "./chunk-QKPOIJBT.js";
4
4
 
5
5
  // src/utils/wrapped-desc.ts
6
6
  var BULLET_PREFIX = "\u2022 ";
@@ -9,14 +9,14 @@ import {
9
9
  rectsOverlap,
10
10
  segmentRectOverlap
11
11
  } from "./chunk-KMU65FYM.js";
12
- import {
13
- compactNumber,
14
- computeLegendLayout
15
- } from "./chunk-JILEYZ2V.js";
16
12
  import {
17
13
  TITLE_FONT_SIZE,
18
14
  TITLE_Y
19
15
  } from "./chunk-VLC6WNRD.js";
16
+ import {
17
+ compactNumber,
18
+ computeLegendLayout
19
+ } from "./chunk-JILEYZ2V.js";
20
20
  import {
21
21
  tagAttrKey
22
22
  } from "./chunk-4SACN2C5.js";
@@ -620,6 +620,88 @@ function layoutEdgeLabels(args) {
620
620
  return chosen;
621
621
  }
622
622
 
623
+ // src/map/dot-labels.ts
624
+ var DOT_LABEL_GAP = 3;
625
+ function dotLabelRect(dot, w, h, side) {
626
+ const GAP2 = DOT_LABEL_GAP;
627
+ switch (side) {
628
+ case "right":
629
+ return { x: dot.cx + dot.r + GAP2, y: dot.cy - h / 2, w, h };
630
+ case "left":
631
+ return { x: dot.cx - dot.r - GAP2 - w, y: dot.cy - h / 2, w, h };
632
+ case "above":
633
+ return { x: dot.cx - w / 2, y: dot.cy - dot.r - GAP2 - h, w, h };
634
+ case "below":
635
+ return { x: dot.cx - w / 2, y: dot.cy + dot.r + GAP2, w, h };
636
+ // Diagonal corners: offset both axes by (r+GAP)/√2 so the box's near
637
+ // corner clears the dot rim by the same gap as a cardinal side.
638
+ case "below-right": {
639
+ const d = (dot.r + GAP2) * 0.7071;
640
+ return { x: dot.cx + d, y: dot.cy + d, w, h };
641
+ }
642
+ case "below-left": {
643
+ const d = (dot.r + GAP2) * 0.7071;
644
+ return { x: dot.cx - d - w, y: dot.cy + d, w, h };
645
+ }
646
+ case "above-right": {
647
+ const d = (dot.r + GAP2) * 0.7071;
648
+ return { x: dot.cx + d, y: dot.cy - d - h, w, h };
649
+ }
650
+ case "above-left": {
651
+ const d = (dot.r + GAP2) * 0.7071;
652
+ return { x: dot.cx - d - w, y: dot.cy - d - h, w, h };
653
+ }
654
+ }
655
+ }
656
+ function dotLabelFits(rect, width, height, collides) {
657
+ return rect.x >= 0 && rect.x + rect.w <= width && rect.y >= 0 && rect.y + rect.h <= height && !collides(rect);
658
+ }
659
+ var CLEAR_CAP = 48;
660
+ var CLEAR_W = 8;
661
+ var WATER_W = 0.25;
662
+ var LEGIBILITY = {
663
+ right: 160,
664
+ left: 160,
665
+ above: 100,
666
+ below: 100,
667
+ "below-right": 0,
668
+ "below-left": 0,
669
+ "above-right": 0,
670
+ "above-left": 0
671
+ };
672
+ var ALL_SIDES = Object.keys(LEGIBILITY);
673
+ function ptSegDist(px, py, ax, ay, bx, by) {
674
+ const dx = bx - ax;
675
+ const dy = by - ay;
676
+ const len2 = dx * dx + dy * dy;
677
+ let t = len2 > 0 ? ((px - ax) * dx + (py - ay) * dy) / len2 : 0;
678
+ t = Math.max(0, Math.min(1, t));
679
+ return Math.hypot(px - (ax + t * dx), py - (ay + t * dy));
680
+ }
681
+ function clearanceOf(rect, ownId, dots, segments) {
682
+ const cxL = rect.x + rect.w / 2;
683
+ const cyL = rect.y + rect.h / 2;
684
+ let d = CLEAR_CAP;
685
+ for (const q of dots) {
686
+ if (q.id === ownId) continue;
687
+ d = Math.min(d, Math.hypot(q.cx - cxL, q.cy - cyL) - q.r);
688
+ }
689
+ for (const s of segments)
690
+ d = Math.min(d, ptSegDist(cxL, cyL, s[0], s[1], s[2], s[3]));
691
+ return Math.max(0, d);
692
+ }
693
+ function placeDotLabel(args) {
694
+ const { dot, w, h, width, height, collides, dots, segments, openness } = args;
695
+ let best = null;
696
+ for (const side of ALL_SIDES) {
697
+ const rect = dotLabelRect(dot, w, h, side);
698
+ if (!dotLabelFits(rect, width, height, collides)) continue;
699
+ const v = CLEAR_W * clearanceOf(rect, dot.id, dots, segments) + WATER_W * openness(rect) + LEGIBILITY[side];
700
+ if (best === null || v > best.v) best = { side, rect, v };
701
+ }
702
+ return best && { side: best.side, rect: best.rect };
703
+ }
704
+
623
705
  // src/map/connectors.ts
624
706
  var W_MIN = 1.25;
625
707
  var W_MAX = 8;
@@ -2920,7 +3002,7 @@ function layoutMap(resolvedIn, data, size, opts) {
2920
3002
  const text = labelText(p);
2921
3003
  return { text, w: measureLegendText(text, FONT2) };
2922
3004
  };
2923
- const GAP2 = 3;
3005
+ const GAP2 = DOT_LABEL_GAP;
2924
3006
  const POI_EDGE_CLEAR = Math.max(
2925
3007
  20,
2926
3008
  Math.round(Math.min(width, height) * 0.03)
@@ -2932,46 +3014,7 @@ function layoutMap(resolvedIn, data, size, opts) {
2932
3014
  if (arr) arr.push(p);
2933
3015
  else clusterMembersById.set(p.clusterId, [p]);
2934
3016
  }
2935
- const inlineRect = (p, w, side) => {
2936
- switch (side) {
2937
- case "right":
2938
- return { x: p.cx + p.r + GAP2, y: p.cy - poiLabH / 2, w, h: poiLabH };
2939
- case "left":
2940
- return {
2941
- x: p.cx - p.r - GAP2 - w,
2942
- y: p.cy - poiLabH / 2,
2943
- w,
2944
- h: poiLabH
2945
- };
2946
- case "above":
2947
- return {
2948
- x: p.cx - w / 2,
2949
- y: p.cy - p.r - GAP2 - poiLabH,
2950
- w,
2951
- h: poiLabH
2952
- };
2953
- case "below":
2954
- return { x: p.cx - w / 2, y: p.cy + p.r + GAP2, w, h: poiLabH };
2955
- // Diagonal corners: offset both axes by (r+GAP)/√2 so the box's near
2956
- // corner clears the dot rim by the same gap as a cardinal side.
2957
- case "below-right": {
2958
- const d = (p.r + GAP2) * 0.7071;
2959
- return { x: p.cx + d, y: p.cy + d, w, h: poiLabH };
2960
- }
2961
- case "below-left": {
2962
- const d = (p.r + GAP2) * 0.7071;
2963
- return { x: p.cx - d - w, y: p.cy + d, w, h: poiLabH };
2964
- }
2965
- case "above-right": {
2966
- const d = (p.r + GAP2) * 0.7071;
2967
- return { x: p.cx + d, y: p.cy - d - poiLabH, w, h: poiLabH };
2968
- }
2969
- case "above-left": {
2970
- const d = (p.r + GAP2) * 0.7071;
2971
- return { x: p.cx - d - w, y: p.cy - d - poiLabH, w, h: poiLabH };
2972
- }
2973
- }
2974
- };
3017
+ const inlineRect = (p, w, side) => dotLabelRect(p, w, poiLabH, side);
2975
3018
  const pushInline = (p, text, w, side, clusterId) => {
2976
3019
  const rect = inlineRect(p, w, side);
2977
3020
  obstacles.push(rect);
@@ -2992,10 +3035,6 @@ function layoutMap(resolvedIn, data, size, opts) {
2992
3035
  ...clusterId !== void 0 && { clusterMember: clusterId }
2993
3036
  });
2994
3037
  };
2995
- const inlineFits = (p, w, side) => {
2996
- const rect = inlineRect(p, w, side);
2997
- return rect.x >= 0 && rect.x + rect.w <= width && rect.y >= 0 && rect.y + rect.h <= height && !collides(rect);
2998
- };
2999
3038
  const GROUP_R = 30;
3000
3039
  const groups2 = [];
3001
3040
  for (const p of ordered) {
@@ -3135,7 +3174,7 @@ function layoutMap(resolvedIn, data, size, opts) {
3135
3174
  ];
3136
3175
  const side = order.find((s) => {
3137
3176
  const rect = inlineRect(p, w, s);
3138
- return rect.x >= 0 && rect.x + rect.w <= width && rect.y >= 0 && rect.y + rect.h <= height && !collides(rect) && !temp.some((t) => rectsOverlap(t, rect));
3177
+ return dotLabelFits(rect, width, height, collides) && !temp.some((t) => rectsOverlap(t, rect));
3139
3178
  });
3140
3179
  if (side === void 0) return false;
3141
3180
  temp.push(inlineRect(p, w, side));
@@ -3154,62 +3193,28 @@ function layoutMap(resolvedIn, data, size, opts) {
3154
3193
  const side = cleanR && cleanL ? defaultColumnSide(items) : cleanR ? "right" : cleanL ? "left" : defaultColumnSide(items);
3155
3194
  commitColumn(items, side, clusterId);
3156
3195
  }
3157
- const CLEAR_CAP = 48;
3158
- const CLEAR_W = 8;
3159
- const WATER_W = 0.25;
3160
- const ptSegDist = (px, py, ax, ay, bx, by) => {
3161
- const dx = bx - ax;
3162
- const dy = by - ay;
3163
- const len2 = dx * dx + dy * dy;
3164
- let t = len2 > 0 ? ((px - ax) * dx + (py - ay) * dy) / len2 : 0;
3165
- t = Math.max(0, Math.min(1, t));
3166
- return Math.hypot(px - (ax + t * dx), py - (ay + t * dy));
3167
- };
3168
- const clearanceOf = (rect, ownId) => {
3169
- const cxL = rect.x + rect.w / 2;
3170
- const cyL = rect.y + rect.h / 2;
3171
- let d = CLEAR_CAP;
3172
- for (const q of pois) {
3173
- if (q.id === ownId) continue;
3174
- d = Math.min(d, Math.hypot(q.cx - cxL, q.cy - cyL) - q.r);
3175
- }
3176
- for (const s of legSegments)
3177
- d = Math.min(d, ptSegDist(cxL, cyL, s[0], s[1], s[2], s[3]));
3178
- return Math.max(0, d);
3179
- };
3180
- const LEGIBILITY = {
3181
- right: 160,
3182
- left: 160,
3183
- above: 100,
3184
- below: 100,
3185
- "below-right": 0,
3186
- "below-left": 0,
3187
- "above-right": 0,
3188
- "above-left": 0
3189
- };
3190
- const ALL_SIDES = Object.keys(LEGIBILITY);
3191
- const placementScore = (p, w, side) => {
3192
- const rect = inlineRect(p, w, side);
3193
- return CLEAR_W * clearanceOf(rect, p.id) + WATER_W * openness(rect) + LEGIBILITY[side];
3194
- };
3195
3196
  const maxExtent = MAX_CLUSTER_EXTENT_FACTOR * Math.min(width, height);
3196
3197
  const clusterPending = [];
3197
3198
  for (const g of groups2) {
3198
3199
  const items = makeItems(g);
3199
3200
  if (g.length === 1) {
3200
3201
  const { p, text, w } = items[0];
3201
- const scored = ALL_SIDES.filter((s) => inlineFits(p, w, s)).map(
3202
- (s) => ({
3203
- s,
3204
- v: placementScore(p, w, s)
3205
- })
3206
- );
3207
- if (scored.length === 0) {
3202
+ const placed = placeDotLabel({
3203
+ dot: p,
3204
+ w,
3205
+ h: poiLabH,
3206
+ width,
3207
+ height,
3208
+ collides,
3209
+ dots: pois,
3210
+ segments: legSegments,
3211
+ openness
3212
+ });
3213
+ if (placed === null) {
3208
3214
  commitColumn(items, defaultColumnSide(items));
3209
3215
  continue;
3210
3216
  }
3211
- const side = scored.reduce((b, c) => c.v > b.v ? c : b).s;
3212
- pushInline(p, text, w, side);
3217
+ pushInline(p, text, w, placed.side);
3213
3218
  continue;
3214
3219
  }
3215
3220
  const left = Math.min(...items.map((o) => o.p.cx - o.p.r));