@almadar/ui 6.5.0 → 6.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{EntityBindingContext-0Evn_LcT.d.cts → EntityBindingContext-Bn3ePJQC.d.cts} +1 -1
- package/dist/{EntityBindingContext-0Evn_LcT.d.ts → EntityBindingContext-Bn3ePJQC.d.ts} +1 -1
- package/dist/{GameAudioProvider-B48iXwz3.d.ts → GameAudioProvider-Ctceau1s.d.ts} +6 -18
- package/dist/{GameAudioProvider-CQAdPreB.d.cts → GameAudioProvider-Dk_Y3LN3.d.cts} +6 -18
- package/dist/avl/index.cjs +1216 -206
- package/dist/avl/index.js +1217 -207
- package/dist/{avl-schema-parser-dvJKXn-o.d.ts → avl-schema-parser-DncJLEn9.d.ts} +18 -0
- package/dist/{avl-schema-parser-Bt4NzAam.d.cts → avl-schema-parser-FQ1474v8.d.cts} +18 -0
- package/dist/{cn-DJSBBm5W.d.cts → cn-CCjFspAo.d.cts} +25 -1
- package/dist/{cn-BbhJq_nr.d.ts → cn-sgpqpN0U.d.ts} +25 -1
- package/dist/components/index.cjs +1025 -235
- package/dist/components/index.d.cts +176 -23
- package/dist/components/index.d.ts +176 -23
- package/dist/components/index.js +1023 -236
- package/dist/context/index.cjs +10 -13
- package/dist/context/index.js +10 -13
- package/dist/hooks/index.cjs +63 -13
- package/dist/hooks/index.d.cts +2 -2
- package/dist/hooks/index.d.ts +2 -2
- package/dist/hooks/index.js +64 -15
- package/dist/lib/drawable/three/index.cjs +126 -13
- package/dist/lib/drawable/three/index.d.cts +2 -2
- package/dist/lib/drawable/three/index.d.ts +2 -2
- package/dist/lib/drawable/three/index.js +127 -14
- package/dist/lib/index.cjs +180 -0
- package/dist/lib/index.d.cts +1 -1
- package/dist/lib/index.d.ts +1 -1
- package/dist/lib/index.js +178 -1
- package/dist/locales/index.cjs +6 -3
- package/dist/locales/index.js +6 -3
- package/dist/providers/index.cjs +922 -188
- package/dist/providers/index.d.cts +2 -2
- package/dist/providers/index.d.ts +2 -2
- package/dist/providers/index.js +922 -188
- package/dist/runtime/index.cjs +1505 -182
- package/dist/runtime/index.d.cts +78 -6
- package/dist/runtime/index.d.ts +78 -6
- package/dist/runtime/index.js +1504 -184
- package/dist/{useEventBus-CQWyAWpK.d.ts → useKeyboardRouter-D84cNWmA.d.ts} +31 -1
- package/dist/{useEventBus-Ckr4wqW3.d.cts → useKeyboardRouter-DQEE_9mq.d.cts} +31 -1
- package/locales/ar.json +2 -1
- package/locales/en.json +2 -1
- package/locales/sl.json +2 -1
- package/package.json +4 -4
- package/themes/comic.css +437 -0
- package/themes/index.css +1 -0
package/dist/runtime/index.cjs
CHANGED
|
@@ -10501,7 +10501,7 @@ function collectDrawnItems(nodes) {
|
|
|
10501
10501
|
for (const n of nodes) {
|
|
10502
10502
|
switch (n.type) {
|
|
10503
10503
|
case "draw-sprite":
|
|
10504
|
-
if (isValidScenePos(n.position)) out.push({ pos: n.position, id: n.id, anchor: n.anchor, width: n.width, height: n.height });
|
|
10504
|
+
if (isValidScenePos(n.position)) out.push({ pos: n.position, id: n.id, anchor: n.anchor, width: n.width, height: n.height, rotation: n.rotation });
|
|
10505
10505
|
break;
|
|
10506
10506
|
case "draw-shape":
|
|
10507
10507
|
case "draw-text":
|
|
@@ -10511,7 +10511,7 @@ function collectDrawnItems(nodes) {
|
|
|
10511
10511
|
break;
|
|
10512
10512
|
case "draw-sprite-layer":
|
|
10513
10513
|
for (const it of n.items) {
|
|
10514
|
-
if (isValidScenePos(it.position)) out.push({ pos: it.position, id: it.id, anchor: it.anchor, width: it.width, height: it.height });
|
|
10514
|
+
if (isValidScenePos(it.position)) out.push({ pos: it.position, id: it.id, anchor: it.anchor, width: it.width, height: it.height, rotation: it.rotation });
|
|
10515
10515
|
}
|
|
10516
10516
|
break;
|
|
10517
10517
|
case "draw-shape-layer":
|
|
@@ -10531,15 +10531,39 @@ function buildHitIndex(items) {
|
|
|
10531
10531
|
}
|
|
10532
10532
|
return m;
|
|
10533
10533
|
}
|
|
10534
|
+
function withPreviewPosition(nodes, id, pos) {
|
|
10535
|
+
return nodes.map((n) => {
|
|
10536
|
+
if (n.type === "draw-sprite-layer" || n.type === "draw-shape-layer" || n.type === "draw-text-layer") {
|
|
10537
|
+
if (!n.items.some((it) => it.id === id)) return n;
|
|
10538
|
+
return { ...n, items: n.items.map((it) => it.id === id ? { ...it, position: pos } : it) };
|
|
10539
|
+
}
|
|
10540
|
+
if (n.type === "draw-group") {
|
|
10541
|
+
if (!Array.isArray(n.items)) return n;
|
|
10542
|
+
return { ...n, items: withPreviewPosition(n.items, id, pos) };
|
|
10543
|
+
}
|
|
10544
|
+
if (n.id === id && "position" in n) {
|
|
10545
|
+
return { ...n, position: pos };
|
|
10546
|
+
}
|
|
10547
|
+
return n;
|
|
10548
|
+
});
|
|
10549
|
+
}
|
|
10534
10550
|
function hitTestSprites(items, projector, point) {
|
|
10535
10551
|
for (let i = items.length - 1; i >= 0; i--) {
|
|
10536
10552
|
const it = items[i];
|
|
10537
10553
|
if (it.id === void 0) continue;
|
|
10538
10554
|
const r = spriteRect(projector, { position: it.pos, anchor: it.anchor, width: it.width, height: it.height });
|
|
10539
|
-
|
|
10555
|
+
const test = it.rotation ? rotatePoint(point, { x: r.x + r.w / 2, y: r.y + r.h / 2 }, -it.rotation) : point;
|
|
10556
|
+
if (test.x >= r.x && test.x <= r.x + r.w && test.y >= r.y && test.y <= r.y + r.h) return it.id;
|
|
10540
10557
|
}
|
|
10541
10558
|
return void 0;
|
|
10542
10559
|
}
|
|
10560
|
+
function rotatePoint(p, center, radians) {
|
|
10561
|
+
const cos = Math.cos(radians);
|
|
10562
|
+
const sin = Math.sin(radians);
|
|
10563
|
+
const dx = p.x - center.x;
|
|
10564
|
+
const dy = p.y - center.y;
|
|
10565
|
+
return { x: center.x + dx * cos - dy * sin, y: center.y + dx * sin + dy * cos };
|
|
10566
|
+
}
|
|
10543
10567
|
var init_hitTest = __esm({
|
|
10544
10568
|
"lib/drawable/hitTest.ts"() {
|
|
10545
10569
|
init_contract();
|
|
@@ -10548,6 +10572,41 @@ var init_hitTest = __esm({
|
|
|
10548
10572
|
function normalizeBackdrop(bg) {
|
|
10549
10573
|
return typeof bg === "string" ? { url: bg, role: "decoration", category: "background" } : bg;
|
|
10550
10574
|
}
|
|
10575
|
+
function selectionOverlayNodes(projector, item) {
|
|
10576
|
+
const r = spriteRect(projector, { position: item.pos, anchor: item.anchor, width: item.width, height: item.height });
|
|
10577
|
+
const tw = projector.tileWidth;
|
|
10578
|
+
const cellTopLeft = projector.anchorPoint(item.pos, "top-left");
|
|
10579
|
+
const offsetX = (r.x - cellTopLeft.x) / tw;
|
|
10580
|
+
const offsetY = (r.y - cellTopLeft.y) / tw;
|
|
10581
|
+
const width = r.w / tw;
|
|
10582
|
+
const height = r.h / tw;
|
|
10583
|
+
const handle = EDIT_HANDLE_SIZE_PX / tw;
|
|
10584
|
+
const ring = {
|
|
10585
|
+
type: "draw-shape",
|
|
10586
|
+
shape: "rect",
|
|
10587
|
+
position: item.pos,
|
|
10588
|
+
anchor: "top-left",
|
|
10589
|
+
offsetX,
|
|
10590
|
+
offsetY,
|
|
10591
|
+
width,
|
|
10592
|
+
height,
|
|
10593
|
+
stroke: EDIT_SELECTION_COLOR,
|
|
10594
|
+
strokeWidth: 2,
|
|
10595
|
+
fill: "none"
|
|
10596
|
+
};
|
|
10597
|
+
const handles = EDIT_SELECTION_CORNERS.map(([cx, cy]) => ({
|
|
10598
|
+
type: "draw-shape",
|
|
10599
|
+
shape: "rect",
|
|
10600
|
+
position: item.pos,
|
|
10601
|
+
anchor: "top-left",
|
|
10602
|
+
offsetX: offsetX + cx * width - handle / 2,
|
|
10603
|
+
offsetY: offsetY + cy * height - handle / 2,
|
|
10604
|
+
width: handle,
|
|
10605
|
+
height: handle,
|
|
10606
|
+
fill: EDIT_SELECTION_COLOR
|
|
10607
|
+
}));
|
|
10608
|
+
return [ring, ...handles];
|
|
10609
|
+
}
|
|
10551
10610
|
function Canvas2D({
|
|
10552
10611
|
className,
|
|
10553
10612
|
isLoading = false,
|
|
@@ -10561,6 +10620,12 @@ function Canvas2D({
|
|
|
10561
10620
|
tileLeaveEvent,
|
|
10562
10621
|
keyMap,
|
|
10563
10622
|
keyUpMap,
|
|
10623
|
+
editable = false,
|
|
10624
|
+
selectedId = null,
|
|
10625
|
+
onSelect,
|
|
10626
|
+
onMove,
|
|
10627
|
+
selectEvent,
|
|
10628
|
+
moveEvent,
|
|
10564
10629
|
camera = "pan-zoom",
|
|
10565
10630
|
scale = 0.4,
|
|
10566
10631
|
tileWidth,
|
|
@@ -10818,10 +10883,22 @@ function Canvas2D({
|
|
|
10818
10883
|
painter.scale(cam.zoom, cam.zoom);
|
|
10819
10884
|
painter.translate(-viewportSize.width / 2 - cam.x, -viewportSize.height / 2 - cam.y);
|
|
10820
10885
|
const dctx = { projector, time: timeMs, invalidate: bumpAtlas };
|
|
10821
|
-
|
|
10886
|
+
let paintNodes = drawables;
|
|
10887
|
+
if (editable) {
|
|
10888
|
+
const drag = editDragRef.current;
|
|
10889
|
+
if (drag && drag.moved) {
|
|
10890
|
+
paintNodes = withPreviewPosition(paintNodes, drag.id, { x: drag.previewX, y: drag.previewY });
|
|
10891
|
+
}
|
|
10892
|
+
const selectedItem = selectedId != null ? drawnItems.find((it) => it.id === selectedId) : void 0;
|
|
10893
|
+
if (selectedItem) {
|
|
10894
|
+
const overlaySource = drag && drag.moved && drag.id === selectedId ? { ...selectedItem, pos: { x: drag.previewX, y: drag.previewY } } : selectedItem;
|
|
10895
|
+
paintNodes = [...paintNodes, ...selectionOverlayNodes(projector, overlaySource)];
|
|
10896
|
+
}
|
|
10897
|
+
}
|
|
10898
|
+
for (const node of paintNodes) paintDrawable(painter, node, dctx);
|
|
10822
10899
|
painter.restore();
|
|
10823
|
-
scheduleAnimation(
|
|
10824
|
-
}, [viewportSize, backgroundImage, bgColor, drawables, projector, cameraRef, bumpAtlas, getImage, cameraPos, defaultGridFocus, camera, dragDistance]);
|
|
10900
|
+
scheduleAnimation(paintNodes);
|
|
10901
|
+
}, [viewportSize, backgroundImage, bgColor, drawables, projector, cameraRef, bumpAtlas, getImage, cameraPos, defaultGridFocus, camera, dragDistance, editable, selectedId, drawnItems]);
|
|
10825
10902
|
React87.useEffect(() => {
|
|
10826
10903
|
drawTimeRef.current = draw;
|
|
10827
10904
|
}, [draw]);
|
|
@@ -10866,23 +10943,83 @@ function Canvas2D({
|
|
|
10866
10943
|
};
|
|
10867
10944
|
}, [camera, followTarget, lerpToTarget, draw]);
|
|
10868
10945
|
const singlePointerActiveRef = React87.useRef(false);
|
|
10946
|
+
const editDragRef = React87.useRef(null);
|
|
10947
|
+
const pointerToScene = React87.useCallback((clientX, clientY) => {
|
|
10948
|
+
const canvas = canvasRef.current;
|
|
10949
|
+
if (!canvas) return { x: 0, y: 0 };
|
|
10950
|
+
const world = screenToWorld(clientX, clientY, canvas, viewportSize);
|
|
10951
|
+
const adjustedX = world.x - scaledTileWidth / 2;
|
|
10952
|
+
const adjustedY = squareGrid ? world.y - scaledTileWidth / 2 : world.y - scaledDiamondTopY - scaledFloorHeight / 2;
|
|
10953
|
+
return unproject(adjustedX, adjustedY);
|
|
10954
|
+
}, [screenToWorld, viewportSize, scaledTileWidth, squareGrid, scaledDiamondTopY, scaledFloorHeight, unproject]);
|
|
10869
10955
|
const handleCanvasPointerDown = React87.useCallback((e) => {
|
|
10870
10956
|
singlePointerActiveRef.current = true;
|
|
10957
|
+
if (editable) {
|
|
10958
|
+
if (!canvasRef.current) return;
|
|
10959
|
+
const world = screenToWorld(e.clientX, e.clientY, canvasRef.current, viewportSize);
|
|
10960
|
+
const hitId = hitTestSprites(drawnItems, projector, world);
|
|
10961
|
+
if (hitId === void 0) return;
|
|
10962
|
+
const item = [...drawnItems].reverse().find((it) => it.id === hitId);
|
|
10963
|
+
if (!item) return;
|
|
10964
|
+
editDragRef.current = {
|
|
10965
|
+
id: hitId,
|
|
10966
|
+
pointerId: e.pointerId,
|
|
10967
|
+
startClientX: e.clientX,
|
|
10968
|
+
startClientY: e.clientY,
|
|
10969
|
+
startSceneX: item.pos.x,
|
|
10970
|
+
startSceneY: item.pos.y,
|
|
10971
|
+
moved: false,
|
|
10972
|
+
previewX: item.pos.x,
|
|
10973
|
+
previewY: item.pos.y
|
|
10974
|
+
};
|
|
10975
|
+
return;
|
|
10976
|
+
}
|
|
10871
10977
|
if (enableCamera) handlePointerDown(e);
|
|
10872
|
-
}, [enableCamera, handlePointerDown]);
|
|
10978
|
+
}, [editable, screenToWorld, viewportSize, drawnItems, projector, enableCamera, handlePointerDown]);
|
|
10873
10979
|
const handleCanvasPointerMove = React87.useCallback((e) => {
|
|
10980
|
+
if (editable) {
|
|
10981
|
+
const drag = editDragRef.current;
|
|
10982
|
+
if (!drag || drag.pointerId !== e.pointerId) return;
|
|
10983
|
+
const dxPx = e.clientX - drag.startClientX;
|
|
10984
|
+
const dyPx = e.clientY - drag.startClientY;
|
|
10985
|
+
if (!drag.moved && Math.abs(dxPx) + Math.abs(dyPx) <= 5) return;
|
|
10986
|
+
drag.moved = true;
|
|
10987
|
+
const nowScene = pointerToScene(e.clientX, e.clientY);
|
|
10988
|
+
const startScene = pointerToScene(drag.startClientX, drag.startClientY);
|
|
10989
|
+
drag.previewX = drag.startSceneX + (nowScene.x - startScene.x);
|
|
10990
|
+
drag.previewY = drag.startSceneY + (nowScene.y - startScene.y);
|
|
10991
|
+
draw();
|
|
10992
|
+
return;
|
|
10993
|
+
}
|
|
10874
10994
|
if (enableCamera) handlePointerMove(e, () => draw());
|
|
10875
|
-
}, [
|
|
10995
|
+
}, [editable, pointerToScene, draw, enableCamera, handlePointerMove]);
|
|
10876
10996
|
const handleCanvasHover = React87.useCallback((e) => {
|
|
10877
10997
|
if (singlePointerActiveRef.current) return;
|
|
10878
10998
|
if (!tileHoverEvent || !canvasRef.current) return;
|
|
10879
|
-
const
|
|
10880
|
-
const adjustedX = world.x - scaledTileWidth / 2;
|
|
10881
|
-
const adjustedY = squareGrid ? world.y - scaledTileWidth / 2 : world.y - scaledDiamondTopY - scaledFloorHeight / 2;
|
|
10882
|
-
const isoPos = unproject(adjustedX, adjustedY);
|
|
10999
|
+
const isoPos = pointerToScene(e.clientX, e.clientY);
|
|
10883
11000
|
eventBus.emit(`UI:${tileHoverEvent}`, { x: isoPos.x, y: isoPos.y });
|
|
10884
|
-
}, [
|
|
11001
|
+
}, [pointerToScene, tileHoverEvent, eventBus]);
|
|
10885
11002
|
const handleCanvasPointerUp = React87.useCallback((e) => {
|
|
11003
|
+
if (editable) {
|
|
11004
|
+
singlePointerActiveRef.current = false;
|
|
11005
|
+
const drag = editDragRef.current;
|
|
11006
|
+
if (drag && drag.pointerId === e.pointerId) {
|
|
11007
|
+
editDragRef.current = null;
|
|
11008
|
+
if (drag.moved) {
|
|
11009
|
+
onMove?.(drag.id, drag.previewX, drag.previewY);
|
|
11010
|
+
if (moveEvent) eventBus.emit(`UI:${moveEvent}`, { id: drag.id, x: drag.previewX, y: drag.previewY });
|
|
11011
|
+
draw();
|
|
11012
|
+
return;
|
|
11013
|
+
}
|
|
11014
|
+
const next = selectedId === drag.id ? null : drag.id;
|
|
11015
|
+
onSelect?.(next);
|
|
11016
|
+
if (selectEvent) eventBus.emit(`UI:${selectEvent}`, { id: next });
|
|
11017
|
+
return;
|
|
11018
|
+
}
|
|
11019
|
+
onSelect?.(null);
|
|
11020
|
+
if (selectEvent) eventBus.emit(`UI:${selectEvent}`, { id: null });
|
|
11021
|
+
return;
|
|
11022
|
+
}
|
|
10886
11023
|
singlePointerActiveRef.current = false;
|
|
10887
11024
|
if (enableCamera) handlePointerUp();
|
|
10888
11025
|
if (dragDistance() > 5) return;
|
|
@@ -10893,16 +11030,14 @@ function Canvas2D({
|
|
|
10893
11030
|
eventBus.emit(`UI:${unitClickEvent}`, { unitId: spriteHit });
|
|
10894
11031
|
return;
|
|
10895
11032
|
}
|
|
10896
|
-
const
|
|
10897
|
-
const adjustedY = squareGrid ? world.y - scaledTileWidth / 2 : world.y - scaledDiamondTopY - scaledFloorHeight / 2;
|
|
10898
|
-
const isoPos = unproject(adjustedX, adjustedY);
|
|
11033
|
+
const isoPos = pointerToScene(e.clientX, e.clientY);
|
|
10899
11034
|
const hitId = hitIndex.get(`${isoPos.x},${isoPos.y}`);
|
|
10900
11035
|
if (hitId !== void 0 && unitClickEvent) {
|
|
10901
11036
|
eventBus.emit(`UI:${unitClickEvent}`, { unitId: hitId });
|
|
10902
11037
|
} else if (tileClickEvent) {
|
|
10903
11038
|
eventBus.emit(`UI:${tileClickEvent}`, { x: isoPos.x, y: isoPos.y });
|
|
10904
11039
|
}
|
|
10905
|
-
}, [
|
|
11040
|
+
}, [editable, selectedId, onMove, moveEvent, onSelect, selectEvent, eventBus, draw, enableCamera, handlePointerUp, dragDistance, screenToWorld, viewportSize, drawnItems, projector, tileClickEvent, unitClickEvent, hitIndex, pointerToScene]);
|
|
10906
11041
|
const handleCanvasPointerLeave = React87.useCallback(() => {
|
|
10907
11042
|
handleMouseLeave();
|
|
10908
11043
|
if (tileLeaveEvent) eventBus.emit(`UI:${tileLeaveEvent}`, {});
|
|
@@ -10921,7 +11056,7 @@ function Canvas2D({
|
|
|
10921
11056
|
}, [enableCamera, handlePointerUp]);
|
|
10922
11057
|
const gestureHandlers = useCanvasGestures({
|
|
10923
11058
|
canvasRef,
|
|
10924
|
-
enabled: enableCamera || !!tileHoverEvent || !!tileClickEvent || !!unitClickEvent,
|
|
11059
|
+
enabled: enableCamera || !!tileHoverEvent || !!tileClickEvent || !!unitClickEvent || editable,
|
|
10925
11060
|
onPointerDown: handleCanvasPointerDown,
|
|
10926
11061
|
onPointerMove: handleCanvasPointerMove,
|
|
10927
11062
|
onPointerUp: handleCanvasPointerUp,
|
|
@@ -11053,7 +11188,7 @@ function Canvas2D({
|
|
|
11053
11188
|
}
|
|
11054
11189
|
) });
|
|
11055
11190
|
}
|
|
11056
|
-
var canvas2DLog;
|
|
11191
|
+
var canvas2DLog, EDIT_SELECTION_COLOR, EDIT_HANDLE_SIZE_PX, EDIT_SELECTION_CORNERS;
|
|
11057
11192
|
var init_Canvas2D = __esm({
|
|
11058
11193
|
"components/game/molecules/Canvas2D.tsx"() {
|
|
11059
11194
|
"use client";
|
|
@@ -11078,8 +11213,12 @@ var init_Canvas2D = __esm({
|
|
|
11078
11213
|
init_DrawGroup();
|
|
11079
11214
|
init_registry();
|
|
11080
11215
|
init_hitTest();
|
|
11216
|
+
init_contract();
|
|
11081
11217
|
init_isometric();
|
|
11082
11218
|
canvas2DLog = logger.createLogger("almadar:ui:game-canvas");
|
|
11219
|
+
EDIT_SELECTION_COLOR = "#3b82f6";
|
|
11220
|
+
EDIT_HANDLE_SIZE_PX = 8;
|
|
11221
|
+
EDIT_SELECTION_CORNERS = [[0, 0], [1, 0], [0, 1], [1, 1]];
|
|
11083
11222
|
Canvas2D.displayName = "Canvas2D";
|
|
11084
11223
|
}
|
|
11085
11224
|
});
|
|
@@ -11121,6 +11260,12 @@ function Canvas({
|
|
|
11121
11260
|
featureClickEvent,
|
|
11122
11261
|
keyMap,
|
|
11123
11262
|
keyUpMap,
|
|
11263
|
+
editable,
|
|
11264
|
+
selectedId,
|
|
11265
|
+
onSelect,
|
|
11266
|
+
onMove,
|
|
11267
|
+
selectEvent,
|
|
11268
|
+
moveEvent,
|
|
11124
11269
|
children
|
|
11125
11270
|
}) {
|
|
11126
11271
|
canvasLog.debug("Canvas render", { mode, drawablesCount: drawables?.length, projection, camera: camera ? JSON.stringify(camera) : void 0 });
|
|
@@ -11198,6 +11343,12 @@ function Canvas({
|
|
|
11198
11343
|
tileLeaveEvent,
|
|
11199
11344
|
keyMap,
|
|
11200
11345
|
keyUpMap,
|
|
11346
|
+
editable,
|
|
11347
|
+
selectedId,
|
|
11348
|
+
onSelect,
|
|
11349
|
+
onMove,
|
|
11350
|
+
selectEvent,
|
|
11351
|
+
moveEvent,
|
|
11201
11352
|
...children !== void 0 ? { children } : {}
|
|
11202
11353
|
}
|
|
11203
11354
|
);
|
|
@@ -11219,15 +11370,19 @@ function GameAudioToggle({
|
|
|
11219
11370
|
size = "sm",
|
|
11220
11371
|
className,
|
|
11221
11372
|
onAsset,
|
|
11222
|
-
offAsset
|
|
11373
|
+
offAsset,
|
|
11374
|
+
toggleEvent
|
|
11223
11375
|
}) {
|
|
11224
11376
|
const ctx = providers.useGameAudioContextOptional();
|
|
11225
11377
|
const [localMuted, setLocalMuted] = React87.useState(false);
|
|
11226
11378
|
const muted = ctx ? ctx.muted : localMuted;
|
|
11227
11379
|
const setMuted = ctx ? ctx.setMuted : setLocalMuted;
|
|
11380
|
+
const eventBus = useEventBus();
|
|
11228
11381
|
const handleToggle = React87.useCallback(() => {
|
|
11229
|
-
|
|
11230
|
-
|
|
11382
|
+
const next = !muted;
|
|
11383
|
+
setMuted(next);
|
|
11384
|
+
if (toggleEvent) eventBus.emit(`UI:${toggleEvent}`, { muted: next });
|
|
11385
|
+
}, [muted, setMuted, toggleEvent, eventBus]);
|
|
11231
11386
|
const activeAsset = muted ? offAsset : onAsset;
|
|
11232
11387
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
11233
11388
|
Button,
|
|
@@ -11246,10 +11401,319 @@ var init_GameAudioToggle = __esm({
|
|
|
11246
11401
|
"use client";
|
|
11247
11402
|
init_atoms();
|
|
11248
11403
|
init_cn();
|
|
11404
|
+
init_useEventBus();
|
|
11249
11405
|
init_GameIcon();
|
|
11250
11406
|
GameAudioToggle.displayName = "GameAudioToggle";
|
|
11251
11407
|
}
|
|
11252
11408
|
});
|
|
11409
|
+
function pickPath(entry) {
|
|
11410
|
+
if (Array.isArray(entry.path)) {
|
|
11411
|
+
return entry.path[Math.floor(Math.random() * entry.path.length)];
|
|
11412
|
+
}
|
|
11413
|
+
return entry.path;
|
|
11414
|
+
}
|
|
11415
|
+
function useGameAudio({
|
|
11416
|
+
manifest,
|
|
11417
|
+
baseUrl = "",
|
|
11418
|
+
initialMuted = false,
|
|
11419
|
+
initialVolume = 1
|
|
11420
|
+
}) {
|
|
11421
|
+
const [muted, setMutedState] = React87.useState(initialMuted);
|
|
11422
|
+
const [masterVolume, setMasterVolumeState] = React87.useState(initialVolume);
|
|
11423
|
+
const mutedRef = React87.useRef(muted);
|
|
11424
|
+
const volumeRef = React87.useRef(masterVolume);
|
|
11425
|
+
const manifestRef = React87.useRef(manifest);
|
|
11426
|
+
mutedRef.current = muted;
|
|
11427
|
+
volumeRef.current = masterVolume;
|
|
11428
|
+
manifestRef.current = manifest;
|
|
11429
|
+
const poolsRef = React87.useRef(/* @__PURE__ */ new Map());
|
|
11430
|
+
const getOrCreateElement = React87.useCallback((key) => {
|
|
11431
|
+
const entry = manifestRef.current[key];
|
|
11432
|
+
if (!entry) return null;
|
|
11433
|
+
let pool = poolsRef.current.get(key);
|
|
11434
|
+
if (!pool) {
|
|
11435
|
+
pool = [];
|
|
11436
|
+
poolsRef.current.set(key, pool);
|
|
11437
|
+
}
|
|
11438
|
+
const maxSize = entry.poolSize ?? 1;
|
|
11439
|
+
for (const audio of pool) {
|
|
11440
|
+
if (audio.paused && (audio.ended || audio.currentTime === 0)) {
|
|
11441
|
+
return audio;
|
|
11442
|
+
}
|
|
11443
|
+
}
|
|
11444
|
+
if (pool.length < maxSize) {
|
|
11445
|
+
const src = baseUrl + pickPath(entry);
|
|
11446
|
+
const audio = new Audio(src);
|
|
11447
|
+
audio.loop = entry.loop ?? false;
|
|
11448
|
+
pool.push(audio);
|
|
11449
|
+
return audio;
|
|
11450
|
+
}
|
|
11451
|
+
if (!entry.loop) {
|
|
11452
|
+
let oldest = pool[0];
|
|
11453
|
+
for (const audio of pool) {
|
|
11454
|
+
if (audio.currentTime > oldest.currentTime) {
|
|
11455
|
+
oldest = audio;
|
|
11456
|
+
}
|
|
11457
|
+
}
|
|
11458
|
+
oldest.pause();
|
|
11459
|
+
oldest.currentTime = 0;
|
|
11460
|
+
return oldest;
|
|
11461
|
+
}
|
|
11462
|
+
return null;
|
|
11463
|
+
}, [baseUrl]);
|
|
11464
|
+
const play = React87.useCallback((key) => {
|
|
11465
|
+
if (mutedRef.current) return;
|
|
11466
|
+
const entry = manifestRef.current[key];
|
|
11467
|
+
if (!entry) return;
|
|
11468
|
+
const audio = getOrCreateElement(key);
|
|
11469
|
+
if (!audio) return;
|
|
11470
|
+
audio.volume = Math.min(1, (entry.volume ?? 1) * volumeRef.current);
|
|
11471
|
+
if (!entry.loop) {
|
|
11472
|
+
audio.currentTime = 0;
|
|
11473
|
+
}
|
|
11474
|
+
const promise = audio.play();
|
|
11475
|
+
if (promise) {
|
|
11476
|
+
promise.catch(() => {
|
|
11477
|
+
});
|
|
11478
|
+
}
|
|
11479
|
+
}, [getOrCreateElement]);
|
|
11480
|
+
const stop = React87.useCallback((key) => {
|
|
11481
|
+
const pool = poolsRef.current.get(key);
|
|
11482
|
+
if (!pool) return;
|
|
11483
|
+
for (const audio of pool) {
|
|
11484
|
+
audio.pause();
|
|
11485
|
+
audio.currentTime = 0;
|
|
11486
|
+
}
|
|
11487
|
+
}, []);
|
|
11488
|
+
const currentMusicKeyRef = React87.useRef(null);
|
|
11489
|
+
const currentMusicElRef = React87.useRef(null);
|
|
11490
|
+
const musicFadeRef = React87.useRef(null);
|
|
11491
|
+
const pendingMusicKeyRef = React87.useRef(null);
|
|
11492
|
+
const clearMusicFade = React87.useCallback(() => {
|
|
11493
|
+
if (musicFadeRef.current) {
|
|
11494
|
+
clearInterval(musicFadeRef.current);
|
|
11495
|
+
musicFadeRef.current = null;
|
|
11496
|
+
}
|
|
11497
|
+
}, []);
|
|
11498
|
+
const playMusic = React87.useCallback((key) => {
|
|
11499
|
+
if (key === currentMusicKeyRef.current) return;
|
|
11500
|
+
pendingMusicKeyRef.current = key;
|
|
11501
|
+
const entry = manifestRef.current[key];
|
|
11502
|
+
if (!entry) return;
|
|
11503
|
+
const fadeDurationMs = entry.crossfadeDurationMs ?? 1500;
|
|
11504
|
+
const stepMs = 50;
|
|
11505
|
+
const totalSteps = Math.max(1, fadeDurationMs / stepMs);
|
|
11506
|
+
const targetVolume = Math.min(1, (entry.volume ?? 1) * volumeRef.current);
|
|
11507
|
+
clearMusicFade();
|
|
11508
|
+
const src = baseUrl + (Array.isArray(entry.path) ? entry.path[0] : entry.path);
|
|
11509
|
+
const incoming = new Audio(src);
|
|
11510
|
+
incoming.loop = true;
|
|
11511
|
+
incoming.volume = 0;
|
|
11512
|
+
const outgoing = currentMusicElRef.current;
|
|
11513
|
+
const outgoingStartVol = outgoing?.volume ?? 0;
|
|
11514
|
+
currentMusicKeyRef.current = key;
|
|
11515
|
+
currentMusicElRef.current = incoming;
|
|
11516
|
+
if (!mutedRef.current) {
|
|
11517
|
+
incoming.play().catch(() => {
|
|
11518
|
+
currentMusicKeyRef.current = null;
|
|
11519
|
+
currentMusicElRef.current = outgoing;
|
|
11520
|
+
});
|
|
11521
|
+
}
|
|
11522
|
+
let step = 0;
|
|
11523
|
+
musicFadeRef.current = setInterval(() => {
|
|
11524
|
+
step++;
|
|
11525
|
+
const progress = Math.min(step / totalSteps, 1);
|
|
11526
|
+
incoming.volume = Math.min(1, targetVolume * progress);
|
|
11527
|
+
if (outgoing) {
|
|
11528
|
+
outgoing.volume = Math.max(0, outgoingStartVol * (1 - progress));
|
|
11529
|
+
}
|
|
11530
|
+
if (progress >= 1) {
|
|
11531
|
+
clearMusicFade();
|
|
11532
|
+
if (outgoing) {
|
|
11533
|
+
outgoing.pause();
|
|
11534
|
+
outgoing.src = "";
|
|
11535
|
+
}
|
|
11536
|
+
}
|
|
11537
|
+
}, stepMs);
|
|
11538
|
+
}, [baseUrl, clearMusicFade]);
|
|
11539
|
+
const stopMusic = React87.useCallback((fadeDurationMs = 1e3) => {
|
|
11540
|
+
const outgoing = currentMusicElRef.current;
|
|
11541
|
+
if (!outgoing) return;
|
|
11542
|
+
currentMusicKeyRef.current = null;
|
|
11543
|
+
currentMusicElRef.current = null;
|
|
11544
|
+
pendingMusicKeyRef.current = null;
|
|
11545
|
+
clearMusicFade();
|
|
11546
|
+
const startVolume = outgoing.volume;
|
|
11547
|
+
const stepMs = 50;
|
|
11548
|
+
const totalSteps = Math.max(1, fadeDurationMs / stepMs);
|
|
11549
|
+
let step = 0;
|
|
11550
|
+
musicFadeRef.current = setInterval(() => {
|
|
11551
|
+
step++;
|
|
11552
|
+
const progress = step / totalSteps;
|
|
11553
|
+
outgoing.volume = Math.max(0, startVolume * (1 - progress));
|
|
11554
|
+
if (progress >= 1) {
|
|
11555
|
+
clearMusicFade();
|
|
11556
|
+
outgoing.pause();
|
|
11557
|
+
outgoing.src = "";
|
|
11558
|
+
}
|
|
11559
|
+
}, stepMs);
|
|
11560
|
+
}, [clearMusicFade]);
|
|
11561
|
+
const stopAll = React87.useCallback(() => {
|
|
11562
|
+
for (const pool of poolsRef.current.values()) {
|
|
11563
|
+
for (const audio of pool) {
|
|
11564
|
+
audio.pause();
|
|
11565
|
+
audio.currentTime = 0;
|
|
11566
|
+
}
|
|
11567
|
+
}
|
|
11568
|
+
stopMusic(0);
|
|
11569
|
+
}, [stopMusic]);
|
|
11570
|
+
const setMuted = React87.useCallback((value) => {
|
|
11571
|
+
setMutedState(value);
|
|
11572
|
+
if (value) {
|
|
11573
|
+
for (const [key, pool] of poolsRef.current.entries()) {
|
|
11574
|
+
if (manifestRef.current[key]?.loop) {
|
|
11575
|
+
for (const audio of pool) {
|
|
11576
|
+
if (!audio.paused) audio.pause();
|
|
11577
|
+
}
|
|
11578
|
+
}
|
|
11579
|
+
}
|
|
11580
|
+
currentMusicElRef.current?.pause();
|
|
11581
|
+
} else {
|
|
11582
|
+
for (const [key, pool] of poolsRef.current.entries()) {
|
|
11583
|
+
const entry = manifestRef.current[key];
|
|
11584
|
+
if (entry?.loop && entry?.autostart) {
|
|
11585
|
+
for (const audio of pool) {
|
|
11586
|
+
if (audio.paused) audio.play().catch(() => {
|
|
11587
|
+
});
|
|
11588
|
+
}
|
|
11589
|
+
}
|
|
11590
|
+
}
|
|
11591
|
+
const musicEl = currentMusicElRef.current;
|
|
11592
|
+
if (musicEl) {
|
|
11593
|
+
musicEl.play().catch(() => {
|
|
11594
|
+
});
|
|
11595
|
+
}
|
|
11596
|
+
}
|
|
11597
|
+
}, []);
|
|
11598
|
+
const setMasterVolume = React87.useCallback((volume) => {
|
|
11599
|
+
const clamped = Math.max(0, Math.min(1, volume));
|
|
11600
|
+
setMasterVolumeState(clamped);
|
|
11601
|
+
for (const [key, pool] of poolsRef.current.entries()) {
|
|
11602
|
+
const entryVol = manifestRef.current[key]?.volume ?? 1;
|
|
11603
|
+
for (const audio of pool) {
|
|
11604
|
+
audio.volume = Math.min(1, entryVol * clamped);
|
|
11605
|
+
}
|
|
11606
|
+
}
|
|
11607
|
+
if (!musicFadeRef.current && currentMusicElRef.current) {
|
|
11608
|
+
const key = currentMusicKeyRef.current;
|
|
11609
|
+
const entryVol = key ? manifestRef.current[key]?.volume ?? 1 : 1;
|
|
11610
|
+
currentMusicElRef.current.volume = Math.min(1, entryVol * clamped);
|
|
11611
|
+
}
|
|
11612
|
+
}, []);
|
|
11613
|
+
const unlockedRef = React87.useRef(false);
|
|
11614
|
+
React87.useEffect(() => {
|
|
11615
|
+
const autoKeys = Object.keys(manifest).filter((k) => manifest[k].autostart);
|
|
11616
|
+
const hasPendingMusic = () => pendingMusicKeyRef.current !== null;
|
|
11617
|
+
const hasAutoStart = autoKeys.length > 0;
|
|
11618
|
+
if (!hasAutoStart && !hasPendingMusic()) return;
|
|
11619
|
+
const unlock = () => {
|
|
11620
|
+
if (unlockedRef.current) return;
|
|
11621
|
+
unlockedRef.current = true;
|
|
11622
|
+
if (!mutedRef.current) {
|
|
11623
|
+
for (const key of autoKeys) {
|
|
11624
|
+
play(key);
|
|
11625
|
+
}
|
|
11626
|
+
const pending = pendingMusicKeyRef.current;
|
|
11627
|
+
if (pending && pending !== currentMusicKeyRef.current) {
|
|
11628
|
+
playMusic(pending);
|
|
11629
|
+
}
|
|
11630
|
+
}
|
|
11631
|
+
};
|
|
11632
|
+
document.addEventListener("click", unlock, { once: true });
|
|
11633
|
+
document.addEventListener("keydown", unlock, { once: true });
|
|
11634
|
+
document.addEventListener("touchstart", unlock, { once: true });
|
|
11635
|
+
return () => {
|
|
11636
|
+
document.removeEventListener("click", unlock);
|
|
11637
|
+
document.removeEventListener("keydown", unlock);
|
|
11638
|
+
document.removeEventListener("touchstart", unlock);
|
|
11639
|
+
};
|
|
11640
|
+
}, [manifest, play, playMusic]);
|
|
11641
|
+
React87.useEffect(() => {
|
|
11642
|
+
return () => {
|
|
11643
|
+
clearMusicFade();
|
|
11644
|
+
for (const pool of poolsRef.current.values()) {
|
|
11645
|
+
for (const audio of pool) {
|
|
11646
|
+
audio.pause();
|
|
11647
|
+
audio.src = "";
|
|
11648
|
+
}
|
|
11649
|
+
}
|
|
11650
|
+
poolsRef.current.clear();
|
|
11651
|
+
if (currentMusicElRef.current) {
|
|
11652
|
+
currentMusicElRef.current.pause();
|
|
11653
|
+
currentMusicElRef.current.src = "";
|
|
11654
|
+
currentMusicElRef.current = null;
|
|
11655
|
+
}
|
|
11656
|
+
};
|
|
11657
|
+
}, [clearMusicFade]);
|
|
11658
|
+
return {
|
|
11659
|
+
play,
|
|
11660
|
+
stop,
|
|
11661
|
+
stopAll,
|
|
11662
|
+
playMusic,
|
|
11663
|
+
stopMusic,
|
|
11664
|
+
muted,
|
|
11665
|
+
setMuted,
|
|
11666
|
+
masterVolume,
|
|
11667
|
+
setMasterVolume
|
|
11668
|
+
};
|
|
11669
|
+
}
|
|
11670
|
+
var init_useGameAudio = __esm({
|
|
11671
|
+
"hooks/useGameAudio.ts"() {
|
|
11672
|
+
"use client";
|
|
11673
|
+
useGameAudio.displayName = "useGameAudio";
|
|
11674
|
+
}
|
|
11675
|
+
});
|
|
11676
|
+
function GameAudioCue({
|
|
11677
|
+
cue,
|
|
11678
|
+
cueSeq,
|
|
11679
|
+
music,
|
|
11680
|
+
muted,
|
|
11681
|
+
volume,
|
|
11682
|
+
manifest,
|
|
11683
|
+
baseUrl
|
|
11684
|
+
}) {
|
|
11685
|
+
const { play, playMusic, stopMusic, setMuted, setMasterVolume } = useGameAudio({
|
|
11686
|
+
manifest,
|
|
11687
|
+
baseUrl,
|
|
11688
|
+
initialMuted: muted,
|
|
11689
|
+
initialVolume: volume
|
|
11690
|
+
});
|
|
11691
|
+
const prevCueSeqRef = React87.useRef(cueSeq);
|
|
11692
|
+
React87.useEffect(() => {
|
|
11693
|
+
if (cue && cueSeq !== void 0 && cueSeq !== prevCueSeqRef.current) {
|
|
11694
|
+
play(cue);
|
|
11695
|
+
}
|
|
11696
|
+
prevCueSeqRef.current = cueSeq;
|
|
11697
|
+
}, [cue, cueSeq, play]);
|
|
11698
|
+
React87.useEffect(() => {
|
|
11699
|
+
if (music) playMusic(music);
|
|
11700
|
+
else stopMusic();
|
|
11701
|
+
}, [music, playMusic, stopMusic]);
|
|
11702
|
+
React87.useEffect(() => {
|
|
11703
|
+
if (muted !== void 0) setMuted(muted);
|
|
11704
|
+
}, [muted, setMuted]);
|
|
11705
|
+
React87.useEffect(() => {
|
|
11706
|
+
if (volume !== void 0) setMasterVolume(volume);
|
|
11707
|
+
}, [volume, setMasterVolume]);
|
|
11708
|
+
return null;
|
|
11709
|
+
}
|
|
11710
|
+
var init_GameAudioCue = __esm({
|
|
11711
|
+
"components/game/atoms/GameAudioCue.tsx"() {
|
|
11712
|
+
"use client";
|
|
11713
|
+
init_useGameAudio();
|
|
11714
|
+
GameAudioCue.displayName = "GameAudioCue";
|
|
11715
|
+
}
|
|
11716
|
+
});
|
|
11253
11717
|
function isKnownState(s) {
|
|
11254
11718
|
return s in DEFAULT_STATE_STYLES;
|
|
11255
11719
|
}
|
|
@@ -11948,15 +12412,18 @@ var init_StateJsonView = __esm({
|
|
|
11948
12412
|
StateJsonView.displayName = "StateJsonView";
|
|
11949
12413
|
}
|
|
11950
12414
|
});
|
|
11951
|
-
|
|
11952
|
-
|
|
11953
|
-
|
|
11954
|
-
|
|
11955
|
-
|
|
11956
|
-
|
|
11957
|
-
|
|
11958
|
-
|
|
11959
|
-
|
|
12415
|
+
|
|
12416
|
+
// lib/gameFonts.ts
|
|
12417
|
+
function resolveGameFontFamily(input) {
|
|
12418
|
+
if (!input) return void 0;
|
|
12419
|
+
const resolved = GAME_FONT_KEYS[input];
|
|
12420
|
+
if (resolved) return `'${resolved}', ui-sans-serif, system-ui, sans-serif`;
|
|
12421
|
+
return input;
|
|
12422
|
+
}
|
|
12423
|
+
var GAME_FONT_KEYS;
|
|
12424
|
+
var init_gameFonts = __esm({
|
|
12425
|
+
"lib/gameFonts.ts"() {
|
|
12426
|
+
GAME_FONT_KEYS = {
|
|
11960
12427
|
fredoka: "Fredoka",
|
|
11961
12428
|
future: "Kenney Future",
|
|
11962
12429
|
"future-narrow": "Kenney Future Narrow",
|
|
@@ -11964,6 +12431,17 @@ var init_GameShell = __esm({
|
|
|
11964
12431
|
blocks: "Kenney Blocks",
|
|
11965
12432
|
mini: "Kenney Mini"
|
|
11966
12433
|
};
|
|
12434
|
+
}
|
|
12435
|
+
});
|
|
12436
|
+
var GameShell;
|
|
12437
|
+
var init_GameShell = __esm({
|
|
12438
|
+
"components/game/templates/GameShell.tsx"() {
|
|
12439
|
+
init_cn();
|
|
12440
|
+
init_gameFonts();
|
|
12441
|
+
init_Box();
|
|
12442
|
+
init_Card();
|
|
12443
|
+
init_Typography();
|
|
12444
|
+
init_AtlasImage();
|
|
11967
12445
|
GameShell = ({
|
|
11968
12446
|
appName = "Game",
|
|
11969
12447
|
hud,
|
|
@@ -11977,7 +12455,7 @@ var init_GameShell = __esm({
|
|
|
11977
12455
|
fontFamily,
|
|
11978
12456
|
"data-theme": dataTheme
|
|
11979
12457
|
}) => {
|
|
11980
|
-
const
|
|
12458
|
+
const displayFont = resolveGameFontFamily(fontFamily);
|
|
11981
12459
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
11982
12460
|
Box,
|
|
11983
12461
|
{
|
|
@@ -11996,7 +12474,7 @@ var init_GameShell = __esm({
|
|
|
11996
12474
|
// passed — an always-on inline stamp would shadow the orbital's
|
|
11997
12475
|
// inline-theme font-family-display token (inline style beats the
|
|
11998
12476
|
// theme provider's vars for the whole shell subtree).
|
|
11999
|
-
...
|
|
12477
|
+
...displayFont ? { "--font-family-display": displayFont } : {}
|
|
12000
12478
|
},
|
|
12001
12479
|
children: [
|
|
12002
12480
|
backgroundAsset && /* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -12194,14 +12672,15 @@ function drawArrowHead(ctx, x1, y1, x2, y2, size) {
|
|
|
12194
12672
|
ctx.closePath();
|
|
12195
12673
|
ctx.fill();
|
|
12196
12674
|
}
|
|
12197
|
-
function drawShape(ctx, shape, width, height, allShapes) {
|
|
12675
|
+
function drawShape(ctx, shape, width, height, allShapes, fontFamily) {
|
|
12198
12676
|
ctx.save();
|
|
12199
12677
|
const opacity = shape.opacity ?? 1;
|
|
12200
12678
|
ctx.globalAlpha = opacity;
|
|
12201
12679
|
const stroke = resolveColor2(shape.color, ctx, "#333333");
|
|
12202
12680
|
const fill = shape.fill ? resolveColor2(shape.fill, ctx, "#cccccc") : void 0;
|
|
12203
12681
|
ctx.lineWidth = shape.lineWidth ?? 2;
|
|
12204
|
-
|
|
12682
|
+
const dashPattern = shape.dash ? DASH_PATTERNS[shape.dash] : void 0;
|
|
12683
|
+
if (dashPattern) ctx.setLineDash([...dashPattern]);
|
|
12205
12684
|
switch (shape.type) {
|
|
12206
12685
|
case "grid": {
|
|
12207
12686
|
const step = shape.step ?? 40;
|
|
@@ -12323,7 +12802,7 @@ function drawShape(ctx, shape, width, height, allShapes) {
|
|
|
12323
12802
|
case "text": {
|
|
12324
12803
|
if (shape.x == null || shape.y == null || !shape.text) break;
|
|
12325
12804
|
ctx.fillStyle = stroke;
|
|
12326
|
-
ctx.font = `${shape.fontSize ?? 14}px ${themeBodyFont(ctx.canvas)}`;
|
|
12805
|
+
ctx.font = `${shape.fontSize ?? 14}px ${shape.fontFamily ?? fontFamily ?? themeBodyFont(ctx.canvas)}`;
|
|
12327
12806
|
ctx.textAlign = shape.align ?? "left";
|
|
12328
12807
|
ctx.textBaseline = "middle";
|
|
12329
12808
|
ctx.fillText(shape.text, shape.x, shape.y);
|
|
@@ -12365,7 +12844,7 @@ function drawShape(ctx, shape, width, height, allShapes) {
|
|
|
12365
12844
|
}
|
|
12366
12845
|
ctx.restore();
|
|
12367
12846
|
}
|
|
12368
|
-
function readoutShapes(readouts, width) {
|
|
12847
|
+
function readoutShapes(readouts, width, fontFamily) {
|
|
12369
12848
|
const out = [];
|
|
12370
12849
|
const chipH = 18;
|
|
12371
12850
|
const gap = 6;
|
|
@@ -12389,13 +12868,14 @@ function readoutShapes(readouts, width) {
|
|
|
12389
12868
|
text,
|
|
12390
12869
|
color: "#ffffff",
|
|
12391
12870
|
fontSize: 10,
|
|
12392
|
-
align: "center"
|
|
12871
|
+
align: "center",
|
|
12872
|
+
fontFamily
|
|
12393
12873
|
});
|
|
12394
12874
|
rightEdge = chipX - gap;
|
|
12395
12875
|
}
|
|
12396
12876
|
return out;
|
|
12397
12877
|
}
|
|
12398
|
-
function traceShapes(panel, k, width, height) {
|
|
12878
|
+
function traceShapes(panel, k, width, height, fontFamily) {
|
|
12399
12879
|
const w = panel.width ?? Math.round(width * 0.32);
|
|
12400
12880
|
const h = panel.height ?? Math.round(height * 0.28);
|
|
12401
12881
|
const x = panel.x ?? width - w - 8;
|
|
@@ -12449,14 +12929,14 @@ function traceShapes(panel, k, width, height) {
|
|
|
12449
12929
|
out.push({ type: "circle", x: last.x, y: last.y, radius: 2, color, fill: color });
|
|
12450
12930
|
}
|
|
12451
12931
|
if (series.label) {
|
|
12452
|
-
out.push({ type: "text", x: x + 6, y: y + 10 + 11 * j, text: series.label, color, fontSize: 9 });
|
|
12932
|
+
out.push({ type: "text", x: x + 6, y: y + 10 + 11 * j, text: series.label, color, fontSize: 9, fontFamily });
|
|
12453
12933
|
}
|
|
12454
12934
|
});
|
|
12455
12935
|
if (panel.yLabel) {
|
|
12456
|
-
out.push({ type: "text", x: x + w - 6, y: y + 10, text: panel.yLabel, color: "#6b7280", fontSize: 9, align: "right" });
|
|
12936
|
+
out.push({ type: "text", x: x + w - 6, y: y + 10, text: panel.yLabel, color: "#6b7280", fontSize: 9, align: "right", fontFamily });
|
|
12457
12937
|
}
|
|
12458
12938
|
if (panel.xLabel) {
|
|
12459
|
-
out.push({ type: "text", x: x + w - 6, y: y + h - 6, text: panel.xLabel, color: "#6b7280", fontSize: 9, align: "right" });
|
|
12939
|
+
out.push({ type: "text", x: x + w - 6, y: y + h - 6, text: panel.xLabel, color: "#6b7280", fontSize: 9, align: "right", fontFamily });
|
|
12460
12940
|
}
|
|
12461
12941
|
return out;
|
|
12462
12942
|
}
|
|
@@ -12479,13 +12959,14 @@ var init_LearningCanvas = __esm({
|
|
|
12479
12959
|
init_useEventBus();
|
|
12480
12960
|
init_webPainter2d();
|
|
12481
12961
|
init_paintDispatch();
|
|
12482
|
-
DASH_PATTERNS = { dashed: [6, 4], dotted: [2, 3] };
|
|
12962
|
+
DASH_PATTERNS = { solid: [], dashed: [6, 4], dotted: [2, 3] };
|
|
12483
12963
|
TRACE_SERIES_COLORS = ["#2563eb", "#dc2626", "#16a34a", "#f59e0b"];
|
|
12484
12964
|
LearningCanvas = ({
|
|
12485
12965
|
className,
|
|
12486
12966
|
width = 600,
|
|
12487
12967
|
height = 400,
|
|
12488
12968
|
backgroundColor,
|
|
12969
|
+
fontFamily,
|
|
12489
12970
|
shapes = [],
|
|
12490
12971
|
drawables,
|
|
12491
12972
|
projector,
|
|
@@ -12521,10 +13002,10 @@ var init_LearningCanvas = __esm({
|
|
|
12521
13002
|
}, [shapes]);
|
|
12522
13003
|
const derivedShapes = React87.useMemo(() => {
|
|
12523
13004
|
if (!traces?.length && !readouts?.length) return shapes;
|
|
12524
|
-
const traceOut = (traces ?? []).flatMap((panel, k) => traceShapes(panel, k, width, height));
|
|
12525
|
-
const readoutOut = readouts?.length ? readoutShapes(readouts, width) : [];
|
|
13005
|
+
const traceOut = (traces ?? []).flatMap((panel, k) => traceShapes(panel, k, width, height, fontFamily));
|
|
13006
|
+
const readoutOut = readouts?.length ? readoutShapes(readouts, width, fontFamily) : [];
|
|
12526
13007
|
return [...shapes, ...traceOut, ...readoutOut];
|
|
12527
|
-
}, [shapes, traces, readouts, width, height]);
|
|
13008
|
+
}, [shapes, traces, readouts, width, height, fontFamily]);
|
|
12528
13009
|
const draw = React87.useCallback(() => {
|
|
12529
13010
|
const _perfT = ui.perfStart("learningcanvas:paint");
|
|
12530
13011
|
const canvas = canvasRef.current;
|
|
@@ -12543,15 +13024,15 @@ var init_LearningCanvas = __esm({
|
|
|
12543
13024
|
ctx.fillRect(0, 0, width, height);
|
|
12544
13025
|
}
|
|
12545
13026
|
for (const shape of derivedShapes) {
|
|
12546
|
-
if (shape.type !== "text") drawShape(ctx, shape, width, height, derivedShapes);
|
|
13027
|
+
if (shape.type !== "text") drawShape(ctx, shape, width, height, derivedShapes, fontFamily);
|
|
12547
13028
|
}
|
|
12548
13029
|
for (const shape of derivedShapes) {
|
|
12549
|
-
if (shape.type === "text") drawShape(ctx, shape, width, height, derivedShapes);
|
|
13030
|
+
if (shape.type === "text") drawShape(ctx, shape, width, height, derivedShapes, fontFamily);
|
|
12550
13031
|
}
|
|
12551
13032
|
if (drawables?.length && projector) {
|
|
12552
13033
|
const painter = createWebPainter(ctx, invalidateRef.current);
|
|
12553
13034
|
const timeMs = needsAnim && typeof performance !== "undefined" ? performance.now() : 0;
|
|
12554
|
-
const dctx = { projector, time: timeMs, invalidate: invalidateRef.current, fontFamily: themeBodyFont(canvas) };
|
|
13035
|
+
const dctx = { projector, time: timeMs, invalidate: invalidateRef.current, fontFamily: fontFamily || themeBodyFont(canvas) };
|
|
12555
13036
|
for (const node of drawables) {
|
|
12556
13037
|
paintDrawable(painter, node, dctx);
|
|
12557
13038
|
}
|
|
@@ -17150,6 +17631,250 @@ var init_EmptyState = __esm({
|
|
|
17150
17631
|
EmptyState.displayName = "EmptyState";
|
|
17151
17632
|
}
|
|
17152
17633
|
});
|
|
17634
|
+
|
|
17635
|
+
// lib/editorMotions.ts
|
|
17636
|
+
function clamp(value, min, max) {
|
|
17637
|
+
return Math.max(min, Math.min(max, value));
|
|
17638
|
+
}
|
|
17639
|
+
function computeLines(text) {
|
|
17640
|
+
const lines = [];
|
|
17641
|
+
let start = 0;
|
|
17642
|
+
for (let i = 0; i <= text.length; i++) {
|
|
17643
|
+
if (i === text.length || text[i] === "\n") {
|
|
17644
|
+
lines.push({ start, end: i });
|
|
17645
|
+
start = i + 1;
|
|
17646
|
+
}
|
|
17647
|
+
}
|
|
17648
|
+
return lines;
|
|
17649
|
+
}
|
|
17650
|
+
function lineIndexAt(lines, pos) {
|
|
17651
|
+
for (let i = 0; i < lines.length; i++) {
|
|
17652
|
+
if (pos <= lines[i].end) return i;
|
|
17653
|
+
}
|
|
17654
|
+
return lines.length - 1;
|
|
17655
|
+
}
|
|
17656
|
+
function findWords(text) {
|
|
17657
|
+
const words = [];
|
|
17658
|
+
const re = /\w+|\S+/g;
|
|
17659
|
+
let m;
|
|
17660
|
+
while ((m = re.exec(text)) !== null) {
|
|
17661
|
+
words.push({ start: m.index, end: m.index + m[0].length });
|
|
17662
|
+
}
|
|
17663
|
+
return words;
|
|
17664
|
+
}
|
|
17665
|
+
function nextWordStart(text, pos) {
|
|
17666
|
+
for (const w of findWords(text)) {
|
|
17667
|
+
if (w.start > pos) return w.start;
|
|
17668
|
+
}
|
|
17669
|
+
return text.length;
|
|
17670
|
+
}
|
|
17671
|
+
function prevWordStart(text, pos) {
|
|
17672
|
+
let result = 0;
|
|
17673
|
+
for (const w of findWords(text)) {
|
|
17674
|
+
if (w.start < pos) result = w.start;
|
|
17675
|
+
else break;
|
|
17676
|
+
}
|
|
17677
|
+
return result;
|
|
17678
|
+
}
|
|
17679
|
+
function nextWordEnd(text, pos) {
|
|
17680
|
+
for (const w of findWords(text)) {
|
|
17681
|
+
const lastChar = w.end - 1;
|
|
17682
|
+
if (lastChar > pos) return lastChar;
|
|
17683
|
+
}
|
|
17684
|
+
return text.length > 0 ? text.length - 1 : 0;
|
|
17685
|
+
}
|
|
17686
|
+
function firstNonBlank(text, line) {
|
|
17687
|
+
let i = line.start;
|
|
17688
|
+
while (i < line.end && (text[i] === " " || text[i] === " ")) i++;
|
|
17689
|
+
return i;
|
|
17690
|
+
}
|
|
17691
|
+
function nextParagraphBoundary(lines, fromLineIdx, textLength) {
|
|
17692
|
+
for (let i = fromLineIdx + 1; i < lines.length; i++) {
|
|
17693
|
+
if (lines[i].start === lines[i].end) return lines[i].start;
|
|
17694
|
+
}
|
|
17695
|
+
return textLength;
|
|
17696
|
+
}
|
|
17697
|
+
function prevParagraphBoundary(lines, fromLineIdx) {
|
|
17698
|
+
for (let i = fromLineIdx - 1; i >= 0; i--) {
|
|
17699
|
+
if (lines[i].start === lines[i].end) return lines[i].start;
|
|
17700
|
+
}
|
|
17701
|
+
return 0;
|
|
17702
|
+
}
|
|
17703
|
+
function applyMotion(text, caret, motion, count) {
|
|
17704
|
+
const n = Math.max(1, count);
|
|
17705
|
+
const lines = computeLines(text);
|
|
17706
|
+
const lineIdx = lineIndexAt(lines, caret);
|
|
17707
|
+
const line = lines[lineIdx];
|
|
17708
|
+
switch (motion) {
|
|
17709
|
+
case "left":
|
|
17710
|
+
return clamp(caret - n, line.start, line.end);
|
|
17711
|
+
case "right":
|
|
17712
|
+
return clamp(caret + n, line.start, line.end);
|
|
17713
|
+
case "up": {
|
|
17714
|
+
const col = caret - line.start;
|
|
17715
|
+
const targetIdx = clamp(lineIdx - n, 0, lines.length - 1);
|
|
17716
|
+
const target = lines[targetIdx];
|
|
17717
|
+
return clamp(target.start + col, target.start, target.end);
|
|
17718
|
+
}
|
|
17719
|
+
case "down": {
|
|
17720
|
+
const col = caret - line.start;
|
|
17721
|
+
const targetIdx = clamp(lineIdx + n, 0, lines.length - 1);
|
|
17722
|
+
const target = lines[targetIdx];
|
|
17723
|
+
return clamp(target.start + col, target.start, target.end);
|
|
17724
|
+
}
|
|
17725
|
+
case "word-forward": {
|
|
17726
|
+
let pos = caret;
|
|
17727
|
+
for (let i = 0; i < n; i++) pos = nextWordStart(text, pos);
|
|
17728
|
+
return pos;
|
|
17729
|
+
}
|
|
17730
|
+
case "word-back": {
|
|
17731
|
+
let pos = caret;
|
|
17732
|
+
for (let i = 0; i < n; i++) pos = prevWordStart(text, pos);
|
|
17733
|
+
return pos;
|
|
17734
|
+
}
|
|
17735
|
+
case "word-end": {
|
|
17736
|
+
let pos = caret;
|
|
17737
|
+
for (let i = 0; i < n; i++) pos = nextWordEnd(text, pos);
|
|
17738
|
+
return pos;
|
|
17739
|
+
}
|
|
17740
|
+
case "line-start":
|
|
17741
|
+
return line.start;
|
|
17742
|
+
case "line-end":
|
|
17743
|
+
return line.end > line.start ? line.end - 1 : line.start;
|
|
17744
|
+
case "first-nonblank":
|
|
17745
|
+
return firstNonBlank(text, line);
|
|
17746
|
+
case "doc-start":
|
|
17747
|
+
return 0;
|
|
17748
|
+
case "doc-end":
|
|
17749
|
+
return text.length;
|
|
17750
|
+
case "paragraph-forward": {
|
|
17751
|
+
let pos = caret;
|
|
17752
|
+
for (let i = 0; i < n; i++) {
|
|
17753
|
+
pos = nextParagraphBoundary(lines, lineIndexAt(lines, pos), text.length);
|
|
17754
|
+
}
|
|
17755
|
+
return pos;
|
|
17756
|
+
}
|
|
17757
|
+
case "paragraph-back": {
|
|
17758
|
+
let pos = caret;
|
|
17759
|
+
for (let i = 0; i < n; i++) {
|
|
17760
|
+
pos = prevParagraphBoundary(lines, lineIndexAt(lines, pos));
|
|
17761
|
+
}
|
|
17762
|
+
return pos;
|
|
17763
|
+
}
|
|
17764
|
+
case "line":
|
|
17765
|
+
case "selection":
|
|
17766
|
+
return caret;
|
|
17767
|
+
default: {
|
|
17768
|
+
const _exhaustive = motion;
|
|
17769
|
+
return _exhaustive;
|
|
17770
|
+
}
|
|
17771
|
+
}
|
|
17772
|
+
}
|
|
17773
|
+
function motionRange(text, caret, motion, count, selection) {
|
|
17774
|
+
if (motion === "selection") {
|
|
17775
|
+
if (!selection) return [caret, caret];
|
|
17776
|
+
return [Math.min(selection[0], selection[1]), Math.max(selection[0], selection[1])];
|
|
17777
|
+
}
|
|
17778
|
+
const lines = computeLines(text);
|
|
17779
|
+
if (motion === "line") {
|
|
17780
|
+
const n = Math.max(1, count);
|
|
17781
|
+
const startIdx = lineIndexAt(lines, caret);
|
|
17782
|
+
const endIdx = clamp(startIdx + n - 1, 0, lines.length - 1);
|
|
17783
|
+
const start2 = lines[startIdx].start;
|
|
17784
|
+
const rawEnd = lines[endIdx].end;
|
|
17785
|
+
const end2 = rawEnd < text.length ? rawEnd + 1 : rawEnd;
|
|
17786
|
+
return [start2, end2];
|
|
17787
|
+
}
|
|
17788
|
+
const newCaret = applyMotion(text, caret, motion, count);
|
|
17789
|
+
let start = Math.min(caret, newCaret);
|
|
17790
|
+
let end = Math.max(caret, newCaret);
|
|
17791
|
+
if (motion === "word-end" || motion === "line-end") {
|
|
17792
|
+
end = Math.min(Math.max(start, newCaret) + 1, text.length);
|
|
17793
|
+
} else if (motion === "word-forward" && newCaret > caret) {
|
|
17794
|
+
const startLine = lineIndexAt(lines, caret);
|
|
17795
|
+
const endLine = lineIndexAt(lines, newCaret);
|
|
17796
|
+
if (endLine !== startLine) {
|
|
17797
|
+
end = lines[startLine].end;
|
|
17798
|
+
}
|
|
17799
|
+
}
|
|
17800
|
+
return [start, end];
|
|
17801
|
+
}
|
|
17802
|
+
function applyOperator(text, range, operator, register) {
|
|
17803
|
+
const start = clamp(range[0], 0, text.length);
|
|
17804
|
+
const end = clamp(range[1], start, text.length);
|
|
17805
|
+
const removed = text.slice(start, end);
|
|
17806
|
+
if (operator === "yank") {
|
|
17807
|
+
return { text, caret: start, register: removed };
|
|
17808
|
+
}
|
|
17809
|
+
return { text: text.slice(0, start) + text.slice(end), caret: start, register: removed };
|
|
17810
|
+
}
|
|
17811
|
+
var init_editorMotions = __esm({
|
|
17812
|
+
"lib/editorMotions.ts"() {
|
|
17813
|
+
}
|
|
17814
|
+
});
|
|
17815
|
+
function isMotionPayload(payload) {
|
|
17816
|
+
return !!payload && typeof payload.editorId === "string" && typeof payload.motion === "string" && typeof payload.count === "number";
|
|
17817
|
+
}
|
|
17818
|
+
function isOperatePayload(payload) {
|
|
17819
|
+
return !!payload && typeof payload.editorId === "string" && typeof payload.operator === "string" && typeof payload.motion === "string" && typeof payload.count === "number";
|
|
17820
|
+
}
|
|
17821
|
+
function isInsertTextPayload(payload) {
|
|
17822
|
+
return !!payload && typeof payload.editorId === "string" && typeof payload.text === "string";
|
|
17823
|
+
}
|
|
17824
|
+
function isSetModePayload(payload) {
|
|
17825
|
+
return !!payload && typeof payload.editorId === "string" && typeof payload.mode === "string" && typeof payload.caret === "string";
|
|
17826
|
+
}
|
|
17827
|
+
function useEditorCapabilities(args) {
|
|
17828
|
+
const [caretMode, setCaretMode] = React87.useState("bar");
|
|
17829
|
+
const registerRef = React87.useRef("");
|
|
17830
|
+
useEventListener(`UI:${args.events.onMotion}`, (evt) => {
|
|
17831
|
+
if (!args.editorId || !isMotionPayload(evt.payload) || evt.payload.editorId !== args.editorId) return;
|
|
17832
|
+
const ta = args.textareaRef.current;
|
|
17833
|
+
if (!ta) return;
|
|
17834
|
+
const { motion, count } = evt.payload;
|
|
17835
|
+
const newCaret = applyMotion(ta.value, ta.selectionStart, motion, count);
|
|
17836
|
+
if (ta.selectionStart !== ta.selectionEnd) {
|
|
17837
|
+
ta.setSelectionRange(ta.selectionStart, newCaret);
|
|
17838
|
+
} else {
|
|
17839
|
+
ta.setSelectionRange(newCaret, newCaret);
|
|
17840
|
+
}
|
|
17841
|
+
});
|
|
17842
|
+
useEventListener(`UI:${args.events.onOperate}`, (evt) => {
|
|
17843
|
+
if (!args.editorId || !isOperatePayload(evt.payload) || evt.payload.editorId !== args.editorId) return;
|
|
17844
|
+
const ta = args.textareaRef.current;
|
|
17845
|
+
if (!ta) return;
|
|
17846
|
+
const { operator, motion, count } = evt.payload;
|
|
17847
|
+
const selection = motion === "selection" ? [ta.selectionStart, ta.selectionEnd] : void 0;
|
|
17848
|
+
const range = motionRange(ta.value, ta.selectionStart, motion, count, selection);
|
|
17849
|
+
const result = applyOperator(ta.value, range, operator, registerRef.current);
|
|
17850
|
+
registerRef.current = result.register;
|
|
17851
|
+
if (operator === "yank") {
|
|
17852
|
+
ta.setSelectionRange(range[0], range[0]);
|
|
17853
|
+
} else {
|
|
17854
|
+
ta.setRangeText("", range[0], range[1], "end");
|
|
17855
|
+
}
|
|
17856
|
+
args.applyChange(ta.value);
|
|
17857
|
+
});
|
|
17858
|
+
useEventListener(`UI:${args.events.onInsertText}`, (evt) => {
|
|
17859
|
+
if (!args.editorId || !isInsertTextPayload(evt.payload) || evt.payload.editorId !== args.editorId) return;
|
|
17860
|
+
const ta = args.textareaRef.current;
|
|
17861
|
+
if (!ta) return;
|
|
17862
|
+
ta.setRangeText(evt.payload.text, ta.selectionStart, ta.selectionEnd, "end");
|
|
17863
|
+
args.applyChange(ta.value);
|
|
17864
|
+
});
|
|
17865
|
+
useEventListener(`UI:${args.events.onSetMode}`, (evt) => {
|
|
17866
|
+
if (!args.editorId || !isSetModePayload(evt.payload) || evt.payload.editorId !== args.editorId) return;
|
|
17867
|
+
setCaretMode(evt.payload.caret);
|
|
17868
|
+
});
|
|
17869
|
+
return { caretMode };
|
|
17870
|
+
}
|
|
17871
|
+
var init_useEditorCapabilities = __esm({
|
|
17872
|
+
"components/core/molecules/markdown/useEditorCapabilities.ts"() {
|
|
17873
|
+
"use client";
|
|
17874
|
+
init_useEventBus();
|
|
17875
|
+
init_editorMotions();
|
|
17876
|
+
}
|
|
17877
|
+
});
|
|
17153
17878
|
function isLanguageRegistered(lang) {
|
|
17154
17879
|
return CODE_LANGUAGE_SET.has(lang) || dynamicallyLoaded.has(lang);
|
|
17155
17880
|
}
|
|
@@ -17235,7 +17960,36 @@ function useLanguageReady(language) {
|
|
|
17235
17960
|
}, [language]);
|
|
17236
17961
|
return ready;
|
|
17237
17962
|
}
|
|
17238
|
-
|
|
17963
|
+
function resolveHighlightStyle(lang) {
|
|
17964
|
+
if (lang === "orb") return orbStyle;
|
|
17965
|
+
if (lang === "lolo") return loloStyle;
|
|
17966
|
+
return dark__default.default;
|
|
17967
|
+
}
|
|
17968
|
+
function plainCodeColorOf(style) {
|
|
17969
|
+
return style['code[class*="language-"]']?.color ?? "#d4d4d4";
|
|
17970
|
+
}
|
|
17971
|
+
function buildLineProps(errorLines, extraClassName) {
|
|
17972
|
+
return (lineNumber) => {
|
|
17973
|
+
const base = {
|
|
17974
|
+
"data-line": String(lineNumber - 1),
|
|
17975
|
+
...extraClassName ? { className: extraClassName } : {}
|
|
17976
|
+
};
|
|
17977
|
+
const severity = errorLines?.get(lineNumber);
|
|
17978
|
+
if (!severity) return base;
|
|
17979
|
+
return {
|
|
17980
|
+
...base,
|
|
17981
|
+
style: {
|
|
17982
|
+
display: "block",
|
|
17983
|
+
backgroundColor: severity === "error" ? "rgba(248, 113, 113, 0.18)" : "rgba(251, 191, 36, 0.18)",
|
|
17984
|
+
// amber-400 @ 18%
|
|
17985
|
+
borderLeft: `3px solid ${severity === "error" ? "#ef4444" : "#f59e0b"}`,
|
|
17986
|
+
paddingLeft: "0.5rem",
|
|
17987
|
+
marginLeft: "-0.5rem"
|
|
17988
|
+
}
|
|
17989
|
+
};
|
|
17990
|
+
};
|
|
17991
|
+
}
|
|
17992
|
+
var dynamicallyLoaded, codeLanguageLoader, orbStyleOverrides, orbStyle, loloStyleOverrides, loloStyle, log6, 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, CodeBlock;
|
|
17239
17993
|
var init_CodeBlock = __esm({
|
|
17240
17994
|
"components/core/molecules/markdown/CodeBlock.tsx"() {
|
|
17241
17995
|
init_cn();
|
|
@@ -17251,6 +18005,7 @@ var init_CodeBlock = __esm({
|
|
|
17251
18005
|
init_Textarea();
|
|
17252
18006
|
init_Icon();
|
|
17253
18007
|
init_useEventBus();
|
|
18008
|
+
init_useEditorCapabilities();
|
|
17254
18009
|
SyntaxHighlighter__default.default.registerLanguage("json", langJson__default.default);
|
|
17255
18010
|
SyntaxHighlighter__default.default.registerLanguage("javascript", langJavascript__default.default);
|
|
17256
18011
|
SyntaxHighlighter__default.default.registerLanguage("js", langJavascript__default.default);
|
|
@@ -17469,6 +18224,15 @@ var init_CodeBlock = __esm({
|
|
|
17469
18224
|
LINE_PROPS_FN = (n) => ({ "data-line": String(n - 1) });
|
|
17470
18225
|
HIDDEN_LINE_NUMBERS = { display: "none" };
|
|
17471
18226
|
HIGHLIGHT_CAPACITY_BYTES = 512 * 1024;
|
|
18227
|
+
MONO_FONT_FAMILY = 'ui-monospace, SFMono-Regular, Menlo, Monaco, "Cascadia Mono", "Courier New", monospace';
|
|
18228
|
+
VIEWER_LINE_NUMBER_STYLE = {
|
|
18229
|
+
minWidth: "2.5em",
|
|
18230
|
+
paddingRight: "1rem",
|
|
18231
|
+
textAlign: "right",
|
|
18232
|
+
userSelect: "none",
|
|
18233
|
+
opacity: 0.5,
|
|
18234
|
+
fontVariantNumeric: "tabular-nums"
|
|
18235
|
+
};
|
|
17472
18236
|
CodeBlock = React87__namespace.default.memo(
|
|
17473
18237
|
({
|
|
17474
18238
|
code: rawCode,
|
|
@@ -17493,15 +18257,39 @@ var init_CodeBlock = __esm({
|
|
|
17493
18257
|
actions,
|
|
17494
18258
|
isLoading = false,
|
|
17495
18259
|
error,
|
|
17496
|
-
showCopy
|
|
18260
|
+
showCopy,
|
|
18261
|
+
// editor capability surface — P1 wires these
|
|
18262
|
+
editorId,
|
|
18263
|
+
onEditorFocus = "EDITOR_FOCUS",
|
|
18264
|
+
onEditorBlur = "EDITOR_BLUR",
|
|
18265
|
+
onMotion = "MOTION",
|
|
18266
|
+
onOperate = "OPERATE",
|
|
18267
|
+
onInsertText = "INSERT_TEXT",
|
|
18268
|
+
onSetMode = "SET_MODE",
|
|
18269
|
+
motions = [
|
|
18270
|
+
"left",
|
|
18271
|
+
"right",
|
|
18272
|
+
"up",
|
|
18273
|
+
"down",
|
|
18274
|
+
"word-forward",
|
|
18275
|
+
"word-back",
|
|
18276
|
+
"word-end",
|
|
18277
|
+
"line-start",
|
|
18278
|
+
"line-end",
|
|
18279
|
+
"first-nonblank",
|
|
18280
|
+
"doc-start",
|
|
18281
|
+
"doc-end",
|
|
18282
|
+
"paragraph-forward",
|
|
18283
|
+
"paragraph-back",
|
|
18284
|
+
"line",
|
|
18285
|
+
"selection"
|
|
18286
|
+
],
|
|
18287
|
+
operators = ["delete", "yank", "change"]
|
|
17497
18288
|
}) => {
|
|
17498
18289
|
const code = typeof rawCode === "string" ? rawCode : String(rawCode ?? "");
|
|
17499
|
-
const
|
|
17500
|
-
const isLolo = language === "lolo";
|
|
17501
|
-
const activeStyle = isOrb ? orbStyle : isLolo ? loloStyle : dark__default.default;
|
|
18290
|
+
const activeStyle = resolveHighlightStyle(language);
|
|
17502
18291
|
const overCapacity = code.length > HIGHLIGHT_CAPACITY_BYTES;
|
|
17503
|
-
const plainCodeColor = activeStyle
|
|
17504
|
-
const languageReady = useLanguageReady(language);
|
|
18292
|
+
const plainCodeColor = plainCodeColorOf(activeStyle);
|
|
17505
18293
|
const eventBus = useEventBus();
|
|
17506
18294
|
const { t } = hooks.useTranslate();
|
|
17507
18295
|
const scrollRef = React87.useRef(null);
|
|
@@ -17513,6 +18301,9 @@ var init_CodeBlock = __esm({
|
|
|
17513
18301
|
const activeFile = files?.[activeFileIndex];
|
|
17514
18302
|
const activeCode = activeFile?.code ?? code;
|
|
17515
18303
|
const activeLanguage = activeFile?.language ?? language;
|
|
18304
|
+
const languageReady = useLanguageReady(activeLanguage);
|
|
18305
|
+
const viewerStyle = resolveHighlightStyle(activeLanguage);
|
|
18306
|
+
const viewerPlainCodeColor = plainCodeColorOf(viewerStyle);
|
|
17516
18307
|
const diffLines = React87.useMemo(() => {
|
|
17517
18308
|
if (propDiff) return propDiff;
|
|
17518
18309
|
if (mode === "diff" && oldValue !== void 0 && newValue !== void 0) {
|
|
@@ -17542,28 +18333,28 @@ var init_CodeBlock = __esm({
|
|
|
17542
18333
|
ov.scrollLeft = ta.scrollLeft;
|
|
17543
18334
|
}
|
|
17544
18335
|
}, []);
|
|
17545
|
-
const
|
|
17546
|
-
|
|
17547
|
-
|
|
17548
|
-
|
|
17549
|
-
|
|
17550
|
-
|
|
17551
|
-
|
|
17552
|
-
|
|
17553
|
-
|
|
17554
|
-
|
|
17555
|
-
|
|
17556
|
-
|
|
17557
|
-
|
|
17558
|
-
|
|
17559
|
-
|
|
17560
|
-
|
|
17561
|
-
|
|
17562
|
-
|
|
17563
|
-
|
|
17564
|
-
|
|
17565
|
-
|
|
17566
|
-
|
|
18336
|
+
const handleEditableChange = React87.useCallback((v) => {
|
|
18337
|
+
lastPropCodeRef.current = v;
|
|
18338
|
+
setEditableValue(v);
|
|
18339
|
+
onChange?.(v);
|
|
18340
|
+
}, [onChange]);
|
|
18341
|
+
const { caretMode } = useEditorCapabilities({
|
|
18342
|
+
editorId: editable ? editorId : void 0,
|
|
18343
|
+
textareaRef: editableTextareaRef,
|
|
18344
|
+
events: { onMotion, onOperate, onInsertText, onSetMode },
|
|
18345
|
+
applyChange: handleEditableChange
|
|
18346
|
+
});
|
|
18347
|
+
const [caretIndex, setCaretIndex] = React87.useState(0);
|
|
18348
|
+
const caretRowCol = React87.useMemo(() => {
|
|
18349
|
+
const before = editableValue.slice(0, Math.min(caretIndex, editableValue.length));
|
|
18350
|
+
const lines = before.split("\n");
|
|
18351
|
+
return { row: lines.length - 1, col: lines[lines.length - 1].length };
|
|
18352
|
+
}, [editableValue, caretIndex]);
|
|
18353
|
+
const errorLineProps = React87.useMemo(() => buildLineProps(errorLines), [errorLines]);
|
|
18354
|
+
const viewerLineProps = React87.useMemo(
|
|
18355
|
+
() => buildLineProps(errorLines, "px-4 py-0.5 hover:bg-muted/50"),
|
|
18356
|
+
[errorLines]
|
|
18357
|
+
);
|
|
17567
18358
|
const isFoldable = foldableProp ?? true;
|
|
17568
18359
|
const [collapsed, setCollapsed] = React87.useState(() => /* @__PURE__ */ new Set());
|
|
17569
18360
|
const foldRegions = React87.useMemo(
|
|
@@ -17686,6 +18477,110 @@ var init_CodeBlock = __esm({
|
|
|
17686
18477
|
),
|
|
17687
18478
|
[code, overCapacity, plainCodeColor, language, activeStyle, languageReady]
|
|
17688
18479
|
);
|
|
18480
|
+
const viewerOverCapacity = activeCode.length > HIGHLIGHT_CAPACITY_BYTES;
|
|
18481
|
+
const viewerHighlightedElement = React87.useMemo(
|
|
18482
|
+
() => viewerOverCapacity ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
18483
|
+
"div",
|
|
18484
|
+
{
|
|
18485
|
+
className: "px-4 py-0.5",
|
|
18486
|
+
style: {
|
|
18487
|
+
margin: 0,
|
|
18488
|
+
whiteSpace: wrap ? "pre-wrap" : "pre",
|
|
18489
|
+
wordBreak: wrap ? "break-all" : "normal",
|
|
18490
|
+
color: viewerPlainCodeColor,
|
|
18491
|
+
fontFamily: MONO_FONT_FAMILY,
|
|
18492
|
+
fontSize: "12px",
|
|
18493
|
+
lineHeight: "1.6"
|
|
18494
|
+
},
|
|
18495
|
+
children: activeCode
|
|
18496
|
+
}
|
|
18497
|
+
) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
18498
|
+
SyntaxHighlighter__default.default,
|
|
18499
|
+
{
|
|
18500
|
+
PreTag: "div",
|
|
18501
|
+
language: activeLanguage,
|
|
18502
|
+
style: viewerStyle,
|
|
18503
|
+
wrapLines: true,
|
|
18504
|
+
wrapLongLines: wrap,
|
|
18505
|
+
showLineNumbers,
|
|
18506
|
+
lineNumberStyle: VIEWER_LINE_NUMBER_STYLE,
|
|
18507
|
+
lineProps: viewerLineProps,
|
|
18508
|
+
customStyle: {
|
|
18509
|
+
backgroundColor: "transparent",
|
|
18510
|
+
borderRadius: 0,
|
|
18511
|
+
padding: "0.25rem 0",
|
|
18512
|
+
margin: 0,
|
|
18513
|
+
whiteSpace: wrap ? "pre-wrap" : "pre",
|
|
18514
|
+
wordBreak: wrap ? "break-all" : "normal",
|
|
18515
|
+
fontFamily: MONO_FONT_FAMILY,
|
|
18516
|
+
fontSize: "12px",
|
|
18517
|
+
lineHeight: "1.6"
|
|
18518
|
+
},
|
|
18519
|
+
codeTagProps: { style: { fontFamily: MONO_FONT_FAMILY, fontSize: "12px", lineHeight: "1.6" } },
|
|
18520
|
+
children: activeCode
|
|
18521
|
+
}
|
|
18522
|
+
),
|
|
18523
|
+
[activeCode, viewerOverCapacity, viewerPlainCodeColor, activeLanguage, viewerStyle, wrap, showLineNumbers, viewerLineProps, languageReady]
|
|
18524
|
+
);
|
|
18525
|
+
const diffOverCapacity = React87.useMemo(
|
|
18526
|
+
() => !!diffLines && diffLines.reduce((n, l) => n + l.content.length + 1, 0) > HIGHLIGHT_CAPACITY_BYTES,
|
|
18527
|
+
[diffLines]
|
|
18528
|
+
);
|
|
18529
|
+
const diffRowElements = React87.useMemo(() => {
|
|
18530
|
+
if (!diffLines) return null;
|
|
18531
|
+
return diffLines.map((line, idx) => {
|
|
18532
|
+
const style = DIFF_STYLES[line.type] ?? DIFF_STYLE_FALLBACK;
|
|
18533
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "none", align: "start", className: cn(style.bg, "px-4 py-0.5"), children: [
|
|
18534
|
+
showLineNumbers && /* @__PURE__ */ jsxRuntime.jsx(
|
|
18535
|
+
Typography,
|
|
18536
|
+
{
|
|
18537
|
+
variant: "caption",
|
|
18538
|
+
color: "secondary",
|
|
18539
|
+
className: "w-8 text-right mr-3 select-none tabular-nums flex-shrink-0",
|
|
18540
|
+
children: line.lineNumber ?? ""
|
|
18541
|
+
}
|
|
18542
|
+
),
|
|
18543
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
18544
|
+
Typography,
|
|
18545
|
+
{
|
|
18546
|
+
variant: "caption",
|
|
18547
|
+
className: cn("font-mono flex-1 min-w-0", style.text, wrap ? "whitespace-pre-wrap break-all" : "whitespace-pre"),
|
|
18548
|
+
children: [
|
|
18549
|
+
/* @__PURE__ */ jsxRuntime.jsx(Box, { as: "span", className: "select-none opacity-50 mr-2", children: style.prefix }),
|
|
18550
|
+
diffOverCapacity ? line.content || " " : /* @__PURE__ */ jsxRuntime.jsx(
|
|
18551
|
+
SyntaxHighlighter__default.default,
|
|
18552
|
+
{
|
|
18553
|
+
PreTag: "span",
|
|
18554
|
+
CodeTag: "span",
|
|
18555
|
+
language: activeLanguage,
|
|
18556
|
+
style: viewerStyle,
|
|
18557
|
+
customStyle: {
|
|
18558
|
+
display: "inline",
|
|
18559
|
+
background: "transparent",
|
|
18560
|
+
padding: 0,
|
|
18561
|
+
margin: 0,
|
|
18562
|
+
whiteSpace: wrap ? "pre-wrap" : "pre",
|
|
18563
|
+
wordBreak: wrap ? "break-all" : "normal",
|
|
18564
|
+
fontFamily: "inherit",
|
|
18565
|
+
fontSize: "inherit",
|
|
18566
|
+
lineHeight: "inherit"
|
|
18567
|
+
},
|
|
18568
|
+
codeTagProps: {
|
|
18569
|
+
style: {
|
|
18570
|
+
whiteSpace: wrap ? "pre-wrap" : "pre",
|
|
18571
|
+
fontFamily: "inherit",
|
|
18572
|
+
fontSize: "inherit"
|
|
18573
|
+
}
|
|
18574
|
+
},
|
|
18575
|
+
children: line.content || " "
|
|
18576
|
+
}
|
|
18577
|
+
)
|
|
18578
|
+
]
|
|
18579
|
+
}
|
|
18580
|
+
)
|
|
18581
|
+
] }, idx);
|
|
18582
|
+
});
|
|
18583
|
+
}, [diffLines, showLineNumbers, wrap, diffOverCapacity, activeLanguage, viewerStyle, languageReady]);
|
|
17689
18584
|
React87.useLayoutEffect(() => {
|
|
17690
18585
|
const container = codeRef.current;
|
|
17691
18586
|
if (!container) return;
|
|
@@ -17829,7 +18724,6 @@ var init_CodeBlock = __esm({
|
|
|
17829
18724
|
label: file.label,
|
|
17830
18725
|
content: null
|
|
17831
18726
|
}));
|
|
17832
|
-
const lines = activeCode.split("\n");
|
|
17833
18727
|
return /* @__PURE__ */ jsxRuntime.jsx(Card, { className: cn("overflow-hidden", className), children: /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", flexDirection: "column" }, children: [
|
|
17834
18728
|
tabItems && tabItems.length > 1 && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "border-b border-border", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
17835
18729
|
Tabs,
|
|
@@ -17892,49 +18786,7 @@ var init_CodeBlock = __esm({
|
|
|
17892
18786
|
]
|
|
17893
18787
|
}
|
|
17894
18788
|
),
|
|
17895
|
-
/* @__PURE__ */ jsxRuntime.jsx(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:
|
|
17896
|
-
const style = DIFF_STYLES[line.type] ?? DIFF_STYLE_FALLBACK;
|
|
17897
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "none", align: "start", className: cn(style.bg, "px-4 py-0.5"), children: [
|
|
17898
|
-
showLineNumbers && /* @__PURE__ */ jsxRuntime.jsx(
|
|
17899
|
-
Typography,
|
|
17900
|
-
{
|
|
17901
|
-
variant: "caption",
|
|
17902
|
-
color: "secondary",
|
|
17903
|
-
className: "w-8 text-right mr-3 select-none tabular-nums flex-shrink-0",
|
|
17904
|
-
children: line.lineNumber ?? ""
|
|
17905
|
-
}
|
|
17906
|
-
),
|
|
17907
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
17908
|
-
Typography,
|
|
17909
|
-
{
|
|
17910
|
-
variant: "caption",
|
|
17911
|
-
className: cn("font-mono flex-1 min-w-0", style.text, wrap ? "whitespace-pre-wrap break-all" : "whitespace-pre"),
|
|
17912
|
-
children: [
|
|
17913
|
-
/* @__PURE__ */ jsxRuntime.jsx(Box, { as: "span", className: "select-none opacity-50 mr-2", children: style.prefix }),
|
|
17914
|
-
line.content
|
|
17915
|
-
]
|
|
17916
|
-
}
|
|
17917
|
-
)
|
|
17918
|
-
] }, idx);
|
|
17919
|
-
}) }) : /* @__PURE__ */ jsxRuntime.jsx("div", { style: { display: "flex", flexDirection: "column" }, className: "font-mono text-xs", children: lines.map((line, idx) => /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "none", align: "start", className: "px-4 py-0.5 hover:bg-muted/50", children: [
|
|
17920
|
-
showLineNumbers && /* @__PURE__ */ jsxRuntime.jsx(
|
|
17921
|
-
Typography,
|
|
17922
|
-
{
|
|
17923
|
-
variant: "caption",
|
|
17924
|
-
color: "secondary",
|
|
17925
|
-
className: "w-8 text-right mr-4 select-none tabular-nums flex-shrink-0",
|
|
17926
|
-
children: idx + 1
|
|
17927
|
-
}
|
|
17928
|
-
),
|
|
17929
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
17930
|
-
Typography,
|
|
17931
|
-
{
|
|
17932
|
-
variant: "caption",
|
|
17933
|
-
className: cn("font-mono flex-1 min-w-0", wrap ? "whitespace-pre-wrap break-all" : "whitespace-pre"),
|
|
17934
|
-
children: line || " "
|
|
17935
|
-
}
|
|
17936
|
-
)
|
|
17937
|
-
] }, idx)) }) })
|
|
18789
|
+
/* @__PURE__ */ jsxRuntime.jsx(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 }) })
|
|
17938
18790
|
] }) });
|
|
17939
18791
|
}
|
|
17940
18792
|
const hasHeader = showLanguageBadge || effectiveCopy;
|
|
@@ -18021,13 +18873,11 @@ var init_CodeBlock = __esm({
|
|
|
18021
18873
|
{
|
|
18022
18874
|
ref: editableTextareaRef,
|
|
18023
18875
|
defaultValue: code,
|
|
18024
|
-
onChange: (e) =>
|
|
18025
|
-
const v = e.target.value;
|
|
18026
|
-
lastPropCodeRef.current = v;
|
|
18027
|
-
setEditableValue(v);
|
|
18028
|
-
onChange?.(v);
|
|
18029
|
-
},
|
|
18876
|
+
onChange: (e) => handleEditableChange(e.target.value),
|
|
18030
18877
|
onScroll: handleEditableScroll,
|
|
18878
|
+
onSelect: (e) => setCaretIndex(e.currentTarget.selectionStart),
|
|
18879
|
+
onFocus: editorId ? () => eventBus.emit(`UI:${onEditorFocus}`, { editorId }) : void 0,
|
|
18880
|
+
onBlur: editorId ? () => eventBus.emit(`UI:${onEditorBlur}`, { editorId }) : void 0,
|
|
18031
18881
|
spellCheck: false,
|
|
18032
18882
|
style: {
|
|
18033
18883
|
position: "absolute",
|
|
@@ -18042,7 +18892,7 @@ var init_CodeBlock = __esm({
|
|
|
18042
18892
|
resize: "none",
|
|
18043
18893
|
backgroundColor: "transparent",
|
|
18044
18894
|
color: "transparent",
|
|
18045
|
-
caretColor: "#e6e6e6",
|
|
18895
|
+
caretColor: caretMode === "block" ? "transparent" : "#e6e6e6",
|
|
18046
18896
|
WebkitTextFillColor: "transparent",
|
|
18047
18897
|
fontFamily: 'ui-monospace, SFMono-Regular, Menlo, Monaco, "Cascadia Mono", "Courier New", monospace',
|
|
18048
18898
|
fontSize: "13px",
|
|
@@ -18053,6 +18903,22 @@ var init_CodeBlock = __esm({
|
|
|
18053
18903
|
}
|
|
18054
18904
|
},
|
|
18055
18905
|
editableTextareaKey
|
|
18906
|
+
),
|
|
18907
|
+
caretMode !== "bar" && /* @__PURE__ */ jsxRuntime.jsx(
|
|
18908
|
+
"span",
|
|
18909
|
+
{
|
|
18910
|
+
"aria-hidden": true,
|
|
18911
|
+
style: {
|
|
18912
|
+
position: "absolute",
|
|
18913
|
+
top: `calc(1rem + ${caretRowCol.row * 19.5}px)`,
|
|
18914
|
+
left: `calc(1rem + ${caretRowCol.col}ch)`,
|
|
18915
|
+
width: "1ch",
|
|
18916
|
+
height: caretMode === "block" ? "19.5px" : "2px",
|
|
18917
|
+
backgroundColor: caretMode === "block" ? "rgba(230, 230, 230, 0.5)" : void 0,
|
|
18918
|
+
borderBottom: caretMode === "underline" ? "2px solid #e6e6e6" : void 0,
|
|
18919
|
+
pointerEvents: "none"
|
|
18920
|
+
}
|
|
18921
|
+
}
|
|
18056
18922
|
)
|
|
18057
18923
|
]
|
|
18058
18924
|
}
|
|
@@ -18080,55 +18946,175 @@ var init_CodeBlock = __esm({
|
|
|
18080
18946
|
)
|
|
18081
18947
|
] });
|
|
18082
18948
|
},
|
|
18083
|
-
(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
|
|
18949
|
+
(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
|
|
18084
18950
|
);
|
|
18085
18951
|
CodeBlock.displayName = "CodeBlock";
|
|
18086
18952
|
}
|
|
18087
18953
|
});
|
|
18954
|
+
|
|
18955
|
+
// components/core/molecules/markdown/mermaidSource.ts
|
|
18956
|
+
function isQuoted(label) {
|
|
18957
|
+
const trimmed = label.trim();
|
|
18958
|
+
return trimmed.length === 0 || trimmed.startsWith('"') && trimmed.endsWith('"') && trimmed.length > 1;
|
|
18959
|
+
}
|
|
18960
|
+
function quote(label) {
|
|
18961
|
+
return `"${label.replace(/"/g, "#quot;")}"`;
|
|
18962
|
+
}
|
|
18963
|
+
function isIdentifierChar(ch) {
|
|
18964
|
+
return /[A-Za-z0-9_\-.]/.test(ch);
|
|
18965
|
+
}
|
|
18966
|
+
function declaredType(code) {
|
|
18967
|
+
for (const line of code.split("\n")) {
|
|
18968
|
+
const trimmed = line.trim();
|
|
18969
|
+
if (trimmed.length === 0 || trimmed.startsWith("%%")) continue;
|
|
18970
|
+
return trimmed;
|
|
18971
|
+
}
|
|
18972
|
+
return "";
|
|
18973
|
+
}
|
|
18974
|
+
function isFlowchart(code) {
|
|
18975
|
+
return FLOWCHART_DIRECTIVE.test(declaredType(code));
|
|
18976
|
+
}
|
|
18977
|
+
function quoteNodeLabels(code) {
|
|
18978
|
+
let out = "";
|
|
18979
|
+
let i = 0;
|
|
18980
|
+
while (i < code.length) {
|
|
18981
|
+
const shape = NODE_SHAPES.find(([open2]) => code.startsWith(open2, i));
|
|
18982
|
+
const precededByIdentifier = i > 0 && isIdentifierChar(code[i - 1] ?? "");
|
|
18983
|
+
if (shape === void 0 || !precededByIdentifier) {
|
|
18984
|
+
out += code[i];
|
|
18985
|
+
i += 1;
|
|
18986
|
+
continue;
|
|
18987
|
+
}
|
|
18988
|
+
const [open, close] = shape;
|
|
18989
|
+
const contentStart = i + open.length;
|
|
18990
|
+
const closeAt = code.indexOf(close, contentStart);
|
|
18991
|
+
const newlineAt = code.indexOf("\n", contentStart);
|
|
18992
|
+
if (closeAt === -1 || newlineAt !== -1 && newlineAt < closeAt) {
|
|
18993
|
+
out += code[i];
|
|
18994
|
+
i += 1;
|
|
18995
|
+
continue;
|
|
18996
|
+
}
|
|
18997
|
+
const label = code.slice(contentStart, closeAt);
|
|
18998
|
+
out += open + (isQuoted(label) ? label : quote(label)) + close;
|
|
18999
|
+
i = closeAt + close.length;
|
|
19000
|
+
}
|
|
19001
|
+
return out;
|
|
19002
|
+
}
|
|
19003
|
+
function quoteEdgeLabels(code) {
|
|
19004
|
+
return code.split("\n").map((line) => {
|
|
19005
|
+
let out = "";
|
|
19006
|
+
let rest = line;
|
|
19007
|
+
for (; ; ) {
|
|
19008
|
+
const open = rest.indexOf("|");
|
|
19009
|
+
if (open === -1) break;
|
|
19010
|
+
const close = rest.indexOf("|", open + 1);
|
|
19011
|
+
if (close === -1) break;
|
|
19012
|
+
const label = rest.slice(open + 1, close);
|
|
19013
|
+
out += rest.slice(0, open + 1) + (isQuoted(label) ? label : quote(label)) + "|";
|
|
19014
|
+
rest = rest.slice(close + 1);
|
|
19015
|
+
}
|
|
19016
|
+
return out + rest;
|
|
19017
|
+
}).join("\n");
|
|
19018
|
+
}
|
|
19019
|
+
function quoteSubgraphTitles(code) {
|
|
19020
|
+
return code.split("\n").map((line) => {
|
|
19021
|
+
const match = /^(\s*subgraph\s+)(.+?)(\s*)$/.exec(line);
|
|
19022
|
+
if (match === null) return line;
|
|
19023
|
+
const [, prefix, title, trailing] = match;
|
|
19024
|
+
if (title === void 0 || prefix === void 0) return line;
|
|
19025
|
+
if (isQuoted(title) || title.includes("[")) return line;
|
|
19026
|
+
return prefix + quote(title) + (trailing ?? "");
|
|
19027
|
+
}).join("\n");
|
|
19028
|
+
}
|
|
19029
|
+
function mermaidRepairCandidates(code) {
|
|
19030
|
+
if (!isFlowchart(code)) return [];
|
|
19031
|
+
const nodes = quoteNodeLabels(code);
|
|
19032
|
+
const nodesAndEdges = quoteEdgeLabels(nodes);
|
|
19033
|
+
const all = quoteSubgraphTitles(nodesAndEdges);
|
|
19034
|
+
const ordered2 = [nodes, nodesAndEdges, all];
|
|
19035
|
+
const seen = /* @__PURE__ */ new Set([code]);
|
|
19036
|
+
const candidates = [];
|
|
19037
|
+
for (const candidate of ordered2) {
|
|
19038
|
+
if (seen.has(candidate)) continue;
|
|
19039
|
+
seen.add(candidate);
|
|
19040
|
+
candidates.push(candidate);
|
|
19041
|
+
}
|
|
19042
|
+
return candidates;
|
|
19043
|
+
}
|
|
19044
|
+
var NODE_SHAPES, FLOWCHART_DIRECTIVE;
|
|
19045
|
+
var init_mermaidSource = __esm({
|
|
19046
|
+
"components/core/molecules/markdown/mermaidSource.ts"() {
|
|
19047
|
+
NODE_SHAPES = [
|
|
19048
|
+
["[[", "]]"],
|
|
19049
|
+
["[(", ")]"],
|
|
19050
|
+
["([", "])"],
|
|
19051
|
+
["((", "))"],
|
|
19052
|
+
["{{", "}}"],
|
|
19053
|
+
["[", "]"],
|
|
19054
|
+
["(", ")"],
|
|
19055
|
+
["{", "}"]
|
|
19056
|
+
];
|
|
19057
|
+
FLOWCHART_DIRECTIVE = /^(?:graph|flowchart)\b/;
|
|
19058
|
+
}
|
|
19059
|
+
});
|
|
18088
19060
|
function loadMermaid() {
|
|
18089
19061
|
mermaidModule ?? (mermaidModule = import('mermaid').then((m) => m.default));
|
|
18090
19062
|
return mermaidModule;
|
|
18091
19063
|
}
|
|
18092
|
-
var mermaidModule, MermaidDiagram;
|
|
19064
|
+
var log7, mermaidModule, MermaidDiagram;
|
|
18093
19065
|
var init_MermaidDiagram = __esm({
|
|
18094
19066
|
"components/core/molecules/markdown/MermaidDiagram.tsx"() {
|
|
18095
19067
|
init_Box();
|
|
18096
19068
|
init_Typography();
|
|
18097
19069
|
init_CodeBlock();
|
|
19070
|
+
init_mermaidSource();
|
|
18098
19071
|
init_cn();
|
|
19072
|
+
log7 = logger.createLogger("almadar:ui:mermaid-diagram");
|
|
18099
19073
|
mermaidModule = null;
|
|
18100
19074
|
MermaidDiagram = React87__namespace.default.memo(
|
|
18101
19075
|
({ code, className }) => {
|
|
18102
19076
|
const { resolvedMode } = context.useTheme();
|
|
19077
|
+
const { t } = hooks.useTranslate();
|
|
18103
19078
|
const containerRef = React87.useRef(null);
|
|
18104
|
-
const [
|
|
19079
|
+
const [unrenderable, setUnrenderable] = React87.useState(false);
|
|
18105
19080
|
const reactId = React87.useId();
|
|
18106
19081
|
React87.useEffect(() => {
|
|
18107
19082
|
let active = true;
|
|
18108
19083
|
void (async () => {
|
|
18109
|
-
|
|
18110
|
-
|
|
18111
|
-
|
|
18112
|
-
|
|
18113
|
-
|
|
18114
|
-
|
|
18115
|
-
|
|
18116
|
-
|
|
18117
|
-
|
|
18118
|
-
|
|
18119
|
-
|
|
18120
|
-
|
|
18121
|
-
|
|
18122
|
-
|
|
19084
|
+
const mermaid = await loadMermaid();
|
|
19085
|
+
mermaid.initialize({
|
|
19086
|
+
startOnLoad: false,
|
|
19087
|
+
securityLevel: "strict",
|
|
19088
|
+
theme: resolvedMode === "dark" ? "dark" : "default"
|
|
19089
|
+
});
|
|
19090
|
+
const domId = `mermaid-${reactId.replace(/[^a-zA-Z0-9]/g, "")}`;
|
|
19091
|
+
let firstError = null;
|
|
19092
|
+
for (const [index, source] of [code, ...mermaidRepairCandidates(code)].entries()) {
|
|
19093
|
+
try {
|
|
19094
|
+
const { svg } = await mermaid.render(domId, source);
|
|
19095
|
+
if (!active) return;
|
|
19096
|
+
const container = containerRef.current;
|
|
19097
|
+
if (container === null) return;
|
|
19098
|
+
container.innerHTML = svg;
|
|
19099
|
+
container.dataset.mermaidRepaired = String(index > 0);
|
|
19100
|
+
setUnrenderable(false);
|
|
19101
|
+
if (index > 0) log7.debug("mermaid:repaired", { candidate: index });
|
|
19102
|
+
return;
|
|
19103
|
+
} catch (err) {
|
|
19104
|
+
firstError ?? (firstError = err instanceof Error ? err : new Error(String(err)));
|
|
19105
|
+
}
|
|
18123
19106
|
}
|
|
19107
|
+
if (!active) return;
|
|
19108
|
+
log7.warn("mermaid:unrenderable", { error: firstError?.message ?? "", code });
|
|
19109
|
+
setUnrenderable(true);
|
|
18124
19110
|
})();
|
|
18125
19111
|
return () => {
|
|
18126
19112
|
active = false;
|
|
18127
19113
|
};
|
|
18128
19114
|
}, [code, resolvedMode, reactId]);
|
|
18129
19115
|
return /* @__PURE__ */ jsxRuntime.jsxs(Box, { className: cn("not-prose my-4", className), children: [
|
|
18130
|
-
|
|
18131
|
-
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", className: "text-
|
|
19116
|
+
unrenderable && /* @__PURE__ */ jsxRuntime.jsxs(Box, { className: "space-y-2 mb-2", "data-testid": "mermaid-unrenderable", children: [
|
|
19117
|
+
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", className: "text-muted-foreground", children: t("mermaid.unrenderable") }),
|
|
18132
19118
|
/* @__PURE__ */ jsxRuntime.jsx(CodeBlock, { code, language: "mermaid" })
|
|
18133
19119
|
] }),
|
|
18134
19120
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -18137,7 +19123,7 @@ var init_MermaidDiagram = __esm({
|
|
|
18137
19123
|
ref: containerRef,
|
|
18138
19124
|
"data-testid": "mermaid-diagram",
|
|
18139
19125
|
className: "overflow-x-auto",
|
|
18140
|
-
style:
|
|
19126
|
+
style: unrenderable ? { display: "none" } : void 0
|
|
18141
19127
|
}
|
|
18142
19128
|
)
|
|
18143
19129
|
] });
|
|
@@ -21492,14 +22478,14 @@ function useSafeEventBus2() {
|
|
|
21492
22478
|
} };
|
|
21493
22479
|
}
|
|
21494
22480
|
}
|
|
21495
|
-
var
|
|
22481
|
+
var log8, lookStyles4, ButtonGroup;
|
|
21496
22482
|
var init_ButtonGroup = __esm({
|
|
21497
22483
|
"components/core/molecules/ButtonGroup.tsx"() {
|
|
21498
22484
|
"use client";
|
|
21499
22485
|
init_cn();
|
|
21500
22486
|
init_atoms();
|
|
21501
22487
|
init_useEventBus();
|
|
21502
|
-
|
|
22488
|
+
log8 = logger.createLogger("almadar:ui:button-group");
|
|
21503
22489
|
lookStyles4 = {
|
|
21504
22490
|
"right-aligned-buttons": "",
|
|
21505
22491
|
"floating-bar": "fixed bottom-section left-1/2 -translate-x-1/2 shadow-elevation-toast bg-card p-card-sm rounded-container",
|
|
@@ -21580,7 +22566,7 @@ var init_ButtonGroup = __esm({
|
|
|
21580
22566
|
{
|
|
21581
22567
|
variant: "ghost",
|
|
21582
22568
|
onClick: () => {
|
|
21583
|
-
|
|
22569
|
+
log8.debug("Filter clicked", { field: filter.field });
|
|
21584
22570
|
},
|
|
21585
22571
|
children: filter.label
|
|
21586
22572
|
},
|
|
@@ -24297,6 +25283,12 @@ function commandMatches(command, query) {
|
|
|
24297
25283
|
if (matchesQuery(query, command.label)) return true;
|
|
24298
25284
|
return (command.keywords ?? []).some((keyword) => matchesQuery(query, keyword));
|
|
24299
25285
|
}
|
|
25286
|
+
function dispatchCommandPaletteCommand(command, deps) {
|
|
25287
|
+
if (command.disabled) return;
|
|
25288
|
+
if (command.event) deps.emit(`UI:${command.event}`, { commandId: command.id });
|
|
25289
|
+
if (command.action) deps.emit(`UI:${command.action}`, command.actionPayload ?? {});
|
|
25290
|
+
deps.onSelect?.(command);
|
|
25291
|
+
}
|
|
24300
25292
|
var UNGROUPED, CommandPalette;
|
|
24301
25293
|
var init_CommandPalette = __esm({
|
|
24302
25294
|
"components/core/molecules/CommandPalette.tsx"() {
|
|
@@ -24351,9 +25343,7 @@ var init_CommandPalette = __esm({
|
|
|
24351
25343
|
const handleSelect = React87.useCallback(
|
|
24352
25344
|
(command) => {
|
|
24353
25345
|
if (command.disabled) return;
|
|
24354
|
-
|
|
24355
|
-
if (command.action) eventBus.emit(`UI:${command.action}`, command.actionPayload ?? {});
|
|
24356
|
-
onSelect?.(command);
|
|
25346
|
+
dispatchCommandPaletteCommand(command, { emit: eventBus.emit, onSelect });
|
|
24357
25347
|
handleClose();
|
|
24358
25348
|
},
|
|
24359
25349
|
[eventBus, onSelect, handleClose]
|
|
@@ -29148,9 +30138,9 @@ function debug(...args) {
|
|
|
29148
30138
|
const [first, ...rest] = args;
|
|
29149
30139
|
const message = typeof first === "string" ? first : "<debug>";
|
|
29150
30140
|
if (rest.length === 0 && typeof first === "string") {
|
|
29151
|
-
|
|
30141
|
+
log9.debug(message);
|
|
29152
30142
|
} else {
|
|
29153
|
-
|
|
30143
|
+
log9.debug(message, { args: rest.length > 0 ? formatArgs(rest) : formatArgs([first]) });
|
|
29154
30144
|
}
|
|
29155
30145
|
}
|
|
29156
30146
|
function debugGroup(label) {
|
|
@@ -29178,11 +30168,11 @@ function toLogMetaValue(v) {
|
|
|
29178
30168
|
}
|
|
29179
30169
|
return String(v);
|
|
29180
30170
|
}
|
|
29181
|
-
var NAMESPACE,
|
|
30171
|
+
var NAMESPACE, log9;
|
|
29182
30172
|
var init_debug = __esm({
|
|
29183
30173
|
"lib/debug.ts"() {
|
|
29184
30174
|
NAMESPACE = "almadar:ui:debug";
|
|
29185
|
-
|
|
30175
|
+
log9 = logger.createLogger(NAMESPACE);
|
|
29186
30176
|
logger.createLogger("almadar:ui:debug:input");
|
|
29187
30177
|
logger.createLogger("almadar:ui:debug:collision");
|
|
29188
30178
|
logger.createLogger("almadar:ui:debug:physics");
|
|
@@ -30835,6 +31825,7 @@ var init_MathCanvas = __esm({
|
|
|
30835
31825
|
init_perf();
|
|
30836
31826
|
init_atoms();
|
|
30837
31827
|
init_Stack();
|
|
31828
|
+
init_gameFonts();
|
|
30838
31829
|
init_LearningCanvas();
|
|
30839
31830
|
MathCanvas = ({
|
|
30840
31831
|
className,
|
|
@@ -30854,6 +31845,7 @@ var init_MathCanvas = __esm({
|
|
|
30854
31845
|
showTickLabels = false,
|
|
30855
31846
|
tickLabelFontSize = 10,
|
|
30856
31847
|
labelFontSize = 12,
|
|
31848
|
+
fontFamily: fontFamilyProp,
|
|
30857
31849
|
showCurveLabels = false,
|
|
30858
31850
|
curves = [],
|
|
30859
31851
|
points = [],
|
|
@@ -30876,6 +31868,7 @@ var init_MathCanvas = __esm({
|
|
|
30876
31868
|
error
|
|
30877
31869
|
}) => {
|
|
30878
31870
|
const eventBus = useEventBus();
|
|
31871
|
+
const fontFamily = resolveGameFontFamily(fontFamilyProp);
|
|
30879
31872
|
const keyMapKey = keyMap ? JSON.stringify(keyMap) : null;
|
|
30880
31873
|
const keyUpMapKey = keyUpMap ? JSON.stringify(keyUpMap) : null;
|
|
30881
31874
|
const stableKeyMap = React87.useMemo(() => keyMap, [keyMapKey]);
|
|
@@ -30925,18 +31918,18 @@ var init_MathCanvas = __esm({
|
|
|
30925
31918
|
let kx = 0;
|
|
30926
31919
|
for (let x = Math.ceil(xMin / gridStep) * gridStep; x <= xMax; x += gridStep, kx++) {
|
|
30927
31920
|
if (kx % labelEveryX === 0 && x !== 0) {
|
|
30928
|
-
out.push({ type: "text", x: mapX(x), y: xAxisY + 12, text: formatTick(x), color: "#6b7280", fontSize: tickLabelFontSize, align: "center" });
|
|
31921
|
+
out.push({ type: "text", fontFamily, x: mapX(x), y: xAxisY + 12, text: formatTick(x), color: "#6b7280", fontSize: tickLabelFontSize, align: "center" });
|
|
30929
31922
|
}
|
|
30930
31923
|
}
|
|
30931
31924
|
const labelEveryY = Math.max(1, Math.ceil((yMax - yMin) / gridStep / Math.floor(plotH / 28)));
|
|
30932
31925
|
let ky = 0;
|
|
30933
31926
|
for (let y = Math.ceil(yMin / gridStep) * gridStep; y <= yMax; y += gridStep, ky++) {
|
|
30934
31927
|
if (ky % labelEveryY === 0 && y !== 0) {
|
|
30935
|
-
out.push({ type: "text", x: yAxisX - 6, y: mapY(y), text: formatTick(y), color: "#6b7280", fontSize: tickLabelFontSize, align: "right" });
|
|
31928
|
+
out.push({ type: "text", fontFamily, x: yAxisX - 6, y: mapY(y), text: formatTick(y), color: "#6b7280", fontSize: tickLabelFontSize, align: "right" });
|
|
30936
31929
|
}
|
|
30937
31930
|
}
|
|
30938
31931
|
if (xMin <= 0 && xMax >= 0 && yMin <= 0 && yMax >= 0) {
|
|
30939
|
-
out.push({ type: "text", x: yAxisX - 6, y: xAxisY + 12, text: "0", color: "#6b7280", fontSize: tickLabelFontSize, align: "right" });
|
|
31932
|
+
out.push({ type: "text", fontFamily, x: yAxisX - 6, y: xAxisY + 12, text: "0", color: "#6b7280", fontSize: tickLabelFontSize, align: "right" });
|
|
30940
31933
|
}
|
|
30941
31934
|
}
|
|
30942
31935
|
for (const region of regions) {
|
|
@@ -30963,6 +31956,7 @@ var init_MathCanvas = __esm({
|
|
|
30963
31956
|
const mid = Math.floor(region.samples.length / 2);
|
|
30964
31957
|
out.push({
|
|
30965
31958
|
type: "text",
|
|
31959
|
+
fontFamily,
|
|
30966
31960
|
x: mapX((first.x + last.x) / 2),
|
|
30967
31961
|
y: (mapY(region.samples[mid].y) + mapY(baseline)) / 2,
|
|
30968
31962
|
text: region.label,
|
|
@@ -30999,14 +31993,14 @@ var init_MathCanvas = __esm({
|
|
|
30999
31993
|
const px = mapX(guide.at);
|
|
31000
31994
|
out.push({ type: "line", x1: px, y1: margin, x2: px, y2: height - margin, color, dash });
|
|
31001
31995
|
if (guide.label) {
|
|
31002
|
-
out.push({ type: "text", x: px + 4, y: margin + 10, text: guide.label, color, fontSize: 11 });
|
|
31996
|
+
out.push({ type: "text", fontFamily, x: px + 4, y: margin + 10, text: guide.label, color, fontSize: 11 });
|
|
31003
31997
|
}
|
|
31004
31998
|
} else {
|
|
31005
31999
|
if (guide.at < yMin || guide.at > yMax) continue;
|
|
31006
32000
|
const py = mapY(guide.at);
|
|
31007
32001
|
out.push({ type: "line", x1: margin, y1: py, x2: width - margin, y2: py, color, dash });
|
|
31008
32002
|
if (guide.label) {
|
|
31009
|
-
out.push({ type: "text", x: width - margin - 4, y: py - 8, text: guide.label, color, fontSize: labelFontSize, align: "right" });
|
|
32003
|
+
out.push({ type: "text", fontFamily, x: width - margin - 4, y: py - 8, text: guide.label, color, fontSize: labelFontSize, align: "right" });
|
|
31010
32004
|
}
|
|
31011
32005
|
}
|
|
31012
32006
|
}
|
|
@@ -31045,6 +32039,7 @@ var init_MathCanvas = __esm({
|
|
|
31045
32039
|
if (showCurveLabels && curve.label && lastInRange) {
|
|
31046
32040
|
out.push({
|
|
31047
32041
|
type: "text",
|
|
32042
|
+
fontFamily,
|
|
31048
32043
|
x: mapX(lastInRange.x) + 6,
|
|
31049
32044
|
y: mapY(lastInRange.y) - 6,
|
|
31050
32045
|
text: curve.label,
|
|
@@ -31082,6 +32077,7 @@ var init_MathCanvas = __esm({
|
|
|
31082
32077
|
if (hop.label) {
|
|
31083
32078
|
out.push({
|
|
31084
32079
|
type: "text",
|
|
32080
|
+
fontFamily,
|
|
31085
32081
|
x: (x1 + x2) / 2,
|
|
31086
32082
|
y: xAxisY - peak - 8,
|
|
31087
32083
|
text: hop.label,
|
|
@@ -31109,6 +32105,7 @@ var init_MathCanvas = __esm({
|
|
|
31109
32105
|
const rad = mid * Math.PI / 180;
|
|
31110
32106
|
out.push({
|
|
31111
32107
|
type: "text",
|
|
32108
|
+
fontFamily,
|
|
31112
32109
|
x: mapX(angle.x + 1.35 * radius * Math.cos(rad)),
|
|
31113
32110
|
y: mapY(angle.y + 1.35 * radius * Math.sin(rad)),
|
|
31114
32111
|
text: angle.label,
|
|
@@ -31130,7 +32127,7 @@ var init_MathCanvas = __esm({
|
|
|
31130
32127
|
fill: isOpen ? "#ffffff" : p.color ?? "#dc2626"
|
|
31131
32128
|
});
|
|
31132
32129
|
if (p.label) {
|
|
31133
|
-
out.push({ type: "text", x: mapX(p.x) + 8, y: mapY(p.y) - 8, text: p.label, color: p.color ?? "#111827", fontSize: labelFontSize });
|
|
32130
|
+
out.push({ type: "text", fontFamily, x: mapX(p.x) + 8, y: mapY(p.y) - 8, text: p.label, color: p.color ?? "#111827", fontSize: labelFontSize });
|
|
31134
32131
|
}
|
|
31135
32132
|
}
|
|
31136
32133
|
for (const v of vectors) {
|
|
@@ -31141,7 +32138,7 @@ var init_MathCanvas = __esm({
|
|
|
31141
32138
|
const y2 = mapY(v.y + v.vy);
|
|
31142
32139
|
out.push({ type: "arrow", x1, y1, x2, y2, color: v.color ?? "#7c3aed", lineWidth: 2 });
|
|
31143
32140
|
if (v.label) {
|
|
31144
|
-
out.push({ type: "text", x: x2 + 6, y: y2 - 6, text: v.label, color: v.color ?? "#7c3aed", fontSize: labelFontSize });
|
|
32141
|
+
out.push({ type: "text", fontFamily, x: x2 + 6, y: y2 - 6, text: v.label, color: v.color ?? "#7c3aed", fontSize: labelFontSize });
|
|
31145
32142
|
}
|
|
31146
32143
|
}
|
|
31147
32144
|
out.push(...shapes);
|
|
@@ -31162,6 +32159,7 @@ var init_MathCanvas = __esm({
|
|
|
31162
32159
|
showTickLabels,
|
|
31163
32160
|
tickLabelFontSize,
|
|
31164
32161
|
labelFontSize,
|
|
32162
|
+
fontFamily,
|
|
31165
32163
|
showCurveLabels,
|
|
31166
32164
|
curves,
|
|
31167
32165
|
points,
|
|
@@ -31217,6 +32215,7 @@ var init_MathCanvas = __esm({
|
|
|
31217
32215
|
width,
|
|
31218
32216
|
height,
|
|
31219
32217
|
backgroundColor,
|
|
32218
|
+
fontFamily,
|
|
31220
32219
|
shapes: derivedShapes,
|
|
31221
32220
|
drawables,
|
|
31222
32221
|
projector,
|
|
@@ -32449,13 +33448,13 @@ var init_MapView = __esm({
|
|
|
32449
33448
|
shadowSize: [41, 41]
|
|
32450
33449
|
});
|
|
32451
33450
|
L.Marker.prototype.options.icon = defaultIcon;
|
|
32452
|
-
const { useEffect:
|
|
33451
|
+
const { useEffect: useEffect67, useRef: useRef69, useCallback: useCallback95, useState: useState102 } = React87__namespace.default;
|
|
32453
33452
|
const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
|
|
32454
33453
|
const { useEventBus: useEventBus2 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
|
|
32455
33454
|
function MapUpdater({ centerLat, centerLng, zoom }) {
|
|
32456
33455
|
const map = useMap();
|
|
32457
|
-
const prevRef =
|
|
32458
|
-
|
|
33456
|
+
const prevRef = useRef69({ centerLat, centerLng, zoom });
|
|
33457
|
+
useEffect67(() => {
|
|
32459
33458
|
const prev = prevRef.current;
|
|
32460
33459
|
if (prev.centerLat !== centerLat || prev.centerLng !== centerLng || prev.zoom !== zoom) {
|
|
32461
33460
|
map.setView([centerLat, centerLng], zoom);
|
|
@@ -32466,7 +33465,7 @@ var init_MapView = __esm({
|
|
|
32466
33465
|
}
|
|
32467
33466
|
function MapClickHandler({ onMapClick }) {
|
|
32468
33467
|
const map = useMap();
|
|
32469
|
-
|
|
33468
|
+
useEffect67(() => {
|
|
32470
33469
|
if (!onMapClick) return;
|
|
32471
33470
|
const handler = (e) => {
|
|
32472
33471
|
onMapClick(e.latlng.lat, e.latlng.lng);
|
|
@@ -32494,8 +33493,8 @@ var init_MapView = __esm({
|
|
|
32494
33493
|
showAttribution = true
|
|
32495
33494
|
}) {
|
|
32496
33495
|
const eventBus = useEventBus2();
|
|
32497
|
-
const [clickedPosition, setClickedPosition] =
|
|
32498
|
-
const handleMapClick =
|
|
33496
|
+
const [clickedPosition, setClickedPosition] = useState102(null);
|
|
33497
|
+
const handleMapClick = useCallback95((lat, lng) => {
|
|
32499
33498
|
if (showClickedPin) {
|
|
32500
33499
|
setClickedPosition({ lat, lng });
|
|
32501
33500
|
}
|
|
@@ -32504,7 +33503,7 @@ var init_MapView = __esm({
|
|
|
32504
33503
|
eventBus.emit(`UI:${mapClickEvent}`, { latitude: lat, longitude: lng });
|
|
32505
33504
|
}
|
|
32506
33505
|
}, [onMapClick, mapClickEvent, eventBus, showClickedPin]);
|
|
32507
|
-
const handleMarkerClick =
|
|
33506
|
+
const handleMarkerClick = useCallback95((marker) => {
|
|
32508
33507
|
onMarkerClick?.(marker);
|
|
32509
33508
|
if (markerClickEvent) {
|
|
32510
33509
|
eventBus.emit(`UI:${markerClickEvent}`, { ...marker });
|
|
@@ -47084,7 +48083,7 @@ function getAllEvents(traits2) {
|
|
|
47084
48083
|
function EventDispatcherTab({ traits: traits2, schema }) {
|
|
47085
48084
|
const eventBus = useEventBus();
|
|
47086
48085
|
const { t } = hooks.useTranslate();
|
|
47087
|
-
const [
|
|
48086
|
+
const [log11, setLog] = React87__namespace.useState([]);
|
|
47088
48087
|
const prevStatesRef = React87__namespace.useRef(/* @__PURE__ */ new Map());
|
|
47089
48088
|
React87__namespace.useEffect(() => {
|
|
47090
48089
|
for (const trait of traits2) {
|
|
@@ -47148,9 +48147,9 @@ function EventDispatcherTab({ traits: traits2, schema }) {
|
|
|
47148
48147
|
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", weight: "medium", className: "text-muted-foreground mb-1", children: t("debug.otherEvents") }),
|
|
47149
48148
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-wrap gap-1", children: unavailableEvents.map((event) => /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: "default", size: "sm", className: "opacity-50", children: event }, event)) })
|
|
47150
48149
|
] }),
|
|
47151
|
-
|
|
48150
|
+
log11.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
47152
48151
|
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", weight: "medium", className: "text-muted-foreground mb-1", children: t("debug.recentTransitions") }),
|
|
47153
|
-
/* @__PURE__ */ jsxRuntime.jsx(Stack, { gap: "xs", children:
|
|
48152
|
+
/* @__PURE__ */ jsxRuntime.jsx(Stack, { gap: "xs", children: log11.map((entry, i) => /* @__PURE__ */ jsxRuntime.jsxs(Typography, { variant: "small", className: "font-mono text-xs", children: [
|
|
47154
48153
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-primary", children: entry.traitName }),
|
|
47155
48154
|
" ",
|
|
47156
48155
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: entry.from }),
|
|
@@ -49341,6 +50340,7 @@ var init_component_registry_generated = __esm({
|
|
|
49341
50340
|
init_FormSection();
|
|
49342
50341
|
init_FormSectionHeader();
|
|
49343
50342
|
init_FxOverlay();
|
|
50343
|
+
init_GameAudioCue();
|
|
49344
50344
|
init_GameAudioToggle();
|
|
49345
50345
|
init_GameHud();
|
|
49346
50346
|
init_GameIcon();
|
|
@@ -49618,6 +50618,7 @@ var init_component_registry_generated = __esm({
|
|
|
49618
50618
|
"FormLayout": FormLayout,
|
|
49619
50619
|
"FormSectionHeader": FormSectionHeader,
|
|
49620
50620
|
"FxOverlay": FxOverlay,
|
|
50621
|
+
"GameAudioCue": GameAudioCue,
|
|
49621
50622
|
"GameAudioToggle": GameAudioToggle,
|
|
49622
50623
|
"GameHud": GameHud,
|
|
49623
50624
|
"GameIcon": GameIcon,
|
|
@@ -51592,7 +52593,7 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
|
|
|
51592
52593
|
};
|
|
51593
52594
|
}, [traitBindings]);
|
|
51594
52595
|
const executeTransitionEffects = React87.useCallback(async (params) => {
|
|
51595
|
-
const { binding, previousState, newState, payload, flushEvent, syncOnly, log:
|
|
52596
|
+
const { binding, previousState, newState, payload, flushEvent, syncOnly, log: log11 } = params;
|
|
51596
52597
|
const traitName = binding.trait.name;
|
|
51597
52598
|
const linkedEntity = binding.linkedEntity || "";
|
|
51598
52599
|
const entityId = payload?.entityId;
|
|
@@ -51721,7 +52722,7 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
|
|
|
51721
52722
|
if (sharedKey !== void 0) {
|
|
51722
52723
|
sharedWrites.push({ field, value });
|
|
51723
52724
|
}
|
|
51724
|
-
|
|
52725
|
+
log11.debug("set:write", {
|
|
51725
52726
|
traitName,
|
|
51726
52727
|
field,
|
|
51727
52728
|
value: JSON.stringify(value),
|
|
@@ -51789,7 +52790,7 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
|
|
|
51789
52790
|
core.mergeEntityFrame(sharedEntityStore.getSnapshot(sharedKey), sharedWrites)
|
|
51790
52791
|
);
|
|
51791
52792
|
}
|
|
51792
|
-
|
|
52793
|
+
log11.debug("effects:executed", () => ({
|
|
51793
52794
|
traitName,
|
|
51794
52795
|
transition: `${previousState}->${newState}`,
|
|
51795
52796
|
event: flushEvent,
|
|
@@ -51799,7 +52800,7 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
|
|
|
51799
52800
|
slotsTouched: Array.from(pendingSlots.keys()).join(",")
|
|
51800
52801
|
}));
|
|
51801
52802
|
for (const [slot, patterns] of pendingSlots) {
|
|
51802
|
-
|
|
52803
|
+
log11.debug("flush:slot", {
|
|
51803
52804
|
traitName,
|
|
51804
52805
|
slot,
|
|
51805
52806
|
patternCount: patterns.length,
|
|
@@ -51817,7 +52818,7 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
|
|
|
51817
52818
|
publishBindingSnapshot(traitName, liveEntity);
|
|
51818
52819
|
}
|
|
51819
52820
|
} catch (error) {
|
|
51820
|
-
|
|
52821
|
+
log11.error("effects:error", {
|
|
51821
52822
|
traitName,
|
|
51822
52823
|
transition: `${previousState}->${newState}`,
|
|
51823
52824
|
event: flushEvent,
|
|
@@ -52999,11 +54000,20 @@ function BrowserPlayground({
|
|
|
52999
54000
|
mode = "mock",
|
|
53000
54001
|
initialPagePath,
|
|
53001
54002
|
height,
|
|
53002
|
-
className
|
|
54003
|
+
className,
|
|
54004
|
+
paused
|
|
53003
54005
|
}) {
|
|
53004
54006
|
const [runtime] = React87.useState(
|
|
53005
54007
|
() => new OrbitalServerRuntime.OrbitalServerRuntime({ mode, debug: false })
|
|
53006
54008
|
);
|
|
54009
|
+
React87.useEffect(() => {
|
|
54010
|
+
if (paused === void 0) return;
|
|
54011
|
+
if (paused) {
|
|
54012
|
+
runtime.pauseTicks();
|
|
54013
|
+
} else {
|
|
54014
|
+
runtime.resumeTicks();
|
|
54015
|
+
}
|
|
54016
|
+
}, [runtime, paused]);
|
|
53007
54017
|
const registrationReady = React87.useMemo(() => {
|
|
53008
54018
|
const orbitalNames = schema.orbitals.map((o) => o.name);
|
|
53009
54019
|
playgroundLog.debug("register:start", { schema: schema.name, orbitalNames });
|
|
@@ -53058,6 +54068,316 @@ function BrowserPlayground({
|
|
|
53058
54068
|
);
|
|
53059
54069
|
}
|
|
53060
54070
|
|
|
54071
|
+
// runtime/OrbitalPluginHost.tsx
|
|
54072
|
+
init_useEventBus();
|
|
54073
|
+
var log10 = logger.createLogger("almadar:ui:plugin-host");
|
|
54074
|
+
var UI_SLOT_SET = new Set(core.UI_SLOTS);
|
|
54075
|
+
function isUISlot(value) {
|
|
54076
|
+
return UI_SLOT_SET.has(value);
|
|
54077
|
+
}
|
|
54078
|
+
var OrbitalPluginHostContext = React87.createContext(null);
|
|
54079
|
+
function useOrbitalPluginHostContext(caller) {
|
|
54080
|
+
const ctx = React87.useContext(OrbitalPluginHostContext);
|
|
54081
|
+
if (!ctx) {
|
|
54082
|
+
throw new Error(`${caller} must be used within an <OrbitalPluginHost>.`);
|
|
54083
|
+
}
|
|
54084
|
+
return ctx;
|
|
54085
|
+
}
|
|
54086
|
+
function isTraitState(value) {
|
|
54087
|
+
return value !== void 0 && typeof value.currentState === "string";
|
|
54088
|
+
}
|
|
54089
|
+
function toTraitState(runtime, orbital, trait, stateName, lastEvent) {
|
|
54090
|
+
if (runtime) {
|
|
54091
|
+
const raw = runtime.getState(orbital, trait);
|
|
54092
|
+
if (isTraitState(raw)) return raw;
|
|
54093
|
+
}
|
|
54094
|
+
return { traitName: trait, currentState: stateName, previousState: null, lastEvent, context: {} };
|
|
54095
|
+
}
|
|
54096
|
+
function isKeymapValue(value) {
|
|
54097
|
+
if (value === null || value === void 0 || typeof value !== "object" || Array.isArray(value)) return false;
|
|
54098
|
+
return Object.values(value).every((entry) => Array.isArray(entry) && entry.every((k) => typeof k === "string"));
|
|
54099
|
+
}
|
|
54100
|
+
function findInlineTrait(schema, orbitalName, traitName) {
|
|
54101
|
+
const orbital = schema.orbitals.find((o) => o.name === orbitalName);
|
|
54102
|
+
if (!orbital) return void 0;
|
|
54103
|
+
for (const ref of orbital.traits) {
|
|
54104
|
+
if (typeof ref === "string") continue;
|
|
54105
|
+
if ("ref" in ref) continue;
|
|
54106
|
+
if (ref.name === traitName) return ref;
|
|
54107
|
+
}
|
|
54108
|
+
return void 0;
|
|
54109
|
+
}
|
|
54110
|
+
function readKeymapConfig(trait, knob) {
|
|
54111
|
+
const declared = trait?.config?.[knob]?.default;
|
|
54112
|
+
return isKeymapValue(declared) ? declared : {};
|
|
54113
|
+
}
|
|
54114
|
+
function traitNamesOf(orbital) {
|
|
54115
|
+
const names = [];
|
|
54116
|
+
for (const ref of orbital.traits) {
|
|
54117
|
+
if (typeof ref === "string") {
|
|
54118
|
+
names.push(ref);
|
|
54119
|
+
} else if ("ref" in ref) {
|
|
54120
|
+
if (ref.name) names.push(ref.name);
|
|
54121
|
+
} else {
|
|
54122
|
+
names.push(ref.name);
|
|
54123
|
+
}
|
|
54124
|
+
}
|
|
54125
|
+
return names;
|
|
54126
|
+
}
|
|
54127
|
+
function useDeclaredCaptureTable(opts) {
|
|
54128
|
+
const { pluginId, orbital, trait, keymapKnob = "keymap", target = "shell" } = opts;
|
|
54129
|
+
const ctx = useOrbitalPluginHostContext("useDeclaredCaptureTable");
|
|
54130
|
+
return React87.useMemo(() => {
|
|
54131
|
+
const schema = ctx.getPluginSchema(pluginId);
|
|
54132
|
+
const inlineTrait = schema ? findInlineTrait(schema, orbital, trait) : void 0;
|
|
54133
|
+
const keymap = readKeymapConfig(inlineTrait, keymapKnob);
|
|
54134
|
+
const currentState = ctx.getState(pluginId, orbital, trait)?.currentState;
|
|
54135
|
+
const keys = currentState !== void 0 ? keymap[currentState] ?? [] : [];
|
|
54136
|
+
return { [target]: { mode: currentState ?? "unknown", keys: new Set(keys) } };
|
|
54137
|
+
}, [ctx, pluginId, orbital, trait, keymapKnob, target]);
|
|
54138
|
+
}
|
|
54139
|
+
function useOrbitalPluginHost() {
|
|
54140
|
+
const ctx = useOrbitalPluginHostContext("useOrbitalPluginHost");
|
|
54141
|
+
return { getState: ctx.getState, lastEvent: ctx.lastEvent, errors: ctx.errors };
|
|
54142
|
+
}
|
|
54143
|
+
function buildMockEffectHandlers(opts) {
|
|
54144
|
+
const { pluginId, denySet, slotsRef, navigateRef, notifyRef } = opts;
|
|
54145
|
+
const handlers = {
|
|
54146
|
+
renderUI: (slot, pattern, props, priority) => {
|
|
54147
|
+
if (!isUISlot(slot)) {
|
|
54148
|
+
log10.warn("renderUI:unknown-slot", { pluginId, slot });
|
|
54149
|
+
return;
|
|
54150
|
+
}
|
|
54151
|
+
if (pattern === null) {
|
|
54152
|
+
slotsRef.current.clear(slot);
|
|
54153
|
+
return;
|
|
54154
|
+
}
|
|
54155
|
+
slotsRef.current.render({ target: slot, pattern: pattern.type, props, priority, sourceTrait: pluginId });
|
|
54156
|
+
}
|
|
54157
|
+
};
|
|
54158
|
+
if (denySet.has("navigate")) {
|
|
54159
|
+
handlers.navigate = (path) => log10.warn("navigate:denied", { pluginId, path });
|
|
54160
|
+
} else if (navigateRef.current) {
|
|
54161
|
+
handlers.navigate = (path) => navigateRef.current?.(path);
|
|
54162
|
+
}
|
|
54163
|
+
if (denySet.has("notify")) {
|
|
54164
|
+
handlers.notify = (message, type) => log10.warn("notify:denied", { pluginId, message, type });
|
|
54165
|
+
} else if (notifyRef.current) {
|
|
54166
|
+
handlers.notify = (message, type) => notifyRef.current?.(message, type);
|
|
54167
|
+
}
|
|
54168
|
+
if (denySet.has("persist")) {
|
|
54169
|
+
handlers.persist = async (action, entityType) => {
|
|
54170
|
+
log10.warn("persist:denied", { pluginId, action, entityType });
|
|
54171
|
+
return void 0;
|
|
54172
|
+
};
|
|
54173
|
+
}
|
|
54174
|
+
if (denySet.has("call-service")) {
|
|
54175
|
+
handlers.callService = async (service, action) => {
|
|
54176
|
+
log10.warn("call-service:denied", { pluginId, service, action });
|
|
54177
|
+
return null;
|
|
54178
|
+
};
|
|
54179
|
+
}
|
|
54180
|
+
return handlers;
|
|
54181
|
+
}
|
|
54182
|
+
function PluginRuntimeMount({
|
|
54183
|
+
plugin,
|
|
54184
|
+
mode,
|
|
54185
|
+
transport,
|
|
54186
|
+
deny,
|
|
54187
|
+
navigate,
|
|
54188
|
+
notify,
|
|
54189
|
+
onTransition,
|
|
54190
|
+
onDispatched,
|
|
54191
|
+
onError
|
|
54192
|
+
}) {
|
|
54193
|
+
const bus = useEventBus();
|
|
54194
|
+
const slots = context.useUISlots();
|
|
54195
|
+
const slotsRef = React87.useRef(slots);
|
|
54196
|
+
slotsRef.current = slots;
|
|
54197
|
+
const navigateRef = React87.useRef(navigate);
|
|
54198
|
+
navigateRef.current = navigate;
|
|
54199
|
+
const notifyRef = React87.useRef(notify);
|
|
54200
|
+
notifyRef.current = notify;
|
|
54201
|
+
const [mockRuntime] = React87.useState(() => {
|
|
54202
|
+
if (mode !== "mock") return void 0;
|
|
54203
|
+
return new OrbitalServerRuntime.OrbitalServerRuntime({
|
|
54204
|
+
mode: "mock",
|
|
54205
|
+
debug: false,
|
|
54206
|
+
effectHandlers: buildMockEffectHandlers({
|
|
54207
|
+
pluginId: plugin.id,
|
|
54208
|
+
denySet: new Set(deny ?? []),
|
|
54209
|
+
slotsRef,
|
|
54210
|
+
navigateRef,
|
|
54211
|
+
notifyRef
|
|
54212
|
+
})
|
|
54213
|
+
});
|
|
54214
|
+
});
|
|
54215
|
+
const registrationReady = React87.useMemo(() => {
|
|
54216
|
+
if (mockRuntime) return mockRuntime.register(plugin.schema);
|
|
54217
|
+
if (mode === "server" && transport) return transport.register(plugin.schema);
|
|
54218
|
+
return Promise.resolve();
|
|
54219
|
+
}, [mockRuntime, transport, mode, plugin.schema]);
|
|
54220
|
+
React87.useEffect(() => {
|
|
54221
|
+
if (mode === "server" && !transport) {
|
|
54222
|
+
onError(plugin.id, 'OrbitalPluginHost: mode "server" requires a transport');
|
|
54223
|
+
}
|
|
54224
|
+
}, [mode, transport, plugin.id, onError]);
|
|
54225
|
+
const teardownTimerRef = React87.useRef(null);
|
|
54226
|
+
React87.useEffect(() => {
|
|
54227
|
+
if (teardownTimerRef.current !== null) {
|
|
54228
|
+
clearTimeout(teardownTimerRef.current);
|
|
54229
|
+
teardownTimerRef.current = null;
|
|
54230
|
+
}
|
|
54231
|
+
return () => {
|
|
54232
|
+
teardownTimerRef.current = setTimeout(() => {
|
|
54233
|
+
teardownTimerRef.current = null;
|
|
54234
|
+
if (mockRuntime) {
|
|
54235
|
+
mockRuntime.unregisterAll();
|
|
54236
|
+
} else if (mode === "server" && transport) {
|
|
54237
|
+
void transport.unregister();
|
|
54238
|
+
}
|
|
54239
|
+
}, 0);
|
|
54240
|
+
};
|
|
54241
|
+
}, [mockRuntime, transport, mode]);
|
|
54242
|
+
const ownOrbitals = React87.useMemo(
|
|
54243
|
+
() => new Set(plugin.schema.orbitals.map((o) => o.name)),
|
|
54244
|
+
[plugin.schema]
|
|
54245
|
+
);
|
|
54246
|
+
React87.useEffect(() => {
|
|
54247
|
+
let cancelled = false;
|
|
54248
|
+
void registrationReady.then(() => {
|
|
54249
|
+
if (cancelled || !mockRuntime) return;
|
|
54250
|
+
const states = [];
|
|
54251
|
+
for (const orbital of plugin.schema.orbitals) {
|
|
54252
|
+
for (const traitName of traitNamesOf(orbital)) {
|
|
54253
|
+
const raw = mockRuntime.getState(orbital.name, traitName);
|
|
54254
|
+
if (isTraitState(raw)) states.push([orbital.name, traitName, raw]);
|
|
54255
|
+
}
|
|
54256
|
+
}
|
|
54257
|
+
if (states.length > 0) onDispatched(plugin.id, null, states);
|
|
54258
|
+
}).catch((err) => {
|
|
54259
|
+
if (!cancelled) onError(plugin.id, err instanceof Error ? err.message : String(err));
|
|
54260
|
+
});
|
|
54261
|
+
return () => {
|
|
54262
|
+
cancelled = true;
|
|
54263
|
+
};
|
|
54264
|
+
}, [registrationReady, mockRuntime, plugin, onDispatched, onError]);
|
|
54265
|
+
const dispatch = React87.useCallback(
|
|
54266
|
+
async (row, payload) => {
|
|
54267
|
+
await registrationReady;
|
|
54268
|
+
let response;
|
|
54269
|
+
if (mockRuntime) {
|
|
54270
|
+
response = await mockRuntime.processOrbitalEvent(row.orbital, {
|
|
54271
|
+
event: row.trigger,
|
|
54272
|
+
payload,
|
|
54273
|
+
targetTrait: row.trait
|
|
54274
|
+
});
|
|
54275
|
+
} else if (mode === "server" && transport) {
|
|
54276
|
+
response = await transport.sendEvent(row.orbital, row.trigger, payload);
|
|
54277
|
+
} else {
|
|
54278
|
+
return;
|
|
54279
|
+
}
|
|
54280
|
+
if (!response.success) {
|
|
54281
|
+
onError(plugin.id, response.error ?? `${plugin.id}: ${row.trigger} failed`);
|
|
54282
|
+
}
|
|
54283
|
+
for (const entry of response.emittedEvents ?? []) {
|
|
54284
|
+
if (entry.event === row.trigger) continue;
|
|
54285
|
+
const sourceOrbital = entry.source?.orbital;
|
|
54286
|
+
if (sourceOrbital !== void 0 && ownOrbitals.has(sourceOrbital)) {
|
|
54287
|
+
bus.emit(`UI:${entry.event}`, entry.payload, entry.source);
|
|
54288
|
+
}
|
|
54289
|
+
}
|
|
54290
|
+
const states = [];
|
|
54291
|
+
for (const [traitName, stateName] of Object.entries(response.states)) {
|
|
54292
|
+
states.push([row.orbital, traitName, toTraitState(mockRuntime, row.orbital, traitName, stateName, row.trigger)]);
|
|
54293
|
+
onTransition?.(plugin.id, row.orbital, traitName, stateName);
|
|
54294
|
+
}
|
|
54295
|
+
onDispatched(plugin.id, row.trigger, states);
|
|
54296
|
+
},
|
|
54297
|
+
[registrationReady, mockRuntime, mode, transport, plugin, bus, ownOrbitals, onError, onTransition, onDispatched]
|
|
54298
|
+
);
|
|
54299
|
+
React87.useEffect(() => {
|
|
54300
|
+
const unsubs = plugin.inbound.map(
|
|
54301
|
+
(row) => bus.on(`UI:${row.busEvent}`, (event) => {
|
|
54302
|
+
void dispatch(row, event.payload).catch((err) => {
|
|
54303
|
+
onError(plugin.id, err instanceof Error ? err.message : String(err));
|
|
54304
|
+
});
|
|
54305
|
+
})
|
|
54306
|
+
);
|
|
54307
|
+
return () => {
|
|
54308
|
+
for (const unsub of unsubs) unsub();
|
|
54309
|
+
};
|
|
54310
|
+
}, [bus, plugin, dispatch, onError]);
|
|
54311
|
+
return null;
|
|
54312
|
+
}
|
|
54313
|
+
function OrbitalPluginHost({
|
|
54314
|
+
plugins,
|
|
54315
|
+
mode = "mock",
|
|
54316
|
+
transport,
|
|
54317
|
+
deny,
|
|
54318
|
+
navigate,
|
|
54319
|
+
notify,
|
|
54320
|
+
onTransition,
|
|
54321
|
+
children
|
|
54322
|
+
}) {
|
|
54323
|
+
const [tick, setTick] = React87.useState(0);
|
|
54324
|
+
const [errors, setErrors] = React87.useState({});
|
|
54325
|
+
const statesRef = React87.useRef(/* @__PURE__ */ new Map());
|
|
54326
|
+
const lastEventRef = React87.useRef(/* @__PURE__ */ new Map());
|
|
54327
|
+
const pluginSchemas = React87.useMemo(
|
|
54328
|
+
() => new Map(plugins.map((plugin) => [plugin.id, plugin.schema])),
|
|
54329
|
+
[plugins]
|
|
54330
|
+
);
|
|
54331
|
+
const handleDispatched = React87.useCallback(
|
|
54332
|
+
(pluginId, trigger, states) => {
|
|
54333
|
+
if (trigger !== null) {
|
|
54334
|
+
lastEventRef.current.set(pluginId, trigger);
|
|
54335
|
+
}
|
|
54336
|
+
let byKey = statesRef.current.get(pluginId);
|
|
54337
|
+
if (!byKey) {
|
|
54338
|
+
byKey = /* @__PURE__ */ new Map();
|
|
54339
|
+
statesRef.current.set(pluginId, byKey);
|
|
54340
|
+
}
|
|
54341
|
+
for (const [orbital, trait, state] of states) {
|
|
54342
|
+
byKey.set(`${orbital}.${trait}`, state);
|
|
54343
|
+
}
|
|
54344
|
+
setTick((t) => t + 1);
|
|
54345
|
+
},
|
|
54346
|
+
[]
|
|
54347
|
+
);
|
|
54348
|
+
const handleError = React87.useCallback((pluginId, message) => {
|
|
54349
|
+
setErrors((prev) => prev[pluginId] === message ? prev : { ...prev, [pluginId]: message });
|
|
54350
|
+
}, []);
|
|
54351
|
+
const contextValue = React87.useMemo(
|
|
54352
|
+
() => ({
|
|
54353
|
+
getState: (pluginId, orbital, trait) => statesRef.current.get(pluginId)?.get(`${orbital}.${trait}`),
|
|
54354
|
+
lastEvent: (pluginId) => lastEventRef.current.get(pluginId),
|
|
54355
|
+
errors,
|
|
54356
|
+
tick,
|
|
54357
|
+
getPluginSchema: (pluginId) => pluginSchemas.get(pluginId)
|
|
54358
|
+
}),
|
|
54359
|
+
[errors, tick, pluginSchemas]
|
|
54360
|
+
);
|
|
54361
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(OrbitalPluginHostContext.Provider, { value: contextValue, children: [
|
|
54362
|
+
plugins.map((plugin) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
54363
|
+
PluginRuntimeMount,
|
|
54364
|
+
{
|
|
54365
|
+
plugin,
|
|
54366
|
+
mode,
|
|
54367
|
+
transport,
|
|
54368
|
+
deny,
|
|
54369
|
+
navigate,
|
|
54370
|
+
notify,
|
|
54371
|
+
onTransition,
|
|
54372
|
+
onDispatched: handleDispatched,
|
|
54373
|
+
onError: handleError
|
|
54374
|
+
},
|
|
54375
|
+
plugin.id
|
|
54376
|
+
)),
|
|
54377
|
+
children ?? null
|
|
54378
|
+
] });
|
|
54379
|
+
}
|
|
54380
|
+
|
|
53061
54381
|
// runtime/index.ts
|
|
53062
54382
|
init_perf();
|
|
53063
54383
|
|
|
@@ -53135,8 +54455,11 @@ Object.defineProperty(exports, "wrapCallbackForEvent", {
|
|
|
53135
54455
|
});
|
|
53136
54456
|
exports.BrowserPlayground = BrowserPlayground;
|
|
53137
54457
|
exports.OrbPreview = OrbPreview;
|
|
54458
|
+
exports.OrbitalPluginHost = OrbitalPluginHost;
|
|
53138
54459
|
exports.clearSchemaCache = clearSchemaCache;
|
|
53139
54460
|
exports.createClientEffectHandlers = createClientEffectHandlers;
|
|
54461
|
+
exports.useDeclaredCaptureTable = useDeclaredCaptureTable;
|
|
54462
|
+
exports.useOrbitalPluginHost = useOrbitalPluginHost;
|
|
53140
54463
|
exports.usePerfBuffer = usePerfBuffer;
|
|
53141
54464
|
exports.useResolvedSchema = useResolvedSchema;
|
|
53142
54465
|
exports.useTraitStateMachine = useTraitStateMachine;
|