@almadar/ui 1.0.30 → 1.0.32

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 (91) hide show
  1. package/dist/KaTeX_AMS-Regular-CYEKBG2K.woff +0 -0
  2. package/dist/KaTeX_AMS-Regular-JKX5W2C4.ttf +0 -0
  3. package/dist/KaTeX_AMS-Regular-U6PRYMIZ.woff2 +0 -0
  4. package/dist/KaTeX_Caligraphic-Bold-5QL5CMTE.woff2 +0 -0
  5. package/dist/KaTeX_Caligraphic-Bold-WZ3QSGD3.woff +0 -0
  6. package/dist/KaTeX_Caligraphic-Bold-ZTS3R3HK.ttf +0 -0
  7. package/dist/KaTeX_Caligraphic-Regular-3LKEU76G.woff +0 -0
  8. package/dist/KaTeX_Caligraphic-Regular-A7XRTZ5Q.ttf +0 -0
  9. package/dist/KaTeX_Caligraphic-Regular-KX5MEWCF.woff2 +0 -0
  10. package/dist/KaTeX_Fraktur-Bold-2QVFK6NQ.woff2 +0 -0
  11. package/dist/KaTeX_Fraktur-Bold-T4SWXBMT.woff +0 -0
  12. package/dist/KaTeX_Fraktur-Bold-WGHVTYOR.ttf +0 -0
  13. package/dist/KaTeX_Fraktur-Regular-2PEIFJSJ.woff2 +0 -0
  14. package/dist/KaTeX_Fraktur-Regular-5U4OPH2X.ttf +0 -0
  15. package/dist/KaTeX_Fraktur-Regular-PQMHCIK6.woff +0 -0
  16. package/dist/KaTeX_Main-Bold-2GA4IZIN.woff +0 -0
  17. package/dist/KaTeX_Main-Bold-W5FBVCZM.ttf +0 -0
  18. package/dist/KaTeX_Main-Bold-YP5VVQRP.woff2 +0 -0
  19. package/dist/KaTeX_Main-BoldItalic-4P4C7HJH.woff +0 -0
  20. package/dist/KaTeX_Main-BoldItalic-N4V3DX7S.woff2 +0 -0
  21. package/dist/KaTeX_Main-BoldItalic-ODMLBJJQ.ttf +0 -0
  22. package/dist/KaTeX_Main-Italic-I43T2HSR.ttf +0 -0
  23. package/dist/KaTeX_Main-Italic-RELBIK7M.woff2 +0 -0
  24. package/dist/KaTeX_Main-Italic-SASNQFN2.woff +0 -0
  25. package/dist/KaTeX_Main-Regular-ARRPAO67.woff2 +0 -0
  26. package/dist/KaTeX_Main-Regular-P5I74A2A.woff +0 -0
  27. package/dist/KaTeX_Main-Regular-W74P5G27.ttf +0 -0
  28. package/dist/KaTeX_Math-BoldItalic-6EBV3DK5.woff +0 -0
  29. package/dist/KaTeX_Math-BoldItalic-K4WTGH3J.woff2 +0 -0
  30. package/dist/KaTeX_Math-BoldItalic-VB447A4D.ttf +0 -0
  31. package/dist/KaTeX_Math-Italic-6KGCHLFN.woff2 +0 -0
  32. package/dist/KaTeX_Math-Italic-KKK3USB2.woff +0 -0
  33. package/dist/KaTeX_Math-Italic-SON4MRCA.ttf +0 -0
  34. package/dist/KaTeX_SansSerif-Bold-RRNVJFFW.woff2 +0 -0
  35. package/dist/KaTeX_SansSerif-Bold-STQ6RXC7.ttf +0 -0
  36. package/dist/KaTeX_SansSerif-Bold-X5M5EMOD.woff +0 -0
  37. package/dist/KaTeX_SansSerif-Italic-HMPFTM52.woff2 +0 -0
  38. package/dist/KaTeX_SansSerif-Italic-PSN4QKYX.woff +0 -0
  39. package/dist/KaTeX_SansSerif-Italic-WTBAZBGY.ttf +0 -0
  40. package/dist/KaTeX_SansSerif-Regular-2TL3USAE.ttf +0 -0
  41. package/dist/KaTeX_SansSerif-Regular-OQCII6EP.woff +0 -0
  42. package/dist/KaTeX_SansSerif-Regular-XIQ62X4E.woff2 +0 -0
  43. package/dist/KaTeX_Script-Regular-72OLXYNA.ttf +0 -0
  44. package/dist/KaTeX_Script-Regular-A5IFOEBS.woff +0 -0
  45. package/dist/KaTeX_Script-Regular-APUWIHLP.woff2 +0 -0
  46. package/dist/KaTeX_Size1-Regular-4HRHTS65.woff +0 -0
  47. package/dist/KaTeX_Size1-Regular-5LRUTBFT.woff2 +0 -0
  48. package/dist/KaTeX_Size1-Regular-7K6AASVL.ttf +0 -0
  49. package/dist/KaTeX_Size2-Regular-222HN3GT.ttf +0 -0
  50. package/dist/KaTeX_Size2-Regular-K5ZHAIS6.woff +0 -0
  51. package/dist/KaTeX_Size2-Regular-LELKET5D.woff2 +0 -0
  52. package/dist/KaTeX_Size3-Regular-TLFPAHDE.woff +0 -0
  53. package/dist/KaTeX_Size3-Regular-UFCO6WCA.ttf +0 -0
  54. package/dist/KaTeX_Size3-Regular-WQRQ47UD.woff2 +0 -0
  55. package/dist/KaTeX_Size4-Regular-7PGNVPQK.ttf +0 -0
  56. package/dist/KaTeX_Size4-Regular-CDMV7U5C.woff2 +0 -0
  57. package/dist/KaTeX_Size4-Regular-PKMWZHNC.woff +0 -0
  58. package/dist/KaTeX_Typewriter-Regular-3F5K6SQ6.ttf +0 -0
  59. package/dist/KaTeX_Typewriter-Regular-MJMFSK64.woff +0 -0
  60. package/dist/KaTeX_Typewriter-Regular-VBYJ4NRC.woff2 +0 -0
  61. package/dist/{ThemeContext-lI5bo85E.d.ts → ThemeContext-D9xUORq5.d.ts} +2 -0
  62. package/dist/chunk-45CTDYBT.js +174 -0
  63. package/dist/{chunk-4UFNDD6B.js → chunk-BTXQJGFB.js} +41 -8
  64. package/dist/chunk-N6DJVKZ6.js +747 -0
  65. package/dist/chunk-PE2H3NAW.js +383 -0
  66. package/dist/{chunk-7IYF4RUG.js → chunk-RFD6G3ZR.js} +23 -68
  67. package/dist/chunk-UCXZS2YJ.js +9501 -0
  68. package/dist/chunk-YXZM3WCF.js +222 -0
  69. package/dist/cn-BoBXsxuX.d.ts +194 -0
  70. package/dist/components/index.css +1154 -0
  71. package/dist/components/index.d.ts +1082 -673
  72. package/dist/components/index.js +8935 -16486
  73. package/dist/components/organisms/game/three/index.css +315 -0
  74. package/dist/components/organisms/game/three/index.d.ts +1162 -0
  75. package/dist/components/organisms/game/three/index.js +2378 -0
  76. package/dist/context/index.d.ts +4 -4
  77. package/dist/context/index.js +2 -3
  78. package/dist/{event-bus-types-8-cjyMxw.d.ts → event-bus-types-CjJduURa.d.ts} +8 -0
  79. package/dist/hooks/index.d.ts +120 -106
  80. package/dist/hooks/index.js +4 -3
  81. package/dist/isometric-ynNHVPZx.d.ts +111 -0
  82. package/dist/lib/index.d.ts +117 -153
  83. package/dist/lib/index.js +4 -683
  84. package/dist/providers/index.css +1154 -0
  85. package/dist/providers/index.d.ts +84 -4
  86. package/dist/providers/index.js +165 -8
  87. package/dist/{useUISlots-mnggE9X9.d.ts → useUISlots-D0mttBSP.d.ts} +0 -20
  88. package/package.json +32 -11
  89. package/dist/chunk-HY5EFCOK.js +0 -419
  90. package/dist/chunk-W5YTXLXL.js +0 -29
  91. package/dist/cn-mqkxz8Sd.d.ts +0 -9
