@almadar/ui 6.5.0 → 6.7.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 (48) hide show
  1. package/dist/{EntityBindingContext-0Evn_LcT.d.cts → EntityBindingContext-Bn3ePJQC.d.cts} +1 -1
  2. package/dist/{EntityBindingContext-0Evn_LcT.d.ts → EntityBindingContext-Bn3ePJQC.d.ts} +1 -1
  3. package/dist/{GameAudioProvider-B48iXwz3.d.ts → GameAudioProvider-Ctceau1s.d.ts} +6 -18
  4. package/dist/{GameAudioProvider-CQAdPreB.d.cts → GameAudioProvider-Dk_Y3LN3.d.cts} +6 -18
  5. package/dist/avl/index.cjs +1268 -220
  6. package/dist/avl/index.js +1269 -221
  7. package/dist/{avl-schema-parser-dvJKXn-o.d.ts → avl-schema-parser-DncJLEn9.d.ts} +18 -0
  8. package/dist/{avl-schema-parser-Bt4NzAam.d.cts → avl-schema-parser-FQ1474v8.d.cts} +18 -0
  9. package/dist/{cn-DJSBBm5W.d.cts → cn-CCjFspAo.d.cts} +25 -1
  10. package/dist/{cn-BbhJq_nr.d.ts → cn-sgpqpN0U.d.ts} +25 -1
  11. package/dist/components/index.cjs +1092 -249
  12. package/dist/components/index.d.cts +193 -23
  13. package/dist/components/index.d.ts +193 -23
  14. package/dist/components/index.js +1089 -250
  15. package/dist/context/index.cjs +10 -13
  16. package/dist/context/index.js +10 -13
  17. package/dist/hooks/index.cjs +63 -13
  18. package/dist/hooks/index.d.cts +2 -2
  19. package/dist/hooks/index.d.ts +2 -2
  20. package/dist/hooks/index.js +64 -15
  21. package/dist/lib/drawable/three/index.cjs +126 -13
  22. package/dist/lib/drawable/three/index.d.cts +2 -2
  23. package/dist/lib/drawable/three/index.d.ts +2 -2
  24. package/dist/lib/drawable/three/index.js +127 -14
  25. package/dist/lib/index.cjs +180 -0
  26. package/dist/lib/index.d.cts +1 -1
  27. package/dist/lib/index.d.ts +1 -1
  28. package/dist/lib/index.js +178 -1
  29. package/dist/locales/index.cjs +6 -3
  30. package/dist/locales/index.js +6 -3
  31. package/dist/providers/index.cjs +974 -202
  32. package/dist/providers/index.d.cts +2 -2
  33. package/dist/providers/index.d.ts +2 -2
  34. package/dist/providers/index.js +974 -202
  35. package/dist/runtime/index.cjs +1594 -196
  36. package/dist/runtime/index.d.cts +79 -6
  37. package/dist/runtime/index.d.ts +79 -6
  38. package/dist/runtime/index.js +1592 -198
  39. package/dist/slot-host-Czc2JAxo.d.cts +47 -0
  40. package/dist/slot-host-Czc2JAxo.d.ts +47 -0
  41. package/dist/{useEventBus-CQWyAWpK.d.ts → useKeyboardRouter-D84cNWmA.d.ts} +31 -1
  42. package/dist/{useEventBus-Ckr4wqW3.d.cts → useKeyboardRouter-DQEE_9mq.d.cts} +31 -1
  43. package/locales/ar.json +2 -1
  44. package/locales/en.json +2 -1
  45. package/locales/sl.json +2 -1
  46. package/package.json +4 -4
  47. package/themes/comic.css +437 -0
  48. package/themes/index.css +1 -0
@@ -9922,14 +9922,15 @@ function drawArrowHead(ctx, x1, y1, x2, y2, size) {
9922
9922
  ctx.closePath();
9923
9923
  ctx.fill();
9924
9924
  }