@@ -0,0 +1,747 @@
1
+ // lib/visualizer/index.ts
2
+ function formatSExprGuardToDomain(guard, _entityName) {
3
+ if (Array.isArray(guard)) {
4
+ const [op, ...args] = guard;
5
+ return `${op}(${args.map((a) => JSON.stringify(a)).join(", ")})`;
6
+ }
7
+ return JSON.stringify(guard);
8
+ }
9
+ function formatSExprEffectToDomain(effect, _entityName) {
10
+ if (Array.isArray(effect)) {
11
+ const [op, ...args] = effect;
12
+ return `${op}(${args.map((a) => JSON.stringify(a)).join(", ")})`;
13
+ }
14
+ return JSON.stringify(effect);
15
+ }
16
+ function isArraySExpr(expr) {
17
+ return Array.isArray(expr);
18
+ }
19
+ var DEFAULT_CONFIG = {
20
+ nodeRadius: 70,
21
+ nodeSpacing: 650,
22
+ // Increased to give more room for transitions
23
+ initialIndicatorOffset: 45,
24
+ arrowSize: 12,
25
+ colors: {
26
+ background: "#0d1117",
27
+ node: "#161b22",
28
+ nodeBorder: "#30363d",
29
+ nodeText: "#e6edf3",
30
+ initialNode: "#238636",
31
+ finalNode: "#f85149",
32
+ arrow: "#8b949e",
33
+ arrowText: "#8b949e",
34
+ effectText: "#ffb86c",
35
+ guardText: "#ff79c6",
36
+ initial: "#238636"
37
+ },
38
+ fonts: {
39
+ node: '18px "Inter", sans-serif',
40
+ event: '16px "JetBrains Mono", monospace',
41
+ effect: '14px "JetBrains Mono", monospace'
42
+ }
43
+ };
44
+ function isBinding(val) {
45
+ return typeof val === "string" && val.startsWith("@");
46
+ }
47
+ function parseBinding(binding) {
48
+ if (!isBinding(binding)) return null;
49
+ const withoutAt = binding.substring(1);
50
+ const parts = withoutAt.split(".");
51
+ return {
52
+ root: parts[0],
53
+ path: parts.slice(1),
54
+ raw: binding
55
+ };
56
+ }
57
+ function formatGuard(guard) {
58
+ let text = "";
59
+ if (typeof guard === "string") {
60
+ text = guard;
61
+ } else if (Array.isArray(guard)) {
62
+ text = formatSExprCompact(guard);
63
+ }
64
+ return text ? `[${text}]` : "";
65
+ }
66
+ function formatGuardHuman(guard, entityName) {
67
+ if (!guard) return "";
68
+ if (typeof guard === "string") {
69
+ return `if ${guard}`;
70
+ }
71
+ if (isArraySExpr(guard)) {
72
+ return formatSExprGuardToDomain(guard);
73
+ }
74
+ return "";
75
+ }
76
+ function formatEffectsHuman(effects, entityName) {
77
+ if (!Array.isArray(effects) || effects.length === 0) return [];
78
+ return effects.map((effect) => {
79
+ if (isArraySExpr(effect)) {
80
+ return formatSExprEffectToDomain(effect);
81
+ }
82
+ return String(effect);
83
+ }).filter(Boolean);
84
+ }
85
+ function formatSExprCompact(expr) {
86
+ if (!Array.isArray(expr) || expr.length === 0) return "[]";
87
+ const op = expr[0];
88
+ const args = expr.slice(1);
89
+ const formattedArgs = args.map((a) => {
90
+ if (isBinding(a)) {
91
+ const parsed = parseBinding(a);
92
+ if (parsed && parsed.path.length > 0) {
93
+ return `${parsed.root}.${parsed.path.join(".")}`;
94
+ }
95
+ return parsed?.root || a;
96
+ }
97
+ if (typeof a === "string") return a;
98
+ if (typeof a === "number" || typeof a === "boolean") return String(a);
99
+ if (Array.isArray(a)) return formatSExprCompact(a);
100
+ return "{...}";
101
+ });
102
+ return `${op} ${formattedArgs.join(" ")}`;
103
+ }
104
+ function getEffectSummary(effects) {
105
+ if (!Array.isArray(effects) || effects.length === 0) return "";
106
+ const setFields = [];
107
+ const otherEffects = [];
108
+ effects.forEach((effect) => {
109
+ if (!Array.isArray(effect)) return;
110
+ const op = effect[0];
111
+ if (op === "set" && effect[1] && typeof effect[1] === "string") {
112
+ const parsed = parseBinding(effect[1]);
113
+ if (parsed && parsed.path.length > 0) {
114
+ setFields.push(parsed.path[parsed.path.length - 1]);
115
+ } else {
116
+ setFields.push("field");
117
+ }
118
+ } else {
119
+ otherEffects.push(effect);
120
+ }
121
+ });
122
+ const summaries = [];
123
+ if (setFields.length > 0) {
124
+ summaries.push(`\u2192 ${setFields.join(", ")}`);
125
+ }
126
+ otherEffects.forEach((effect) => {
127
+ const op = effect[0];
128
+ switch (op) {
129
+ case "emit":
130
+ summaries.push(`\u2191 ${effect[1] || "event"}`);
131
+ break;
132
+ case "notify":
133
+ summaries.push(`\u{1F4E7} ${effect[1] || ""}`);
134
+ break;
135
+ case "persist":
136
+ summaries.push(`\u{1F4BE} ${effect[1] || "save"}`);
137
+ break;
138
+ case "navigate":
139
+ summaries.push(`\u{1F517} nav`);
140
+ break;
141
+ case "spawn":
142
+ summaries.push(`+ ${effect[1] || "spawn"}`);
143
+ break;
144
+ case "despawn":
145
+ summaries.push(`- despawn`);
146
+ break;
147
+ default:
148
+ summaries.push(op);
149
+ }
150
+ });
151
+ return summaries.join(" | ");
152
+ }
153
+ function extractOutputsFromTransitions(transitions) {
154
+ const outputs = /* @__PURE__ */ new Set();
155
+ transitions.forEach((t) => {
156
+ if (t.effects) {
157
+ t.effects.forEach((effect) => {
158
+ if (Array.isArray(effect)) {
159
+ const op = effect[0];
160
+ if (["emit", "notify", "persist", "navigate", "call-service"].includes(op)) {
161
+ if (isArraySExpr(effect)) {
162
+ const humanText = formatSExprEffectToDomain(effect);
163
+ outputs.add(humanText);
164
+ }
165
+ }
166
+ }
167
+ });
168
+ }
169
+ });
170
+ return Array.from(outputs);
171
+ }
172
+ function getNodeRadius(stateName, config) {
173
+ const baseRadius = config.nodeRadius;
174
+ const textLength = stateName.length;
175
+ if (textLength > 12) return baseRadius + 25;
176
+ if (textLength > 8) return baseRadius + 15;
177
+ if (textLength > 6) return baseRadius + 8;
178
+ return baseRadius;
179
+ }
180
+ function calculateLayout(states, transitions, options, config) {
181
+ const positions = {};
182
+ const entityBoxWidth = options.hasEntity ? 200 : 0;
183
+ const outputBoxWidth = options.hasOutputs ? 200 : 0;
184
+ const leftOffset = 100 + entityBoxWidth;
185
+ const initialState = states.find((s) => s.isInitial) || states[0];
186
+ states.filter((s) => s.isFinal);
187
+ states.filter((s) => !s.isInitial && !s.isFinal);
188
+ let maxLabelLength = 0;
189
+ transitions.forEach((t) => {
190
+ if (t.effects && t.effects.length > 0) {
191
+ const summary = getEffectSummary(t.effects);
192
+ maxLabelLength = Math.max(maxLabelLength, summary.length);
193
+ }
194
+ if (t.guard) {
195
+ const guardStr = formatGuard(t.guard);
196
+ maxLabelLength = Math.max(maxLabelLength, guardStr.length);
197
+ }
198
+ if (t.event) {
199
+ maxLabelLength = Math.max(maxLabelLength, t.event.length);
200
+ }
201
+ });
202
+ const labelWidth = Math.min(maxLabelLength * 10, 350);
203
+ const dynamicSpacing = Math.min(Math.max(config.nodeSpacing, labelWidth + 100), 400);
204
+ const stateColumn = {};
205
+ if (initialState) {
206
+ const queue = [{ name: initialState.name, col: 0 }];
207
+ const visited = /* @__PURE__ */ new Set();
208
+ while (queue.length > 0) {
209
+ const { name, col } = queue.shift();
210
+ if (visited.has(name)) continue;
211
+ visited.add(name);
212
+ if (stateColumn[name] === void 0) {
213
+ stateColumn[name] = col;
214
+ }
215
+ transitions.forEach((t) => {
216
+ if (t.from === name && t.from !== t.to && !visited.has(t.to)) {
217
+ queue.push({ name: t.to, col: col + 1 });
218
+ }
219
+ });
220
+ }
221
+ }
222
+ states.forEach((s) => {
223
+ if (stateColumn[s.name] === void 0) {
224
+ stateColumn[s.name] = 0;
225
+ }
226
+ });
227
+ const columns = {};
228
+ Object.entries(stateColumn).forEach(([name, col]) => {
229
+ if (!columns[col]) columns[col] = [];
230
+ columns[col].push(name);
231
+ });
232
+ Object.values(columns).forEach((stateNames) => {
233
+ stateNames.sort((a, b) => {
234
+ const stateA = states.find((s) => s.name === a);
235
+ const stateB = states.find((s) => s.name === b);
236
+ if (stateA?.isInitial) return -1;
237
+ if (stateB?.isInitial) return 1;
238
+ if (stateA?.isFinal && !stateB?.isFinal) return 1;
239
+ if (stateB?.isFinal && !stateA?.isFinal) return -1;
240
+ return a.localeCompare(b);
241
+ });
242
+ });
243
+ const numColumns = Math.max(...Object.keys(columns).map(Number)) + 1;
244
+ const maxRowsInColumn = Math.max(...Object.values(columns).map((arr) => arr.length));
245
+ const minVerticalSpacing = 420;
246
+ const tooltipPadding = 150;
247
+ const width = Math.max(1400, numColumns * dynamicSpacing + entityBoxWidth + outputBoxWidth + 400);
248
+ const height = Math.max(1e3, maxRowsInColumn * minVerticalSpacing + 350 + tooltipPadding);
249
+ Object.entries(columns).forEach(([colStr, stateNames]) => {
250
+ const col = parseInt(colStr);
251
+ const x = leftOffset + col * dynamicSpacing;
252
+ const numInColumn = stateNames.length;
253
+ const verticalSpacing = Math.max(minVerticalSpacing, height / (numInColumn + 1));
254
+ stateNames.forEach((stateName, rowIndex) => {
255
+ const state = states.find((s) => s.name === stateName);
256
+ if (state) {
257
+ const y = verticalSpacing * (rowIndex + 1);
258
+ positions[stateName] = { x, y, state };
259
+ }
260
+ });
261
+ });
262
+ return { positions, width, height: height + 60 };
263
+ }
264
+ function escapeXml(unsafe) {
265
+ return unsafe.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&apos;");
266
+ }
267
+ function createArrowMarkerSvg(id, color, config) {
268
+ return `<marker id="${id}" viewBox="0 0 10 10" refX="8" refY="5" markerWidth="${config.arrowSize}" markerHeight="${config.arrowSize}" orient="auto-start-reverse">
269
+ <path d="M 0 0 L 10 5 L 0 10 z" fill="${color}"/>
270
+ </marker>`;
271
+ }
272
+ function drawStateSvg(name, x, y, state, config) {
273
+ const radius = getNodeRadius(name, config);
274
+ let borderColor = config.colors.nodeBorder;
275
+ let borderWidth = 2;
276
+ if (state.isInitial) {
277
+ borderColor = config.colors.initialNode;
278
+ borderWidth = 3;
279
+ } else if (state.isFinal) {
280
+ borderColor = config.colors.finalNode;
281
+ borderWidth = 3;
282
+ }
283
+ let svg = `<g class="state-node">
284
+ <circle cx="${x}" cy="${y}" r="${radius}" fill="${config.colors.node}" stroke="${borderColor}" stroke-width="${borderWidth}"/>`;
285
+ if (state.isFinal) {
286
+ svg += `<circle cx="${x}" cy="${y}" r="${radius - 6}" fill="none" stroke="${borderColor}" stroke-width="2"/>`;
287
+ }
288
+ if (state.isInitial) {
289
+ svg += `<path d="M ${x - radius - config.initialIndicatorOffset} ${y} L ${x - radius - 5} ${y}" stroke="${config.colors.initial}" stroke-width="2" fill="none" marker-end="url(#arrow-initial)"/>`;
290
+ }
291
+ svg += `<text x="${x}" y="${y + 7}" text-anchor="middle" fill="${config.colors.nodeText}" font-family="Inter, sans-serif" font-size="18px" font-weight="600">${escapeXml(name)}</text>`;
292
+ svg += `</g>`;
293
+ return svg;
294
+ }
295
+ function drawTransitionPathSvg(from, to, transitions, positions, config) {
296
+ const fromPos = positions[from];
297
+ const toPos = positions[to];
298
+ if (!fromPos || !toPos) return "";
299
+ const relevantTransitions = transitions.filter((t) => t.from === from && t.to === to);
300
+ if (relevantTransitions.length === 0) return "";
301
+ const fromRadius = getNodeRadius(from, config);
302
+ const toRadius = getNodeRadius(to, config);
303
+ const dx = toPos.x - fromPos.x;
304
+ const dy = toPos.y - fromPos.y;
305
+ const dist = Math.sqrt(dx * dx + dy * dy);
306
+ if (dist === 0) return "";
307
+ const nx = dx / dist;
308
+ const ny = dy / dist;
309
+ const startX = fromPos.x + nx * fromRadius;
310
+ const startY = fromPos.y + ny * fromRadius;
311
+ const endX = toPos.x - nx * (toRadius + 5);
312
+ const endY = toPos.y - ny * (toRadius + 5);
313
+ const hasReverse = transitions.some((t) => t.from === to && t.to === from);
314
+ const isReverse = hasReverse && from > to;
315
+ const baseOffset = hasReverse ? 50 : 30;
316
+ const curveOffset = baseOffset + (relevantTransitions.length > 1 ? 20 : 0);
317
+ const curveDirection = isReverse ? 1 : -1;
318
+ const midX = (startX + endX) / 2;
319
+ const midY = (startY + endY) / 2 + curveOffset * curveDirection;
320
+ return `<path class="transition-path" data-from="${from}" data-to="${to}" d="M ${startX} ${startY} Q ${midX} ${midY} ${endX} ${endY}" stroke="${config.colors.arrow}" stroke-width="1.5" fill="none" marker-end="url(#arrow)"/>`;
321
+ }
322
+ function drawTransitionLabelsSvg(from, to, transitions, positions, config) {
323
+ const fromPos = positions[from];
324
+ const toPos = positions[to];
325
+ if (!fromPos || !toPos) return "";
326
+ const relevantTransitions = transitions.filter((t) => t.from === from && t.to === to);
327
+ if (relevantTransitions.length === 0) return "";
328
+ const fromRadius = getNodeRadius(from, config);
329
+ const toRadius = getNodeRadius(to, config);
330
+ const dx = toPos.x - fromPos.x;
331
+ const dy = toPos.y - fromPos.y;
332
+ const dist = Math.sqrt(dx * dx + dy * dy);
333
+ if (dist === 0) return "";
334
+ const nx = dx / dist;
335
+ const ny = dy / dist;
336
+ const startX = fromPos.x + nx * fromRadius;
337
+ const startY = fromPos.y + ny * fromRadius;
338
+ const endX = toPos.x - nx * (toRadius + 5);
339
+ const endY = toPos.y - ny * (toRadius + 5);
340
+ const hasReverse = transitions.some((t) => t.from === to && t.to === from);
341
+ const isReverse = hasReverse && from > to;
342
+ const baseOffset = hasReverse ? 50 : 40;
343
+ const curveOffset = baseOffset + (relevantTransitions.length > 1 ? 25 : 0);
344
+ const curveDirection = isReverse ? 1 : -1;
345
+ const midX = (startX + endX) / 2;
346
+ const midY = (startY + endY) / 2 + curveOffset * curveDirection;
347
+ let svg = "";
348
+ relevantTransitions.forEach((transition, index) => {
349
+ const blockOffset = index * 60 * curveDirection;
350
+ const dataAttrs = `data-from="${from}" data-to="${to}" data-event="${transition.event}"`;
351
+ const labelY = midY + curveDirection * 5 + blockOffset;
352
+ svg += `<g class="transition-group" ${dataAttrs}>`;
353
+ svg += `<text class="transition-label transition-event" x="${midX}" y="${labelY}" text-anchor="middle" fill="${config.colors.arrowText}" font-family="JetBrains Mono, monospace" font-size="14px" font-weight="600">${escapeXml(transition.event)}</text>`;
354
+ const hasGuard = !!transition.guard;
355
+ const guardText = hasGuard ? formatGuardHuman(transition.guard) : "";
356
+ const effectLines = transition.effects ? formatEffectsHuman(transition.effects) : [];
357
+ const hasEffects = effectLines.length > 0;
358
+ if (hasGuard || hasEffects) {
359
+ const tooltipStartY = labelY + 20 * curveDirection;
360
+ const lineHeight = 18;
361
+ const padding = 12;
362
+ let maxTextWidth = 0;
363
+ if (guardText) maxTextWidth = Math.max(maxTextWidth, guardText.length * 7);
364
+ effectLines.forEach((line) => {
365
+ maxTextWidth = Math.max(maxTextWidth, line.length * 7);
366
+ });
367
+ const boxWidth = Math.max(180, Math.min(maxTextWidth + padding * 2 + 20, 400));
368
+ const numLines = (hasGuard ? 1 : 0) + effectLines.length;
369
+ const boxHeight = numLines * lineHeight + padding * 2;
370
+ const boxY = curveDirection > 0 ? tooltipStartY : tooltipStartY - boxHeight;
371
+ svg += `<g class="transition-detail">`;
372
+ svg += `<rect x="${midX - boxWidth / 2}" y="${boxY}" width="${boxWidth}" height="${boxHeight}" fill="rgba(22, 27, 34, 0.95)" stroke="${config.colors.nodeBorder}" stroke-width="1" rx="6"/>`;
373
+ let currentY = boxY + padding + 12;
374
+ if (hasGuard && guardText) {
375
+ svg += `<text x="${midX - boxWidth / 2 + padding}" y="${currentY}" fill="${config.colors.guardText}" font-family="Inter, sans-serif" font-size="12px">`;
376
+ svg += `<tspan font-weight="600">Guard:</tspan> ${escapeXml(guardText)}</text>`;
377
+ currentY += lineHeight;
378
+ }
379
+ if (hasEffects) {
380
+ effectLines.forEach((effectText, idx) => {
381
+ const prefix = idx === 0 ? "Then: " : " ";
382
+ svg += `<text x="${midX - boxWidth / 2 + padding}" y="${currentY}" fill="${config.colors.effectText}" font-family="Inter, sans-serif" font-size="12px">`;
383
+ svg += `<tspan font-weight="${idx === 0 ? "600" : "400"}">${prefix}</tspan>${escapeXml(effectText)}</text>`;
384
+ currentY += lineHeight;
385
+ });
386
+ }
387
+ svg += `</g>`;
388
+ }
389
+ svg += `</g>`;
390
+ });
391
+ return svg;
392
+ }
393
+ function drawEntityInputSvg(entity, x, y, _height) {
394
+ const fieldCount = entity.fields ? entity.fields.length : 0;
395
+ const boxWidth = 160;
396
+ const boxHeight = Math.max(80, fieldCount * 22 + 50);
397
+ const boxY = y - boxHeight / 2;
398
+ let svg = `<g class="entity-input">
399
+ <rect x="${x}" y="${boxY}" width="${boxWidth}" height="${boxHeight}" fill="#1a1f2e" stroke="#4a9eff" stroke-width="2" rx="8"/>
400
+ <text x="${x + boxWidth / 2}" y="${boxY + 24}" text-anchor="middle" fill="#4a9eff" font-family="Inter, sans-serif" font-size="14px" font-weight="600">\u{1F4E6} ${escapeXml(entity.name || "Entity")}</text>`;
401
+ if (entity.fields && entity.fields.length > 0) {
402
+ entity.fields.forEach((field, idx) => {
403
+ const fieldName = typeof field === "string" ? field : field.name;
404
+ svg += `<text x="${x + 12}" y="${boxY + 48 + idx * 20}" fill="#8b949e" font-family="JetBrains Mono, monospace" font-size="11px">\u2022 ${escapeXml(fieldName)}</text>`;
405
+ });
406
+ }
407
+ svg += `<path d="M ${x + boxWidth + 5} ${y} L ${x + boxWidth + 40} ${y}" stroke="#4a9eff" stroke-width="2" fill="none" marker-end="url(#arrow-input)"/>`;
408
+ svg += `</g>`;
409
+ return svg;
410
+ }
411
+ function drawOutputsSvg(outputs, x, y, height) {
412
+ if (!outputs || outputs.length === 0) return "";
413
+ const maxOutputLength = Math.max(...outputs.map((o) => o.length));
414
+ const boxWidth = Math.max(200, maxOutputLength * 7 + 30);
415
+ const lineHeight = 22;
416
+ const boxHeight = outputs.length * lineHeight + 50;
417
+ const boxY = y - boxHeight / 2;
418
+ let svg = `<g class="outputs">
419
+ <rect x="${x}" y="${boxY}" width="${boxWidth}" height="${boxHeight}" fill="#1a1f2e" stroke="#ffb86c" stroke-width="2" rx="8"/>
420
+ <text x="${x + boxWidth / 2}" y="${boxY + 24}" text-anchor="middle" fill="#ffb86c" font-family="Inter, sans-serif" font-size="13px" font-weight="600">\u{1F4E4} External Effects</text>`;
421
+ outputs.forEach((output, idx) => {
422
+ svg += `<text x="${x + 12}" y="${boxY + 48 + idx * lineHeight}" fill="#e6edf3" font-family="Inter, sans-serif" font-size="11px">\u2022 ${escapeXml(output)}</text>`;
423
+ });
424
+ svg += `</g>`;
425
+ return svg;
426
+ }
427
+ function drawLegendSvg(y, config) {
428
+ const items = [
429
+ { label: "Initial", color: config.colors.initialNode },
430
+ { label: "Final", color: config.colors.finalNode },
431
+ { label: "State", color: config.colors.nodeBorder }
432
+ ];
433
+ let svg = `<g class="legend">`;
434
+ let x = 20;
435
+ items.forEach((item) => {
436
+ svg += `<circle cx="${x}" cy="${y}" r="6" fill="${config.colors.node}" stroke="${item.color}" stroke-width="2"/>`;
437
+ svg += `<text x="${x + 12}" y="${y + 4}" fill="${config.colors.arrowText}" font-family="Inter, sans-serif" font-size="10px">${escapeXml(item.label)}</text>`;
438
+ x += 70;
439
+ });
440
+ svg += `</g>`;
441
+ return svg;
442
+ }
443
+ function renderStateMachineToSvg(stateMachine, options = {}, config = DEFAULT_CONFIG) {
444
+ const states = stateMachine.states || [];
445
+ const transitions = stateMachine.transitions || [];
446
+ const title = options.title || "";
447
+ const entity = options.entity;
448
+ const outputs = extractOutputsFromTransitions(transitions);
449
+ const layoutOptions = {
450
+ hasEntity: !!entity,
451
+ hasOutputs: outputs.length > 0
452
+ };
453
+ const { positions, width, height } = calculateLayout(states, transitions, layoutOptions, config);
454
+ let svg = `<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height + 40}" viewBox="0 0 ${width} ${height + 40}" class="orbital-state-machine" style="display: block; max-width: none;">`;
455
+ svg += `<defs>`;
456
+ svg += createArrowMarkerSvg("arrow", config.colors.arrow, config);
457
+ svg += createArrowMarkerSvg("arrow-initial", config.colors.initial, config);
458
+ svg += createArrowMarkerSvg("arrow-input", "#4a9eff", config);
459
+ svg += createArrowMarkerSvg("arrow-output", "#ffb86c", config);
460
+ svg += `</defs>`;
461
+ svg += `<rect x="0" y="0" width="${width}" height="${height + 40}" fill="${config.colors.background}" rx="8"/>`;
462
+ if (title) {
463
+ svg += `<text x="${width / 2}" y="20" text-anchor="middle" fill="${config.colors.nodeText}" font-family="Inter, sans-serif" font-size="14px" font-weight="600">${escapeXml(title)}</text>`;
464
+ }
465
+ const offsetY = title ? 30 : 0;
466
+ svg += `<g transform="translate(0, ${offsetY})">`;
467
+ if (entity) {
468
+ svg += drawEntityInputSvg(entity, 20, height / 2);
469
+ }
470
+ const drawnPairs = /* @__PURE__ */ new Set();
471
+ transitions.forEach((transition) => {
472
+ const pairKey = `${transition.from}->${transition.to}`;
473
+ if (!drawnPairs.has(pairKey)) {
474
+ drawnPairs.add(pairKey);
475
+ svg += drawTransitionPathSvg(transition.from, transition.to, transitions, positions, config);
476
+ }
477
+ });
478
+ for (const [name, pos] of Object.entries(positions)) {
479
+ svg += drawStateSvg(name, pos.x, pos.y, pos.state, config);
480
+ }
481
+ drawnPairs.clear();
482
+ transitions.forEach((transition) => {
483
+ const pairKey = `${transition.from}->${transition.to}`;
484
+ if (!drawnPairs.has(pairKey)) {
485
+ drawnPairs.add(pairKey);
486
+ svg += drawTransitionLabelsSvg(transition.from, transition.to, transitions, positions, config);
487
+ }
488
+ });
489
+ if (outputs.length > 0) {
490
+ const maxX = Math.max(...Object.values(positions).map((p) => p.x));
491
+ svg += drawOutputsSvg(outputs, maxX + config.nodeRadius + 60, height / 2);
492
+ }
493
+ svg += `</g>`;
494
+ svg += drawLegendSvg(height + 25, config);
495
+ svg += `</svg>`;
496
+ return svg;
497
+ }
498
+ function extractStateMachine(data) {
499
+ if (!data || typeof data !== "object") return null;
500
+ const obj = data;
501
+ if (obj.states && obj.transitions) {
502
+ return obj;
503
+ }
504
+ if (obj.stateMachine) {
505
+ return obj.stateMachine;
506
+ }
507
+ if (Array.isArray(obj.traits)) {
508
+ const traitWithSM = obj.traits.find(
509
+ (t) => typeof t === "object" && t !== null && "stateMachine" in t
510
+ );
511
+ if (traitWithSM && typeof traitWithSM === "object" && "stateMachine" in traitWithSM) {
512
+ return traitWithSM.stateMachine;
513
+ }
514
+ }
515
+ return null;
516
+ }
517
+ function calculateTransitionPathData(from, to, transitions, positions, config) {
518
+ const fromPos = positions[from];
519
+ const toPos = positions[to];
520
+ if (!fromPos || !toPos) return null;
521
+ const relevantTransitions = transitions.filter((t) => t.from === from && t.to === to);
522
+ if (relevantTransitions.length === 0) return null;
523
+ const fromRadius = getNodeRadius(from, config);
524
+ const toRadius = getNodeRadius(to, config);
525
+ if (from === to) {
526
+ const loopRadius = 50;
527
+ const cx = fromPos.x;
528
+ const cy = fromPos.y - fromRadius - loopRadius;
529
+ const startAngle = -0.5;
530
+ const endAngle = 0.5;
531
+ const startX2 = fromPos.x + Math.cos(-Math.PI / 2 + startAngle) * fromRadius;
532
+ const startY2 = fromPos.y + Math.sin(-Math.PI / 2 + startAngle) * fromRadius;
533
+ const endX2 = fromPos.x + Math.cos(-Math.PI / 2 + endAngle) * fromRadius;
534
+ const endY2 = fromPos.y + Math.sin(-Math.PI / 2 + endAngle) * fromRadius;
535
+ const pathData = `M ${startX2} ${startY2} A ${loopRadius} ${loopRadius} 0 1 1 ${endX2} ${endY2}`;
536
+ return {
537
+ pathData,
538
+ labelX: cx,
539
+ labelY: cy - loopRadius * 0.5
540
+ };
541
+ }
542
+ const dx = toPos.x - fromPos.x;
543
+ const dy = toPos.y - fromPos.y;
544
+ const dist = Math.sqrt(dx * dx + dy * dy);
545
+ if (dist === 0) return null;
546
+ const nx = dx / dist;
547
+ const ny = dy / dist;
548
+ const startX = fromPos.x + nx * fromRadius;
549
+ const startY = fromPos.y + ny * fromRadius;
550
+ const endX = toPos.x - nx * (toRadius + 5);
551
+ const endY = toPos.y - ny * (toRadius + 5);
552
+ const hasReverse = transitions.some((t) => t.from === to && t.to === from);
553
+ const isReverse = hasReverse && from > to;
554
+ const baseOffset = hasReverse ? 50 : 30;
555
+ const curveOffset = baseOffset + (relevantTransitions.length > 1 ? 20 : 0);
556
+ const curveDirection = isReverse ? 1 : -1;
557
+ const midX = (startX + endX) / 2;
558
+ const midY = (startY + endY) / 2 + curveOffset * curveDirection;
559
+ return {
560
+ pathData: `M ${startX} ${startY} Q ${midX} ${midY} ${endX} ${endY}`,
561
+ labelX: midX,
562
+ labelY: midY + curveDirection * 5
563
+ };
564
+ }
565
+ function renderStateMachineToDomData(stateMachine, options = {}, config = DEFAULT_CONFIG) {
566
+ const states = stateMachine.states || [];
567
+ const transitions = stateMachine.transitions || [];
568
+ const title = options.title || "";
569
+ const entity = options.entity;
570
+ const outputs = extractOutputsFromTransitions(transitions);
571
+ const layoutOptions = {
572
+ hasEntity: !!entity,
573
+ hasOutputs: outputs.length > 0
574
+ };
575
+ const { positions, width, height } = calculateLayout(states, transitions, layoutOptions, config);
576
+ const domStates = Object.entries(positions).map(([name, pos]) => ({
577
+ id: `state-${name}`,
578
+ name,
579
+ x: pos.x,
580
+ y: pos.y,
581
+ radius: getNodeRadius(name, config),
582
+ isInitial: pos.state.isInitial ?? false,
583
+ isFinal: pos.state.isFinal ?? false,
584
+ description: pos.state.description
585
+ }));
586
+ const domPaths = [];
587
+ const domLabels = [];
588
+ const drawnPairs = /* @__PURE__ */ new Set();
589
+ transitions.forEach((transition, idx) => {
590
+ const pairKey = `${transition.from}->${transition.to}`;
591
+ if (!drawnPairs.has(pairKey)) {
592
+ drawnPairs.add(pairKey);
593
+ const pathData2 = calculateTransitionPathData(
594
+ transition.from,
595
+ transition.to,
596
+ transitions,
597
+ positions,
598
+ config
599
+ );
600
+ if (pathData2) {
601
+ domPaths.push({
602
+ id: `path-${transition.from}-${transition.to}`,
603
+ from: transition.from,
604
+ to: transition.to,
605
+ pathData: pathData2.pathData,
606
+ labelX: pathData2.labelX,
607
+ labelY: pathData2.labelY
608
+ });
609
+ }
610
+ }
611
+ const guardText = transition.guard ? formatGuardHuman(transition.guard) : void 0;
612
+ const effectTexts = transition.effects ? formatEffectsHuman(transition.effects) : [];
613
+ const hasDetails = !!guardText || effectTexts.length > 0;
614
+ const pathData = calculateTransitionPathData(
615
+ transition.from,
616
+ transition.to,
617
+ transitions,
618
+ positions,
619
+ config
620
+ );
621
+ if (pathData) {
622
+ const sameEventIndex = domLabels.filter(
623
+ (l) => l.from === transition.from && l.to === transition.to
624
+ ).length;
625
+ const labelOffset = sameEventIndex * 60;
626
+ domLabels.push({
627
+ id: `label-${transition.from}-${transition.to}-${idx}`,
628
+ from: transition.from,
629
+ to: transition.to,
630
+ event: transition.event,
631
+ x: pathData.labelX,
632
+ y: pathData.labelY + labelOffset,
633
+ guardText,
634
+ effectTexts,
635
+ hasDetails
636
+ });
637
+ }
638
+ });
639
+ let domEntity;
640
+ if (entity) {
641
+ const fieldCount = entity.fields ? entity.fields.length : 0;
642
+ const boxWidth = 160;
643
+ const boxHeight = Math.max(80, fieldCount * 22 + 50);
644
+ domEntity = {
645
+ name: entity.name || "Entity",
646
+ fields: entity.fields?.map((f) => typeof f === "string" ? f : f.name) || [],
647
+ x: 20,
648
+ y: height / 2 - boxHeight / 2,
649
+ width: boxWidth,
650
+ height: boxHeight
651
+ };
652
+ }
653
+ let domOutputs;
654
+ if (outputs.length > 0) {
655
+ const maxX = Math.max(...Object.values(positions).map((p) => p.x));
656
+ const maxOutputLength = Math.max(...outputs.map((o) => o.length));
657
+ const boxWidth = Math.max(200, maxOutputLength * 7 + 30);
658
+ const lineHeight = 22;
659
+ const boxHeight = outputs.length * lineHeight + 50;
660
+ domOutputs = {
661
+ outputs,
662
+ x: maxX + config.nodeRadius + 300,
663
+ y: height / 2 - boxHeight / 2,
664
+ width: boxWidth,
665
+ height: boxHeight
666
+ };
667
+ }
668
+ return {
669
+ width,
670
+ height: height + 40,
671
+ title: title || void 0,
672
+ states: domStates,
673
+ paths: domPaths,
674
+ labels: domLabels,
675
+ entity: domEntity,
676
+ outputs: domOutputs,
677
+ config
678
+ };
679
+ }
680
+
681
+ // lib/parseContentSegments.ts
682
+ function tryParseOrbitalSchema(code) {
683
+ try {
684
+ const parsed = JSON.parse(code);
685
+ if (parsed.states && parsed.transitions || Array.isArray(parsed.orbitals)) {
686
+ return parsed;
687
+ }
688
+ } catch {
689
+ }
690
+ return null;
691
+ }
692
+ function parseMarkdownWithCodeBlocks(content) {
693
+ if (!content) return [];
694
+ const segments = [];
695
+ const codeBlockRegex = /```(\w+)?\n([\s\S]*?)```/g;
696
+ let lastIndex = 0;
697
+ let match;
698
+ while ((match = codeBlockRegex.exec(content)) !== null) {
699
+ const before = content.slice(lastIndex, match.index);
700
+ if (before.trim()) {
701
+ segments.push({ type: "markdown", content: before });
702
+ }
703
+ const language = match[1] || "text";
704
+ const code = match[2].trim();
705
+ if (language === "json" || language === "orb") {
706
+ const schema = tryParseOrbitalSchema(code);
707
+ if (schema) {
708
+ segments.push({ type: "orbital", language, content: code, schema });
709
+ lastIndex = codeBlockRegex.lastIndex;
710
+ continue;
711
+ }
712
+ }
713
+ segments.push({ type: "code", language, content: code });
714
+ lastIndex = codeBlockRegex.lastIndex;
715
+ }
716
+ const remaining = content.slice(lastIndex);
717
+ if (remaining.trim()) {
718
+ segments.push({ type: "markdown", content: remaining });
719
+ }
720
+ return segments;
721
+ }
722
+ function parseContentSegments(content) {
723
+ if (!content) return [];
724
+ const segments = [];
725
+ const tagRegex = /<question>([\s\S]*?)<\/question>\s*<answer>([\s\S]*?)<\/answer>/gi;
726
+ let lastIndex = 0;
727
+ let match;
728
+ while ((match = tagRegex.exec(content)) !== null) {
729
+ const before = content.slice(lastIndex, match.index);
730
+ if (before.trim()) {
731
+ segments.push(...parseMarkdownWithCodeBlocks(before));
732
+ }
733
+ segments.push({
734
+ type: "quiz",
735
+ question: match[1].trim(),
736
+ answer: match[2].trim()
737
+ });
738
+ lastIndex = tagRegex.lastIndex;
739
+ }
740
+ const remaining = content.slice(lastIndex);
741
+ if (remaining.trim()) {
742
+ segments.push(...parseMarkdownWithCodeBlocks(remaining));
743
+ }
744
+ return segments;
745
+ }
746
+
747
+ export { DEFAULT_CONFIG, extractOutputsFromTransitions, extractStateMachine, formatGuard, getEffectSummary, parseContentSegments, parseMarkdownWithCodeBlocks, renderStateMachineToDomData, renderStateMachineToSvg };