9925
- function drawShape(ctx, shape, width, height, allShapes) {
9925
+ function drawShape(ctx, shape, width, height, allShapes, fontFamily) {
9926
9926
  ctx.save();
9927
9927
  const opacity = shape.opacity ?? 1;
9928
9928
  ctx.globalAlpha = opacity;
9929
9929
  const stroke = resolveColor2(shape.color, ctx, "#333333");
9930
9930
  const fill = shape.fill ? resolveColor2(shape.fill, ctx, "#cccccc") : void 0;
9931
9931
  ctx.lineWidth = shape.lineWidth ?? 2;
9932
- if (shape.dash) ctx.setLineDash([...DASH_PATTERNS[shape.dash]]);
9932
+ const dashPattern = shape.dash ? DASH_PATTERNS[shape.dash] : void 0;
9933
+ if (dashPattern) ctx.setLineDash([...dashPattern]);
9933
9934
  switch (shape.type) {
9934
9935
  case "grid": {
9935
9936
  const step = shape.step ?? 40;
@@ -10051,7 +10052,7 @@ function drawShape(ctx, shape, width, height, allShapes) {
10051
10052
  case "text": {
10052
10053
  if (shape.x == null || shape.y == null || !shape.text) break;
10053
10054
  ctx.fillStyle = stroke;
10054
- ctx.font = `${shape.fontSize ?? 14}px ${themeBodyFont(ctx.canvas)}`;
10055
+ ctx.font = `${shape.fontSize ?? 14}px ${shape.fontFamily ?? fontFamily ?? themeBodyFont(ctx.canvas)}`;
10055
10056
  ctx.textAlign = shape.align ?? "left";
10056
10057
  ctx.textBaseline = "middle";
10057
10058
  ctx.fillText(shape.text, shape.x, shape.y);
@@ -10093,7 +10094,7 @@ function drawShape(ctx, shape, width, height, allShapes) {
10093
10094
  }
10094
10095
  ctx.restore();
10095
10096
  }
10096
- function readoutShapes(readouts, width) {
10097
+ function readoutShapes(readouts, width, fontFamily) {
10097
10098
  const out = [];
10098
10099
  const chipH = 18;
10099
10100
  const gap = 6;
@@ -10117,13 +10118,14 @@ function readoutShapes(readouts, width) {
10117
10118
  text,
10118
10119
  color: "#ffffff",
10119
10120
  fontSize: 10,
10120
- align: "center"
10121
+ align: "center",
10122
+ fontFamily
10121
10123
  });
10122
10124
  rightEdge = chipX - gap;
10123
10125
  }
10124
10126
  return out;
10125
10127
  }
10126
- function traceShapes(panel, k, width, height) {
10128
+ function traceShapes(panel, k, width, height, fontFamily) {
10127
10129
  const w = panel.width ?? Math.round(width * 0.32);
10128
10130
  const h = panel.height ?? Math.round(height * 0.28);
10129
10131
  const x = panel.x ?? width - w - 8;
@@ -10177,14 +10179,14 @@ function traceShapes(panel, k, width, height) {
10177
10179
  out.push({ type: "circle", x: last.x, y: last.y, radius: 2, color, fill: color });
10178
10180
  }
10179
10181
  if (series.label) {
10180
- out.push({ type: "text", x: x + 6, y: y + 10 + 11 * j, text: series.label, color, fontSize: 9 });
10182
+ out.push({ type: "text", x: x + 6, y: y + 10 + 11 * j, text: series.label, color, fontSize: 9, fontFamily });
10181
10183
  }
10182
10184
  });
10183
10185
  if (panel.yLabel) {
10184
- out.push({ type: "text", x: x + w - 6, y: y + 10, text: panel.yLabel, color: "#6b7280", fontSize: 9, align: "right" });
10186
+ out.push({ type: "text", x: x + w - 6, y: y + 10, text: panel.yLabel, color: "#6b7280", fontSize: 9, align: "right", fontFamily });
10185
10187
  }
10186
10188
  if (panel.xLabel) {
10187
- out.push({ type: "text", x: x + w - 6, y: y + h - 6, text: panel.xLabel, color: "#6b7280", fontSize: 9, align: "right" });
10189
+ out.push({ type: "text", x: x + w - 6, y: y + h - 6, text: panel.xLabel, color: "#6b7280", fontSize: 9, align: "right", fontFamily });
10188
10190
  }
10189
10191
  return out;
10190
10192
  }
@@ -10207,13 +10209,14 @@ var init_LearningCanvas = __esm({
10207
10209
  init_useEventBus();
10208
10210
  init_webPainter2d();
10209
10211
  init_paintDispatch();
10210
- DASH_PATTERNS = { dashed: [6, 4], dotted: [2, 3] };
10212
+ DASH_PATTERNS = { solid: [], dashed: [6, 4], dotted: [2, 3] };
10211
10213
  TRACE_SERIES_COLORS = ["#2563eb", "#dc2626", "#16a34a", "#f59e0b"];
10212
10214
  exports.LearningCanvas = ({
10213
10215
  className,
10214
10216
  width = 600,
10215
10217
  height = 400,
10216
10218
  backgroundColor,
10219
+ fontFamily,
10217
10220
  shapes = [],
10218
10221
  drawables,
10219
10222
  projector,
@@ -10249,10 +10252,10 @@ var init_LearningCanvas = __esm({
10249
10252
  }, [shapes]);
10250
10253
  const derivedShapes = React79.useMemo(() => {
10251
10254
  if (!traces?.length && !readouts?.length) return shapes;
10252
- const traceOut = (traces ?? []).flatMap((panel, k) => traceShapes(panel, k, width, height));
10253
- const readoutOut = readouts?.length ? readoutShapes(readouts, width) : [];
10255
+ const traceOut = (traces ?? []).flatMap((panel, k) => traceShapes(panel, k, width, height, fontFamily));
10256
+ const readoutOut = readouts?.length ? readoutShapes(readouts, width, fontFamily) : [];
10254
10257
  return [...shapes, ...traceOut, ...readoutOut];
10255
- }, [shapes, traces, readouts, width, height]);
10258
+ }, [shapes, traces, readouts, width, height, fontFamily]);
10256
10259
  const draw = React79.useCallback(() => {
10257
10260
  const _perfT = ui.perfStart("learningcanvas:paint");
10258
10261
  const canvas = canvasRef.current;
@@ -10271,15 +10274,15 @@ var init_LearningCanvas = __esm({
10271
10274
  ctx.fillRect(0, 0, width, height);
10272
10275
  }
10273
10276
  for (const shape of derivedShapes) {
10274
- if (shape.type !== "text") drawShape(ctx, shape, width, height, derivedShapes);
10277
+ if (shape.type !== "text") drawShape(ctx, shape, width, height, derivedShapes, fontFamily);
10275
10278
  }
10276
10279
  for (const shape of derivedShapes) {
10277
- if (shape.type === "text") drawShape(ctx, shape, width, height, derivedShapes);
10280
+ if (shape.type === "text") drawShape(ctx, shape, width, height, derivedShapes, fontFamily);
10278
10281
  }
10279
10282
  if (drawables?.length && projector) {
10280
10283
  const painter = createWebPainter(ctx, invalidateRef.current);
10281
10284
  const timeMs = needsAnim && typeof performance !== "undefined" ? performance.now() : 0;
10282
- const dctx = { projector, time: timeMs, invalidate: invalidateRef.current, fontFamily: themeBodyFont(canvas) };
10285
+ const dctx = { projector, time: timeMs, invalidate: invalidateRef.current, fontFamily: fontFamily || themeBodyFont(canvas) };
10283
10286
  for (const node of drawables) {
10284
10287
  paintDrawable(painter, node, dctx);
10285
10288
  }
@@ -13396,6 +13399,250 @@ var init_EmptyState = __esm({
13396
13399
  exports.EmptyState.displayName = "EmptyState";
13397
13400
  }
13398
13401
  });
13402
+
13403
+ // lib/editorMotions.ts
13404
+ function clamp(value, min, max) {
13405
+ return Math.max(min, Math.min(max, value));
13406
+ }
13407
+ function computeLines(text) {
13408
+ const lines = [];
13409
+ let start = 0;
13410
+ for (let i = 0; i <= text.length; i++) {
13411
+ if (i === text.length || text[i] === "\n") {
13412
+ lines.push({ start, end: i });
13413
+ start = i + 1;
13414
+ }
13415
+ }
13416
+ return lines;
13417
+ }
13418
+ function lineIndexAt(lines, pos) {
13419
+ for (let i = 0; i < lines.length; i++) {
13420
+ if (pos <= lines[i].end) return i;
13421
+ }
13422
+ return lines.length - 1;
13423
+ }
13424
+ function findWords(text) {
13425
+ const words = [];
13426
+ const re = /\w+|\S+/g;
13427
+ let m;
13428
+ while ((m = re.exec(text)) !== null) {
13429
+ words.push({ start: m.index, end: m.index + m[0].length });
13430
+ }
13431
+ return words;
13432
+ }
13433
+ function nextWordStart(text, pos) {
13434
+ for (const w of findWords(text)) {
13435
+ if (w.start > pos) return w.start;
13436
+ }
13437
+ return text.length;
13438
+ }
13439
+ function prevWordStart(text, pos) {
13440
+ let result = 0;
13441
+ for (const w of findWords(text)) {
13442
+ if (w.start < pos) result = w.start;
13443
+ else break;
13444
+ }
13445
+ return result;
13446
+ }
13447
+ function nextWordEnd(text, pos) {
13448
+ for (const w of findWords(text)) {
13449
+ const lastChar = w.end - 1;
13450
+ if (lastChar > pos) return lastChar;
13451
+ }
13452
+ return text.length > 0 ? text.length - 1 : 0;
13453
+ }
13454
+ function firstNonBlank(text, line) {
13455
+ let i = line.start;
13456
+ while (i < line.end && (text[i] === " " || text[i] === " ")) i++;
13457
+ return i;
13458
+ }
13459
+ function nextParagraphBoundary(lines, fromLineIdx, textLength) {
13460
+ for (let i = fromLineIdx + 1; i < lines.length; i++) {
13461
+ if (lines[i].start === lines[i].end) return lines[i].start;
13462
+ }
13463
+ return textLength;
13464
+ }
13465
+ function prevParagraphBoundary(lines, fromLineIdx) {
13466
+ for (let i = fromLineIdx - 1; i >= 0; i--) {
13467
+ if (lines[i].start === lines[i].end) return lines[i].start;
13468
+ }
13469
+ return 0;
13470
+ }
13471
+ function applyMotion(text, caret, motion, count) {
13472
+ const n = Math.max(1, count);
13473
+ const lines = computeLines(text);
13474
+ const lineIdx = lineIndexAt(lines, caret);
13475
+ const line = lines[lineIdx];
13476
+ switch (motion) {
13477
+ case "left":
13478
+ return clamp(caret - n, line.start, line.end);
13479
+ case "right":
13480
+ return clamp(caret + n, line.start, line.end);
13481
+ case "up": {
13482
+ const col = caret - line.start;
13483
+ const targetIdx = clamp(lineIdx - n, 0, lines.length - 1);
13484
+ const target = lines[targetIdx];
13485
+ return clamp(target.start + col, target.start, target.end);
13486
+ }
13487
+ case "down": {
13488
+ const col = caret - line.start;
13489
+ const targetIdx = clamp(lineIdx + n, 0, lines.length - 1);
13490
+ const target = lines[targetIdx];
13491
+ return clamp(target.start + col, target.start, target.end);
13492
+ }
13493
+ case "word-forward": {
13494
+ let pos = caret;
13495
+ for (let i = 0; i < n; i++) pos = nextWordStart(text, pos);
13496
+ return pos;
13497
+ }
13498
+ case "word-back": {
13499
+ let pos = caret;
13500
+ for (let i = 0; i < n; i++) pos = prevWordStart(text, pos);
13501
+ return pos;
13502
+ }
13503
+ case "word-end": {
13504
+ let pos = caret;
13505
+ for (let i = 0; i < n; i++) pos = nextWordEnd(text, pos);
13506
+ return pos;
13507
+ }
13508
+ case "line-start":
13509
+ return line.start;
13510
+ case "line-end":
13511
+ return line.end > line.start ? line.end - 1 : line.start;
13512
+ case "first-nonblank":
13513
+ return firstNonBlank(text, line);
13514
+ case "doc-start":
13515
+ return 0;
13516
+ case "doc-end":
13517
+ return text.length;
13518
+ case "paragraph-forward": {
13519
+ let pos = caret;
13520
+ for (let i = 0; i < n; i++) {
13521
+ pos = nextParagraphBoundary(lines, lineIndexAt(lines, pos), text.length);
13522
+ }
13523
+ return pos;
13524
+ }
13525
+ case "paragraph-back": {
13526
+ let pos = caret;
13527
+ for (let i = 0; i < n; i++) {
13528
+ pos = prevParagraphBoundary(lines, lineIndexAt(lines, pos));
13529
+ }
13530
+ return pos;
13531
+ }
13532
+ case "line":
13533
+ case "selection":
13534
+ return caret;
13535
+ default: {
13536
+ const _exhaustive = motion;
13537
+ return _exhaustive;
13538
+ }
13539
+ }
13540
+ }
13541
+ function motionRange(text, caret, motion, count, selection) {
13542
+ if (motion === "selection") {
13543
+ if (!selection) return [caret, caret];
13544
+ return [Math.min(selection[0], selection[1]), Math.max(selection[0], selection[1])];
13545
+ }
13546
+ const lines = computeLines(text);
13547
+ if (motion === "line") {
13548
+ const n = Math.max(1, count);
13549
+ const startIdx = lineIndexAt(lines, caret);
13550
+ const endIdx = clamp(startIdx + n - 1, 0, lines.length - 1);
13551
+ const start2 = lines[startIdx].start;
13552
+ const rawEnd = lines[endIdx].end;
13553
+ const end2 = rawEnd < text.length ? rawEnd + 1 : rawEnd;
13554
+ return [start2, end2];
13555
+ }
13556
+ const newCaret = applyMotion(text, caret, motion, count);
13557
+ let start = Math.min(caret, newCaret);
13558
+ let end = Math.max(caret, newCaret);
13559
+ if (motion === "word-end" || motion === "line-end") {
13560
+ end = Math.min(Math.max(start, newCaret) + 1, text.length);
13561
+ } else if (motion === "word-forward" && newCaret > caret) {
13562
+ const startLine = lineIndexAt(lines, caret);
13563
+ const endLine = lineIndexAt(lines, newCaret);
13564
+ if (endLine !== startLine) {
13565
+ end = lines[startLine].end;
13566
+ }
13567
+ }
13568
+ return [start, end];
13569
+ }
13570
+ function applyOperator(text, range, operator, register) {
13571
+ const start = clamp(range[0], 0, text.length);
13572
+ const end = clamp(range[1], start, text.length);
13573
+ const removed = text.slice(start, end);
13574
+ if (operator === "yank") {
13575
+ return { text, caret: start, register: removed };
13576
+ }
13577
+ return { text: text.slice(0, start) + text.slice(end), caret: start, register: removed };
13578
+ }
13579
+ var init_editorMotions = __esm({
13580
+ "lib/editorMotions.ts"() {
13581
+ }
13582
+ });
13583
+ function isMotionPayload(payload) {
13584
+ return !!payload && typeof payload.editorId === "string" && typeof payload.motion === "string" && typeof payload.count === "number";
13585
+ }
13586
+ function isOperatePayload(payload) {
13587
+ return !!payload && typeof payload.editorId === "string" && typeof payload.operator === "string" && typeof payload.motion === "string" && typeof payload.count === "number";
13588
+ }
13589
+ function isInsertTextPayload(payload) {
13590
+ return !!payload && typeof payload.editorId === "string" && typeof payload.text === "string";
13591
+ }
13592
+ function isSetModePayload(payload) {
13593
+ return !!payload && typeof payload.editorId === "string" && typeof payload.mode === "string" && typeof payload.caret === "string";
13594
+ }
13595
+ function useEditorCapabilities(args) {
13596
+ const [caretMode, setCaretMode] = React79.useState("bar");
13597
+ const registerRef = React79.useRef("");
13598
+ useEventListener(`UI:${args.events.onMotion}`, (evt) => {
13599
+ if (!args.editorId || !isMotionPayload(evt.payload) || evt.payload.editorId !== args.editorId) return;
13600
+ const ta = args.textareaRef.current;
13601
+ if (!ta) return;
13602
+ const { motion, count } = evt.payload;
13603
+ const newCaret = applyMotion(ta.value, ta.selectionStart, motion, count);
13604
+ if (ta.selectionStart !== ta.selectionEnd) {
13605
+ ta.setSelectionRange(ta.selectionStart, newCaret);
13606
+ } else {
13607
+ ta.setSelectionRange(newCaret, newCaret);
13608
+ }
13609
+ });
13610
+ useEventListener(`UI:${args.events.onOperate}`, (evt) => {
13611
+ if (!args.editorId || !isOperatePayload(evt.payload) || evt.payload.editorId !== args.editorId) return;
13612
+ const ta = args.textareaRef.current;
13613
+ if (!ta) return;
13614
+ const { operator, motion, count } = evt.payload;
13615
+ const selection = motion === "selection" ? [ta.selectionStart, ta.selectionEnd] : void 0;
13616
+ const range = motionRange(ta.value, ta.selectionStart, motion, count, selection);
13617
+ const result = applyOperator(ta.value, range, operator, registerRef.current);
13618
+ registerRef.current = result.register;
13619
+ if (operator === "yank") {
13620
+ ta.setSelectionRange(range[0], range[0]);
13621
+ } else {
13622
+ ta.setRangeText("", range[0], range[1], "end");
13623
+ }
13624
+ args.applyChange(ta.value);
13625
+ });
13626
+ useEventListener(`UI:${args.events.onInsertText}`, (evt) => {
13627
+ if (!args.editorId || !isInsertTextPayload(evt.payload) || evt.payload.editorId !== args.editorId) return;
13628
+ const ta = args.textareaRef.current;
13629
+ if (!ta) return;
13630
+ ta.setRangeText(evt.payload.text, ta.selectionStart, ta.selectionEnd, "end");
13631
+ args.applyChange(ta.value);
13632
+ });
13633
+ useEventListener(`UI:${args.events.onSetMode}`, (evt) => {
13634
+ if (!args.editorId || !isSetModePayload(evt.payload) || evt.payload.editorId !== args.editorId) return;
13635
+ setCaretMode(evt.payload.caret);
13636
+ });
13637
+ return { caretMode };
13638
+ }
13639
+ var init_useEditorCapabilities = __esm({
13640
+ "components/core/molecules/markdown/useEditorCapabilities.ts"() {
13641
+ "use client";
13642
+ init_useEventBus();
13643
+ init_editorMotions();
13644
+ }
13645
+ });
13399
13646
  function registerCodeLanguageLoader(loader) {
13400
13647
  codeLanguageLoader = loader;
13401
13648
  }
@@ -13484,7 +13731,36 @@ function useLanguageReady(language) {
13484
13731
  }, [language]);
13485
13732
  return ready;
13486
13733
  }
13487
- var dynamicallyLoaded, codeLanguageLoader, orbStyleOverrides, orbStyle, loloStyleOverrides, loloStyle, log5, CODE_LANGUAGES, CODE_LANGUAGE_SET, DIFF_STYLES, DIFF_STYLE_FALLBACK, LINE_PROPS_FN, HIDDEN_LINE_NUMBERS, HIGHLIGHT_CAPACITY_BYTES; exports.CodeBlock = void 0;
13734
+ function resolveHighlightStyle(lang) {
13735
+ if (lang === "orb") return orbStyle;
13736
+ if (lang === "lolo") return loloStyle;
13737
+ return dark__default.default;
13738
+ }
13739
+ function plainCodeColorOf(style) {
13740
+ return style['code[class*="language-"]']?.color ?? "#d4d4d4";
13741
+ }
13742
+ function buildLineProps(errorLines, extraClassName) {
13743
+ return (lineNumber) => {
13744
+ const base = {
13745
+ "data-line": String(lineNumber - 1),
13746
+ ...extraClassName ? { className: extraClassName } : {}
13747
+ };
13748
+ const severity = errorLines?.get(lineNumber);
13749
+ if (!severity) return base;
13750
+ return {
13751
+ ...base,
13752
+ style: {
13753
+ display: "block",
13754
+ backgroundColor: severity === "error" ? "rgba(248, 113, 113, 0.18)" : "rgba(251, 191, 36, 0.18)",
13755
+ // amber-400 @ 18%
13756
+ borderLeft: `3px solid ${severity === "error" ? "#ef4444" : "#f59e0b"}`,
13757
+ paddingLeft: "0.5rem",
13758
+ marginLeft: "-0.5rem"
13759
+ }
13760
+ };
13761
+ };
13762
+ }
13763
+ var dynamicallyLoaded, codeLanguageLoader, orbStyleOverrides, orbStyle, loloStyleOverrides, loloStyle, log5, CODE_LANGUAGES, CODE_LANGUAGE_SET, DIFF_STYLES, DIFF_STYLE_FALLBACK, LINE_PROPS_FN, HIDDEN_LINE_NUMBERS, HIGHLIGHT_CAPACITY_BYTES, MONO_FONT_FAMILY, VIEWER_LINE_NUMBER_STYLE; exports.CodeBlock = void 0;
13488
13764
  var init_CodeBlock = __esm({
13489
13765
  "components/core/molecules/markdown/CodeBlock.tsx"() {
13490
13766
  init_cn();
@@ -13500,6 +13776,7 @@ var init_CodeBlock = __esm({
13500
13776
  init_Textarea();
13501
13777
  init_Icon();
13502
13778
  init_useEventBus();
13779
+ init_useEditorCapabilities();
13503
13780
  SyntaxHighlighter__default.default.registerLanguage("json", langJson__default.default);
13504
13781
  SyntaxHighlighter__default.default.registerLanguage("javascript", langJavascript__default.default);
13505
13782
  SyntaxHighlighter__default.default.registerLanguage("js", langJavascript__default.default);
@@ -13718,6 +13995,15 @@ var init_CodeBlock = __esm({
13718
13995
  LINE_PROPS_FN = (n) => ({ "data-line": String(n - 1) });
13719
13996
  HIDDEN_LINE_NUMBERS = { display: "none" };
13720
13997
  HIGHLIGHT_CAPACITY_BYTES = 512 * 1024;
13998
+ MONO_FONT_FAMILY = 'ui-monospace, SFMono-Regular, Menlo, Monaco, "Cascadia Mono", "Courier New", monospace';
13999
+ VIEWER_LINE_NUMBER_STYLE = {
14000
+ minWidth: "2.5em",
14001
+ paddingRight: "1rem",
14002
+ textAlign: "right",
14003
+ userSelect: "none",
14004
+ opacity: 0.5,
14005
+ fontVariantNumeric: "tabular-nums"
14006
+ };
13721
14007
  exports.CodeBlock = React79__namespace.default.memo(
13722
14008
  ({
13723
14009
  code: rawCode,
@@ -13742,15 +14028,39 @@ var init_CodeBlock = __esm({
13742
14028
  actions,
13743
14029
  isLoading = false,
13744
14030
  error,
13745
- showCopy
14031
+ showCopy,
14032
+ // editor capability surface — P1 wires these
14033
+ editorId,
14034
+ onEditorFocus = "EDITOR_FOCUS",
14035
+ onEditorBlur = "EDITOR_BLUR",
14036
+ onMotion = "MOTION",
14037
+ onOperate = "OPERATE",
14038
+ onInsertText = "INSERT_TEXT",
14039
+ onSetMode = "SET_MODE",
14040
+ motions = [
14041
+ "left",
14042
+ "right",
14043
+ "up",
14044
+ "down",
14045
+ "word-forward",
14046
+ "word-back",
14047
+ "word-end",
14048
+ "line-start",
14049
+ "line-end",
14050
+ "first-nonblank",
14051
+ "doc-start",
14052
+ "doc-end",
14053
+ "paragraph-forward",
14054
+ "paragraph-back",
14055
+ "line",
14056
+ "selection"
14057
+ ],
14058
+ operators = ["delete", "yank", "change"]
13746
14059
  }) => {
13747
14060
  const code = typeof rawCode === "string" ? rawCode : String(rawCode ?? "");
13748
- const isOrb = language === "orb";
13749
- const isLolo = language === "lolo";
13750
- const activeStyle = isOrb ? orbStyle : isLolo ? loloStyle : dark__default.default;
14061
+ const activeStyle = resolveHighlightStyle(language);
13751
14062
  const overCapacity = code.length > HIGHLIGHT_CAPACITY_BYTES;
13752
- const plainCodeColor = activeStyle['code[class*="language-"]']?.color ?? "#d4d4d4";
13753
- const languageReady = useLanguageReady(language);
14063
+ const plainCodeColor = plainCodeColorOf(activeStyle);
13754
14064
  const eventBus = useEventBus();
13755
14065
  const { t } = hooks.useTranslate();
13756
14066
  const scrollRef = React79.useRef(null);
@@ -13762,6 +14072,9 @@ var init_CodeBlock = __esm({
13762
14072
  const activeFile = files?.[activeFileIndex];
13763
14073
  const activeCode = activeFile?.code ?? code;
13764
14074
  const activeLanguage = activeFile?.language ?? language;
14075
+ const languageReady = useLanguageReady(activeLanguage);
14076
+ const viewerStyle = resolveHighlightStyle(activeLanguage);
14077
+ const viewerPlainCodeColor = plainCodeColorOf(viewerStyle);
13765
14078
  const diffLines = React79.useMemo(() => {
13766
14079
  if (propDiff) return propDiff;
13767
14080
  if (mode === "diff" && oldValue !== void 0 && newValue !== void 0) {
@@ -13791,28 +14104,28 @@ var init_CodeBlock = __esm({
13791
14104
  ov.scrollLeft = ta.scrollLeft;
13792
14105
  }
13793
14106
  }, []);
13794
- const errorLineProps = React79.useMemo(() => {
13795
- if (!errorLines || errorLines.size === 0) {
13796
- return LINE_PROPS_FN;
13797
- }
13798
- return (lineNumber) => {
13799
- const severity = errorLines.get(lineNumber);
13800
- if (!severity) {
13801
- return { "data-line": String(lineNumber - 1) };
13802
- }
13803
- return {
13804
- "data-line": String(lineNumber - 1),
13805
- style: {
13806
- display: "block",
13807
- backgroundColor: severity === "error" ? "rgba(248, 113, 113, 0.18)" : "rgba(251, 191, 36, 0.18)",
13808
- // amber-400 @ 18%
13809
- borderLeft: `3px solid ${severity === "error" ? "#ef4444" : "#f59e0b"}`,
13810
- paddingLeft: "0.5rem",
13811
- marginLeft: "-0.5rem"
13812
- }
13813
- };
13814
- };
13815
- }, [errorLines]);
14107
+ const handleEditableChange = React79.useCallback((v) => {
14108
+ lastPropCodeRef.current = v;
14109
+ setEditableValue(v);
14110
+ onChange?.(v);
14111
+ }, [onChange]);
14112
+ const { caretMode } = useEditorCapabilities({
14113
+ editorId: editable ? editorId : void 0,
14114
+ textareaRef: editableTextareaRef,
14115
+ events: { onMotion, onOperate, onInsertText, onSetMode },
14116
+ applyChange: handleEditableChange
14117
+ });
14118
+ const [caretIndex, setCaretIndex] = React79.useState(0);
14119
+ const caretRowCol = React79.useMemo(() => {
14120
+ const before = editableValue.slice(0, Math.min(caretIndex, editableValue.length));
14121
+ const lines = before.split("\n");
14122
+ return { row: lines.length - 1, col: lines[lines.length - 1].length };
14123
+ }, [editableValue, caretIndex]);
14124
+ const errorLineProps = React79.useMemo(() => buildLineProps(errorLines), [errorLines]);
14125
+ const viewerLineProps = React79.useMemo(
14126
+ () => buildLineProps(errorLines, "px-4 py-0.5 hover:bg-muted/50"),
14127
+ [errorLines]
14128
+ );
13816
14129
  const isFoldable = foldableProp ?? true;
13817
14130
  const [collapsed, setCollapsed] = React79.useState(() => /* @__PURE__ */ new Set());
13818
14131
  const foldRegions = React79.useMemo(
@@ -13935,6 +14248,110 @@ var init_CodeBlock = __esm({
13935
14248
  ),
13936
14249
  [code, overCapacity, plainCodeColor, language, activeStyle, languageReady]
13937
14250
  );
14251
+ const viewerOverCapacity = activeCode.length > HIGHLIGHT_CAPACITY_BYTES;
14252
+ const viewerHighlightedElement = React79.useMemo(
14253
+ () => viewerOverCapacity ? /* @__PURE__ */ jsxRuntime.jsx(
14254
+ "div",
14255
+ {
14256
+ className: "px-4 py-0.5",
14257
+ style: {
14258
+ margin: 0,
14259
+ whiteSpace: wrap ? "pre-wrap" : "pre",
14260
+ wordBreak: wrap ? "break-all" : "normal",
14261
+ color: viewerPlainCodeColor,
14262
+ fontFamily: MONO_FONT_FAMILY,
14263
+ fontSize: "12px",
14264
+ lineHeight: "1.6"
14265
+ },
14266
+ children: activeCode
14267
+ }
14268
+ ) : /* @__PURE__ */ jsxRuntime.jsx(
14269
+ SyntaxHighlighter__default.default,
14270
+ {
14271
+ PreTag: "div",
14272
+ language: activeLanguage,
14273
+ style: viewerStyle,
14274
+ wrapLines: true,
14275
+ wrapLongLines: wrap,
14276
+ showLineNumbers,
14277
+ lineNumberStyle: VIEWER_LINE_NUMBER_STYLE,
14278
+ lineProps: viewerLineProps,
14279
+ customStyle: {
14280
+ backgroundColor: "transparent",
14281
+ borderRadius: 0,
14282
+ padding: "0.25rem 0",
14283
+ margin: 0,
14284
+ whiteSpace: wrap ? "pre-wrap" : "pre",
14285
+ wordBreak: wrap ? "break-all" : "normal",
14286
+ fontFamily: MONO_FONT_FAMILY,
14287
+ fontSize: "12px",
14288
+ lineHeight: "1.6"
14289
+ },
14290
+ codeTagProps: { style: { fontFamily: MONO_FONT_FAMILY, fontSize: "12px", lineHeight: "1.6" } },
14291
+ children: activeCode
14292
+ }
14293
+ ),
14294
+ [activeCode, viewerOverCapacity, viewerPlainCodeColor, activeLanguage, viewerStyle, wrap, showLineNumbers, viewerLineProps, languageReady]
14295
+ );
14296
+ const diffOverCapacity = React79.useMemo(
14297
+ () => !!diffLines && diffLines.reduce((n, l) => n + l.content.length + 1, 0) > HIGHLIGHT_CAPACITY_BYTES,
14298
+ [diffLines]
14299
+ );
14300
+ const diffRowElements = React79.useMemo(() => {
14301
+ if (!diffLines) return null;
14302
+ return diffLines.map((line, idx) => {
14303
+ const style = DIFF_STYLES[line.type] ?? DIFF_STYLE_FALLBACK;
14304
+ return /* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { gap: "none", align: "start", className: cn(style.bg, "px-4 py-0.5"), children: [
14305
+ showLineNumbers && /* @__PURE__ */ jsxRuntime.jsx(
14306
+ exports.Typography,
14307
+ {
14308
+ variant: "caption",
14309
+ color: "secondary",
14310
+ className: "w-8 text-right mr-3 select-none tabular-nums flex-shrink-0",
14311
+ children: line.lineNumber ?? ""
14312
+ }
14313
+ ),
14314
+ /* @__PURE__ */ jsxRuntime.jsxs(
14315
+ exports.Typography,
14316
+ {
14317
+ variant: "caption",
14318
+ className: cn("font-mono flex-1 min-w-0", style.text, wrap ? "whitespace-pre-wrap break-all" : "whitespace-pre"),
14319
+ children: [
14320
+ /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { as: "span", className: "select-none opacity-50 mr-2", children: style.prefix }),
14321
+ diffOverCapacity ? line.content || " " : /* @__PURE__ */ jsxRuntime.jsx(
14322
+ SyntaxHighlighter__default.default,
14323
+ {
14324
+ PreTag: "span",
14325
+ CodeTag: "span",
14326
+ language: activeLanguage,
14327
+ style: viewerStyle,
14328
+ customStyle: {
14329
+ display: "inline",
14330
+ background: "transparent",
14331
+ padding: 0,
14332
+ margin: 0,
14333
+ whiteSpace: wrap ? "pre-wrap" : "pre",
14334
+ wordBreak: wrap ? "break-all" : "normal",
14335
+ fontFamily: "inherit",
14336
+ fontSize: "inherit",
14337
+ lineHeight: "inherit"
14338
+ },
14339
+ codeTagProps: {
14340
+ style: {
14341
+ whiteSpace: wrap ? "pre-wrap" : "pre",
14342
+ fontFamily: "inherit",
14343
+ fontSize: "inherit"
14344
+ }
14345
+ },
14346
+ children: line.content || " "
14347
+ }
14348
+ )
14349
+ ]
14350
+ }
14351
+ )
14352
+ ] }, idx);
14353
+ });
14354
+ }, [diffLines, showLineNumbers, wrap, diffOverCapacity, activeLanguage, viewerStyle, languageReady]);
13938
14355
  React79.useLayoutEffect(() => {
13939
14356
  const container = codeRef.current;
13940
14357
  if (!container) return;
@@ -14078,7 +14495,6 @@ var init_CodeBlock = __esm({
14078
14495
  label: file.label,
14079
14496
  content: null
14080
14497
  }));
14081
- const lines = activeCode.split("\n");
14082
14498
  return /* @__PURE__ */ jsxRuntime.jsx(exports.Card, { className: cn("overflow-hidden", className), children: /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", flexDirection: "column" }, children: [
14083
14499
  tabItems && tabItems.length > 1 && /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { className: "border-b border-border", children: /* @__PURE__ */ jsxRuntime.jsx(
14084
14500
  exports.Tabs,
@@ -14141,49 +14557,7 @@ var init_CodeBlock = __esm({
14141
14557
  ]
14142
14558
  }
14143
14559
  ),
14144
- /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { className: "overflow-auto bg-muted/20", style: { maxHeight }, children: diffLines ? /* @__PURE__ */ jsxRuntime.jsx("div", { style: { display: "flex", flexDirection: "column" }, className: "font-mono text-xs", children: diffLines.map((line, idx) => {
14145
- const style = DIFF_STYLES[line.type] ?? DIFF_STYLE_FALLBACK;
14146
- return /* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { gap: "none", align: "start", className: cn(style.bg, "px-4 py-0.5"), children: [
14147
- showLineNumbers && /* @__PURE__ */ jsxRuntime.jsx(
14148
- exports.Typography,
14149
- {
14150
- variant: "caption",
14151
- color: "secondary",
14152
- className: "w-8 text-right mr-3 select-none tabular-nums flex-shrink-0",
14153
- children: line.lineNumber ?? ""
14154
- }
14155
- ),
14156
- /* @__PURE__ */ jsxRuntime.jsxs(
14157
- exports.Typography,
14158
- {
14159
- variant: "caption",
14160
- className: cn("font-mono flex-1 min-w-0", style.text, wrap ? "whitespace-pre-wrap break-all" : "whitespace-pre"),
14161
- children: [
14162
- /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { as: "span", className: "select-none opacity-50 mr-2", children: style.prefix }),
14163
- line.content
14164
- ]
14165
- }
14166
- )
14167
- ] }, idx);
14168
- }) }) : /* @__PURE__ */ jsxRuntime.jsx("div", { style: { display: "flex", flexDirection: "column" }, className: "font-mono text-xs", children: lines.map((line, idx) => /* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { gap: "none", align: "start", className: "px-4 py-0.5 hover:bg-muted/50", children: [
14169
- showLineNumbers && /* @__PURE__ */ jsxRuntime.jsx(
14170
- exports.Typography,
14171
- {
14172
- variant: "caption",
14173
- color: "secondary",
14174
- className: "w-8 text-right mr-4 select-none tabular-nums flex-shrink-0",
14175
- children: idx + 1
14176
- }
14177
- ),
14178
- /* @__PURE__ */ jsxRuntime.jsx(
14179
- exports.Typography,
14180
- {
14181
- variant: "caption",
14182
- className: cn("font-mono flex-1 min-w-0", wrap ? "whitespace-pre-wrap break-all" : "whitespace-pre"),
14183
- children: line || " "
14184
- }
14185
- )
14186
- ] }, idx)) }) })
14560
+ /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { className: "overflow-auto bg-muted/20", style: { maxHeight }, children: diffLines ? /* @__PURE__ */ jsxRuntime.jsx("div", { style: { display: "flex", flexDirection: "column" }, className: "font-mono text-xs", children: diffRowElements }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "font-mono text-xs", children: viewerHighlightedElement }) })
14187
14561
  ] }) });
14188
14562
  }
14189
14563
  const hasHeader = showLanguageBadge || effectiveCopy;
@@ -14270,13 +14644,11 @@ var init_CodeBlock = __esm({
14270
14644
  {
14271
14645
  ref: editableTextareaRef,
14272
14646
  defaultValue: code,
14273
- onChange: (e) => {
14274
- const v = e.target.value;
14275
- lastPropCodeRef.current = v;
14276
- setEditableValue(v);
14277
- onChange?.(v);
14278
- },
14647
+ onChange: (e) => handleEditableChange(e.target.value),
14279
14648
  onScroll: handleEditableScroll,
14649
+ onSelect: (e) => setCaretIndex(e.currentTarget.selectionStart),
14650
+ onFocus: editorId ? () => eventBus.emit(`UI:${onEditorFocus}`, { editorId }) : void 0,
14651
+ onBlur: editorId ? () => eventBus.emit(`UI:${onEditorBlur}`, { editorId }) : void 0,
14280
14652
  spellCheck: false,
14281
14653
  style: {
14282
14654
  position: "absolute",
@@ -14291,7 +14663,7 @@ var init_CodeBlock = __esm({
14291
14663
  resize: "none",
14292
14664
  backgroundColor: "transparent",
14293
14665
  color: "transparent",
14294
- caretColor: "#e6e6e6",
14666
+ caretColor: caretMode === "block" ? "transparent" : "#e6e6e6",
14295
14667
  WebkitTextFillColor: "transparent",
14296
14668
  fontFamily: 'ui-monospace, SFMono-Regular, Menlo, Monaco, "Cascadia Mono", "Courier New", monospace',
14297
14669
  fontSize: "13px",
@@ -14302,6 +14674,22 @@ var init_CodeBlock = __esm({
14302
14674
  }
14303
14675
  },
14304
14676
  editableTextareaKey
14677
+ ),
14678
+ caretMode !== "bar" && /* @__PURE__ */ jsxRuntime.jsx(
14679
+ "span",
14680
+ {
14681
+ "aria-hidden": true,
14682
+ style: {
14683
+ position: "absolute",
14684
+ top: `calc(1rem + ${caretRowCol.row * 19.5}px)`,
14685
+ left: `calc(1rem + ${caretRowCol.col}ch)`,
14686
+ width: "1ch",
14687
+ height: caretMode === "block" ? "19.5px" : "2px",
14688
+ backgroundColor: caretMode === "block" ? "rgba(230, 230, 230, 0.5)" : void 0,
14689
+ borderBottom: caretMode === "underline" ? "2px solid #e6e6e6" : void 0,
14690
+ pointerEvents: "none"
14691
+ }
14692
+ }
14305
14693
  )
14306
14694
  ]
14307
14695
  }
@@ -14329,55 +14717,175 @@ var init_CodeBlock = __esm({
14329
14717
  )
14330
14718
  ] });
14331
14719
  },
14332
- (prev, next) => prev.language === next.language && prev.code === next.code && prev.showCopyButton === next.showCopyButton && prev.showCopy === next.showCopy && prev.maxHeight === next.maxHeight && prev.foldable === next.foldable && prev.editable === next.editable && prev.onChange === next.onChange && prev.errorLines === next.errorLines && prev.mode === next.mode && prev.title === next.title && prev.diff === next.diff && prev.files === next.files && prev.actions === next.actions && prev.isLoading === next.isLoading && prev.error === next.error
14720
+ (prev, next) => prev.language === next.language && prev.code === next.code && prev.showCopyButton === next.showCopyButton && prev.showCopy === next.showCopy && prev.maxHeight === next.maxHeight && prev.foldable === next.foldable && prev.editable === next.editable && prev.onChange === next.onChange && prev.errorLines === next.errorLines && prev.mode === next.mode && prev.title === next.title && prev.diff === next.diff && prev.files === next.files && prev.actions === next.actions && prev.isLoading === next.isLoading && prev.error === next.error && prev.editorId === next.editorId && prev.onEditorFocus === next.onEditorFocus && prev.onEditorBlur === next.onEditorBlur && prev.onMotion === next.onMotion && prev.onOperate === next.onOperate && prev.onInsertText === next.onInsertText && prev.onSetMode === next.onSetMode && prev.motions === next.motions && prev.operators === next.operators
14333
14721
  );
14334
14722
  exports.CodeBlock.displayName = "CodeBlock";
14335
14723
  }
14336
14724
  });
14725
+
14726
+ // components/core/molecules/markdown/mermaidSource.ts
14727
+ function isQuoted(label) {
14728
+ const trimmed = label.trim();
14729
+ return trimmed.length === 0 || trimmed.startsWith('"') && trimmed.endsWith('"') && trimmed.length > 1;
14730
+ }
14731
+ function quote(label) {
14732
+ return `"${label.replace(/"/g, "#quot;")}"`;
14733
+ }
14734
+ function isIdentifierChar(ch) {
14735
+ return /[A-Za-z0-9_\-.]/.test(ch);
14736
+ }
14737
+ function declaredType(code) {
14738
+ for (const line of code.split("\n")) {
14739
+ const trimmed = line.trim();
14740
+ if (trimmed.length === 0 || trimmed.startsWith("%%")) continue;
14741
+ return trimmed;
14742
+ }
14743
+ return "";
14744
+ }
14745
+ function isFlowchart(code) {
14746
+ return FLOWCHART_DIRECTIVE.test(declaredType(code));
14747
+ }
14748
+ function quoteNodeLabels(code) {
14749
+ let out = "";
14750
+ let i = 0;
14751
+ while (i < code.length) {
14752
+ const shape = NODE_SHAPES.find(([open2]) => code.startsWith(open2, i));
14753
+ const precededByIdentifier = i > 0 && isIdentifierChar(code[i - 1] ?? "");
14754
+ if (shape === void 0 || !precededByIdentifier) {
14755
+ out += code[i];
14756
+ i += 1;
14757
+ continue;
14758
+ }
14759
+ const [open, close] = shape;
14760
+ const contentStart = i + open.length;
14761
+ const closeAt = code.indexOf(close, contentStart);
14762
+ const newlineAt = code.indexOf("\n", contentStart);
14763
+ if (closeAt === -1 || newlineAt !== -1 && newlineAt < closeAt) {
14764
+ out += code[i];
14765
+ i += 1;
14766
+ continue;
14767
+ }
14768
+ const label = code.slice(contentStart, closeAt);
14769
+ out += open + (isQuoted(label) ? label : quote(label)) + close;
14770
+ i = closeAt + close.length;
14771
+ }
14772
+ return out;
14773
+ }
14774
+ function quoteEdgeLabels(code) {
14775
+ return code.split("\n").map((line) => {
14776
+ let out = "";
14777
+ let rest = line;
14778
+ for (; ; ) {
14779
+ const open = rest.indexOf("|");
14780
+ if (open === -1) break;
14781
+ const close = rest.indexOf("|", open + 1);
14782
+ if (close === -1) break;
14783
+ const label = rest.slice(open + 1, close);
14784
+ out += rest.slice(0, open + 1) + (isQuoted(label) ? label : quote(label)) + "|";
14785
+ rest = rest.slice(close + 1);
14786
+ }
14787
+ return out + rest;
14788
+ }).join("\n");
14789
+ }
14790
+ function quoteSubgraphTitles(code) {
14791
+ return code.split("\n").map((line) => {
14792
+ const match = /^(\s*subgraph\s+)(.+?)(\s*)$/.exec(line);
14793
+ if (match === null) return line;
14794
+ const [, prefix, title, trailing] = match;
14795
+ if (title === void 0 || prefix === void 0) return line;
14796
+ if (isQuoted(title) || title.includes("[")) return line;
14797
+ return prefix + quote(title) + (trailing ?? "");
14798
+ }).join("\n");
14799
+ }
14800
+ function mermaidRepairCandidates(code) {
14801
+ if (!isFlowchart(code)) return [];
14802
+ const nodes = quoteNodeLabels(code);
14803
+ const nodesAndEdges = quoteEdgeLabels(nodes);
14804
+ const all = quoteSubgraphTitles(nodesAndEdges);
14805
+ const ordered2 = [nodes, nodesAndEdges, all];
14806
+ const seen = /* @__PURE__ */ new Set([code]);
14807
+ const candidates = [];
14808
+ for (const candidate of ordered2) {
14809
+ if (seen.has(candidate)) continue;
14810
+ seen.add(candidate);
14811
+ candidates.push(candidate);
14812
+ }
14813
+ return candidates;
14814
+ }
14815
+ var NODE_SHAPES, FLOWCHART_DIRECTIVE;
14816
+ var init_mermaidSource = __esm({
14817
+ "components/core/molecules/markdown/mermaidSource.ts"() {
14818
+ NODE_SHAPES = [
14819
+ ["[[", "]]"],
14820
+ ["[(", ")]"],
14821
+ ["([", "])"],
14822
+ ["((", "))"],
14823
+ ["{{", "}}"],
14824
+ ["[", "]"],
14825
+ ["(", ")"],
14826
+ ["{", "}"]
14827
+ ];
14828
+ FLOWCHART_DIRECTIVE = /^(?:graph|flowchart)\b/;
14829
+ }
14830
+ });
14337
14831
  function loadMermaid() {
14338
14832
  mermaidModule ?? (mermaidModule = import('mermaid').then((m) => m.default));
14339
14833
  return mermaidModule;
14340
14834
  }
14341
- var mermaidModule, MermaidDiagram;
14835
+ var log6, mermaidModule, MermaidDiagram;
14342
14836
  var init_MermaidDiagram = __esm({
14343
14837
  "components/core/molecules/markdown/MermaidDiagram.tsx"() {
14344
14838
  init_Box();
14345
14839
  init_Typography();
14346
14840
  init_CodeBlock();
14841
+ init_mermaidSource();
14347
14842
  init_cn();
14843
+ log6 = logger.createLogger("almadar:ui:mermaid-diagram");
14348
14844
  mermaidModule = null;
14349
14845
  MermaidDiagram = React79__namespace.default.memo(
14350
14846
  ({ code, className }) => {
14351
14847
  const { resolvedMode } = context.useTheme();
14848
+ const { t } = hooks.useTranslate();
14352
14849
  const containerRef = React79.useRef(null);
14353
- const [error, setError] = React79.useState(null);
14850
+ const [unrenderable, setUnrenderable] = React79.useState(false);
14354
14851
  const reactId = React79.useId();
14355
14852
  React79.useEffect(() => {
14356
14853
  let active = true;
14357
14854
  void (async () => {
14358
- try {
14359
- const mermaid = await loadMermaid();
14360
- mermaid.initialize({
14361
- startOnLoad: false,
14362
- securityLevel: "strict",
14363
- theme: resolvedMode === "dark" ? "dark" : "default"
14364
- });
14365
- const domId = `mermaid-${reactId.replace(/[^a-zA-Z0-9]/g, "")}`;
14366
- const { svg } = await mermaid.render(domId, code);
14367
- if (!active || !containerRef.current) return;
14368
- containerRef.current.innerHTML = svg;
14369
- setError(null);
14370
- } catch (err) {
14371
- if (active) setError(err instanceof Error ? err.message : String(err));
14855
+ const mermaid = await loadMermaid();
14856
+ mermaid.initialize({
14857
+ startOnLoad: false,
14858
+ securityLevel: "strict",
14859
+ theme: resolvedMode === "dark" ? "dark" : "default"
14860
+ });
14861
+ const domId = `mermaid-${reactId.replace(/[^a-zA-Z0-9]/g, "")}`;
14862
+ let firstError = null;
14863
+ for (const [index, source] of [code, ...mermaidRepairCandidates(code)].entries()) {
14864
+ try {
14865
+ const { svg } = await mermaid.render(domId, source);
14866
+ if (!active) return;
14867
+ const container = containerRef.current;
14868
+ if (container === null) return;
14869
+ container.innerHTML = svg;
14870
+ container.dataset.mermaidRepaired = String(index > 0);
14871
+ setUnrenderable(false);
14872
+ if (index > 0) log6.debug("mermaid:repaired", { candidate: index });
14873
+ return;
14874
+ } catch (err) {
14875
+ firstError ?? (firstError = err instanceof Error ? err : new Error(String(err)));
14876
+ }
14372
14877
  }
14878
+ if (!active) return;
14879
+ log6.warn("mermaid:unrenderable", { error: firstError?.message ?? "", code });
14880
+ setUnrenderable(true);
14373
14881
  })();
14374
14882
  return () => {
14375
14883
  active = false;
14376
14884
  };
14377
14885
  }, [code, resolvedMode, reactId]);
14378
14886
  return /* @__PURE__ */ jsxRuntime.jsxs(exports.Box, { className: cn("not-prose my-4", className), children: [
14379
- error !== null && /* @__PURE__ */ jsxRuntime.jsxs(exports.Box, { className: "space-y-2 mb-2", children: [
14380
- /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "caption", className: "text-error whitespace-pre-wrap", children: error }),
14887
+ unrenderable && /* @__PURE__ */ jsxRuntime.jsxs(exports.Box, { className: "space-y-2 mb-2", "data-testid": "mermaid-unrenderable", children: [
14888
+ /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "caption", className: "text-muted-foreground", children: t("mermaid.unrenderable") }),
14381
14889
  /* @__PURE__ */ jsxRuntime.jsx(exports.CodeBlock, { code, language: "mermaid" })
14382
14890
  ] }),
14383
14891
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -14386,7 +14894,7 @@ var init_MermaidDiagram = __esm({
14386
14894
  ref: containerRef,
14387
14895
  "data-testid": "mermaid-diagram",
14388
14896
  className: "overflow-x-auto",
14389
- style: error !== null ? { display: "none" } : void 0
14897
+ style: unrenderable ? { display: "none" } : void 0
14390
14898
  }
14391
14899
  )
14392
14900
  ] });
@@ -17791,14 +18299,14 @@ function useSafeEventBus2() {
17791
18299
  } };
17792
18300
  }
17793
18301
  }
17794
- var log6, lookStyles4; exports.ButtonGroup = void 0;
18302
+ var log7, lookStyles4; exports.ButtonGroup = void 0;
17795
18303
  var init_ButtonGroup = __esm({
17796
18304
  "components/core/molecules/ButtonGroup.tsx"() {
17797
18305
  "use client";
17798
18306
  init_cn();
17799
18307
  init_atoms();
17800
18308
  init_useEventBus();
17801
- log6 = logger.createLogger("almadar:ui:button-group");
18309
+ log7 = logger.createLogger("almadar:ui:button-group");
17802
18310
  lookStyles4 = {
17803
18311
  "right-aligned-buttons": "",
17804
18312
  "floating-bar": "fixed bottom-section left-1/2 -translate-x-1/2 shadow-elevation-toast bg-card p-card-sm rounded-container",
@@ -17879,7 +18387,7 @@ var init_ButtonGroup = __esm({
17879
18387
  {
17880
18388
  variant: "ghost",
17881
18389
  onClick: () => {
17882
- log6.debug("Filter clicked", { field: filter.field });
18390
+ log7.debug("Filter clicked", { field: filter.field });
17883
18391
  },
17884
18392
  children: filter.label
17885
18393
  },
@@ -18897,7 +19405,7 @@ function collectDrawnItems(nodes) {
18897
19405
  for (const n of nodes) {
18898
19406
  switch (n.type) {
18899
19407
  case "draw-sprite":
18900
- if (isValidScenePos(n.position)) out.push({ pos: n.position, id: n.id, anchor: n.anchor, width: n.width, height: n.height });
19408
+ if (isValidScenePos(n.position)) out.push({ pos: n.position, id: n.id, anchor: n.anchor, width: n.width, height: n.height, rotation: n.rotation });
18901
19409
  break;
18902
19410
  case "draw-shape":
18903
19411
  case "draw-text":
@@ -18907,7 +19415,7 @@ function collectDrawnItems(nodes) {
18907
19415
  break;
18908
19416
  case "draw-sprite-layer":
18909
19417
  for (const it of n.items) {
18910
- if (isValidScenePos(it.position)) out.push({ pos: it.position, id: it.id, anchor: it.anchor, width: it.width, height: it.height });
19418
+ if (isValidScenePos(it.position)) out.push({ pos: it.position, id: it.id, anchor: it.anchor, width: it.width, height: it.height, rotation: it.rotation });
18911
19419
  }
18912
19420
  break;
18913
19421
  case "draw-shape-layer":
@@ -18927,15 +19435,39 @@ function buildHitIndex(items) {
18927
19435
  }
18928
19436
  return m;
18929
19437
  }
19438
+ function withPreviewPosition(nodes, id, pos) {
19439
+ return nodes.map((n) => {
19440
+ if (n.type === "draw-sprite-layer" || n.type === "draw-shape-layer" || n.type === "draw-text-layer") {
19441
+ if (!n.items.some((it) => it.id === id)) return n;
19442
+ return { ...n, items: n.items.map((it) => it.id === id ? { ...it, position: pos } : it) };
19443
+ }
19444
+ if (n.type === "draw-group") {
19445
+ if (!Array.isArray(n.items)) return n;
19446
+ return { ...n, items: withPreviewPosition(n.items, id, pos) };
19447
+ }
19448
+ if (n.id === id && "position" in n) {
19449
+ return { ...n, position: pos };
19450
+ }
19451
+ return n;
19452
+ });
19453
+ }
18930
19454
  function hitTestSprites(items, projector, point) {
18931
19455
  for (let i = items.length - 1; i >= 0; i--) {
18932
19456
  const it = items[i];
18933
19457
  if (it.id === void 0) continue;
18934
19458
  const r = spriteRect(projector, { position: it.pos, anchor: it.anchor, width: it.width, height: it.height });
18935
- if (point.x >= r.x && point.x <= r.x + r.w && point.y >= r.y && point.y <= r.y + r.h) return it.id;
19459
+ const test = it.rotation ? rotatePoint(point, { x: r.x + r.w / 2, y: r.y + r.h / 2 }, -it.rotation) : point;
19460
+ if (test.x >= r.x && test.x <= r.x + r.w && test.y >= r.y && test.y <= r.y + r.h) return it.id;
18936
19461
  }
18937
19462
  return void 0;
18938
19463
  }
19464
+ function rotatePoint(p, center, radians) {
19465
+ const cos = Math.cos(radians);
19466
+ const sin = Math.sin(radians);
19467
+ const dx = p.x - center.x;
19468
+ const dy = p.y - center.y;
19469
+ return { x: center.x + dx * cos - dy * sin, y: center.y + dx * sin + dy * cos };
19470
+ }
18939
19471
  var init_hitTest = __esm({
18940
19472
  "lib/drawable/hitTest.ts"() {
18941
19473
  init_contract();
@@ -18944,6 +19476,41 @@ var init_hitTest = __esm({
18944
19476
  function normalizeBackdrop(bg) {
18945
19477
  return typeof bg === "string" ? { url: bg, role: "decoration", category: "background" } : bg;
18946
19478
  }
19479
+ function selectionOverlayNodes(projector, item) {
19480
+ const r = spriteRect(projector, { position: item.pos, anchor: item.anchor, width: item.width, height: item.height });
19481
+ const tw = projector.tileWidth;
19482
+ const cellTopLeft = projector.anchorPoint(item.pos, "top-left");
19483
+ const offsetX = (r.x - cellTopLeft.x) / tw;
19484
+ const offsetY = (r.y - cellTopLeft.y) / tw;
19485
+ const width = r.w / tw;
19486
+ const height = r.h / tw;
19487
+ const handle = EDIT_HANDLE_SIZE_PX / tw;
19488
+ const ring = {
19489
+ type: "draw-shape",
19490
+ shape: "rect",
19491
+ position: item.pos,
19492
+ anchor: "top-left",
19493
+ offsetX,
19494
+ offsetY,
19495
+ width,
19496
+ height,
19497
+ stroke: EDIT_SELECTION_COLOR,
19498
+ strokeWidth: 2,
19499
+ fill: "none"
19500
+ };
19501
+ const handles = EDIT_SELECTION_CORNERS.map(([cx, cy]) => ({
19502
+ type: "draw-shape",
19503
+ shape: "rect",
19504
+ position: item.pos,
19505
+ anchor: "top-left",
19506
+ offsetX: offsetX + cx * width - handle / 2,
19507
+ offsetY: offsetY + cy * height - handle / 2,
19508
+ width: handle,
19509
+ height: handle,
19510
+ fill: EDIT_SELECTION_COLOR
19511
+ }));
19512
+ return [ring, ...handles];
19513
+ }
18947
19514
  function Canvas2D({
18948
19515
  className,
18949
19516
  isLoading = false,
@@ -18957,6 +19524,12 @@ function Canvas2D({
18957
19524
  tileLeaveEvent,
18958
19525
  keyMap,
18959
19526
  keyUpMap,
19527
+ editable = false,
19528
+ selectedId = null,
19529
+ onSelect,
19530
+ onMove,
19531
+ selectEvent,
19532
+ moveEvent,
18960
19533
  camera = "pan-zoom",
18961
19534
  scale = 0.4,
18962
19535
  tileWidth,
@@ -19214,10 +19787,22 @@ function Canvas2D({
19214
19787
  painter.scale(cam.zoom, cam.zoom);
19215
19788
  painter.translate(-viewportSize.width / 2 - cam.x, -viewportSize.height / 2 - cam.y);
19216
19789
  const dctx = { projector, time: timeMs, invalidate: bumpAtlas };
19217
- for (const node of drawables) paintDrawable(painter, node, dctx);
19790
+ let paintNodes = drawables;
19791
+ if (editable) {
19792
+ const drag = editDragRef.current;
19793
+ if (drag && drag.moved) {
19794
+ paintNodes = withPreviewPosition(paintNodes, drag.id, { x: drag.previewX, y: drag.previewY });
19795
+ }
19796
+ const selectedItem = selectedId != null ? drawnItems.find((it) => it.id === selectedId) : void 0;
19797
+ if (selectedItem) {
19798
+ const overlaySource = drag && drag.moved && drag.id === selectedId ? { ...selectedItem, pos: { x: drag.previewX, y: drag.previewY } } : selectedItem;
19799
+ paintNodes = [...paintNodes, ...selectionOverlayNodes(projector, overlaySource)];
19800
+ }
19801
+ }
19802
+ for (const node of paintNodes) paintDrawable(painter, node, dctx);
19218
19803
  painter.restore();
19219
- scheduleAnimation(drawables);
19220
- }, [viewportSize, backgroundImage, bgColor, drawables, projector, cameraRef, bumpAtlas, getImage, cameraPos, defaultGridFocus, camera, dragDistance]);
19804
+ scheduleAnimation(paintNodes);
19805
+ }, [viewportSize, backgroundImage, bgColor, drawables, projector, cameraRef, bumpAtlas, getImage, cameraPos, defaultGridFocus, camera, dragDistance, editable, selectedId, drawnItems]);
19221
19806
  React79.useEffect(() => {
19222
19807
  drawTimeRef.current = draw;
19223
19808
  }, [draw]);
@@ -19262,23 +19847,83 @@ function Canvas2D({
19262
19847
  };
19263
19848
  }, [camera, followTarget, lerpToTarget, draw]);
19264
19849
  const singlePointerActiveRef = React79.useRef(false);
19850
+ const editDragRef = React79.useRef(null);
19851
+ const pointerToScene = React79.useCallback((clientX, clientY) => {
19852
+ const canvas = canvasRef.current;
19853
+ if (!canvas) return { x: 0, y: 0 };
19854
+ const world = screenToWorld(clientX, clientY, canvas, viewportSize);
19855
+ const adjustedX = world.x - scaledTileWidth / 2;
19856
+ const adjustedY = squareGrid ? world.y - scaledTileWidth / 2 : world.y - scaledDiamondTopY - scaledFloorHeight / 2;
19857
+ return unproject(adjustedX, adjustedY);
19858
+ }, [screenToWorld, viewportSize, scaledTileWidth, squareGrid, scaledDiamondTopY, scaledFloorHeight, unproject]);
19265
19859
  const handleCanvasPointerDown = React79.useCallback((e) => {
19266
19860
  singlePointerActiveRef.current = true;
19861
+ if (editable) {
19862
+ if (!canvasRef.current) return;
19863
+ const world = screenToWorld(e.clientX, e.clientY, canvasRef.current, viewportSize);
19864
+ const hitId = hitTestSprites(drawnItems, projector, world);
19865
+ if (hitId === void 0) return;
19866
+ const item = [...drawnItems].reverse().find((it) => it.id === hitId);
19867
+ if (!item) return;
19868
+ editDragRef.current = {
19869
+ id: hitId,
19870
+ pointerId: e.pointerId,
19871
+ startClientX: e.clientX,
19872
+ startClientY: e.clientY,
19873
+ startSceneX: item.pos.x,
19874
+ startSceneY: item.pos.y,
19875
+ moved: false,
19876
+ previewX: item.pos.x,
19877
+ previewY: item.pos.y
19878
+ };
19879
+ return;
19880
+ }
19267
19881
  if (enableCamera) handlePointerDown(e);
19268
- }, [enableCamera, handlePointerDown]);
19882
+ }, [editable, screenToWorld, viewportSize, drawnItems, projector, enableCamera, handlePointerDown]);
19269
19883
  const handleCanvasPointerMove = React79.useCallback((e) => {
19884
+ if (editable) {
19885
+ const drag = editDragRef.current;
19886
+ if (!drag || drag.pointerId !== e.pointerId) return;
19887
+ const dxPx = e.clientX - drag.startClientX;
19888
+ const dyPx = e.clientY - drag.startClientY;
19889
+ if (!drag.moved && Math.abs(dxPx) + Math.abs(dyPx) <= 5) return;
19890
+ drag.moved = true;
19891
+ const nowScene = pointerToScene(e.clientX, e.clientY);
19892
+ const startScene = pointerToScene(drag.startClientX, drag.startClientY);
19893
+ drag.previewX = drag.startSceneX + (nowScene.x - startScene.x);
19894
+ drag.previewY = drag.startSceneY + (nowScene.y - startScene.y);
19895
+ draw();
19896
+ return;
19897
+ }
19270
19898
  if (enableCamera) handlePointerMove(e, () => draw());
19271
- }, [enableCamera, handlePointerMove, draw]);
19899
+ }, [editable, pointerToScene, draw, enableCamera, handlePointerMove]);
19272
19900
  const handleCanvasHover = React79.useCallback((e) => {
19273
19901
  if (singlePointerActiveRef.current) return;
19274
19902
  if (!tileHoverEvent || !canvasRef.current) return;
19275
- const world = screenToWorld(e.clientX, e.clientY, canvasRef.current, viewportSize);
19276
- const adjustedX = world.x - scaledTileWidth / 2;
19277
- const adjustedY = squareGrid ? world.y - scaledTileWidth / 2 : world.y - scaledDiamondTopY - scaledFloorHeight / 2;
19278
- const isoPos = unproject(adjustedX, adjustedY);
19903
+ const isoPos = pointerToScene(e.clientX, e.clientY);
19279
19904
  eventBus.emit(`UI:${tileHoverEvent}`, { x: isoPos.x, y: isoPos.y });
19280
- }, [screenToWorld, viewportSize, scaledTileWidth, squareGrid, scaledDiamondTopY, scaledFloorHeight, unproject, tileHoverEvent, eventBus]);
19905
+ }, [pointerToScene, tileHoverEvent, eventBus]);
19281
19906
  const handleCanvasPointerUp = React79.useCallback((e) => {
19907
+ if (editable) {
19908
+ singlePointerActiveRef.current = false;
19909
+ const drag = editDragRef.current;
19910
+ if (drag && drag.pointerId === e.pointerId) {
19911
+ editDragRef.current = null;
19912
+ if (drag.moved) {
19913
+ onMove?.(drag.id, drag.previewX, drag.previewY);
19914
+ if (moveEvent) eventBus.emit(`UI:${moveEvent}`, { id: drag.id, x: drag.previewX, y: drag.previewY });
19915
+ draw();
19916
+ return;
19917
+ }
19918
+ const next = selectedId === drag.id ? null : drag.id;
19919
+ onSelect?.(next);
19920
+ if (selectEvent) eventBus.emit(`UI:${selectEvent}`, { id: next });
19921
+ return;
19922
+ }
19923
+ onSelect?.(null);
19924
+ if (selectEvent) eventBus.emit(`UI:${selectEvent}`, { id: null });
19925
+ return;
19926
+ }
19282
19927
  singlePointerActiveRef.current = false;
19283
19928
  if (enableCamera) handlePointerUp();
19284
19929
  if (dragDistance() > 5) return;
@@ -19289,16 +19934,14 @@ function Canvas2D({
19289
19934
  eventBus.emit(`UI:${unitClickEvent}`, { unitId: spriteHit });
19290
19935
  return;
19291
19936
  }
19292
- const adjustedX = world.x - scaledTileWidth / 2;
19293
- const adjustedY = squareGrid ? world.y - scaledTileWidth / 2 : world.y - scaledDiamondTopY - scaledFloorHeight / 2;
19294
- const isoPos = unproject(adjustedX, adjustedY);
19937
+ const isoPos = pointerToScene(e.clientX, e.clientY);
19295
19938
  const hitId = hitIndex.get(`${isoPos.x},${isoPos.y}`);
19296
19939
  if (hitId !== void 0 && unitClickEvent) {
19297
19940
  eventBus.emit(`UI:${unitClickEvent}`, { unitId: hitId });
19298
19941
  } else if (tileClickEvent) {
19299
19942
  eventBus.emit(`UI:${tileClickEvent}`, { x: isoPos.x, y: isoPos.y });
19300
19943
  }
19301
- }, [enableCamera, handlePointerUp, dragDistance, screenToWorld, viewportSize, scaledTileWidth, squareGrid, scaledDiamondTopY, scaledFloorHeight, unproject, hitIndex, drawnItems, projector, tileClickEvent, unitClickEvent, eventBus]);
19944
+ }, [editable, selectedId, onMove, moveEvent, onSelect, selectEvent, eventBus, draw, enableCamera, handlePointerUp, dragDistance, screenToWorld, viewportSize, drawnItems, projector, tileClickEvent, unitClickEvent, hitIndex, pointerToScene]);
19302
19945
  const handleCanvasPointerLeave = React79.useCallback(() => {
19303
19946
  handleMouseLeave();
19304
19947
  if (tileLeaveEvent) eventBus.emit(`UI:${tileLeaveEvent}`, {});
@@ -19317,7 +19960,7 @@ function Canvas2D({
19317
19960
  }, [enableCamera, handlePointerUp]);
19318
19961
  const gestureHandlers = useCanvasGestures({
19319
19962
  canvasRef,
19320
- enabled: enableCamera || !!tileHoverEvent || !!tileClickEvent || !!unitClickEvent,
19963
+ enabled: enableCamera || !!tileHoverEvent || !!tileClickEvent || !!unitClickEvent || editable,
19321
19964
  onPointerDown: handleCanvasPointerDown,
19322
19965
  onPointerMove: handleCanvasPointerMove,
19323
19966
  onPointerUp: handleCanvasPointerUp,
@@ -19449,7 +20092,7 @@ function Canvas2D({
19449
20092
  }
19450
20093
  ) });
19451
20094
  }
19452
- var canvas2DLog;
20095
+ var canvas2DLog, EDIT_SELECTION_COLOR, EDIT_HANDLE_SIZE_PX, EDIT_SELECTION_CORNERS;
19453
20096
  var init_Canvas2D = __esm({
19454
20097
  "components/game/molecules/Canvas2D.tsx"() {
19455
20098
  "use client";
@@ -19474,8 +20117,12 @@ var init_Canvas2D = __esm({
19474
20117
  init_DrawGroup();
19475
20118
  init_registry();
19476
20119
  init_hitTest();
20120
+ init_contract();
19477
20121
  init_isometric();
19478
20122
  canvas2DLog = logger.createLogger("almadar:ui:game-canvas");
20123
+ EDIT_SELECTION_COLOR = "#3b82f6";
20124
+ EDIT_HANDLE_SIZE_PX = 8;
20125
+ EDIT_SELECTION_CORNERS = [[0, 0], [1, 0], [0, 1], [1, 1]];
19479
20126
  Canvas2D.displayName = "Canvas2D";
19480
20127
  }
19481
20128
  });
@@ -19517,6 +20164,12 @@ function Canvas({
19517
20164
  featureClickEvent,
19518
20165
  keyMap,
19519
20166
  keyUpMap,
20167
+ editable,
20168
+ selectedId,
20169
+ onSelect,
20170
+ onMove,
20171
+ selectEvent,
20172
+ moveEvent,
19520
20173
  children
19521
20174
  }) {
19522
20175
  canvasLog.debug("Canvas render", { mode, drawablesCount: drawables?.length, projection, camera: camera ? JSON.stringify(camera) : void 0 });
@@ -19594,6 +20247,12 @@ function Canvas({
19594
20247
  tileLeaveEvent,
19595
20248
  keyMap,
19596
20249
  keyUpMap,
20250
+ editable,
20251
+ selectedId,
20252
+ onSelect,
20253
+ onMove,
20254
+ selectEvent,
20255
+ moveEvent,
19597
20256
  ...children !== void 0 ? { children } : {}
19598
20257
  }
19599
20258
  );
@@ -21976,6 +22635,12 @@ function commandMatches(command, query) {
21976
22635
  if (matchesQuery(query, command.label)) return true;
21977
22636
  return (command.keywords ?? []).some((keyword) => matchesQuery(query, keyword));
21978
22637
  }
22638
+ function dispatchCommandPaletteCommand(command, deps) {
22639
+ if (command.disabled) return;
22640
+ if (command.event) deps.emit(`UI:${command.event}`, { commandId: command.id });
22641
+ if (command.action) deps.emit(`UI:${command.action}`, command.actionPayload ?? {});
22642
+ deps.onSelect?.(command);
22643
+ }
21979
22644
  var UNGROUPED; exports.CommandPalette = void 0;
21980
22645
  var init_CommandPalette = __esm({
21981
22646
  "components/core/molecules/CommandPalette.tsx"() {
@@ -22030,9 +22695,7 @@ var init_CommandPalette = __esm({
22030
22695
  const handleSelect = React79.useCallback(
22031
22696
  (command) => {
22032
22697
  if (command.disabled) return;
22033
- if (command.event) eventBus.emit(`UI:${command.event}`, { commandId: command.id });
22034
- if (command.action) eventBus.emit(`UI:${command.action}`, command.actionPayload ?? {});
22035
- onSelect?.(command);
22698
+ dispatchCommandPaletteCommand(command, { emit: eventBus.emit, onSelect });
22036
22699
  handleClose();
22037
22700
  },
22038
22701
  [eventBus, onSelect, handleClose]
@@ -28252,9 +28915,9 @@ function debug(...args) {
28252
28915
  const [first, ...rest] = args;
28253
28916
  const message = typeof first === "string" ? first : "<debug>";
28254
28917
  if (rest.length === 0 && typeof first === "string") {
28255
- log7.debug(message);
28918
+ log8.debug(message);
28256
28919
  } else {
28257
- log7.debug(message, { args: rest.length > 0 ? formatArgs(rest) : formatArgs([first]) });
28920
+ log8.debug(message, { args: rest.length > 0 ? formatArgs(rest) : formatArgs([first]) });
28258
28921
  }
28259
28922
  }
28260
28923
  function debugGroup(label) {
@@ -28282,11 +28945,11 @@ function toLogMetaValue(v) {
28282
28945
  }
28283
28946
  return String(v);
28284
28947
  }
28285
- var NAMESPACE, log7;
28948
+ var NAMESPACE, log8;
28286
28949
  var init_debug = __esm({
28287
28950
  "lib/debug.ts"() {
28288
28951
  NAMESPACE = "almadar:ui:debug";
28289
- log7 = logger.createLogger(NAMESPACE);
28952
+ log8 = logger.createLogger(NAMESPACE);
28290
28953
  logger.createLogger("almadar:ui:debug:input");
28291
28954
  logger.createLogger("almadar:ui:debug:collision");
28292
28955
  logger.createLogger("almadar:ui:debug:physics");
@@ -30972,15 +31635,19 @@ function GameAudioToggle({
30972
31635
  size = "sm",
30973
31636
  className,
30974
31637
  onAsset,
30975
- offAsset
31638
+ offAsset,
31639
+ toggleEvent
30976
31640
  }) {
30977
31641
  const ctx = providers.useGameAudioContextOptional();
30978
31642
  const [localMuted, setLocalMuted] = React79.useState(false);
30979
31643
  const muted = ctx ? ctx.muted : localMuted;
30980
31644
  const setMuted = ctx ? ctx.setMuted : setLocalMuted;
31645
+ const eventBus = useEventBus();
30981
31646
  const handleToggle = React79.useCallback(() => {
30982
- setMuted(!muted);
30983
- }, [muted, setMuted]);
31647
+ const next = !muted;
31648
+ setMuted(next);
31649
+ if (toggleEvent) eventBus.emit(`UI:${toggleEvent}`, { muted: next });
31650
+ }, [muted, setMuted, toggleEvent, eventBus]);
30984
31651
  const activeAsset = muted ? offAsset : onAsset;
30985
31652
  return /* @__PURE__ */ jsxRuntime.jsx(
30986
31653
  exports.Button,
@@ -30999,6 +31666,7 @@ var init_GameAudioToggle = __esm({
30999
31666
  "use client";
31000
31667
  init_atoms();
31001
31668
  init_cn();
31669
+ init_useEventBus();
31002
31670
  init_GameIcon();
31003
31671
  GameAudioToggle.displayName = "GameAudioToggle";
31004
31672
  }
@@ -31270,6 +31938,47 @@ var init_useGameAudio = __esm({
31270
31938
  useGameAudio.displayName = "useGameAudio";
31271
31939
  }
31272
31940
  });
31941
+ function GameAudioCue({
31942
+ cue,
31943
+ cueSeq,
31944
+ music,
31945
+ muted,
31946
+ volume,
31947
+ manifest,
31948
+ baseUrl
31949
+ }) {
31950
+ const { play, playMusic, stopMusic, setMuted, setMasterVolume } = useGameAudio({
31951
+ manifest,
31952
+ baseUrl,
31953
+ initialMuted: muted,
31954
+ initialVolume: volume
31955
+ });
31956
+ const prevCueSeqRef = React79.useRef(cueSeq);
31957
+ React79.useEffect(() => {
31958
+ if (cue && cueSeq !== void 0 && cueSeq !== prevCueSeqRef.current) {
31959
+ play(cue);
31960
+ }
31961
+ prevCueSeqRef.current = cueSeq;
31962
+ }, [cue, cueSeq, play]);
31963
+ React79.useEffect(() => {
31964
+ if (music) playMusic(music);
31965
+ else stopMusic();
31966
+ }, [music, playMusic, stopMusic]);
31967
+ React79.useEffect(() => {
31968
+ if (muted !== void 0) setMuted(muted);
31969
+ }, [muted, setMuted]);
31970
+ React79.useEffect(() => {
31971
+ if (volume !== void 0) setMasterVolume(volume);
31972
+ }, [volume, setMasterVolume]);
31973
+ return null;
31974
+ }
31975
+ var init_GameAudioCue = __esm({
31976
+ "components/game/atoms/GameAudioCue.tsx"() {
31977
+ "use client";
31978
+ init_useGameAudio();
31979
+ GameAudioCue.displayName = "GameAudioCue";
31980
+ }
31981
+ });
31273
31982
  function isKnownState(s) {
31274
31983
  return s in DEFAULT_STATE_STYLES;
31275
31984
  }
@@ -32166,15 +32875,18 @@ var init_physicsPresets = __esm({
32166
32875
  ];
32167
32876
  }
32168
32877
  });
32169
- var GAME_FONTS; exports.GameShell = void 0;
32170
- var init_GameShell = __esm({
32171
- "components/game/templates/GameShell.tsx"() {
32172
- init_cn();
32173
- init_Box();
32174
- init_Card();
32175
- init_Typography();
32176
- init_AtlasImage();
32177
- GAME_FONTS = {
32878
+
32879
+ // lib/gameFonts.ts
32880
+ function resolveGameFontFamily(input) {
32881
+ if (!input) return void 0;
32882
+ const resolved = GAME_FONT_KEYS[input];
32883
+ if (resolved) return `'${resolved}', ui-sans-serif, system-ui, sans-serif`;
32884
+ return input;
32885
+ }
32886
+ var GAME_FONT_KEYS;
32887
+ var init_gameFonts = __esm({
32888
+ "lib/gameFonts.ts"() {
32889
+ GAME_FONT_KEYS = {
32178
32890
  fredoka: "Fredoka",
32179
32891
  future: "Kenney Future",
32180
32892
  "future-narrow": "Kenney Future Narrow",
@@ -32182,6 +32894,17 @@ var init_GameShell = __esm({
32182
32894
  blocks: "Kenney Blocks",
32183
32895
  mini: "Kenney Mini"
32184
32896
  };
32897
+ }
32898
+ });
32899
+ exports.GameShell = void 0;
32900
+ var init_GameShell = __esm({
32901
+ "components/game/templates/GameShell.tsx"() {
32902
+ init_cn();
32903
+ init_gameFonts();
32904
+ init_Box();
32905
+ init_Card();
32906
+ init_Typography();
32907
+ init_AtlasImage();
32185
32908
  exports.GameShell = ({
32186
32909
  appName = "Game",
32187
32910
  hud,
@@ -32195,7 +32918,7 @@ var init_GameShell = __esm({
32195
32918
  fontFamily,
32196
32919
  "data-theme": dataTheme
32197
32920
  }) => {
32198
- const font = fontFamily ? GAME_FONTS[fontFamily] ?? fontFamily : void 0;
32921
+ const displayFont = resolveGameFontFamily(fontFamily);
32199
32922
  return /* @__PURE__ */ jsxRuntime.jsxs(
32200
32923
  exports.Box,
32201
32924
  {
@@ -32214,7 +32937,7 @@ var init_GameShell = __esm({
32214
32937
  // passed — an always-on inline stamp would shadow the orbital's
32215
32938
  // inline-theme font-family-display token (inline style beats the
32216
32939
  // theme provider's vars for the whole shell subtree).
32217
- ...font ? { "--font-family-display": `'${font}', ui-sans-serif, system-ui, sans-serif` } : {}
32940
+ ...displayFont ? { "--font-family-display": displayFont } : {}
32218
32941
  },
32219
32942
  children: [
32220
32943
  backgroundAsset && /* @__PURE__ */ jsxRuntime.jsx(
@@ -32347,6 +33070,7 @@ var init_molecules = __esm({
32347
33070
  init_Canvas();
32348
33071
  init_useUnitSpriteAtlas();
32349
33072
  init_GameAudioToggle();
33073
+ init_GameAudioCue();
32350
33074
  init_useGameAudio();
32351
33075
  init_useCamera();
32352
33076
  init_TraitStateViewer();
@@ -32375,6 +33099,7 @@ var init_MathCanvas = __esm({
32375
33099
  init_perf();
32376
33100
  init_atoms();
32377
33101
  init_Stack();
33102
+ init_gameFonts();
32378
33103
  init_LearningCanvas();
32379
33104
  exports.MathCanvas = ({
32380
33105
  className,
@@ -32394,6 +33119,7 @@ var init_MathCanvas = __esm({
32394
33119
  showTickLabels = false,
32395
33120
  tickLabelFontSize = 10,
32396
33121
  labelFontSize = 12,
33122
+ fontFamily: fontFamilyProp,
32397
33123
  showCurveLabels = false,
32398
33124
  curves = [],
32399
33125
  points = [],
@@ -32416,6 +33142,7 @@ var init_MathCanvas = __esm({
32416
33142
  error
32417
33143
  }) => {
32418
33144
  const eventBus = useEventBus();
33145
+ const fontFamily = resolveGameFontFamily(fontFamilyProp);
32419
33146
  const keyMapKey = keyMap ? JSON.stringify(keyMap) : null;
32420
33147
  const keyUpMapKey = keyUpMap ? JSON.stringify(keyUpMap) : null;
32421
33148
  const stableKeyMap = React79.useMemo(() => keyMap, [keyMapKey]);
@@ -32465,18 +33192,18 @@ var init_MathCanvas = __esm({
32465
33192
  let kx = 0;
32466
33193
  for (let x = Math.ceil(xMin / gridStep) * gridStep; x <= xMax; x += gridStep, kx++) {
32467
33194
  if (kx % labelEveryX === 0 && x !== 0) {
32468
- out.push({ type: "text", x: mapX(x), y: xAxisY + 12, text: formatTick(x), color: "#6b7280", fontSize: tickLabelFontSize, align: "center" });
33195
+ out.push({ type: "text", fontFamily, x: mapX(x), y: xAxisY + 12, text: formatTick(x), color: "#6b7280", fontSize: tickLabelFontSize, align: "center" });
32469
33196
  }
32470
33197
  }
32471
33198
  const labelEveryY = Math.max(1, Math.ceil((yMax - yMin) / gridStep / Math.floor(plotH / 28)));
32472
33199
  let ky = 0;
32473
33200
  for (let y = Math.ceil(yMin / gridStep) * gridStep; y <= yMax; y += gridStep, ky++) {
32474
33201
  if (ky % labelEveryY === 0 && y !== 0) {
32475
- out.push({ type: "text", x: yAxisX - 6, y: mapY(y), text: formatTick(y), color: "#6b7280", fontSize: tickLabelFontSize, align: "right" });
33202
+ out.push({ type: "text", fontFamily, x: yAxisX - 6, y: mapY(y), text: formatTick(y), color: "#6b7280", fontSize: tickLabelFontSize, align: "right" });
32476
33203
  }
32477
33204
  }
32478
33205
  if (xMin <= 0 && xMax >= 0 && yMin <= 0 && yMax >= 0) {
32479
- out.push({ type: "text", x: yAxisX - 6, y: xAxisY + 12, text: "0", color: "#6b7280", fontSize: tickLabelFontSize, align: "right" });
33206
+ out.push({ type: "text", fontFamily, x: yAxisX - 6, y: xAxisY + 12, text: "0", color: "#6b7280", fontSize: tickLabelFontSize, align: "right" });
32480
33207
  }
32481
33208
  }
32482
33209
  for (const region of regions) {
@@ -32503,6 +33230,7 @@ var init_MathCanvas = __esm({
32503
33230
  const mid = Math.floor(region.samples.length / 2);
32504
33231
  out.push({
32505
33232
  type: "text",
33233
+ fontFamily,
32506
33234
  x: mapX((first.x + last.x) / 2),
32507
33235
  y: (mapY(region.samples[mid].y) + mapY(baseline)) / 2,
32508
33236
  text: region.label,
@@ -32539,14 +33267,14 @@ var init_MathCanvas = __esm({
32539
33267
  const px = mapX(guide.at);
32540
33268
  out.push({ type: "line", x1: px, y1: margin, x2: px, y2: height - margin, color, dash });
32541
33269
  if (guide.label) {
32542
- out.push({ type: "text", x: px + 4, y: margin + 10, text: guide.label, color, fontSize: 11 });
33270
+ out.push({ type: "text", fontFamily, x: px + 4, y: margin + 10, text: guide.label, color, fontSize: 11 });
32543
33271
  }
32544
33272
  } else {
32545
33273
  if (guide.at < yMin || guide.at > yMax) continue;
32546
33274
  const py = mapY(guide.at);
32547
33275
  out.push({ type: "line", x1: margin, y1: py, x2: width - margin, y2: py, color, dash });
32548
33276
  if (guide.label) {
32549
- out.push({ type: "text", x: width - margin - 4, y: py - 8, text: guide.label, color, fontSize: labelFontSize, align: "right" });
33277
+ out.push({ type: "text", fontFamily, x: width - margin - 4, y: py - 8, text: guide.label, color, fontSize: labelFontSize, align: "right" });
32550
33278
  }
32551
33279
  }
32552
33280
  }
@@ -32585,6 +33313,7 @@ var init_MathCanvas = __esm({
32585
33313
  if (showCurveLabels && curve.label && lastInRange) {
32586
33314
  out.push({
32587
33315
  type: "text",
33316
+ fontFamily,
32588
33317
  x: mapX(lastInRange.x) + 6,
32589
33318
  y: mapY(lastInRange.y) - 6,
32590
33319
  text: curve.label,
@@ -32622,6 +33351,7 @@ var init_MathCanvas = __esm({
32622
33351
  if (hop.label) {
32623
33352
  out.push({
32624
33353
  type: "text",
33354
+ fontFamily,
32625
33355
  x: (x1 + x2) / 2,
32626
33356
  y: xAxisY - peak - 8,
32627
33357
  text: hop.label,
@@ -32649,6 +33379,7 @@ var init_MathCanvas = __esm({
32649
33379
  const rad = mid * Math.PI / 180;
32650
33380
  out.push({
32651
33381
  type: "text",
33382
+ fontFamily,
32652
33383
  x: mapX(angle.x + 1.35 * radius * Math.cos(rad)),
32653
33384
  y: mapY(angle.y + 1.35 * radius * Math.sin(rad)),
32654
33385
  text: angle.label,
@@ -32670,7 +33401,7 @@ var init_MathCanvas = __esm({
32670
33401
  fill: isOpen ? "#ffffff" : p.color ?? "#dc2626"
32671
33402
  });
32672
33403
  if (p.label) {
32673
- out.push({ type: "text", x: mapX(p.x) + 8, y: mapY(p.y) - 8, text: p.label, color: p.color ?? "#111827", fontSize: labelFontSize });
33404
+ out.push({ type: "text", fontFamily, x: mapX(p.x) + 8, y: mapY(p.y) - 8, text: p.label, color: p.color ?? "#111827", fontSize: labelFontSize });
32674
33405
  }
32675
33406
  }
32676
33407
  for (const v of vectors) {
@@ -32681,7 +33412,7 @@ var init_MathCanvas = __esm({
32681
33412
  const y2 = mapY(v.y + v.vy);
32682
33413
  out.push({ type: "arrow", x1, y1, x2, y2, color: v.color ?? "#7c3aed", lineWidth: 2 });
32683
33414
  if (v.label) {
32684
- out.push({ type: "text", x: x2 + 6, y: y2 - 6, text: v.label, color: v.color ?? "#7c3aed", fontSize: labelFontSize });
33415
+ out.push({ type: "text", fontFamily, x: x2 + 6, y: y2 - 6, text: v.label, color: v.color ?? "#7c3aed", fontSize: labelFontSize });
32685
33416
  }
32686
33417
  }
32687
33418
  out.push(...shapes);
@@ -32702,6 +33433,7 @@ var init_MathCanvas = __esm({
32702
33433
  showTickLabels,
32703
33434
  tickLabelFontSize,
32704
33435
  labelFontSize,
33436
+ fontFamily,
32705
33437
  showCurveLabels,
32706
33438
  curves,
32707
33439
  points,
@@ -32757,6 +33489,7 @@ var init_MathCanvas = __esm({
32757
33489
  width,
32758
33490
  height,
32759
33491
  backgroundColor,
33492
+ fontFamily,
32760
33493
  shapes: derivedShapes,
32761
33494
  drawables,
32762
33495
  projector,
@@ -33989,13 +34722,13 @@ var init_MapView = __esm({
33989
34722
  shadowSize: [41, 41]
33990
34723
  });
33991
34724
  L.Marker.prototype.options.icon = defaultIcon;
33992
- const { useEffect: useEffect70, useRef: useRef67, useCallback: useCallback109, useState: useState109 } = React79__namespace.default;
34725
+ const { useEffect: useEffect72, useRef: useRef70, useCallback: useCallback109, useState: useState110 } = React79__namespace.default;
33993
34726
  const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
33994
34727
  const { useEventBus: useEventBus2 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
33995
34728
  function MapUpdater({ centerLat, centerLng, zoom }) {
33996
34729
  const map = useMap();
33997
- const prevRef = useRef67({ centerLat, centerLng, zoom });
33998
- useEffect70(() => {
34730
+ const prevRef = useRef70({ centerLat, centerLng, zoom });
34731
+ useEffect72(() => {
33999
34732
  const prev = prevRef.current;
34000
34733
  if (prev.centerLat !== centerLat || prev.centerLng !== centerLng || prev.zoom !== zoom) {
34001
34734
  map.setView([centerLat, centerLng], zoom);
@@ -34006,7 +34739,7 @@ var init_MapView = __esm({
34006
34739
  }
34007
34740
  function MapClickHandler({ onMapClick }) {
34008
34741
  const map = useMap();
34009
- useEffect70(() => {
34742
+ useEffect72(() => {
34010
34743
  if (!onMapClick) return;
34011
34744
  const handler = (e) => {
34012
34745
  onMapClick(e.latlng.lat, e.latlng.lng);
@@ -34034,7 +34767,7 @@ var init_MapView = __esm({
34034
34767
  showAttribution = true
34035
34768
  }) {
34036
34769
  const eventBus = useEventBus2();
34037
- const [clickedPosition, setClickedPosition] = useState109(null);
34770
+ const [clickedPosition, setClickedPosition] = useState110(null);
34038
34771
  const handleMapClick = useCallback109((lat, lng) => {
34039
34772
  if (showClickedPin) {
34040
34773
  setClickedPosition({ lat, lng });
@@ -48972,7 +49705,7 @@ function getAllEvents(traits2) {
48972
49705
  function EventDispatcherTab({ traits: traits2, schema }) {
48973
49706
  const eventBus = useEventBus();
48974
49707
  const { t } = hooks.useTranslate();
48975
- const [log19, setLog] = React79__namespace.useState([]);
49708
+ const [log20, setLog] = React79__namespace.useState([]);
48976
49709
  const prevStatesRef = React79__namespace.useRef(/* @__PURE__ */ new Map());
48977
49710
  React79__namespace.useEffect(() => {
48978
49711
  for (const trait of traits2) {
@@ -49036,9 +49769,9 @@ function EventDispatcherTab({ traits: traits2, schema }) {
49036
49769
  /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "small", weight: "medium", className: "text-muted-foreground mb-1", children: t("debug.otherEvents") }),
49037
49770
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-wrap gap-1", children: unavailableEvents.map((event) => /* @__PURE__ */ jsxRuntime.jsx(exports.Badge, { variant: "default", size: "sm", className: "opacity-50", children: event }, event)) })
49038
49771
  ] }),
49039
- log19.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
49772
+ log20.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
49040
49773
  /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "small", weight: "medium", className: "text-muted-foreground mb-1", children: t("debug.recentTransitions") }),
49041
- /* @__PURE__ */ jsxRuntime.jsx(exports.Stack, { gap: "xs", children: log19.map((entry, i) => /* @__PURE__ */ jsxRuntime.jsxs(exports.Typography, { variant: "small", className: "font-mono text-xs", children: [
49774
+ /* @__PURE__ */ jsxRuntime.jsx(exports.Stack, { gap: "xs", children: log20.map((entry, i) => /* @__PURE__ */ jsxRuntime.jsxs(exports.Typography, { variant: "small", className: "font-mono text-xs", children: [
49042
49775
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-primary", children: entry.traitName }),
49043
49776
  " ",
49044
49777
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: entry.from }),
@@ -51229,6 +51962,7 @@ var init_component_registry_generated = __esm({
51229
51962
  init_FormSection();
51230
51963
  init_FormSectionHeader();
51231
51964
  init_FxOverlay();
51965
+ init_GameAudioCue();
51232
51966
  init_GameAudioToggle();
51233
51967
  init_GameHud();
51234
51968
  init_GameIcon();
@@ -51506,6 +52240,7 @@ var init_component_registry_generated = __esm({
51506
52240
  "FormLayout": exports.FormLayout,
51507
52241
  "FormSectionHeader": exports.FormSectionHeader,
51508
52242
  "FxOverlay": exports.FxOverlay,
52243
+ "GameAudioCue": GameAudioCue,
51509
52244
  "GameAudioToggle": GameAudioToggle,
51510
52245
  "GameHud": GameHud,
51511
52246
  "GameIcon": GameIcon,
@@ -51954,7 +52689,9 @@ function UISlotComponentInner({
51954
52689
  className,
51955
52690
  children,
51956
52691
  pattern,
51957
- sourceTrait
52692
+ sourceTrait,
52693
+ fallback,
52694
+ mode = "replace"
51958
52695
  }) {
51959
52696
  const { slots, clear } = context.useUISlots();
51960
52697
  const eventBus = useEventBus();
@@ -52006,12 +52743,26 @@ function UISlotComponentInner({
52006
52743
  );
52007
52744
  }
52008
52745
  if (!content) {
52746
+ if (fallback !== void 0) {
52747
+ return /* @__PURE__ */ jsxRuntime.jsx(
52748
+ exports.Box,
52749
+ {
52750
+ id: `slot-${slot}`,
52751
+ className: cn("ui-slot", `ui-slot-${slot}`, className),
52752
+ "data-testid": `ui-slot-${slot}`,
52753
+ "data-slot-mode": "fallback",
52754
+ children: fallback
52755
+ }
52756
+ );
52757
+ }
52009
52758
  if (!portal) {
52010
52759
  return /* @__PURE__ */ jsxRuntime.jsx(
52011
52760
  exports.Box,
52012
52761
  {
52013
52762
  id: `slot-${slot}`,
52014
- className: cn("ui-slot", `ui-slot-${slot}`, className)
52763
+ className: cn("ui-slot", `ui-slot-${slot}`, className),
52764
+ "data-testid": `ui-slot-${slot}`,
52765
+ "data-slot-mode": "empty"
52015
52766
  }
52016
52767
  );
52017
52768
  }
@@ -52028,29 +52779,51 @@ function UISlotComponentInner({
52028
52779
  clear(slot);
52029
52780
  };
52030
52781
  if (portal) {
52031
- if (contained) {
52032
- return renderContainedPortal(t, slot, content, handleDismiss);
52033
- }
52034
- return /* @__PURE__ */ jsxRuntime.jsx(
52035
- SlotPortal,
52782
+ const inlineFallback = mode === "append" && fallback !== void 0 ? /* @__PURE__ */ jsxRuntime.jsx(
52783
+ exports.Box,
52036
52784
  {
52037
- slot,
52038
- content,
52039
- position,
52040
- onDismiss: handleDismiss
52785
+ id: `slot-${slot}-fallback`,
52786
+ className: cn("ui-slot", `ui-slot-${slot}-fallback`, className),
52787
+ "data-testid": `ui-slot-${slot}-fallback`,
52788
+ "data-slot-mode": "append",
52789
+ children: fallback
52041
52790
  }
52042
- );
52791
+ ) : null;
52792
+ if (contained) {
52793
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
52794
+ inlineFallback,
52795
+ renderContainedPortal(t, slot, content, handleDismiss)
52796
+ ] });
52797
+ }
52798
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
52799
+ inlineFallback,
52800
+ /* @__PURE__ */ jsxRuntime.jsx(
52801
+ SlotPortal,
52802
+ {
52803
+ slot,
52804
+ content,
52805
+ position,
52806
+ onDismiss: handleDismiss
52807
+ }
52808
+ )
52809
+ ] });
52043
52810
  }
52044
52811
  const slotContent = /* @__PURE__ */ jsxRuntime.jsx(SlotContentRenderer, { content, onDismiss: handleDismiss });
52045
52812
  const wrappedContent = suspenseConfig.enabled ? /* @__PURE__ */ jsxRuntime.jsx(exports.ErrorBoundary, { children: /* @__PURE__ */ jsxRuntime.jsx(React79.Suspense, { fallback: getSlotFallback(slot, suspenseConfig), children: slotContent }) }) : /* @__PURE__ */ jsxRuntime.jsx(exports.ErrorBoundary, { children: slotContent });
52046
- return /* @__PURE__ */ jsxRuntime.jsx(
52813
+ const showFallback = mode === "append" && fallback !== void 0;
52814
+ return /* @__PURE__ */ jsxRuntime.jsxs(
52047
52815
  exports.Box,
52048
52816
  {
52049
52817
  id: `slot-${slot}`,
52050
52818
  className: cn("ui-slot", `ui-slot-${slot}`, className),
52051
52819
  "data-pattern": content.pattern,
52052
52820
  "data-source-trait": content.sourceTrait,
52053
- children: /* @__PURE__ */ jsxRuntime.jsx(MaybeTraitScope, { sourceTrait: content.sourceTrait, children: wrappedContent })
52821
+ "data-testid": `ui-slot-${slot}`,
52822
+ "data-slot-mode": showFallback ? "append" : "content",
52823
+ children: [
52824
+ showFallback ? fallback : null,
52825
+ /* @__PURE__ */ jsxRuntime.jsx(MaybeTraitScope, { sourceTrait: content.sourceTrait, children: wrappedContent })
52826
+ ]
52054
52827
  }
52055
52828
  );
52056
52829
  }
@@ -52992,7 +53765,7 @@ init_AboutPageTemplate();
52992
53765
 
52993
53766
  // components/index.ts
52994
53767
  init_cn();
52995
- var log8 = logger.createLogger("almadar:ui:orbital-history");
53768
+ var log9 = logger.createLogger("almadar:ui:orbital-history");
52996
53769
  function useOrbitalHistory(options) {
52997
53770
  const { appId, authToken, userId, onHistoryChange, onRevertSuccess } = options;
52998
53771
  const getHeaders = React79.useCallback(() => {
@@ -53054,7 +53827,7 @@ function useOrbitalHistory(options) {
53054
53827
  setCurrentVersion(mergedTimeline[0].version);
53055
53828
  }
53056
53829
  } catch (err) {
53057
- log8.error("Failed to load history", { error: err instanceof Error ? err : String(err) });
53830
+ log9.error("Failed to load history", { error: err instanceof Error ? err : String(err) });
53058
53831
  setError(err instanceof Error ? err.message : "Failed to load history");
53059
53832
  } finally {
53060
53833
  setIsLoading(false);
@@ -53087,7 +53860,7 @@ function useOrbitalHistory(options) {
53087
53860
  error: data.error || "Unknown error during revert"
53088
53861
  };
53089
53862
  } catch (err) {
53090
- log8.error("Failed to revert", { error: err instanceof Error ? err : String(err) });
53863
+ log9.error("Failed to revert", { error: err instanceof Error ? err : String(err) });
53091
53864
  return {
53092
53865
  success: false,
53093
53866
  error: err instanceof Error ? err.message : "Failed to revert"
@@ -53111,7 +53884,7 @@ function useOrbitalHistory(options) {
53111
53884
  refresh
53112
53885
  };
53113
53886
  }
53114
- var log9 = logger.createLogger("almadar:ui:filesystem");
53887
+ var log10 = logger.createLogger("almadar:ui:filesystem");
53115
53888
  function useFileSystem() {
53116
53889
  const [status, setStatus] = React79.useState("idle");
53117
53890
  const [error, setError] = React79.useState(null);
@@ -53126,7 +53899,7 @@ function useFileSystem() {
53126
53899
  setError(null);
53127
53900
  setIsLoading(true);
53128
53901
  try {
53129
- log9.debug("Booting WebContainer");
53902
+ log10.debug("Booting WebContainer");
53130
53903
  await new Promise((resolve) => setTimeout(resolve, 100));
53131
53904
  setStatus("ready");
53132
53905
  } catch (err) {
@@ -53187,7 +53960,7 @@ function useFileSystem() {
53187
53960
  setFiles(newTree);
53188
53961
  setStatus("running");
53189
53962
  } catch (err) {
53190
- log9.error("Failed to mount files", { error: err instanceof Error ? err : String(err) });
53963
+ log10.error("Failed to mount files", { error: err instanceof Error ? err : String(err) });
53191
53964
  } finally {
53192
53965
  setIsLoading(false);
53193
53966
  }
@@ -53228,7 +54001,7 @@ function useFileSystem() {
53228
54001
  const path = contentArg !== void 0 ? pathOrContent : selectedPath;
53229
54002
  const content = contentArg !== void 0 ? contentArg : pathOrContent;
53230
54003
  if (!path) {
53231
- log9.warn("updateContent called without path and no file selected");
54004
+ log10.warn("updateContent called without path and no file selected");
53232
54005
  return;
53233
54006
  }
53234
54007
  setFileContents((prev) => {
@@ -53244,14 +54017,14 @@ function useFileSystem() {
53244
54017
  setSelectedFile((prev) => prev ? { ...prev, content, isDirty: true } : null);
53245
54018
  }, []);
53246
54019
  const refreshTree = React79.useCallback(async () => {
53247
- log9.debug("Refreshing tree");
54020
+ log10.debug("Refreshing tree");
53248
54021
  }, []);
53249
54022
  const runCommand = React79.useCallback(async (command) => {
53250
- log9.debug("Running command", { command });
54023
+ log10.debug("Running command", { command });
53251
54024
  return { exitCode: 0, output: "" };
53252
54025
  }, []);
53253
54026
  const startDevServer = React79.useCallback(async () => {
53254
- log9.debug("Starting dev server");
54027
+ log10.debug("Starting dev server");
53255
54028
  setPreviewUrl("http://localhost:5173");
53256
54029
  }, []);
53257
54030
  return {
@@ -53274,7 +54047,7 @@ function useFileSystem() {
53274
54047
  startDevServer
53275
54048
  };
53276
54049
  }
53277
- var log10 = logger.createLogger("almadar:ui:extensions");
54050
+ var log11 = logger.createLogger("almadar:ui:extensions");
53278
54051
  var defaultManifest = {
53279
54052
  languages: {
53280
54053
  typescript: { extensions: [".ts", ".tsx"], icon: "ts", color: "#3178c6" },
@@ -53293,7 +54066,7 @@ function useExtensions(options) {
53293
54066
  const [isLoading, setIsLoading] = React79.useState(false);
53294
54067
  const [error, setError] = React79.useState(null);
53295
54068
  const loadExtension = React79.useCallback(async (extensionId) => {
53296
- log10.debug("Loading extension", { extensionId });
54069
+ log11.debug("Loading extension", { extensionId });
53297
54070
  }, []);
53298
54071
  const loadExtensions = React79.useCallback(async () => {
53299
54072
  setIsLoading(true);
@@ -53365,7 +54138,7 @@ function useExtensions(options) {
53365
54138
  getExtensionForFile
53366
54139
  };
53367
54140
  }
53368
- var log11 = logger.createLogger("almadar:ui:file-editor");
54141
+ var log12 = logger.createLogger("almadar:ui:file-editor");
53369
54142
  function useFileEditor(options) {
53370
54143
  const { extensions, fileSystem, onSchemaUpdate } = options;
53371
54144
  const [openFiles, setOpenFiles] = React79.useState([]);
@@ -53390,7 +54163,7 @@ function useFileEditor(options) {
53390
54163
  setOpenFiles((prev) => [...prev, newFile]);
53391
54164
  setActiveFilePath(path);
53392
54165
  } catch (err) {
53393
- log11.error("Failed to open file", { error: err instanceof Error ? err : String(err) });
54166
+ log12.error("Failed to open file", { error: err instanceof Error ? err : String(err) });
53394
54167
  }
53395
54168
  }, [openFiles, fileSystem, extensions]);
53396
54169
  const closeFile = React79.useCallback((path) => {
@@ -53451,7 +54224,7 @@ function useFileEditor(options) {
53451
54224
  }
53452
54225
  }
53453
54226
  } catch (err) {
53454
- log11.error("Failed to save file", { error: err instanceof Error ? err : String(err) });
54227
+ log12.error("Failed to save file", { error: err instanceof Error ? err : String(err) });
53455
54228
  } finally {
53456
54229
  setIsSaving(false);
53457
54230
  }
@@ -53480,7 +54253,7 @@ function useFileEditor(options) {
53480
54253
  saveAllFiles
53481
54254
  };
53482
54255
  }
53483
- var log12 = logger.createLogger("almadar:ui:compile");
54256
+ var log13 = logger.createLogger("almadar:ui:compile");
53484
54257
  function useCompile() {
53485
54258
  const [isCompiling, setIsCompiling] = React79.useState(false);
53486
54259
  const [stage, setStage] = React79.useState("idle");
@@ -53491,7 +54264,7 @@ function useCompile() {
53491
54264
  setStage("compiling");
53492
54265
  setError(null);
53493
54266
  try {
53494
- log12.debug("Compiling schema", { name: schema.name });
54267
+ log13.debug("Compiling schema", { name: schema.name });
53495
54268
  const result = {
53496
54269
  success: true,
53497
54270
  files: []
@@ -53517,7 +54290,7 @@ function useCompile() {
53517
54290
  compileSchema
53518
54291
  };
53519
54292
  }
53520
- var log13 = logger.createLogger("almadar:ui:preview");
54293
+ var log14 = logger.createLogger("almadar:ui:preview");
53521
54294
  function usePreview(options) {
53522
54295
  const [previewUrl, setPreviewUrl] = React79.useState(null);
53523
54296
  const [isLoading, setIsLoading] = React79.useState(!!options?.appId);
@@ -53551,17 +54324,17 @@ function usePreview(options) {
53551
54324
  setIsLoading(false);
53552
54325
  return;
53553
54326
  }
53554
- log13.debug("Setting up preview for app", { appId });
54327
+ log14.debug("Setting up preview for app", { appId });
53555
54328
  setPreviewUrl(`/api/orbitals/${appId}`);
53556
54329
  setIsLoading(false);
53557
54330
  }, [options?.appId]);
53558
54331
  const startPreview = React79.useCallback(async () => {
53559
- log13.debug("startPreview called");
54332
+ log14.debug("startPreview called");
53560
54333
  }, []);
53561
54334
  const stopPreview = React79.useCallback(async () => {
53562
54335
  setIsLoading(true);
53563
54336
  try {
53564
- log13.debug("Stopping preview server");
54337
+ log14.debug("Stopping preview server");
53565
54338
  setPreviewUrl(null);
53566
54339
  setApp(null);
53567
54340
  } finally {
@@ -53570,15 +54343,15 @@ function usePreview(options) {
53570
54343
  }, []);
53571
54344
  const refresh = React79.useCallback(async () => {
53572
54345
  if (!previewUrl) return;
53573
- log13.debug("Refreshing preview");
54346
+ log14.debug("Refreshing preview");
53574
54347
  setPreviewUrl(`${previewUrl.split("?")[0]}?t=${Date.now()}`);
53575
54348
  }, [previewUrl]);
53576
54349
  const handleRefresh = React79.useCallback(async () => {
53577
- log13.debug("Handle refresh");
54350
+ log14.debug("Handle refresh");
53578
54351
  await refresh();
53579
54352
  }, [refresh]);
53580
54353
  const handleReset = React79.useCallback(async () => {
53581
- log13.debug("Resetting preview");
54354
+ log14.debug("Resetting preview");
53582
54355
  setError(null);
53583
54356
  setLoadError(null);
53584
54357
  setErrorToast(null);
@@ -53612,7 +54385,7 @@ function usePreview(options) {
53612
54385
  dismissErrorToast
53613
54386
  };
53614
54387
  }
53615
- var log14 = logger.createLogger("almadar:ui:agent-chat");
54388
+ var log15 = logger.createLogger("almadar:ui:agent-chat");
53616
54389
  function useAgentChat(options) {
53617
54390
  const [messages, setMessages] = React79.useState([]);
53618
54391
  const [status, setStatus] = React79.useState("idle");
@@ -53635,7 +54408,7 @@ function useAgentChat(options) {
53635
54408
  timestamp: Date.now()
53636
54409
  };
53637
54410
  setMessages((prev) => [...prev, userMessage]);
53638
- log14.debug("Sending message", { content });
54411
+ log15.debug("Sending message", { content });
53639
54412
  const assistantMessage = {
53640
54413
  id: (Date.now() + 1).toString(),
53641
54414
  role: "assistant",
@@ -53658,7 +54431,7 @@ function useAgentChat(options) {
53658
54431
  setError(null);
53659
54432
  const skillName = Array.isArray(skill) ? skill[0] : skill;
53660
54433
  try {
53661
- log14.debug("Starting generation", () => ({ skillName, prompt, genOptions: JSON.stringify(genOptions) }));
54434
+ log15.debug("Starting generation", () => ({ skillName, prompt, genOptions: JSON.stringify(genOptions) }));
53662
54435
  await new Promise((resolve) => setTimeout(resolve, 100));
53663
54436
  setStatus("complete");
53664
54437
  options?.onComplete?.();
@@ -53670,10 +54443,10 @@ function useAgentChat(options) {
53670
54443
  }
53671
54444
  }, [options]);
53672
54445
  const continueConversation = React79.useCallback(async (message) => {
53673
- log14.debug("Continue conversation", { message: Array.isArray(message) ? message : [message] });
54446
+ log15.debug("Continue conversation", { message: Array.isArray(message) ? message : [message] });
53674
54447
  }, []);
53675
54448
  const resumeWithDecision = React79.useCallback(async (decisions) => {
53676
- log14.debug("Resume with decision", () => ({ decisions: JSON.stringify(decisions), count: decisions.length }));
54449
+ log15.debug("Resume with decision", () => ({ decisions: JSON.stringify(decisions), count: decisions.length }));
53677
54450
  setInterrupt(null);
53678
54451
  }, []);
53679
54452
  const cancel = React79.useCallback(() => {
@@ -53710,7 +54483,7 @@ function useAgentChat(options) {
53710
54483
  clearHistory
53711
54484
  };
53712
54485
  }
53713
- var log15 = logger.createLogger("almadar:ui:validation");
54486
+ var log16 = logger.createLogger("almadar:ui:validation");
53714
54487
  function useValidation() {
53715
54488
  const [result, setResult] = React79.useState(null);
53716
54489
  const [isValidating, setIsValidating] = React79.useState(false);
@@ -53724,7 +54497,7 @@ function useValidation() {
53724
54497
  setStage("validating");
53725
54498
  setProgressMessage("Validating schema...");
53726
54499
  try {
53727
- log15.debug("Validating app", { appId });
54500
+ log16.debug("Validating app", { appId });
53728
54501
  const validationResult = {
53729
54502
  valid: true,
53730
54503
  errors: [],
@@ -53777,7 +54550,7 @@ function useValidation() {
53777
54550
  reset
53778
54551
  };
53779
54552
  }
53780
- var log16 = logger.createLogger("almadar:ui:deep-agent");
54553
+ var log17 = logger.createLogger("almadar:ui:deep-agent");
53781
54554
  function useDeepAgentGeneration() {
53782
54555
  const [requests, setRequests] = React79.useState([]);
53783
54556
  const [currentRequest, setCurrentRequest] = React79.useState(null);
@@ -53801,7 +54574,7 @@ function useDeepAgentGeneration() {
53801
54574
  setCurrentRequest(request);
53802
54575
  setRequests((prev) => [...prev, request]);
53803
54576
  try {
53804
- log16.debug("Generating from prompt", { prompt });
54577
+ log17.debug("Generating from prompt", { prompt });
53805
54578
  await new Promise((resolve) => setTimeout(resolve, 100));
53806
54579
  request.status = "completed";
53807
54580
  setCurrentRequest(request);
@@ -53821,7 +54594,7 @@ function useDeepAgentGeneration() {
53821
54594
  }
53822
54595
  }, []);
53823
54596
  const startGeneration = React79.useCallback(async (skill, prompt, _options) => {
53824
- log16.debug("Starting generation with skill", { skill });
54597
+ log17.debug("Starting generation with skill", { skill });
53825
54598
  await generate(prompt);
53826
54599
  }, [generate]);
53827
54600
  const cancelGeneration = React79.useCallback(() => {
@@ -53843,7 +54616,7 @@ function useDeepAgentGeneration() {
53843
54616
  setIsComplete(false);
53844
54617
  }, []);
53845
54618
  const submitInterruptDecisions = React79.useCallback((decisions) => {
53846
- log16.debug("Submitting interrupt decisions", () => ({ decisions: JSON.stringify(decisions), count: decisions.length }));
54619
+ log17.debug("Submitting interrupt decisions", () => ({ decisions: JSON.stringify(decisions), count: decisions.length }));
53847
54620
  setInterrupt(null);
53848
54621
  }, []);
53849
54622
  return {
@@ -53865,6 +54638,66 @@ function useDeepAgentGeneration() {
53865
54638
 
53866
54639
  // hooks/index.ts
53867
54640
  init_useEventBus();
54641
+
54642
+ // hooks/useKeyboardRouter.ts
54643
+ init_useEventBus();
54644
+ function useKeyboardRouter(options) {
54645
+ const {
54646
+ captureTable,
54647
+ editorFocusEvent = "EDITOR_FOCUS",
54648
+ editorBlurEvent = "EDITOR_BLUR",
54649
+ keyEvent = "KEY",
54650
+ enabled = true
54651
+ } = options;
54652
+ const eventBus = useEventBus();
54653
+ const captureTableRef = React79.useRef(captureTable);
54654
+ captureTableRef.current = captureTable;
54655
+ const focusedEditorIdRef = React79.useRef(null);
54656
+ React79.useEffect(() => {
54657
+ const unsubFocus = eventBus.on(`UI:${editorFocusEvent}`, (event) => {
54658
+ const editorId = event.payload?.editorId;
54659
+ if (typeof editorId === "string") {
54660
+ focusedEditorIdRef.current = editorId;
54661
+ }
54662
+ });
54663
+ const unsubBlur = eventBus.on(`UI:${editorBlurEvent}`, (event) => {
54664
+ const editorId = event.payload?.editorId;
54665
+ if (typeof editorId === "string" && focusedEditorIdRef.current === editorId) {
54666
+ focusedEditorIdRef.current = null;
54667
+ }
54668
+ });
54669
+ return () => {
54670
+ unsubFocus();
54671
+ unsubBlur();
54672
+ };
54673
+ }, [eventBus, editorFocusEvent, editorBlurEvent]);
54674
+ React79.useEffect(() => {
54675
+ if (!enabled) return;
54676
+ const handleKeyDown = (event) => {
54677
+ if (event.isComposing) return;
54678
+ const target = focusedEditorIdRef.current ?? "shell";
54679
+ const entry = captureTableRef.current[target];
54680
+ const captured = entry !== void 0 && (entry.mode === "any" || entry.keys.has(event.key));
54681
+ if (captured) {
54682
+ event.preventDefault();
54683
+ }
54684
+ eventBus.emit(`UI:${keyEvent}`, {
54685
+ editorId: target,
54686
+ key: event.key,
54687
+ code: event.code,
54688
+ ctrl: event.ctrlKey,
54689
+ alt: event.altKey,
54690
+ shift: event.shiftKey,
54691
+ meta: event.metaKey,
54692
+ repeat: event.repeat
54693
+ });
54694
+ };
54695
+ window.addEventListener("keydown", handleKeyDown, { capture: true });
54696
+ return () => {
54697
+ window.removeEventListener("keydown", handleKeyDown, { capture: true });
54698
+ };
54699
+ }, [eventBus, enabled, keyEvent]);
54700
+ }
53868
54701
  function expressionEqual(a, b) {
53869
54702
  if (Object.is(a, b)) return true;
53870
54703
  if (Array.isArray(a) && Array.isArray(b)) {
@@ -53947,21 +54780,12 @@ function reconcileSlotProps(prev, next) {
53947
54780
  }
53948
54781
 
53949
54782
  // hooks/useUISlots.ts
53950
- var log17 = logger.createLogger("almadar:ui:ui-slots");
54783
+ var log18 = logger.createLogger("almadar:ui:ui-slots");
53951
54784
  var DEFAULT_SOURCE_KEY = "__default__";
53952
54785
  var MULTI_SOURCE_STACK_TRAIT = "__multi_source_stack__";
53953
- var ALL_SLOTS2 = [
53954
- "main",
53955
- "sidebar",
53956
- "modal",
53957
- "drawer",
53958
- "overlay",
53959
- "center",
53960
- "toast",
53961
- "hud-top",
53962
- "hud-bottom",
53963
- "floating"
53964
- ];
54786
+ var ALL_SLOTS2 = core.UI_SLOTS.filter(
54787
+ (slot) => !slot.includes(".") && slot !== "hud" && slot !== "screen"
54788
+ );
53965
54789
  var DEFAULT_SLOTS = ALL_SLOTS2.reduce(
53966
54790
  (acc, slot) => {
53967
54791
  acc[slot] = null;
@@ -54024,7 +54848,7 @@ function useUISlotManager() {
54024
54848
  try {
54025
54849
  callback(slot, content);
54026
54850
  } catch (error) {
54027
- log17.error("Subscriber error", { error: error instanceof Error ? error : String(error) });
54851
+ log18.error("Subscriber error", { error: error instanceof Error ? error : String(error) });
54028
54852
  }
54029
54853
  });
54030
54854
  }, []);
@@ -54036,7 +54860,7 @@ function useUISlotManager() {
54036
54860
  try {
54037
54861
  callback(content);
54038
54862
  } catch (error) {
54039
- log17.error("Trait subscriber error", { traitName, error: error instanceof Error ? error : String(error) });
54863
+ log18.error("Trait subscriber error", { traitName, error: error instanceof Error ? error : String(error) });
54040
54864
  }
54041
54865
  });
54042
54866
  },
@@ -54093,7 +54917,7 @@ function useUISlotManager() {
54093
54917
  const slotSources = prev[config.target] ?? {};
54094
54918
  const existing = slotSources[sourceKey];
54095
54919
  if (existing && existing.priority > content.priority) {
54096
- log17.warn("Slot already has higher priority content", {
54920
+ log18.warn("Slot already has higher priority content", {
54097
54921
  slot: config.target,
54098
54922
  sourceKey,
54099
54923
  existingPriority: existing.priority,
@@ -54104,7 +54928,7 @@ function useUISlotManager() {
54104
54928
  if (existing && existing.priority === content.priority && existing.pattern === content.pattern && existing.animation === content.animation && existing.transitionEvent === content.transitionEvent && existing.fromState === content.fromState && existing.entity === content.entity && existing.nodeId === content.nodeId && existing.autoDismissAt === void 0 && content.autoDismissAt === void 0 && existing.onDismiss === void 0 === (content.onDismiss === void 0)) {
54105
54929
  const reconciled = reconcileSlotProps(existing.props, content.props);
54106
54930
  if (reconciled.equal) {
54107
- log17.debug("slot:flush-bail", { slot: config.target, sourceKey, pattern: content.pattern });
54931
+ log18.debug("slot:flush-bail", { slot: config.target, sourceKey, pattern: content.pattern });
54108
54932
  return prev;
54109
54933
  }
54110
54934
  content.props = reconciled.value;
@@ -54116,7 +54940,7 @@ function useUISlotManager() {
54116
54940
  const nextAll = { ...prev, [config.target]: nextSources };
54117
54941
  const priorWriters = Object.keys(slotSources);
54118
54942
  if (priorWriters.length === 1 && priorWriters[0] !== sourceKey) {
54119
- log17.warn("slot:contention", {
54943
+ log18.warn("slot:contention", {
54120
54944
  slot: config.target,
54121
54945
  writers: [priorWriters[0], sourceKey],
54122
54946
  patternTypes: [slotSources[priorWriters[0]].pattern, content.pattern]
@@ -54126,7 +54950,7 @@ function useUISlotManager() {
54126
54950
  indexTraitRender(content.sourceTrait, content);
54127
54951
  notifyTraitSubscribers(content.sourceTrait, content);
54128
54952
  }
54129
- log17.info("slot:written", {
54953
+ log18.info("slot:written", {
54130
54954
  slot: config.target,
54131
54955
  sourceKey,
54132
54956
  sourceTrait: content.sourceTrait,
@@ -54171,7 +54995,7 @@ function useUISlotManager() {
54171
54995
  setSources((prev) => {
54172
54996
  const slotSources = prev[slot];
54173
54997
  if (!slotSources || !(sourceKey in slotSources)) {
54174
- log17.debug("slot:clear-noop", { slot, sourceTrait, reason: !slotSources ? "no-slot" : "no-source" });
54998
+ log18.debug("slot:clear-noop", { slot, sourceTrait, reason: !slotSources ? "no-slot" : "no-source" });
54175
54999
  return prev;
54176
55000
  }
54177
55001
  const content = slotSources[sourceKey];
@@ -54187,7 +55011,7 @@ function useUISlotManager() {
54187
55011
  }
54188
55012
  const nextSources = { ...slotSources };
54189
55013
  delete nextSources[sourceKey];
54190
- log17.info("slot:cleared", { slot, sourceTrait, lastPatternType: content.pattern });
55014
+ log18.info("slot:cleared", { slot, sourceTrait, lastPatternType: content.pattern });
54191
55015
  notifySubscribers(slot, aggregateSlot(nextSources));
54192
55016
  return { ...prev, [slot]: nextSources };
54193
55017
  });
@@ -54363,7 +55187,7 @@ function useTraitListens(dispatch, listens, eventBusInstance) {
54363
55187
  };
54364
55188
  }, [eventBus, dispatch, stableListens]);
54365
55189
  }
54366
- var log18 = logger.createLogger("almadar:ui:shared-entity-store");
55190
+ var log19 = logger.createLogger("almadar:ui:shared-entity-store");
54367
55191
  var EMPTY_ENTITY_STATE = {};
54368
55192
  function createSharedEntityStore() {
54369
55193
  const states = /* @__PURE__ */ new Map();
@@ -54393,7 +55217,7 @@ function createSharedEntityStore() {
54393
55217
  try {
54394
55218
  callback();
54395
55219
  } catch (error) {
54396
- log18.error("Shared entity subscriber error", {
55220
+ log19.error("Shared entity subscriber error", {
54397
55221
  entityId,
54398
55222
  error: error instanceof Error ? error : String(error)
54399
55223
  });
@@ -54999,7 +55823,8 @@ var en_default = {
54999
55823
  "richTextEditor.image": "Insert image",
55000
55824
  "richTextEditor.imagePrompt": "Image URL",
55001
55825
  "richTextEditor.placeholder": "Start writing\u2026",
55002
- "form.imageUrlFallback": "\u2026or paste an image address"
55826
+ "form.imageUrlFallback": "\u2026or paste an image address",
55827
+ "mermaid.unrenderable": "This diagram could not be displayed. Its source is shown below."
55003
55828
  };
55004
55829
 
55005
55830
  // hooks/useTranslate.ts
@@ -55020,7 +55845,7 @@ var I18nContext = React79.createContext({
55020
55845
  });
55021
55846
  I18nContext.displayName = "I18nContext";
55022
55847
  var I18nProvider = I18nContext.Provider;
55023
- function useTranslate116() {
55848
+ function useTranslate117() {
55024
55849
  return React79.useContext(I18nContext);
55025
55850
  }
55026
55851
  function createTranslate(messages) {
@@ -55206,6 +56031,20 @@ function useGitHubBranches(owner, repo, enabled = true) {
55206
56031
  });
55207
56032
  }
55208
56033
 
56034
+ // types/slot-host.ts
56035
+ function assertUniqueSlotsPerHost(manifest) {
56036
+ const seenBySlot = /* @__PURE__ */ new Map();
56037
+ for (const [regionId, region] of Object.entries(manifest.regions)) {
56038
+ const existingRegionId = seenBySlot.get(region.slot);
56039
+ if (existingRegionId !== void 0) {
56040
+ throw new Error(
56041
+ `assertUniqueSlotsPerHost: regions "${existingRegionId}" and "${regionId}" both bind slot "${region.slot}" \u2014 a slot may be mounted by only one region per host.`
56042
+ );
56043
+ }
56044
+ seenBySlot.set(region.slot, regionId);
56045
+ }
56046
+ }
56047
+
55209
56048
  Object.defineProperty(exports, "GameAudioContext", {
55210
56049
  enumerable: true,
55211
56050
  get: function () { return providers.GameAudioContext; }
@@ -55251,6 +56090,7 @@ exports.EditorSelect = EditorSelect;
55251
56090
  exports.EditorSlider = EditorSlider;
55252
56091
  exports.EditorTextInput = EditorTextInput;
55253
56092
  exports.EditorToolbar = EditorToolbar;
56093
+ exports.GameAudioCue = GameAudioCue;
55254
56094
  exports.GameAudioToggle = GameAudioToggle;
55255
56095
  exports.GameHud = GameHud;
55256
56096
  exports.GameIcon = GameIcon;
@@ -55284,6 +56124,7 @@ exports.TransitionArrow = TransitionArrow;
55284
56124
  exports.UISlotComponent = UISlotComponent;
55285
56125
  exports.UISlotRenderer = UISlotRenderer;
55286
56126
  exports.arrowBetween = arrowBetween;
56127
+ exports.assertUniqueSlotsPerHost = assertUniqueSlotsPerHost;
55287
56128
  exports.billboardLabel = billboardLabel;
55288
56129
  exports.boardEntity = boardEntity;
55289
56130
  exports.bool = bool;
@@ -55295,6 +56136,7 @@ exports.createSharedEntityStore = createSharedEntityStore;
55295
56136
  exports.createTranslate = createTranslate;
55296
56137
  exports.createUnitAnimationState = createUnitAnimationState;
55297
56138
  exports.cylinderBetween = cylinderBetween;
56139
+ exports.dispatchCommandPaletteCommand = dispatchCommandPaletteCommand;
55298
56140
  exports.get3DClickPayload = get3DClickPayload;
55299
56141
  exports.getCurrentFrame = getCurrentFrame;
55300
56142
  exports.getTileDimensions = getTileDimensions;
@@ -55360,6 +56202,7 @@ exports.useGitHubRepos = useGitHubRepos;
55360
56202
  exports.useGitHubStatus = useGitHubStatus;
55361
56203
  exports.useImageCache = useImageCache;
55362
56204
  exports.useInfiniteScroll = useInfiniteScroll;
56205
+ exports.useKeyboardRouter = useKeyboardRouter;
55363
56206
  exports.useLongPress = useLongPress;
55364
56207
  exports.useMediaQuery = useMediaQuery;
55365
56208
  exports.useOrbitalHistory = useOrbitalHistory;
@@ -55374,7 +56217,7 @@ exports.useSharedEntityStoreContext = useSharedEntityStoreContext;
55374
56217
  exports.useSwipeGesture = useSwipeGesture;
55375
56218
  exports.useTapReveal = useTapReveal;
55376
56219
  exports.useTraitListens = useTraitListens;
55377
- exports.useTranslate = useTranslate116;
56220
+ exports.useTranslate = useTranslate117;
55378
56221
  exports.useUIEvents = useUIEvents;
55379
56222
  exports.useUISlotManager = useUISlotManager;
55380
56223
  exports.useUnitSpriteAtlas = useUnitSpriteAtlas;