@almadar/ui 6.5.0 → 6.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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 +1268 -220
- package/dist/avl/index.js +1269 -221
- 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 +1092 -249
- package/dist/components/index.d.cts +193 -23
- package/dist/components/index.d.ts +193 -23
- package/dist/components/index.js +1089 -250
- 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 +974 -202
- package/dist/providers/index.d.cts +2 -2
- package/dist/providers/index.d.ts +2 -2
- package/dist/providers/index.js +974 -202
- package/dist/runtime/index.cjs +1594 -196
- package/dist/runtime/index.d.cts +79 -6
- package/dist/runtime/index.d.ts +79 -6
- package/dist/runtime/index.js +1592 -198
- package/dist/slot-host-Czc2JAxo.d.cts +47 -0
- package/dist/slot-host-Czc2JAxo.d.ts +47 -0
- 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.js
CHANGED
|
@@ -6,7 +6,7 @@ import { createLogger, setNamespaceLevel, isLogLevelEnabled } from '@almadar/log
|
|
|
6
6
|
import { perfStore, pushPerfEntry, wrapCallbackForEvent, perfStart, perfEnd, perfGauge, prepareSchemaForPreview, perfTimeAsync, collectTraitRefsFromResolvedTrait, buildOrbitalsByTrait, collectEmbeddedTraits } from '@almadar/runtime/ui';
|
|
7
7
|
export { PERF_NAMESPACE, adjustSchemaForMockData, buildMockData, clearPerf, perfEnd, perfStart, perfTime, prepareSchemaForPreview, wrapCallbackForEvent } from '@almadar/runtime/ui';
|
|
8
8
|
import { StateMachineManager, collectDeclaredConfigDefaults, resolveCallSitePayloadCaptures, createServerEffectHandlers, EffectExecutor, createContextFromBindings, createTickScheduler, isValidCronExpression, parseDurationString, InMemoryPersistence, normalizeCallSiteConfigToValues, interpolateValue } from '@almadar/runtime';
|
|
9
|
-
import { mergeEntityFrame, isCircuitEvent, applyListenPayloadMapping, schemaToIR, getPage, clearSchemaCache as clearSchemaCache$1, walkSExpr, buildResolvedTraitConfigs, isRenderBindingMarker, isFileValue, isInlineTrait, containsEntityBinding, isSExpr, isEventPayloadValue, RENDER_BINDING_MARKER } from '@almadar/core';
|
|
9
|
+
import { UI_SLOTS, mergeEntityFrame, isCircuitEvent, applyListenPayloadMapping, schemaToIR, getPage, clearSchemaCache as clearSchemaCache$1, walkSExpr, buildResolvedTraitConfigs, isRenderBindingMarker, isFileValue, isInlineTrait, containsEntityBinding, isSExpr, isEventPayloadValue, RENDER_BINDING_MARKER } from '@almadar/core';
|
|
10
10
|
import { clsx } from 'clsx';
|
|
11
11
|
import { twMerge } from 'tailwind-merge';
|
|
12
12
|
import * as LucideIcons2 from 'lucide-react';
|
|
@@ -10427,7 +10427,7 @@ function collectDrawnItems(nodes) {
|
|
|
10427
10427
|
for (const n of nodes) {
|
|
10428
10428
|
switch (n.type) {
|
|
10429
10429
|
case "draw-sprite":
|
|
10430
|
-
if (isValidScenePos(n.position)) out.push({ pos: n.position, id: n.id, anchor: n.anchor, width: n.width, height: n.height });
|
|
10430
|
+
if (isValidScenePos(n.position)) out.push({ pos: n.position, id: n.id, anchor: n.anchor, width: n.width, height: n.height, rotation: n.rotation });
|
|
10431
10431
|
break;
|
|
10432
10432
|
case "draw-shape":
|
|
10433
10433
|
case "draw-text":
|
|
@@ -10437,7 +10437,7 @@ function collectDrawnItems(nodes) {
|
|
|
10437
10437
|
break;
|
|
10438
10438
|
case "draw-sprite-layer":
|
|
10439
10439
|
for (const it of n.items) {
|
|
10440
|
-
if (isValidScenePos(it.position)) out.push({ pos: it.position, id: it.id, anchor: it.anchor, width: it.width, height: it.height });
|
|
10440
|
+
if (isValidScenePos(it.position)) out.push({ pos: it.position, id: it.id, anchor: it.anchor, width: it.width, height: it.height, rotation: it.rotation });
|
|
10441
10441
|
}
|
|
10442
10442
|
break;
|
|
10443
10443
|
case "draw-shape-layer":
|
|
@@ -10457,15 +10457,39 @@ function buildHitIndex(items) {
|
|
|
10457
10457
|
}
|
|
10458
10458
|
return m;
|
|
10459
10459
|
}
|
|
10460
|
+
function withPreviewPosition(nodes, id, pos) {
|
|
10461
|
+
return nodes.map((n) => {
|
|
10462
|
+
if (n.type === "draw-sprite-layer" || n.type === "draw-shape-layer" || n.type === "draw-text-layer") {
|
|
10463
|
+
if (!n.items.some((it) => it.id === id)) return n;
|
|
10464
|
+
return { ...n, items: n.items.map((it) => it.id === id ? { ...it, position: pos } : it) };
|
|
10465
|
+
}
|
|
10466
|
+
if (n.type === "draw-group") {
|
|
10467
|
+
if (!Array.isArray(n.items)) return n;
|
|
10468
|
+
return { ...n, items: withPreviewPosition(n.items, id, pos) };
|
|
10469
|
+
}
|
|
10470
|
+
if (n.id === id && "position" in n) {
|
|
10471
|
+
return { ...n, position: pos };
|
|
10472
|
+
}
|
|
10473
|
+
return n;
|
|
10474
|
+
});
|
|
10475
|
+
}
|
|
10460
10476
|
function hitTestSprites(items, projector, point) {
|
|
10461
10477
|
for (let i = items.length - 1; i >= 0; i--) {
|
|
10462
10478
|
const it = items[i];
|
|
10463
10479
|
if (it.id === void 0) continue;
|
|
10464
10480
|
const r = spriteRect(projector, { position: it.pos, anchor: it.anchor, width: it.width, height: it.height });
|
|
10465
|
-
|
|
10481
|
+
const test = it.rotation ? rotatePoint(point, { x: r.x + r.w / 2, y: r.y + r.h / 2 }, -it.rotation) : point;
|
|
10482
|
+
if (test.x >= r.x && test.x <= r.x + r.w && test.y >= r.y && test.y <= r.y + r.h) return it.id;
|
|
10466
10483
|
}
|
|
10467
10484
|
return void 0;
|
|
10468
10485
|
}
|
|
10486
|
+
function rotatePoint(p, center, radians) {
|
|
10487
|
+
const cos = Math.cos(radians);
|
|
10488
|
+
const sin = Math.sin(radians);
|
|
10489
|
+
const dx = p.x - center.x;
|
|
10490
|
+
const dy = p.y - center.y;
|
|
10491
|
+
return { x: center.x + dx * cos - dy * sin, y: center.y + dx * sin + dy * cos };
|
|
10492
|
+
}
|
|
10469
10493
|
var init_hitTest = __esm({
|
|
10470
10494
|
"lib/drawable/hitTest.ts"() {
|
|
10471
10495
|
init_contract();
|
|
@@ -10474,6 +10498,41 @@ var init_hitTest = __esm({
|
|
|
10474
10498
|
function normalizeBackdrop(bg) {
|
|
10475
10499
|
return typeof bg === "string" ? { url: bg, role: "decoration", category: "background" } : bg;
|
|
10476
10500
|
}
|
|
10501
|
+
function selectionOverlayNodes(projector, item) {
|
|
10502
|
+
const r = spriteRect(projector, { position: item.pos, anchor: item.anchor, width: item.width, height: item.height });
|
|
10503
|
+
const tw = projector.tileWidth;
|
|
10504
|
+
const cellTopLeft = projector.anchorPoint(item.pos, "top-left");
|
|
10505
|
+
const offsetX = (r.x - cellTopLeft.x) / tw;
|
|
10506
|
+
const offsetY = (r.y - cellTopLeft.y) / tw;
|
|
10507
|
+
const width = r.w / tw;
|
|
10508
|
+
const height = r.h / tw;
|
|
10509
|
+
const handle = EDIT_HANDLE_SIZE_PX / tw;
|
|
10510
|
+
const ring = {
|
|
10511
|
+
type: "draw-shape",
|
|
10512
|
+
shape: "rect",
|
|
10513
|
+
position: item.pos,
|
|
10514
|
+
anchor: "top-left",
|
|
10515
|
+
offsetX,
|
|
10516
|
+
offsetY,
|
|
10517
|
+
width,
|
|
10518
|
+
height,
|
|
10519
|
+
stroke: EDIT_SELECTION_COLOR,
|
|
10520
|
+
strokeWidth: 2,
|
|
10521
|
+
fill: "none"
|
|
10522
|
+
};
|
|
10523
|
+
const handles = EDIT_SELECTION_CORNERS.map(([cx, cy]) => ({
|
|
10524
|
+
type: "draw-shape",
|
|
10525
|
+
shape: "rect",
|
|
10526
|
+
position: item.pos,
|
|
10527
|
+
anchor: "top-left",
|
|
10528
|
+
offsetX: offsetX + cx * width - handle / 2,
|
|
10529
|
+
offsetY: offsetY + cy * height - handle / 2,
|
|
10530
|
+
width: handle,
|
|
10531
|
+
height: handle,
|
|
10532
|
+
fill: EDIT_SELECTION_COLOR
|
|
10533
|
+
}));
|
|
10534
|
+
return [ring, ...handles];
|
|
10535
|
+
}
|
|
10477
10536
|
function Canvas2D({
|
|
10478
10537
|
className,
|
|
10479
10538
|
isLoading = false,
|
|
@@ -10487,6 +10546,12 @@ function Canvas2D({
|
|
|
10487
10546
|
tileLeaveEvent,
|
|
10488
10547
|
keyMap,
|
|
10489
10548
|
keyUpMap,
|
|
10549
|
+
editable = false,
|
|
10550
|
+
selectedId = null,
|
|
10551
|
+
onSelect,
|
|
10552
|
+
onMove,
|
|
10553
|
+
selectEvent,
|
|
10554
|
+
moveEvent,
|
|
10490
10555
|
camera = "pan-zoom",
|
|
10491
10556
|
scale = 0.4,
|
|
10492
10557
|
tileWidth,
|
|
@@ -10744,10 +10809,22 @@ function Canvas2D({
|
|
|
10744
10809
|
painter.scale(cam.zoom, cam.zoom);
|
|
10745
10810
|
painter.translate(-viewportSize.width / 2 - cam.x, -viewportSize.height / 2 - cam.y);
|
|
10746
10811
|
const dctx = { projector, time: timeMs, invalidate: bumpAtlas };
|
|
10747
|
-
|
|
10812
|
+
let paintNodes = drawables;
|
|
10813
|
+
if (editable) {
|
|
10814
|
+
const drag = editDragRef.current;
|
|
10815
|
+
if (drag && drag.moved) {
|
|
10816
|
+
paintNodes = withPreviewPosition(paintNodes, drag.id, { x: drag.previewX, y: drag.previewY });
|
|
10817
|
+
}
|
|
10818
|
+
const selectedItem = selectedId != null ? drawnItems.find((it) => it.id === selectedId) : void 0;
|
|
10819
|
+
if (selectedItem) {
|
|
10820
|
+
const overlaySource = drag && drag.moved && drag.id === selectedId ? { ...selectedItem, pos: { x: drag.previewX, y: drag.previewY } } : selectedItem;
|
|
10821
|
+
paintNodes = [...paintNodes, ...selectionOverlayNodes(projector, overlaySource)];
|
|
10822
|
+
}
|
|
10823
|
+
}
|
|
10824
|
+
for (const node of paintNodes) paintDrawable(painter, node, dctx);
|
|
10748
10825
|
painter.restore();
|
|
10749
|
-
scheduleAnimation(
|
|
10750
|
-
}, [viewportSize, backgroundImage, bgColor, drawables, projector, cameraRef, bumpAtlas, getImage, cameraPos, defaultGridFocus, camera, dragDistance]);
|
|
10826
|
+
scheduleAnimation(paintNodes);
|
|
10827
|
+
}, [viewportSize, backgroundImage, bgColor, drawables, projector, cameraRef, bumpAtlas, getImage, cameraPos, defaultGridFocus, camera, dragDistance, editable, selectedId, drawnItems]);
|
|
10751
10828
|
useEffect(() => {
|
|
10752
10829
|
drawTimeRef.current = draw;
|
|
10753
10830
|
}, [draw]);
|
|
@@ -10792,23 +10869,83 @@ function Canvas2D({
|
|
|
10792
10869
|
};
|
|
10793
10870
|
}, [camera, followTarget, lerpToTarget, draw]);
|
|
10794
10871
|
const singlePointerActiveRef = useRef(false);
|
|
10872
|
+
const editDragRef = useRef(null);
|
|
10873
|
+
const pointerToScene = useCallback((clientX, clientY) => {
|
|
10874
|
+
const canvas = canvasRef.current;
|
|
10875
|
+
if (!canvas) return { x: 0, y: 0 };
|
|
10876
|
+
const world = screenToWorld(clientX, clientY, canvas, viewportSize);
|
|
10877
|
+
const adjustedX = world.x - scaledTileWidth / 2;
|
|
10878
|
+
const adjustedY = squareGrid ? world.y - scaledTileWidth / 2 : world.y - scaledDiamondTopY - scaledFloorHeight / 2;
|
|
10879
|
+
return unproject(adjustedX, adjustedY);
|
|
10880
|
+
}, [screenToWorld, viewportSize, scaledTileWidth, squareGrid, scaledDiamondTopY, scaledFloorHeight, unproject]);
|
|
10795
10881
|
const handleCanvasPointerDown = useCallback((e) => {
|
|
10796
10882
|
singlePointerActiveRef.current = true;
|
|
10883
|
+
if (editable) {
|
|
10884
|
+
if (!canvasRef.current) return;
|
|
10885
|
+
const world = screenToWorld(e.clientX, e.clientY, canvasRef.current, viewportSize);
|
|
10886
|
+
const hitId = hitTestSprites(drawnItems, projector, world);
|
|
10887
|
+
if (hitId === void 0) return;
|
|
10888
|
+
const item = [...drawnItems].reverse().find((it) => it.id === hitId);
|
|
10889
|
+
if (!item) return;
|
|
10890
|
+
editDragRef.current = {
|
|
10891
|
+
id: hitId,
|
|
10892
|
+
pointerId: e.pointerId,
|
|
10893
|
+
startClientX: e.clientX,
|
|
10894
|
+
startClientY: e.clientY,
|
|
10895
|
+
startSceneX: item.pos.x,
|
|
10896
|
+
startSceneY: item.pos.y,
|
|
10897
|
+
moved: false,
|
|
10898
|
+
previewX: item.pos.x,
|
|
10899
|
+
previewY: item.pos.y
|
|
10900
|
+
};
|
|
10901
|
+
return;
|
|
10902
|
+
}
|
|
10797
10903
|
if (enableCamera) handlePointerDown(e);
|
|
10798
|
-
}, [enableCamera, handlePointerDown]);
|
|
10904
|
+
}, [editable, screenToWorld, viewportSize, drawnItems, projector, enableCamera, handlePointerDown]);
|
|
10799
10905
|
const handleCanvasPointerMove = useCallback((e) => {
|
|
10906
|
+
if (editable) {
|
|
10907
|
+
const drag = editDragRef.current;
|
|
10908
|
+
if (!drag || drag.pointerId !== e.pointerId) return;
|
|
10909
|
+
const dxPx = e.clientX - drag.startClientX;
|
|
10910
|
+
const dyPx = e.clientY - drag.startClientY;
|
|
10911
|
+
if (!drag.moved && Math.abs(dxPx) + Math.abs(dyPx) <= 5) return;
|
|
10912
|
+
drag.moved = true;
|
|
10913
|
+
const nowScene = pointerToScene(e.clientX, e.clientY);
|
|
10914
|
+
const startScene = pointerToScene(drag.startClientX, drag.startClientY);
|
|
10915
|
+
drag.previewX = drag.startSceneX + (nowScene.x - startScene.x);
|
|
10916
|
+
drag.previewY = drag.startSceneY + (nowScene.y - startScene.y);
|
|
10917
|
+
draw();
|
|
10918
|
+
return;
|
|
10919
|
+
}
|
|
10800
10920
|
if (enableCamera) handlePointerMove(e, () => draw());
|
|
10801
|
-
}, [
|
|
10921
|
+
}, [editable, pointerToScene, draw, enableCamera, handlePointerMove]);
|
|
10802
10922
|
const handleCanvasHover = useCallback((e) => {
|
|
10803
10923
|
if (singlePointerActiveRef.current) return;
|
|
10804
10924
|
if (!tileHoverEvent || !canvasRef.current) return;
|
|
10805
|
-
const
|
|
10806
|
-
const adjustedX = world.x - scaledTileWidth / 2;
|
|
10807
|
-
const adjustedY = squareGrid ? world.y - scaledTileWidth / 2 : world.y - scaledDiamondTopY - scaledFloorHeight / 2;
|
|
10808
|
-
const isoPos = unproject(adjustedX, adjustedY);
|
|
10925
|
+
const isoPos = pointerToScene(e.clientX, e.clientY);
|
|
10809
10926
|
eventBus.emit(`UI:${tileHoverEvent}`, { x: isoPos.x, y: isoPos.y });
|
|
10810
|
-
}, [
|
|
10927
|
+
}, [pointerToScene, tileHoverEvent, eventBus]);
|
|
10811
10928
|
const handleCanvasPointerUp = useCallback((e) => {
|
|
10929
|
+
if (editable) {
|
|
10930
|
+
singlePointerActiveRef.current = false;
|
|
10931
|
+
const drag = editDragRef.current;
|
|
10932
|
+
if (drag && drag.pointerId === e.pointerId) {
|
|
10933
|
+
editDragRef.current = null;
|
|
10934
|
+
if (drag.moved) {
|
|
10935
|
+
onMove?.(drag.id, drag.previewX, drag.previewY);
|
|
10936
|
+
if (moveEvent) eventBus.emit(`UI:${moveEvent}`, { id: drag.id, x: drag.previewX, y: drag.previewY });
|
|
10937
|
+
draw();
|
|
10938
|
+
return;
|
|
10939
|
+
}
|
|
10940
|
+
const next = selectedId === drag.id ? null : drag.id;
|
|
10941
|
+
onSelect?.(next);
|
|
10942
|
+
if (selectEvent) eventBus.emit(`UI:${selectEvent}`, { id: next });
|
|
10943
|
+
return;
|
|
10944
|
+
}
|
|
10945
|
+
onSelect?.(null);
|
|
10946
|
+
if (selectEvent) eventBus.emit(`UI:${selectEvent}`, { id: null });
|
|
10947
|
+
return;
|
|
10948
|
+
}
|
|
10812
10949
|
singlePointerActiveRef.current = false;
|
|
10813
10950
|
if (enableCamera) handlePointerUp();
|
|
10814
10951
|
if (dragDistance() > 5) return;
|
|
@@ -10819,16 +10956,14 @@ function Canvas2D({
|
|
|
10819
10956
|
eventBus.emit(`UI:${unitClickEvent}`, { unitId: spriteHit });
|
|
10820
10957
|
return;
|
|
10821
10958
|
}
|
|
10822
|
-
const
|
|
10823
|
-
const adjustedY = squareGrid ? world.y - scaledTileWidth / 2 : world.y - scaledDiamondTopY - scaledFloorHeight / 2;
|
|
10824
|
-
const isoPos = unproject(adjustedX, adjustedY);
|
|
10959
|
+
const isoPos = pointerToScene(e.clientX, e.clientY);
|
|
10825
10960
|
const hitId = hitIndex.get(`${isoPos.x},${isoPos.y}`);
|
|
10826
10961
|
if (hitId !== void 0 && unitClickEvent) {
|
|
10827
10962
|
eventBus.emit(`UI:${unitClickEvent}`, { unitId: hitId });
|
|
10828
10963
|
} else if (tileClickEvent) {
|
|
10829
10964
|
eventBus.emit(`UI:${tileClickEvent}`, { x: isoPos.x, y: isoPos.y });
|
|
10830
10965
|
}
|
|
10831
|
-
}, [
|
|
10966
|
+
}, [editable, selectedId, onMove, moveEvent, onSelect, selectEvent, eventBus, draw, enableCamera, handlePointerUp, dragDistance, screenToWorld, viewportSize, drawnItems, projector, tileClickEvent, unitClickEvent, hitIndex, pointerToScene]);
|
|
10832
10967
|
const handleCanvasPointerLeave = useCallback(() => {
|
|
10833
10968
|
handleMouseLeave();
|
|
10834
10969
|
if (tileLeaveEvent) eventBus.emit(`UI:${tileLeaveEvent}`, {});
|
|
@@ -10847,7 +10982,7 @@ function Canvas2D({
|
|
|
10847
10982
|
}, [enableCamera, handlePointerUp]);
|
|
10848
10983
|
const gestureHandlers = useCanvasGestures({
|
|
10849
10984
|
canvasRef,
|
|
10850
|
-
enabled: enableCamera || !!tileHoverEvent || !!tileClickEvent || !!unitClickEvent,
|
|
10985
|
+
enabled: enableCamera || !!tileHoverEvent || !!tileClickEvent || !!unitClickEvent || editable,
|
|
10851
10986
|
onPointerDown: handleCanvasPointerDown,
|
|
10852
10987
|
onPointerMove: handleCanvasPointerMove,
|
|
10853
10988
|
onPointerUp: handleCanvasPointerUp,
|
|
@@ -10979,7 +11114,7 @@ function Canvas2D({
|
|
|
10979
11114
|
}
|
|
10980
11115
|
) });
|
|
10981
11116
|
}
|
|
10982
|
-
var canvas2DLog;
|
|
11117
|
+
var canvas2DLog, EDIT_SELECTION_COLOR, EDIT_HANDLE_SIZE_PX, EDIT_SELECTION_CORNERS;
|
|
10983
11118
|
var init_Canvas2D = __esm({
|
|
10984
11119
|
"components/game/molecules/Canvas2D.tsx"() {
|
|
10985
11120
|
"use client";
|
|
@@ -11004,8 +11139,12 @@ var init_Canvas2D = __esm({
|
|
|
11004
11139
|
init_DrawGroup();
|
|
11005
11140
|
init_registry();
|
|
11006
11141
|
init_hitTest();
|
|
11142
|
+
init_contract();
|
|
11007
11143
|
init_isometric();
|
|
11008
11144
|
canvas2DLog = createLogger("almadar:ui:game-canvas");
|
|
11145
|
+
EDIT_SELECTION_COLOR = "#3b82f6";
|
|
11146
|
+
EDIT_HANDLE_SIZE_PX = 8;
|
|
11147
|
+
EDIT_SELECTION_CORNERS = [[0, 0], [1, 0], [0, 1], [1, 1]];
|
|
11009
11148
|
Canvas2D.displayName = "Canvas2D";
|
|
11010
11149
|
}
|
|
11011
11150
|
});
|
|
@@ -11047,6 +11186,12 @@ function Canvas({
|
|
|
11047
11186
|
featureClickEvent,
|
|
11048
11187
|
keyMap,
|
|
11049
11188
|
keyUpMap,
|
|
11189
|
+
editable,
|
|
11190
|
+
selectedId,
|
|
11191
|
+
onSelect,
|
|
11192
|
+
onMove,
|
|
11193
|
+
selectEvent,
|
|
11194
|
+
moveEvent,
|
|
11050
11195
|
children
|
|
11051
11196
|
}) {
|
|
11052
11197
|
canvasLog.debug("Canvas render", { mode, drawablesCount: drawables?.length, projection, camera: camera ? JSON.stringify(camera) : void 0 });
|
|
@@ -11124,6 +11269,12 @@ function Canvas({
|
|
|
11124
11269
|
tileLeaveEvent,
|
|
11125
11270
|
keyMap,
|
|
11126
11271
|
keyUpMap,
|
|
11272
|
+
editable,
|
|
11273
|
+
selectedId,
|
|
11274
|
+
onSelect,
|
|
11275
|
+
onMove,
|
|
11276
|
+
selectEvent,
|
|
11277
|
+
moveEvent,
|
|
11127
11278
|
...children !== void 0 ? { children } : {}
|
|
11128
11279
|
}
|
|
11129
11280
|
);
|
|
@@ -11145,15 +11296,19 @@ function GameAudioToggle({
|
|
|
11145
11296
|
size = "sm",
|
|
11146
11297
|
className,
|
|
11147
11298
|
onAsset,
|
|
11148
|
-
offAsset
|
|
11299
|
+
offAsset,
|
|
11300
|
+
toggleEvent
|
|
11149
11301
|
}) {
|
|
11150
11302
|
const ctx = useGameAudioContextOptional();
|
|
11151
11303
|
const [localMuted, setLocalMuted] = useState(false);
|
|
11152
11304
|
const muted = ctx ? ctx.muted : localMuted;
|
|
11153
11305
|
const setMuted = ctx ? ctx.setMuted : setLocalMuted;
|
|
11306
|
+
const eventBus = useEventBus();
|
|
11154
11307
|
const handleToggle = useCallback(() => {
|
|
11155
|
-
|
|
11156
|
-
|
|
11308
|
+
const next = !muted;
|
|
11309
|
+
setMuted(next);
|
|
11310
|
+
if (toggleEvent) eventBus.emit(`UI:${toggleEvent}`, { muted: next });
|
|
11311
|
+
}, [muted, setMuted, toggleEvent, eventBus]);
|
|
11157
11312
|
const activeAsset = muted ? offAsset : onAsset;
|
|
11158
11313
|
return /* @__PURE__ */ jsx(
|
|
11159
11314
|
Button,
|
|
@@ -11172,10 +11327,319 @@ var init_GameAudioToggle = __esm({
|
|
|
11172
11327
|
"use client";
|
|
11173
11328
|
init_atoms();
|
|
11174
11329
|
init_cn();
|
|
11330
|
+
init_useEventBus();
|
|
11175
11331
|
init_GameIcon();
|
|
11176
11332
|
GameAudioToggle.displayName = "GameAudioToggle";
|
|
11177
11333
|
}
|
|
11178
11334
|
});
|
|
11335
|
+
function pickPath(entry) {
|
|
11336
|
+
if (Array.isArray(entry.path)) {
|
|
11337
|
+
return entry.path[Math.floor(Math.random() * entry.path.length)];
|
|
11338
|
+
}
|
|
11339
|
+
return entry.path;
|
|
11340
|
+
}
|
|
11341
|
+
function useGameAudio({
|
|
11342
|
+
manifest,
|
|
11343
|
+
baseUrl = "",
|
|
11344
|
+
initialMuted = false,
|
|
11345
|
+
initialVolume = 1
|
|
11346
|
+
}) {
|
|
11347
|
+
const [muted, setMutedState] = useState(initialMuted);
|
|
11348
|
+
const [masterVolume, setMasterVolumeState] = useState(initialVolume);
|
|
11349
|
+
const mutedRef = useRef(muted);
|
|
11350
|
+
const volumeRef = useRef(masterVolume);
|
|
11351
|
+
const manifestRef = useRef(manifest);
|
|
11352
|
+
mutedRef.current = muted;
|
|
11353
|
+
volumeRef.current = masterVolume;
|
|
11354
|
+
manifestRef.current = manifest;
|
|
11355
|
+
const poolsRef = useRef(/* @__PURE__ */ new Map());
|
|
11356
|
+
const getOrCreateElement = useCallback((key) => {
|
|
11357
|
+
const entry = manifestRef.current[key];
|
|
11358
|
+
if (!entry) return null;
|
|
11359
|
+
let pool = poolsRef.current.get(key);
|
|
11360
|
+
if (!pool) {
|
|
11361
|
+
pool = [];
|
|
11362
|
+
poolsRef.current.set(key, pool);
|
|
11363
|
+
}
|
|
11364
|
+
const maxSize = entry.poolSize ?? 1;
|
|
11365
|
+
for (const audio of pool) {
|
|
11366
|
+
if (audio.paused && (audio.ended || audio.currentTime === 0)) {
|
|
11367
|
+
return audio;
|
|
11368
|
+
}
|
|
11369
|
+
}
|
|
11370
|
+
if (pool.length < maxSize) {
|
|
11371
|
+
const src = baseUrl + pickPath(entry);
|
|
11372
|
+
const audio = new Audio(src);
|
|
11373
|
+
audio.loop = entry.loop ?? false;
|
|
11374
|
+
pool.push(audio);
|
|
11375
|
+
return audio;
|
|
11376
|
+
}
|
|
11377
|
+
if (!entry.loop) {
|
|
11378
|
+
let oldest = pool[0];
|
|
11379
|
+
for (const audio of pool) {
|
|
11380
|
+
if (audio.currentTime > oldest.currentTime) {
|
|
11381
|
+
oldest = audio;
|
|
11382
|
+
}
|
|
11383
|
+
}
|
|
11384
|
+
oldest.pause();
|
|
11385
|
+
oldest.currentTime = 0;
|
|
11386
|
+
return oldest;
|
|
11387
|
+
}
|
|
11388
|
+
return null;
|
|
11389
|
+
}, [baseUrl]);
|
|
11390
|
+
const play = useCallback((key) => {
|
|
11391
|
+
if (mutedRef.current) return;
|
|
11392
|
+
const entry = manifestRef.current[key];
|
|
11393
|
+
if (!entry) return;
|
|
11394
|
+
const audio = getOrCreateElement(key);
|
|
11395
|
+
if (!audio) return;
|
|
11396
|
+
audio.volume = Math.min(1, (entry.volume ?? 1) * volumeRef.current);
|
|
11397
|
+
if (!entry.loop) {
|
|
11398
|
+
audio.currentTime = 0;
|
|
11399
|
+
}
|
|
11400
|
+
const promise = audio.play();
|
|
11401
|
+
if (promise) {
|
|
11402
|
+
promise.catch(() => {
|
|
11403
|
+
});
|
|
11404
|
+
}
|
|
11405
|
+
}, [getOrCreateElement]);
|
|
11406
|
+
const stop = useCallback((key) => {
|
|
11407
|
+
const pool = poolsRef.current.get(key);
|
|
11408
|
+
if (!pool) return;
|
|
11409
|
+
for (const audio of pool) {
|
|
11410
|
+
audio.pause();
|
|
11411
|
+
audio.currentTime = 0;
|
|
11412
|
+
}
|
|
11413
|
+
}, []);
|
|
11414
|
+
const currentMusicKeyRef = useRef(null);
|
|
11415
|
+
const currentMusicElRef = useRef(null);
|
|
11416
|
+
const musicFadeRef = useRef(null);
|
|
11417
|
+
const pendingMusicKeyRef = useRef(null);
|
|
11418
|
+
const clearMusicFade = useCallback(() => {
|
|
11419
|
+
if (musicFadeRef.current) {
|
|
11420
|
+
clearInterval(musicFadeRef.current);
|
|
11421
|
+
musicFadeRef.current = null;
|
|
11422
|
+
}
|
|
11423
|
+
}, []);
|
|
11424
|
+
const playMusic = useCallback((key) => {
|
|
11425
|
+
if (key === currentMusicKeyRef.current) return;
|
|
11426
|
+
pendingMusicKeyRef.current = key;
|
|
11427
|
+
const entry = manifestRef.current[key];
|
|
11428
|
+
if (!entry) return;
|
|
11429
|
+
const fadeDurationMs = entry.crossfadeDurationMs ?? 1500;
|
|
11430
|
+
const stepMs = 50;
|
|
11431
|
+
const totalSteps = Math.max(1, fadeDurationMs / stepMs);
|
|
11432
|
+
const targetVolume = Math.min(1, (entry.volume ?? 1) * volumeRef.current);
|
|
11433
|
+
clearMusicFade();
|
|
11434
|
+
const src = baseUrl + (Array.isArray(entry.path) ? entry.path[0] : entry.path);
|
|
11435
|
+
const incoming = new Audio(src);
|
|
11436
|
+
incoming.loop = true;
|
|
11437
|
+
incoming.volume = 0;
|
|
11438
|
+
const outgoing = currentMusicElRef.current;
|
|
11439
|
+
const outgoingStartVol = outgoing?.volume ?? 0;
|
|
11440
|
+
currentMusicKeyRef.current = key;
|
|
11441
|
+
currentMusicElRef.current = incoming;
|
|
11442
|
+
if (!mutedRef.current) {
|
|
11443
|
+
incoming.play().catch(() => {
|
|
11444
|
+
currentMusicKeyRef.current = null;
|
|
11445
|
+
currentMusicElRef.current = outgoing;
|
|
11446
|
+
});
|
|
11447
|
+
}
|
|
11448
|
+
let step = 0;
|
|
11449
|
+
musicFadeRef.current = setInterval(() => {
|
|
11450
|
+
step++;
|
|
11451
|
+
const progress = Math.min(step / totalSteps, 1);
|
|
11452
|
+
incoming.volume = Math.min(1, targetVolume * progress);
|
|
11453
|
+
if (outgoing) {
|
|
11454
|
+
outgoing.volume = Math.max(0, outgoingStartVol * (1 - progress));
|
|
11455
|
+
}
|
|
11456
|
+
if (progress >= 1) {
|
|
11457
|
+
clearMusicFade();
|
|
11458
|
+
if (outgoing) {
|
|
11459
|
+
outgoing.pause();
|
|
11460
|
+
outgoing.src = "";
|
|
11461
|
+
}
|
|
11462
|
+
}
|
|
11463
|
+
}, stepMs);
|
|
11464
|
+
}, [baseUrl, clearMusicFade]);
|
|
11465
|
+
const stopMusic = useCallback((fadeDurationMs = 1e3) => {
|
|
11466
|
+
const outgoing = currentMusicElRef.current;
|
|
11467
|
+
if (!outgoing) return;
|
|
11468
|
+
currentMusicKeyRef.current = null;
|
|
11469
|
+
currentMusicElRef.current = null;
|
|
11470
|
+
pendingMusicKeyRef.current = null;
|
|
11471
|
+
clearMusicFade();
|
|
11472
|
+
const startVolume = outgoing.volume;
|
|
11473
|
+
const stepMs = 50;
|
|
11474
|
+
const totalSteps = Math.max(1, fadeDurationMs / stepMs);
|
|
11475
|
+
let step = 0;
|
|
11476
|
+
musicFadeRef.current = setInterval(() => {
|
|
11477
|
+
step++;
|
|
11478
|
+
const progress = step / totalSteps;
|
|
11479
|
+
outgoing.volume = Math.max(0, startVolume * (1 - progress));
|
|
11480
|
+
if (progress >= 1) {
|
|
11481
|
+
clearMusicFade();
|
|
11482
|
+
outgoing.pause();
|
|
11483
|
+
outgoing.src = "";
|
|
11484
|
+
}
|
|
11485
|
+
}, stepMs);
|
|
11486
|
+
}, [clearMusicFade]);
|
|
11487
|
+
const stopAll = useCallback(() => {
|
|
11488
|
+
for (const pool of poolsRef.current.values()) {
|
|
11489
|
+
for (const audio of pool) {
|
|
11490
|
+
audio.pause();
|
|
11491
|
+
audio.currentTime = 0;
|
|
11492
|
+
}
|
|
11493
|
+
}
|
|
11494
|
+
stopMusic(0);
|
|
11495
|
+
}, [stopMusic]);
|
|
11496
|
+
const setMuted = useCallback((value) => {
|
|
11497
|
+
setMutedState(value);
|
|
11498
|
+
if (value) {
|
|
11499
|
+
for (const [key, pool] of poolsRef.current.entries()) {
|
|
11500
|
+
if (manifestRef.current[key]?.loop) {
|
|
11501
|
+
for (const audio of pool) {
|
|
11502
|
+
if (!audio.paused) audio.pause();
|
|
11503
|
+
}
|
|
11504
|
+
}
|
|
11505
|
+
}
|
|
11506
|
+
currentMusicElRef.current?.pause();
|
|
11507
|
+
} else {
|
|
11508
|
+
for (const [key, pool] of poolsRef.current.entries()) {
|
|
11509
|
+
const entry = manifestRef.current[key];
|
|
11510
|
+
if (entry?.loop && entry?.autostart) {
|
|
11511
|
+
for (const audio of pool) {
|
|
11512
|
+
if (audio.paused) audio.play().catch(() => {
|
|
11513
|
+
});
|
|
11514
|
+
}
|
|
11515
|
+
}
|
|
11516
|
+
}
|
|
11517
|
+
const musicEl = currentMusicElRef.current;
|
|
11518
|
+
if (musicEl) {
|
|
11519
|
+
musicEl.play().catch(() => {
|
|
11520
|
+
});
|
|
11521
|
+
}
|
|
11522
|
+
}
|
|
11523
|
+
}, []);
|
|
11524
|
+
const setMasterVolume = useCallback((volume) => {
|
|
11525
|
+
const clamped = Math.max(0, Math.min(1, volume));
|
|
11526
|
+
setMasterVolumeState(clamped);
|
|
11527
|
+
for (const [key, pool] of poolsRef.current.entries()) {
|
|
11528
|
+
const entryVol = manifestRef.current[key]?.volume ?? 1;
|
|
11529
|
+
for (const audio of pool) {
|
|
11530
|
+
audio.volume = Math.min(1, entryVol * clamped);
|
|
11531
|
+
}
|
|
11532
|
+
}
|
|
11533
|
+
if (!musicFadeRef.current && currentMusicElRef.current) {
|
|
11534
|
+
const key = currentMusicKeyRef.current;
|
|
11535
|
+
const entryVol = key ? manifestRef.current[key]?.volume ?? 1 : 1;
|
|
11536
|
+
currentMusicElRef.current.volume = Math.min(1, entryVol * clamped);
|
|
11537
|
+
}
|
|
11538
|
+
}, []);
|
|
11539
|
+
const unlockedRef = useRef(false);
|
|
11540
|
+
useEffect(() => {
|
|
11541
|
+
const autoKeys = Object.keys(manifest).filter((k) => manifest[k].autostart);
|
|
11542
|
+
const hasPendingMusic = () => pendingMusicKeyRef.current !== null;
|
|
11543
|
+
const hasAutoStart = autoKeys.length > 0;
|
|
11544
|
+
if (!hasAutoStart && !hasPendingMusic()) return;
|
|
11545
|
+
const unlock = () => {
|
|
11546
|
+
if (unlockedRef.current) return;
|
|
11547
|
+
unlockedRef.current = true;
|
|
11548
|
+
if (!mutedRef.current) {
|
|
11549
|
+
for (const key of autoKeys) {
|
|
11550
|
+
play(key);
|
|
11551
|
+
}
|
|
11552
|
+
const pending = pendingMusicKeyRef.current;
|
|
11553
|
+
if (pending && pending !== currentMusicKeyRef.current) {
|
|
11554
|
+
playMusic(pending);
|
|
11555
|
+
}
|
|
11556
|
+
}
|
|
11557
|
+
};
|
|
11558
|
+
document.addEventListener("click", unlock, { once: true });
|
|
11559
|
+
document.addEventListener("keydown", unlock, { once: true });
|
|
11560
|
+
document.addEventListener("touchstart", unlock, { once: true });
|
|
11561
|
+
return () => {
|
|
11562
|
+
document.removeEventListener("click", unlock);
|
|
11563
|
+
document.removeEventListener("keydown", unlock);
|
|
11564
|
+
document.removeEventListener("touchstart", unlock);
|
|
11565
|
+
};
|
|
11566
|
+
}, [manifest, play, playMusic]);
|
|
11567
|
+
useEffect(() => {
|
|
11568
|
+
return () => {
|
|
11569
|
+
clearMusicFade();
|
|
11570
|
+
for (const pool of poolsRef.current.values()) {
|
|
11571
|
+
for (const audio of pool) {
|
|
11572
|
+
audio.pause();
|
|
11573
|
+
audio.src = "";
|
|
11574
|
+
}
|
|
11575
|
+
}
|
|
11576
|
+
poolsRef.current.clear();
|
|
11577
|
+
if (currentMusicElRef.current) {
|
|
11578
|
+
currentMusicElRef.current.pause();
|
|
11579
|
+
currentMusicElRef.current.src = "";
|
|
11580
|
+
currentMusicElRef.current = null;
|
|
11581
|
+
}
|
|
11582
|
+
};
|
|
11583
|
+
}, [clearMusicFade]);
|
|
11584
|
+
return {
|
|
11585
|
+
play,
|
|
11586
|
+
stop,
|
|
11587
|
+
stopAll,
|
|
11588
|
+
playMusic,
|
|
11589
|
+
stopMusic,
|
|
11590
|
+
muted,
|
|
11591
|
+
setMuted,
|
|
11592
|
+
masterVolume,
|
|
11593
|
+
setMasterVolume
|
|
11594
|
+
};
|
|
11595
|
+
}
|
|
11596
|
+
var init_useGameAudio = __esm({
|
|
11597
|
+
"hooks/useGameAudio.ts"() {
|
|
11598
|
+
"use client";
|
|
11599
|
+
useGameAudio.displayName = "useGameAudio";
|
|
11600
|
+
}
|
|
11601
|
+
});
|
|
11602
|
+
function GameAudioCue({
|
|
11603
|
+
cue,
|
|
11604
|
+
cueSeq,
|
|
11605
|
+
music,
|
|
11606
|
+
muted,
|
|
11607
|
+
volume,
|
|
11608
|
+
manifest,
|
|
11609
|
+
baseUrl
|
|
11610
|
+
}) {
|
|
11611
|
+
const { play, playMusic, stopMusic, setMuted, setMasterVolume } = useGameAudio({
|
|
11612
|
+
manifest,
|
|
11613
|
+
baseUrl,
|
|
11614
|
+
initialMuted: muted,
|
|
11615
|
+
initialVolume: volume
|
|
11616
|
+
});
|
|
11617
|
+
const prevCueSeqRef = useRef(cueSeq);
|
|
11618
|
+
useEffect(() => {
|
|
11619
|
+
if (cue && cueSeq !== void 0 && cueSeq !== prevCueSeqRef.current) {
|
|
11620
|
+
play(cue);
|
|
11621
|
+
}
|
|
11622
|
+
prevCueSeqRef.current = cueSeq;
|
|
11623
|
+
}, [cue, cueSeq, play]);
|
|
11624
|
+
useEffect(() => {
|
|
11625
|
+
if (music) playMusic(music);
|
|
11626
|
+
else stopMusic();
|
|
11627
|
+
}, [music, playMusic, stopMusic]);
|
|
11628
|
+
useEffect(() => {
|
|
11629
|
+
if (muted !== void 0) setMuted(muted);
|
|
11630
|
+
}, [muted, setMuted]);
|
|
11631
|
+
useEffect(() => {
|
|
11632
|
+
if (volume !== void 0) setMasterVolume(volume);
|
|
11633
|
+
}, [volume, setMasterVolume]);
|
|
11634
|
+
return null;
|
|
11635
|
+
}
|
|
11636
|
+
var init_GameAudioCue = __esm({
|
|
11637
|
+
"components/game/atoms/GameAudioCue.tsx"() {
|
|
11638
|
+
"use client";
|
|
11639
|
+
init_useGameAudio();
|
|
11640
|
+
GameAudioCue.displayName = "GameAudioCue";
|
|
11641
|
+
}
|
|
11642
|
+
});
|
|
11179
11643
|
function isKnownState(s) {
|
|
11180
11644
|
return s in DEFAULT_STATE_STYLES;
|
|
11181
11645
|
}
|
|
@@ -11874,15 +12338,18 @@ var init_StateJsonView = __esm({
|
|
|
11874
12338
|
StateJsonView.displayName = "StateJsonView";
|
|
11875
12339
|
}
|
|
11876
12340
|
});
|
|
11877
|
-
|
|
11878
|
-
|
|
11879
|
-
|
|
11880
|
-
|
|
11881
|
-
|
|
11882
|
-
|
|
11883
|
-
|
|
11884
|
-
|
|
11885
|
-
|
|
12341
|
+
|
|
12342
|
+
// lib/gameFonts.ts
|
|
12343
|
+
function resolveGameFontFamily(input) {
|
|
12344
|
+
if (!input) return void 0;
|
|
12345
|
+
const resolved = GAME_FONT_KEYS[input];
|
|
12346
|
+
if (resolved) return `'${resolved}', ui-sans-serif, system-ui, sans-serif`;
|
|
12347
|
+
return input;
|
|
12348
|
+
}
|
|
12349
|
+
var GAME_FONT_KEYS;
|
|
12350
|
+
var init_gameFonts = __esm({
|
|
12351
|
+
"lib/gameFonts.ts"() {
|
|
12352
|
+
GAME_FONT_KEYS = {
|
|
11886
12353
|
fredoka: "Fredoka",
|
|
11887
12354
|
future: "Kenney Future",
|
|
11888
12355
|
"future-narrow": "Kenney Future Narrow",
|
|
@@ -11890,6 +12357,17 @@ var init_GameShell = __esm({
|
|
|
11890
12357
|
blocks: "Kenney Blocks",
|
|
11891
12358
|
mini: "Kenney Mini"
|
|
11892
12359
|
};
|
|
12360
|
+
}
|
|
12361
|
+
});
|
|
12362
|
+
var GameShell;
|
|
12363
|
+
var init_GameShell = __esm({
|
|
12364
|
+
"components/game/templates/GameShell.tsx"() {
|
|
12365
|
+
init_cn();
|
|
12366
|
+
init_gameFonts();
|
|
12367
|
+
init_Box();
|
|
12368
|
+
init_Card();
|
|
12369
|
+
init_Typography();
|
|
12370
|
+
init_AtlasImage();
|
|
11893
12371
|
GameShell = ({
|
|
11894
12372
|
appName = "Game",
|
|
11895
12373
|
hud,
|
|
@@ -11903,7 +12381,7 @@ var init_GameShell = __esm({
|
|
|
11903
12381
|
fontFamily,
|
|
11904
12382
|
"data-theme": dataTheme
|
|
11905
12383
|
}) => {
|
|
11906
|
-
const
|
|
12384
|
+
const displayFont = resolveGameFontFamily(fontFamily);
|
|
11907
12385
|
return /* @__PURE__ */ jsxs(
|
|
11908
12386
|
Box,
|
|
11909
12387
|
{
|
|
@@ -11922,7 +12400,7 @@ var init_GameShell = __esm({
|
|
|
11922
12400
|
// passed — an always-on inline stamp would shadow the orbital's
|
|
11923
12401
|
// inline-theme font-family-display token (inline style beats the
|
|
11924
12402
|
// theme provider's vars for the whole shell subtree).
|
|
11925
|
-
...
|
|
12403
|
+
...displayFont ? { "--font-family-display": displayFont } : {}
|
|
11926
12404
|
},
|
|
11927
12405
|
children: [
|
|
11928
12406
|
backgroundAsset && /* @__PURE__ */ jsx(
|
|
@@ -12120,14 +12598,15 @@ function drawArrowHead(ctx, x1, y1, x2, y2, size) {
|
|
|
12120
12598
|
ctx.closePath();
|
|
12121
12599
|
ctx.fill();
|
|
12122
12600
|
}
|
|
12123
|
-
function drawShape(ctx, shape, width, height, allShapes) {
|
|
12601
|
+
function drawShape(ctx, shape, width, height, allShapes, fontFamily) {
|
|
12124
12602
|
ctx.save();
|
|
12125
12603
|
const opacity = shape.opacity ?? 1;
|
|
12126
12604
|
ctx.globalAlpha = opacity;
|
|
12127
12605
|
const stroke = resolveColor2(shape.color, ctx, "#333333");
|
|
12128
12606
|
const fill = shape.fill ? resolveColor2(shape.fill, ctx, "#cccccc") : void 0;
|
|
12129
12607
|
ctx.lineWidth = shape.lineWidth ?? 2;
|
|
12130
|
-
|
|
12608
|
+
const dashPattern = shape.dash ? DASH_PATTERNS[shape.dash] : void 0;
|
|
12609
|
+
if (dashPattern) ctx.setLineDash([...dashPattern]);
|
|
12131
12610
|
switch (shape.type) {
|
|
12132
12611
|
case "grid": {
|
|
12133
12612
|
const step = shape.step ?? 40;
|
|
@@ -12249,7 +12728,7 @@ function drawShape(ctx, shape, width, height, allShapes) {
|
|
|
12249
12728
|
case "text": {
|
|
12250
12729
|
if (shape.x == null || shape.y == null || !shape.text) break;
|
|
12251
12730
|
ctx.fillStyle = stroke;
|
|
12252
|
-
ctx.font = `${shape.fontSize ?? 14}px ${themeBodyFont(ctx.canvas)}`;
|
|
12731
|
+
ctx.font = `${shape.fontSize ?? 14}px ${shape.fontFamily ?? fontFamily ?? themeBodyFont(ctx.canvas)}`;
|
|
12253
12732
|
ctx.textAlign = shape.align ?? "left";
|
|
12254
12733
|
ctx.textBaseline = "middle";
|
|
12255
12734
|
ctx.fillText(shape.text, shape.x, shape.y);
|
|
@@ -12291,7 +12770,7 @@ function drawShape(ctx, shape, width, height, allShapes) {
|
|
|
12291
12770
|
}
|
|
12292
12771
|
ctx.restore();
|
|
12293
12772
|
}
|
|
12294
|
-
function readoutShapes(readouts, width) {
|
|
12773
|
+
function readoutShapes(readouts, width, fontFamily) {
|
|
12295
12774
|
const out = [];
|
|
12296
12775
|
const chipH = 18;
|
|
12297
12776
|
const gap = 6;
|
|
@@ -12315,13 +12794,14 @@ function readoutShapes(readouts, width) {
|
|
|
12315
12794
|
text,
|
|
12316
12795
|
color: "#ffffff",
|
|
12317
12796
|
fontSize: 10,
|
|
12318
|
-
align: "center"
|
|
12797
|
+
align: "center",
|
|
12798
|
+
fontFamily
|
|
12319
12799
|
});
|
|
12320
12800
|
rightEdge = chipX - gap;
|
|
12321
12801
|
}
|
|
12322
12802
|
return out;
|
|
12323
12803
|
}
|
|
12324
|
-
function traceShapes(panel, k, width, height) {
|
|
12804
|
+
function traceShapes(panel, k, width, height, fontFamily) {
|
|
12325
12805
|
const w = panel.width ?? Math.round(width * 0.32);
|
|
12326
12806
|
const h = panel.height ?? Math.round(height * 0.28);
|
|
12327
12807
|
const x = panel.x ?? width - w - 8;
|
|
@@ -12375,14 +12855,14 @@ function traceShapes(panel, k, width, height) {
|
|
|
12375
12855
|
out.push({ type: "circle", x: last.x, y: last.y, radius: 2, color, fill: color });
|
|
12376
12856
|
}
|
|
12377
12857
|
if (series.label) {
|
|
12378
|
-
out.push({ type: "text", x: x + 6, y: y + 10 + 11 * j, text: series.label, color, fontSize: 9 });
|
|
12858
|
+
out.push({ type: "text", x: x + 6, y: y + 10 + 11 * j, text: series.label, color, fontSize: 9, fontFamily });
|
|
12379
12859
|
}
|
|
12380
12860
|
});
|
|
12381
12861
|
if (panel.yLabel) {
|
|
12382
|
-
out.push({ type: "text", x: x + w - 6, y: y + 10, text: panel.yLabel, color: "#6b7280", fontSize: 9, align: "right" });
|
|
12862
|
+
out.push({ type: "text", x: x + w - 6, y: y + 10, text: panel.yLabel, color: "#6b7280", fontSize: 9, align: "right", fontFamily });
|
|
12383
12863
|
}
|
|
12384
12864
|
if (panel.xLabel) {
|
|
12385
|
-
out.push({ type: "text", x: x + w - 6, y: y + h - 6, text: panel.xLabel, color: "#6b7280", fontSize: 9, align: "right" });
|
|
12865
|
+
out.push({ type: "text", x: x + w - 6, y: y + h - 6, text: panel.xLabel, color: "#6b7280", fontSize: 9, align: "right", fontFamily });
|
|
12386
12866
|
}
|
|
12387
12867
|
return out;
|
|
12388
12868
|
}
|
|
@@ -12405,13 +12885,14 @@ var init_LearningCanvas = __esm({
|
|
|
12405
12885
|
init_useEventBus();
|
|
12406
12886
|
init_webPainter2d();
|
|
12407
12887
|
init_paintDispatch();
|
|
12408
|
-
DASH_PATTERNS = { dashed: [6, 4], dotted: [2, 3] };
|
|
12888
|
+
DASH_PATTERNS = { solid: [], dashed: [6, 4], dotted: [2, 3] };
|
|
12409
12889
|
TRACE_SERIES_COLORS = ["#2563eb", "#dc2626", "#16a34a", "#f59e0b"];
|
|
12410
12890
|
LearningCanvas = ({
|
|
12411
12891
|
className,
|
|
12412
12892
|
width = 600,
|
|
12413
12893
|
height = 400,
|
|
12414
12894
|
backgroundColor,
|
|
12895
|
+
fontFamily,
|
|
12415
12896
|
shapes = [],
|
|
12416
12897
|
drawables,
|
|
12417
12898
|
projector,
|
|
@@ -12447,10 +12928,10 @@ var init_LearningCanvas = __esm({
|
|
|
12447
12928
|
}, [shapes]);
|
|
12448
12929
|
const derivedShapes = useMemo(() => {
|
|
12449
12930
|
if (!traces?.length && !readouts?.length) return shapes;
|
|
12450
|
-
const traceOut = (traces ?? []).flatMap((panel, k) => traceShapes(panel, k, width, height));
|
|
12451
|
-
const readoutOut = readouts?.length ? readoutShapes(readouts, width) : [];
|
|
12931
|
+
const traceOut = (traces ?? []).flatMap((panel, k) => traceShapes(panel, k, width, height, fontFamily));
|
|
12932
|
+
const readoutOut = readouts?.length ? readoutShapes(readouts, width, fontFamily) : [];
|
|
12452
12933
|
return [...shapes, ...traceOut, ...readoutOut];
|
|
12453
|
-
}, [shapes, traces, readouts, width, height]);
|
|
12934
|
+
}, [shapes, traces, readouts, width, height, fontFamily]);
|
|
12454
12935
|
const draw = useCallback(() => {
|
|
12455
12936
|
const _perfT = perfStart("learningcanvas:paint");
|
|
12456
12937
|
const canvas = canvasRef.current;
|
|
@@ -12469,15 +12950,15 @@ var init_LearningCanvas = __esm({
|
|
|
12469
12950
|
ctx.fillRect(0, 0, width, height);
|
|
12470
12951
|
}
|
|
12471
12952
|
for (const shape of derivedShapes) {
|
|
12472
|
-
if (shape.type !== "text") drawShape(ctx, shape, width, height, derivedShapes);
|
|
12953
|
+
if (shape.type !== "text") drawShape(ctx, shape, width, height, derivedShapes, fontFamily);
|
|
12473
12954
|
}
|
|
12474
12955
|
for (const shape of derivedShapes) {
|
|
12475
|
-
if (shape.type === "text") drawShape(ctx, shape, width, height, derivedShapes);
|
|
12956
|
+
if (shape.type === "text") drawShape(ctx, shape, width, height, derivedShapes, fontFamily);
|
|
12476
12957
|
}
|
|
12477
12958
|
if (drawables?.length && projector) {
|
|
12478
12959
|
const painter = createWebPainter(ctx, invalidateRef.current);
|
|
12479
12960
|
const timeMs = needsAnim && typeof performance !== "undefined" ? performance.now() : 0;
|
|
12480
|
-
const dctx = { projector, time: timeMs, invalidate: invalidateRef.current, fontFamily: themeBodyFont(canvas) };
|
|
12961
|
+
const dctx = { projector, time: timeMs, invalidate: invalidateRef.current, fontFamily: fontFamily || themeBodyFont(canvas) };
|
|
12481
12962
|
for (const node of drawables) {
|
|
12482
12963
|
paintDrawable(painter, node, dctx);
|
|
12483
12964
|
}
|
|
@@ -17076,6 +17557,250 @@ var init_EmptyState = __esm({
|
|
|
17076
17557
|
EmptyState.displayName = "EmptyState";
|
|
17077
17558
|
}
|
|
17078
17559
|
});
|
|
17560
|
+
|
|
17561
|
+
// lib/editorMotions.ts
|
|
17562
|
+
function clamp(value, min, max) {
|
|
17563
|
+
return Math.max(min, Math.min(max, value));
|
|
17564
|
+
}
|
|
17565
|
+
function computeLines(text) {
|
|
17566
|
+
const lines = [];
|
|
17567
|
+
let start = 0;
|
|
17568
|
+
for (let i = 0; i <= text.length; i++) {
|
|
17569
|
+
if (i === text.length || text[i] === "\n") {
|
|
17570
|
+
lines.push({ start, end: i });
|
|
17571
|
+
start = i + 1;
|
|
17572
|
+
}
|
|
17573
|
+
}
|
|
17574
|
+
return lines;
|
|
17575
|
+
}
|
|
17576
|
+
function lineIndexAt(lines, pos) {
|
|
17577
|
+
for (let i = 0; i < lines.length; i++) {
|
|
17578
|
+
if (pos <= lines[i].end) return i;
|
|
17579
|
+
}
|
|
17580
|
+
return lines.length - 1;
|
|
17581
|
+
}
|
|
17582
|
+
function findWords(text) {
|
|
17583
|
+
const words = [];
|
|
17584
|
+
const re = /\w+|\S+/g;
|
|
17585
|
+
let m;
|
|
17586
|
+
while ((m = re.exec(text)) !== null) {
|
|
17587
|
+
words.push({ start: m.index, end: m.index + m[0].length });
|
|
17588
|
+
}
|
|
17589
|
+
return words;
|
|
17590
|
+
}
|
|
17591
|
+
function nextWordStart(text, pos) {
|
|
17592
|
+
for (const w of findWords(text)) {
|
|
17593
|
+
if (w.start > pos) return w.start;
|
|
17594
|
+
}
|
|
17595
|
+
return text.length;
|
|
17596
|
+
}
|
|
17597
|
+
function prevWordStart(text, pos) {
|
|
17598
|
+
let result = 0;
|
|
17599
|
+
for (const w of findWords(text)) {
|
|
17600
|
+
if (w.start < pos) result = w.start;
|
|
17601
|
+
else break;
|
|
17602
|
+
}
|
|
17603
|
+
return result;
|
|
17604
|
+
}
|
|
17605
|
+
function nextWordEnd(text, pos) {
|
|
17606
|
+
for (const w of findWords(text)) {
|
|
17607
|
+
const lastChar = w.end - 1;
|
|
17608
|
+
if (lastChar > pos) return lastChar;
|
|
17609
|
+
}
|
|
17610
|
+
return text.length > 0 ? text.length - 1 : 0;
|
|
17611
|
+
}
|
|
17612
|
+
function firstNonBlank(text, line) {
|
|
17613
|
+
let i = line.start;
|
|
17614
|
+
while (i < line.end && (text[i] === " " || text[i] === " ")) i++;
|
|
17615
|
+
return i;
|
|
17616
|
+
}
|
|
17617
|
+
function nextParagraphBoundary(lines, fromLineIdx, textLength) {
|
|
17618
|
+
for (let i = fromLineIdx + 1; i < lines.length; i++) {
|
|
17619
|
+
if (lines[i].start === lines[i].end) return lines[i].start;
|
|
17620
|
+
}
|
|
17621
|
+
return textLength;
|
|
17622
|
+
}
|
|
17623
|
+
function prevParagraphBoundary(lines, fromLineIdx) {
|
|
17624
|
+
for (let i = fromLineIdx - 1; i >= 0; i--) {
|
|
17625
|
+
if (lines[i].start === lines[i].end) return lines[i].start;
|
|
17626
|
+
}
|
|
17627
|
+
return 0;
|
|
17628
|
+
}
|
|
17629
|
+
function applyMotion(text, caret, motion, count) {
|
|
17630
|
+
const n = Math.max(1, count);
|
|
17631
|
+
const lines = computeLines(text);
|
|
17632
|
+
const lineIdx = lineIndexAt(lines, caret);
|
|
17633
|
+
const line = lines[lineIdx];
|
|
17634
|
+
switch (motion) {
|
|
17635
|
+
case "left":
|
|
17636
|
+
return clamp(caret - n, line.start, line.end);
|
|
17637
|
+
case "right":
|
|
17638
|
+
return clamp(caret + n, line.start, line.end);
|
|
17639
|
+
case "up": {
|
|
17640
|
+
const col = caret - line.start;
|
|
17641
|
+
const targetIdx = clamp(lineIdx - n, 0, lines.length - 1);
|
|
17642
|
+
const target = lines[targetIdx];
|
|
17643
|
+
return clamp(target.start + col, target.start, target.end);
|
|
17644
|
+
}
|
|
17645
|
+
case "down": {
|
|
17646
|
+
const col = caret - line.start;
|
|
17647
|
+
const targetIdx = clamp(lineIdx + n, 0, lines.length - 1);
|
|
17648
|
+
const target = lines[targetIdx];
|
|
17649
|
+
return clamp(target.start + col, target.start, target.end);
|
|
17650
|
+
}
|
|
17651
|
+
case "word-forward": {
|
|
17652
|
+
let pos = caret;
|
|
17653
|
+
for (let i = 0; i < n; i++) pos = nextWordStart(text, pos);
|
|
17654
|
+
return pos;
|
|
17655
|
+
}
|
|
17656
|
+
case "word-back": {
|
|
17657
|
+
let pos = caret;
|
|
17658
|
+
for (let i = 0; i < n; i++) pos = prevWordStart(text, pos);
|
|
17659
|
+
return pos;
|
|
17660
|
+
}
|
|
17661
|
+
case "word-end": {
|
|
17662
|
+
let pos = caret;
|
|
17663
|
+
for (let i = 0; i < n; i++) pos = nextWordEnd(text, pos);
|
|
17664
|
+
return pos;
|
|
17665
|
+
}
|
|
17666
|
+
case "line-start":
|
|
17667
|
+
return line.start;
|
|
17668
|
+
case "line-end":
|
|
17669
|
+
return line.end > line.start ? line.end - 1 : line.start;
|
|
17670
|
+
case "first-nonblank":
|
|
17671
|
+
return firstNonBlank(text, line);
|
|
17672
|
+
case "doc-start":
|
|
17673
|
+
return 0;
|
|
17674
|
+
case "doc-end":
|
|
17675
|
+
return text.length;
|
|
17676
|
+
case "paragraph-forward": {
|
|
17677
|
+
let pos = caret;
|
|
17678
|
+
for (let i = 0; i < n; i++) {
|
|
17679
|
+
pos = nextParagraphBoundary(lines, lineIndexAt(lines, pos), text.length);
|
|
17680
|
+
}
|
|
17681
|
+
return pos;
|
|
17682
|
+
}
|
|
17683
|
+
case "paragraph-back": {
|
|
17684
|
+
let pos = caret;
|
|
17685
|
+
for (let i = 0; i < n; i++) {
|
|
17686
|
+
pos = prevParagraphBoundary(lines, lineIndexAt(lines, pos));
|
|
17687
|
+
}
|
|
17688
|
+
return pos;
|
|
17689
|
+
}
|
|
17690
|
+
case "line":
|
|
17691
|
+
case "selection":
|
|
17692
|
+
return caret;
|
|
17693
|
+
default: {
|
|
17694
|
+
const _exhaustive = motion;
|
|
17695
|
+
return _exhaustive;
|
|
17696
|
+
}
|
|
17697
|
+
}
|
|
17698
|
+
}
|
|
17699
|
+
function motionRange(text, caret, motion, count, selection) {
|
|
17700
|
+
if (motion === "selection") {
|
|
17701
|
+
if (!selection) return [caret, caret];
|
|
17702
|
+
return [Math.min(selection[0], selection[1]), Math.max(selection[0], selection[1])];
|
|
17703
|
+
}
|
|
17704
|
+
const lines = computeLines(text);
|
|
17705
|
+
if (motion === "line") {
|
|
17706
|
+
const n = Math.max(1, count);
|
|
17707
|
+
const startIdx = lineIndexAt(lines, caret);
|
|
17708
|
+
const endIdx = clamp(startIdx + n - 1, 0, lines.length - 1);
|
|
17709
|
+
const start2 = lines[startIdx].start;
|
|
17710
|
+
const rawEnd = lines[endIdx].end;
|
|
17711
|
+
const end2 = rawEnd < text.length ? rawEnd + 1 : rawEnd;
|
|
17712
|
+
return [start2, end2];
|
|
17713
|
+
}
|
|
17714
|
+
const newCaret = applyMotion(text, caret, motion, count);
|
|
17715
|
+
let start = Math.min(caret, newCaret);
|
|
17716
|
+
let end = Math.max(caret, newCaret);
|
|
17717
|
+
if (motion === "word-end" || motion === "line-end") {
|
|
17718
|
+
end = Math.min(Math.max(start, newCaret) + 1, text.length);
|
|
17719
|
+
} else if (motion === "word-forward" && newCaret > caret) {
|
|
17720
|
+
const startLine = lineIndexAt(lines, caret);
|
|
17721
|
+
const endLine = lineIndexAt(lines, newCaret);
|
|
17722
|
+
if (endLine !== startLine) {
|
|
17723
|
+
end = lines[startLine].end;
|
|
17724
|
+
}
|
|
17725
|
+
}
|
|
17726
|
+
return [start, end];
|
|
17727
|
+
}
|
|
17728
|
+
function applyOperator(text, range, operator, register) {
|
|
17729
|
+
const start = clamp(range[0], 0, text.length);
|
|
17730
|
+
const end = clamp(range[1], start, text.length);
|
|
17731
|
+
const removed = text.slice(start, end);
|
|
17732
|
+
if (operator === "yank") {
|
|
17733
|
+
return { text, caret: start, register: removed };
|
|
17734
|
+
}
|
|
17735
|
+
return { text: text.slice(0, start) + text.slice(end), caret: start, register: removed };
|
|
17736
|
+
}
|
|
17737
|
+
var init_editorMotions = __esm({
|
|
17738
|
+
"lib/editorMotions.ts"() {
|
|
17739
|
+
}
|
|
17740
|
+
});
|
|
17741
|
+
function isMotionPayload(payload) {
|
|
17742
|
+
return !!payload && typeof payload.editorId === "string" && typeof payload.motion === "string" && typeof payload.count === "number";
|
|
17743
|
+
}
|
|
17744
|
+
function isOperatePayload(payload) {
|
|
17745
|
+
return !!payload && typeof payload.editorId === "string" && typeof payload.operator === "string" && typeof payload.motion === "string" && typeof payload.count === "number";
|
|
17746
|
+
}
|
|
17747
|
+
function isInsertTextPayload(payload) {
|
|
17748
|
+
return !!payload && typeof payload.editorId === "string" && typeof payload.text === "string";
|
|
17749
|
+
}
|
|
17750
|
+
function isSetModePayload(payload) {
|
|
17751
|
+
return !!payload && typeof payload.editorId === "string" && typeof payload.mode === "string" && typeof payload.caret === "string";
|
|
17752
|
+
}
|
|
17753
|
+
function useEditorCapabilities(args) {
|
|
17754
|
+
const [caretMode, setCaretMode] = useState("bar");
|
|
17755
|
+
const registerRef = useRef("");
|
|
17756
|
+
useEventListener(`UI:${args.events.onMotion}`, (evt) => {
|
|
17757
|
+
if (!args.editorId || !isMotionPayload(evt.payload) || evt.payload.editorId !== args.editorId) return;
|
|
17758
|
+
const ta = args.textareaRef.current;
|
|
17759
|
+
if (!ta) return;
|
|
17760
|
+
const { motion, count } = evt.payload;
|
|
17761
|
+
const newCaret = applyMotion(ta.value, ta.selectionStart, motion, count);
|
|
17762
|
+
if (ta.selectionStart !== ta.selectionEnd) {
|
|
17763
|
+
ta.setSelectionRange(ta.selectionStart, newCaret);
|
|
17764
|
+
} else {
|
|
17765
|
+
ta.setSelectionRange(newCaret, newCaret);
|
|
17766
|
+
}
|
|
17767
|
+
});
|
|
17768
|
+
useEventListener(`UI:${args.events.onOperate}`, (evt) => {
|
|
17769
|
+
if (!args.editorId || !isOperatePayload(evt.payload) || evt.payload.editorId !== args.editorId) return;
|
|
17770
|
+
const ta = args.textareaRef.current;
|
|
17771
|
+
if (!ta) return;
|
|
17772
|
+
const { operator, motion, count } = evt.payload;
|
|
17773
|
+
const selection = motion === "selection" ? [ta.selectionStart, ta.selectionEnd] : void 0;
|
|
17774
|
+
const range = motionRange(ta.value, ta.selectionStart, motion, count, selection);
|
|
17775
|
+
const result = applyOperator(ta.value, range, operator, registerRef.current);
|
|
17776
|
+
registerRef.current = result.register;
|
|
17777
|
+
if (operator === "yank") {
|
|
17778
|
+
ta.setSelectionRange(range[0], range[0]);
|
|
17779
|
+
} else {
|
|
17780
|
+
ta.setRangeText("", range[0], range[1], "end");
|
|
17781
|
+
}
|
|
17782
|
+
args.applyChange(ta.value);
|
|
17783
|
+
});
|
|
17784
|
+
useEventListener(`UI:${args.events.onInsertText}`, (evt) => {
|
|
17785
|
+
if (!args.editorId || !isInsertTextPayload(evt.payload) || evt.payload.editorId !== args.editorId) return;
|
|
17786
|
+
const ta = args.textareaRef.current;
|
|
17787
|
+
if (!ta) return;
|
|
17788
|
+
ta.setRangeText(evt.payload.text, ta.selectionStart, ta.selectionEnd, "end");
|
|
17789
|
+
args.applyChange(ta.value);
|
|
17790
|
+
});
|
|
17791
|
+
useEventListener(`UI:${args.events.onSetMode}`, (evt) => {
|
|
17792
|
+
if (!args.editorId || !isSetModePayload(evt.payload) || evt.payload.editorId !== args.editorId) return;
|
|
17793
|
+
setCaretMode(evt.payload.caret);
|
|
17794
|
+
});
|
|
17795
|
+
return { caretMode };
|
|
17796
|
+
}
|
|
17797
|
+
var init_useEditorCapabilities = __esm({
|
|
17798
|
+
"components/core/molecules/markdown/useEditorCapabilities.ts"() {
|
|
17799
|
+
"use client";
|
|
17800
|
+
init_useEventBus();
|
|
17801
|
+
init_editorMotions();
|
|
17802
|
+
}
|
|
17803
|
+
});
|
|
17079
17804
|
function isLanguageRegistered(lang) {
|
|
17080
17805
|
return CODE_LANGUAGE_SET.has(lang) || dynamicallyLoaded.has(lang);
|
|
17081
17806
|
}
|
|
@@ -17161,7 +17886,36 @@ function useLanguageReady(language) {
|
|
|
17161
17886
|
}, [language]);
|
|
17162
17887
|
return ready;
|
|
17163
17888
|
}
|
|
17164
|
-
|
|
17889
|
+
function resolveHighlightStyle(lang) {
|
|
17890
|
+
if (lang === "orb") return orbStyle;
|
|
17891
|
+
if (lang === "lolo") return loloStyle;
|
|
17892
|
+
return dark;
|
|
17893
|
+
}
|
|
17894
|
+
function plainCodeColorOf(style) {
|
|
17895
|
+
return style['code[class*="language-"]']?.color ?? "#d4d4d4";
|
|
17896
|
+
}
|
|
17897
|
+
function buildLineProps(errorLines, extraClassName) {
|
|
17898
|
+
return (lineNumber) => {
|
|
17899
|
+
const base = {
|
|
17900
|
+
"data-line": String(lineNumber - 1),
|
|
17901
|
+
...extraClassName ? { className: extraClassName } : {}
|
|
17902
|
+
};
|
|
17903
|
+
const severity = errorLines?.get(lineNumber);
|
|
17904
|
+
if (!severity) return base;
|
|
17905
|
+
return {
|
|
17906
|
+
...base,
|
|
17907
|
+
style: {
|
|
17908
|
+
display: "block",
|
|
17909
|
+
backgroundColor: severity === "error" ? "rgba(248, 113, 113, 0.18)" : "rgba(251, 191, 36, 0.18)",
|
|
17910
|
+
// amber-400 @ 18%
|
|
17911
|
+
borderLeft: `3px solid ${severity === "error" ? "#ef4444" : "#f59e0b"}`,
|
|
17912
|
+
paddingLeft: "0.5rem",
|
|
17913
|
+
marginLeft: "-0.5rem"
|
|
17914
|
+
}
|
|
17915
|
+
};
|
|
17916
|
+
};
|
|
17917
|
+
}
|
|
17918
|
+
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;
|
|
17165
17919
|
var init_CodeBlock = __esm({
|
|
17166
17920
|
"components/core/molecules/markdown/CodeBlock.tsx"() {
|
|
17167
17921
|
init_cn();
|
|
@@ -17177,6 +17931,7 @@ var init_CodeBlock = __esm({
|
|
|
17177
17931
|
init_Textarea();
|
|
17178
17932
|
init_Icon();
|
|
17179
17933
|
init_useEventBus();
|
|
17934
|
+
init_useEditorCapabilities();
|
|
17180
17935
|
SyntaxHighlighter.registerLanguage("json", langJson);
|
|
17181
17936
|
SyntaxHighlighter.registerLanguage("javascript", langJavascript);
|
|
17182
17937
|
SyntaxHighlighter.registerLanguage("js", langJavascript);
|
|
@@ -17395,6 +18150,15 @@ var init_CodeBlock = __esm({
|
|
|
17395
18150
|
LINE_PROPS_FN = (n) => ({ "data-line": String(n - 1) });
|
|
17396
18151
|
HIDDEN_LINE_NUMBERS = { display: "none" };
|
|
17397
18152
|
HIGHLIGHT_CAPACITY_BYTES = 512 * 1024;
|
|
18153
|
+
MONO_FONT_FAMILY = 'ui-monospace, SFMono-Regular, Menlo, Monaco, "Cascadia Mono", "Courier New", monospace';
|
|
18154
|
+
VIEWER_LINE_NUMBER_STYLE = {
|
|
18155
|
+
minWidth: "2.5em",
|
|
18156
|
+
paddingRight: "1rem",
|
|
18157
|
+
textAlign: "right",
|
|
18158
|
+
userSelect: "none",
|
|
18159
|
+
opacity: 0.5,
|
|
18160
|
+
fontVariantNumeric: "tabular-nums"
|
|
18161
|
+
};
|
|
17398
18162
|
CodeBlock = React87__default.memo(
|
|
17399
18163
|
({
|
|
17400
18164
|
code: rawCode,
|
|
@@ -17419,15 +18183,39 @@ var init_CodeBlock = __esm({
|
|
|
17419
18183
|
actions,
|
|
17420
18184
|
isLoading = false,
|
|
17421
18185
|
error,
|
|
17422
|
-
showCopy
|
|
18186
|
+
showCopy,
|
|
18187
|
+
// editor capability surface — P1 wires these
|
|
18188
|
+
editorId,
|
|
18189
|
+
onEditorFocus = "EDITOR_FOCUS",
|
|
18190
|
+
onEditorBlur = "EDITOR_BLUR",
|
|
18191
|
+
onMotion = "MOTION",
|
|
18192
|
+
onOperate = "OPERATE",
|
|
18193
|
+
onInsertText = "INSERT_TEXT",
|
|
18194
|
+
onSetMode = "SET_MODE",
|
|
18195
|
+
motions = [
|
|
18196
|
+
"left",
|
|
18197
|
+
"right",
|
|
18198
|
+
"up",
|
|
18199
|
+
"down",
|
|
18200
|
+
"word-forward",
|
|
18201
|
+
"word-back",
|
|
18202
|
+
"word-end",
|
|
18203
|
+
"line-start",
|
|
18204
|
+
"line-end",
|
|
18205
|
+
"first-nonblank",
|
|
18206
|
+
"doc-start",
|
|
18207
|
+
"doc-end",
|
|
18208
|
+
"paragraph-forward",
|
|
18209
|
+
"paragraph-back",
|
|
18210
|
+
"line",
|
|
18211
|
+
"selection"
|
|
18212
|
+
],
|
|
18213
|
+
operators = ["delete", "yank", "change"]
|
|
17423
18214
|
}) => {
|
|
17424
18215
|
const code = typeof rawCode === "string" ? rawCode : String(rawCode ?? "");
|
|
17425
|
-
const
|
|
17426
|
-
const isLolo = language === "lolo";
|
|
17427
|
-
const activeStyle = isOrb ? orbStyle : isLolo ? loloStyle : dark;
|
|
18216
|
+
const activeStyle = resolveHighlightStyle(language);
|
|
17428
18217
|
const overCapacity = code.length > HIGHLIGHT_CAPACITY_BYTES;
|
|
17429
|
-
const plainCodeColor = activeStyle
|
|
17430
|
-
const languageReady = useLanguageReady(language);
|
|
18218
|
+
const plainCodeColor = plainCodeColorOf(activeStyle);
|
|
17431
18219
|
const eventBus = useEventBus();
|
|
17432
18220
|
const { t } = useTranslate();
|
|
17433
18221
|
const scrollRef = useRef(null);
|
|
@@ -17439,6 +18227,9 @@ var init_CodeBlock = __esm({
|
|
|
17439
18227
|
const activeFile = files?.[activeFileIndex];
|
|
17440
18228
|
const activeCode = activeFile?.code ?? code;
|
|
17441
18229
|
const activeLanguage = activeFile?.language ?? language;
|
|
18230
|
+
const languageReady = useLanguageReady(activeLanguage);
|
|
18231
|
+
const viewerStyle = resolveHighlightStyle(activeLanguage);
|
|
18232
|
+
const viewerPlainCodeColor = plainCodeColorOf(viewerStyle);
|
|
17442
18233
|
const diffLines = useMemo(() => {
|
|
17443
18234
|
if (propDiff) return propDiff;
|
|
17444
18235
|
if (mode === "diff" && oldValue !== void 0 && newValue !== void 0) {
|
|
@@ -17468,28 +18259,28 @@ var init_CodeBlock = __esm({
|
|
|
17468
18259
|
ov.scrollLeft = ta.scrollLeft;
|
|
17469
18260
|
}
|
|
17470
18261
|
}, []);
|
|
17471
|
-
const
|
|
17472
|
-
|
|
17473
|
-
|
|
17474
|
-
|
|
17475
|
-
|
|
17476
|
-
|
|
17477
|
-
|
|
17478
|
-
|
|
17479
|
-
|
|
17480
|
-
|
|
17481
|
-
|
|
17482
|
-
|
|
17483
|
-
|
|
17484
|
-
|
|
17485
|
-
|
|
17486
|
-
|
|
17487
|
-
|
|
17488
|
-
|
|
17489
|
-
|
|
17490
|
-
|
|
17491
|
-
|
|
17492
|
-
|
|
18262
|
+
const handleEditableChange = useCallback((v) => {
|
|
18263
|
+
lastPropCodeRef.current = v;
|
|
18264
|
+
setEditableValue(v);
|
|
18265
|
+
onChange?.(v);
|
|
18266
|
+
}, [onChange]);
|
|
18267
|
+
const { caretMode } = useEditorCapabilities({
|
|
18268
|
+
editorId: editable ? editorId : void 0,
|
|
18269
|
+
textareaRef: editableTextareaRef,
|
|
18270
|
+
events: { onMotion, onOperate, onInsertText, onSetMode },
|
|
18271
|
+
applyChange: handleEditableChange
|
|
18272
|
+
});
|
|
18273
|
+
const [caretIndex, setCaretIndex] = useState(0);
|
|
18274
|
+
const caretRowCol = useMemo(() => {
|
|
18275
|
+
const before = editableValue.slice(0, Math.min(caretIndex, editableValue.length));
|
|
18276
|
+
const lines = before.split("\n");
|
|
18277
|
+
return { row: lines.length - 1, col: lines[lines.length - 1].length };
|
|
18278
|
+
}, [editableValue, caretIndex]);
|
|
18279
|
+
const errorLineProps = useMemo(() => buildLineProps(errorLines), [errorLines]);
|
|
18280
|
+
const viewerLineProps = useMemo(
|
|
18281
|
+
() => buildLineProps(errorLines, "px-4 py-0.5 hover:bg-muted/50"),
|
|
18282
|
+
[errorLines]
|
|
18283
|
+
);
|
|
17493
18284
|
const isFoldable = foldableProp ?? true;
|
|
17494
18285
|
const [collapsed, setCollapsed] = useState(() => /* @__PURE__ */ new Set());
|
|
17495
18286
|
const foldRegions = useMemo(
|
|
@@ -17612,6 +18403,110 @@ var init_CodeBlock = __esm({
|
|
|
17612
18403
|
),
|
|
17613
18404
|
[code, overCapacity, plainCodeColor, language, activeStyle, languageReady]
|
|
17614
18405
|
);
|
|
18406
|
+
const viewerOverCapacity = activeCode.length > HIGHLIGHT_CAPACITY_BYTES;
|
|
18407
|
+
const viewerHighlightedElement = useMemo(
|
|
18408
|
+
() => viewerOverCapacity ? /* @__PURE__ */ jsx(
|
|
18409
|
+
"div",
|
|
18410
|
+
{
|
|
18411
|
+
className: "px-4 py-0.5",
|
|
18412
|
+
style: {
|
|
18413
|
+
margin: 0,
|
|
18414
|
+
whiteSpace: wrap ? "pre-wrap" : "pre",
|
|
18415
|
+
wordBreak: wrap ? "break-all" : "normal",
|
|
18416
|
+
color: viewerPlainCodeColor,
|
|
18417
|
+
fontFamily: MONO_FONT_FAMILY,
|
|
18418
|
+
fontSize: "12px",
|
|
18419
|
+
lineHeight: "1.6"
|
|
18420
|
+
},
|
|
18421
|
+
children: activeCode
|
|
18422
|
+
}
|
|
18423
|
+
) : /* @__PURE__ */ jsx(
|
|
18424
|
+
SyntaxHighlighter,
|
|
18425
|
+
{
|
|
18426
|
+
PreTag: "div",
|
|
18427
|
+
language: activeLanguage,
|
|
18428
|
+
style: viewerStyle,
|
|
18429
|
+
wrapLines: true,
|
|
18430
|
+
wrapLongLines: wrap,
|
|
18431
|
+
showLineNumbers,
|
|
18432
|
+
lineNumberStyle: VIEWER_LINE_NUMBER_STYLE,
|
|
18433
|
+
lineProps: viewerLineProps,
|
|
18434
|
+
customStyle: {
|
|
18435
|
+
backgroundColor: "transparent",
|
|
18436
|
+
borderRadius: 0,
|
|
18437
|
+
padding: "0.25rem 0",
|
|
18438
|
+
margin: 0,
|
|
18439
|
+
whiteSpace: wrap ? "pre-wrap" : "pre",
|
|
18440
|
+
wordBreak: wrap ? "break-all" : "normal",
|
|
18441
|
+
fontFamily: MONO_FONT_FAMILY,
|
|
18442
|
+
fontSize: "12px",
|
|
18443
|
+
lineHeight: "1.6"
|
|
18444
|
+
},
|
|
18445
|
+
codeTagProps: { style: { fontFamily: MONO_FONT_FAMILY, fontSize: "12px", lineHeight: "1.6" } },
|
|
18446
|
+
children: activeCode
|
|
18447
|
+
}
|
|
18448
|
+
),
|
|
18449
|
+
[activeCode, viewerOverCapacity, viewerPlainCodeColor, activeLanguage, viewerStyle, wrap, showLineNumbers, viewerLineProps, languageReady]
|
|
18450
|
+
);
|
|
18451
|
+
const diffOverCapacity = useMemo(
|
|
18452
|
+
() => !!diffLines && diffLines.reduce((n, l) => n + l.content.length + 1, 0) > HIGHLIGHT_CAPACITY_BYTES,
|
|
18453
|
+
[diffLines]
|
|
18454
|
+
);
|
|
18455
|
+
const diffRowElements = useMemo(() => {
|
|
18456
|
+
if (!diffLines) return null;
|
|
18457
|
+
return diffLines.map((line, idx) => {
|
|
18458
|
+
const style = DIFF_STYLES[line.type] ?? DIFF_STYLE_FALLBACK;
|
|
18459
|
+
return /* @__PURE__ */ jsxs(HStack, { gap: "none", align: "start", className: cn(style.bg, "px-4 py-0.5"), children: [
|
|
18460
|
+
showLineNumbers && /* @__PURE__ */ jsx(
|
|
18461
|
+
Typography,
|
|
18462
|
+
{
|
|
18463
|
+
variant: "caption",
|
|
18464
|
+
color: "secondary",
|
|
18465
|
+
className: "w-8 text-right mr-3 select-none tabular-nums flex-shrink-0",
|
|
18466
|
+
children: line.lineNumber ?? ""
|
|
18467
|
+
}
|
|
18468
|
+
),
|
|
18469
|
+
/* @__PURE__ */ jsxs(
|
|
18470
|
+
Typography,
|
|
18471
|
+
{
|
|
18472
|
+
variant: "caption",
|
|
18473
|
+
className: cn("font-mono flex-1 min-w-0", style.text, wrap ? "whitespace-pre-wrap break-all" : "whitespace-pre"),
|
|
18474
|
+
children: [
|
|
18475
|
+
/* @__PURE__ */ jsx(Box, { as: "span", className: "select-none opacity-50 mr-2", children: style.prefix }),
|
|
18476
|
+
diffOverCapacity ? line.content || " " : /* @__PURE__ */ jsx(
|
|
18477
|
+
SyntaxHighlighter,
|
|
18478
|
+
{
|
|
18479
|
+
PreTag: "span",
|
|
18480
|
+
CodeTag: "span",
|
|
18481
|
+
language: activeLanguage,
|
|
18482
|
+
style: viewerStyle,
|
|
18483
|
+
customStyle: {
|
|
18484
|
+
display: "inline",
|
|
18485
|
+
background: "transparent",
|
|
18486
|
+
padding: 0,
|
|
18487
|
+
margin: 0,
|
|
18488
|
+
whiteSpace: wrap ? "pre-wrap" : "pre",
|
|
18489
|
+
wordBreak: wrap ? "break-all" : "normal",
|
|
18490
|
+
fontFamily: "inherit",
|
|
18491
|
+
fontSize: "inherit",
|
|
18492
|
+
lineHeight: "inherit"
|
|
18493
|
+
},
|
|
18494
|
+
codeTagProps: {
|
|
18495
|
+
style: {
|
|
18496
|
+
whiteSpace: wrap ? "pre-wrap" : "pre",
|
|
18497
|
+
fontFamily: "inherit",
|
|
18498
|
+
fontSize: "inherit"
|
|
18499
|
+
}
|
|
18500
|
+
},
|
|
18501
|
+
children: line.content || " "
|
|
18502
|
+
}
|
|
18503
|
+
)
|
|
18504
|
+
]
|
|
18505
|
+
}
|
|
18506
|
+
)
|
|
18507
|
+
] }, idx);
|
|
18508
|
+
});
|
|
18509
|
+
}, [diffLines, showLineNumbers, wrap, diffOverCapacity, activeLanguage, viewerStyle, languageReady]);
|
|
17615
18510
|
useLayoutEffect(() => {
|
|
17616
18511
|
const container = codeRef.current;
|
|
17617
18512
|
if (!container) return;
|
|
@@ -17755,7 +18650,6 @@ var init_CodeBlock = __esm({
|
|
|
17755
18650
|
label: file.label,
|
|
17756
18651
|
content: null
|
|
17757
18652
|
}));
|
|
17758
|
-
const lines = activeCode.split("\n");
|
|
17759
18653
|
return /* @__PURE__ */ jsx(Card, { className: cn("overflow-hidden", className), children: /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column" }, children: [
|
|
17760
18654
|
tabItems && tabItems.length > 1 && /* @__PURE__ */ jsx(Box, { className: "border-b border-border", children: /* @__PURE__ */ jsx(
|
|
17761
18655
|
Tabs,
|
|
@@ -17818,49 +18712,7 @@ var init_CodeBlock = __esm({
|
|
|
17818
18712
|
]
|
|
17819
18713
|
}
|
|
17820
18714
|
),
|
|
17821
|
-
/* @__PURE__ */ jsx(Box, { className: "overflow-auto bg-muted/20", style: { maxHeight }, children: diffLines ? /* @__PURE__ */ jsx("div", { style: { display: "flex", flexDirection: "column" }, className: "font-mono text-xs", children:
|
|
17822
|
-
const style = DIFF_STYLES[line.type] ?? DIFF_STYLE_FALLBACK;
|
|
17823
|
-
return /* @__PURE__ */ jsxs(HStack, { gap: "none", align: "start", className: cn(style.bg, "px-4 py-0.5"), children: [
|
|
17824
|
-
showLineNumbers && /* @__PURE__ */ jsx(
|
|
17825
|
-
Typography,
|
|
17826
|
-
{
|
|
17827
|
-
variant: "caption",
|
|
17828
|
-
color: "secondary",
|
|
17829
|
-
className: "w-8 text-right mr-3 select-none tabular-nums flex-shrink-0",
|
|
17830
|
-
children: line.lineNumber ?? ""
|
|
17831
|
-
}
|
|
17832
|
-
),
|
|
17833
|
-
/* @__PURE__ */ jsxs(
|
|
17834
|
-
Typography,
|
|
17835
|
-
{
|
|
17836
|
-
variant: "caption",
|
|
17837
|
-
className: cn("font-mono flex-1 min-w-0", style.text, wrap ? "whitespace-pre-wrap break-all" : "whitespace-pre"),
|
|
17838
|
-
children: [
|
|
17839
|
-
/* @__PURE__ */ jsx(Box, { as: "span", className: "select-none opacity-50 mr-2", children: style.prefix }),
|
|
17840
|
-
line.content
|
|
17841
|
-
]
|
|
17842
|
-
}
|
|
17843
|
-
)
|
|
17844
|
-
] }, idx);
|
|
17845
|
-
}) }) : /* @__PURE__ */ jsx("div", { style: { display: "flex", flexDirection: "column" }, className: "font-mono text-xs", children: lines.map((line, idx) => /* @__PURE__ */ jsxs(HStack, { gap: "none", align: "start", className: "px-4 py-0.5 hover:bg-muted/50", children: [
|
|
17846
|
-
showLineNumbers && /* @__PURE__ */ jsx(
|
|
17847
|
-
Typography,
|
|
17848
|
-
{
|
|
17849
|
-
variant: "caption",
|
|
17850
|
-
color: "secondary",
|
|
17851
|
-
className: "w-8 text-right mr-4 select-none tabular-nums flex-shrink-0",
|
|
17852
|
-
children: idx + 1
|
|
17853
|
-
}
|
|
17854
|
-
),
|
|
17855
|
-
/* @__PURE__ */ jsx(
|
|
17856
|
-
Typography,
|
|
17857
|
-
{
|
|
17858
|
-
variant: "caption",
|
|
17859
|
-
className: cn("font-mono flex-1 min-w-0", wrap ? "whitespace-pre-wrap break-all" : "whitespace-pre"),
|
|
17860
|
-
children: line || " "
|
|
17861
|
-
}
|
|
17862
|
-
)
|
|
17863
|
-
] }, idx)) }) })
|
|
18715
|
+
/* @__PURE__ */ jsx(Box, { className: "overflow-auto bg-muted/20", style: { maxHeight }, children: diffLines ? /* @__PURE__ */ jsx("div", { style: { display: "flex", flexDirection: "column" }, className: "font-mono text-xs", children: diffRowElements }) : /* @__PURE__ */ jsx("div", { className: "font-mono text-xs", children: viewerHighlightedElement }) })
|
|
17864
18716
|
] }) });
|
|
17865
18717
|
}
|
|
17866
18718
|
const hasHeader = showLanguageBadge || effectiveCopy;
|
|
@@ -17947,13 +18799,11 @@ var init_CodeBlock = __esm({
|
|
|
17947
18799
|
{
|
|
17948
18800
|
ref: editableTextareaRef,
|
|
17949
18801
|
defaultValue: code,
|
|
17950
|
-
onChange: (e) =>
|
|
17951
|
-
const v = e.target.value;
|
|
17952
|
-
lastPropCodeRef.current = v;
|
|
17953
|
-
setEditableValue(v);
|
|
17954
|
-
onChange?.(v);
|
|
17955
|
-
},
|
|
18802
|
+
onChange: (e) => handleEditableChange(e.target.value),
|
|
17956
18803
|
onScroll: handleEditableScroll,
|
|
18804
|
+
onSelect: (e) => setCaretIndex(e.currentTarget.selectionStart),
|
|
18805
|
+
onFocus: editorId ? () => eventBus.emit(`UI:${onEditorFocus}`, { editorId }) : void 0,
|
|
18806
|
+
onBlur: editorId ? () => eventBus.emit(`UI:${onEditorBlur}`, { editorId }) : void 0,
|
|
17957
18807
|
spellCheck: false,
|
|
17958
18808
|
style: {
|
|
17959
18809
|
position: "absolute",
|
|
@@ -17968,7 +18818,7 @@ var init_CodeBlock = __esm({
|
|
|
17968
18818
|
resize: "none",
|
|
17969
18819
|
backgroundColor: "transparent",
|
|
17970
18820
|
color: "transparent",
|
|
17971
|
-
caretColor: "#e6e6e6",
|
|
18821
|
+
caretColor: caretMode === "block" ? "transparent" : "#e6e6e6",
|
|
17972
18822
|
WebkitTextFillColor: "transparent",
|
|
17973
18823
|
fontFamily: 'ui-monospace, SFMono-Regular, Menlo, Monaco, "Cascadia Mono", "Courier New", monospace',
|
|
17974
18824
|
fontSize: "13px",
|
|
@@ -17979,6 +18829,22 @@ var init_CodeBlock = __esm({
|
|
|
17979
18829
|
}
|
|
17980
18830
|
},
|
|
17981
18831
|
editableTextareaKey
|
|
18832
|
+
),
|
|
18833
|
+
caretMode !== "bar" && /* @__PURE__ */ jsx(
|
|
18834
|
+
"span",
|
|
18835
|
+
{
|
|
18836
|
+
"aria-hidden": true,
|
|
18837
|
+
style: {
|
|
18838
|
+
position: "absolute",
|
|
18839
|
+
top: `calc(1rem + ${caretRowCol.row * 19.5}px)`,
|
|
18840
|
+
left: `calc(1rem + ${caretRowCol.col}ch)`,
|
|
18841
|
+
width: "1ch",
|
|
18842
|
+
height: caretMode === "block" ? "19.5px" : "2px",
|
|
18843
|
+
backgroundColor: caretMode === "block" ? "rgba(230, 230, 230, 0.5)" : void 0,
|
|
18844
|
+
borderBottom: caretMode === "underline" ? "2px solid #e6e6e6" : void 0,
|
|
18845
|
+
pointerEvents: "none"
|
|
18846
|
+
}
|
|
18847
|
+
}
|
|
17982
18848
|
)
|
|
17983
18849
|
]
|
|
17984
18850
|
}
|
|
@@ -18006,55 +18872,175 @@ var init_CodeBlock = __esm({
|
|
|
18006
18872
|
)
|
|
18007
18873
|
] });
|
|
18008
18874
|
},
|
|
18009
|
-
(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
|
|
18875
|
+
(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
|
|
18010
18876
|
);
|
|
18011
18877
|
CodeBlock.displayName = "CodeBlock";
|
|
18012
18878
|
}
|
|
18013
18879
|
});
|
|
18880
|
+
|
|
18881
|
+
// components/core/molecules/markdown/mermaidSource.ts
|
|
18882
|
+
function isQuoted(label) {
|
|
18883
|
+
const trimmed = label.trim();
|
|
18884
|
+
return trimmed.length === 0 || trimmed.startsWith('"') && trimmed.endsWith('"') && trimmed.length > 1;
|
|
18885
|
+
}
|
|
18886
|
+
function quote(label) {
|
|
18887
|
+
return `"${label.replace(/"/g, "#quot;")}"`;
|
|
18888
|
+
}
|
|
18889
|
+
function isIdentifierChar(ch) {
|
|
18890
|
+
return /[A-Za-z0-9_\-.]/.test(ch);
|
|
18891
|
+
}
|
|
18892
|
+
function declaredType(code) {
|
|
18893
|
+
for (const line of code.split("\n")) {
|
|
18894
|
+
const trimmed = line.trim();
|
|
18895
|
+
if (trimmed.length === 0 || trimmed.startsWith("%%")) continue;
|
|
18896
|
+
return trimmed;
|
|
18897
|
+
}
|
|
18898
|
+
return "";
|
|
18899
|
+
}
|
|
18900
|
+
function isFlowchart(code) {
|
|
18901
|
+
return FLOWCHART_DIRECTIVE.test(declaredType(code));
|
|
18902
|
+
}
|
|
18903
|
+
function quoteNodeLabels(code) {
|
|
18904
|
+
let out = "";
|
|
18905
|
+
let i = 0;
|
|
18906
|
+
while (i < code.length) {
|
|
18907
|
+
const shape = NODE_SHAPES.find(([open2]) => code.startsWith(open2, i));
|
|
18908
|
+
const precededByIdentifier = i > 0 && isIdentifierChar(code[i - 1] ?? "");
|
|
18909
|
+
if (shape === void 0 || !precededByIdentifier) {
|
|
18910
|
+
out += code[i];
|
|
18911
|
+
i += 1;
|
|
18912
|
+
continue;
|
|
18913
|
+
}
|
|
18914
|
+
const [open, close] = shape;
|
|
18915
|
+
const contentStart = i + open.length;
|
|
18916
|
+
const closeAt = code.indexOf(close, contentStart);
|
|
18917
|
+
const newlineAt = code.indexOf("\n", contentStart);
|
|
18918
|
+
if (closeAt === -1 || newlineAt !== -1 && newlineAt < closeAt) {
|
|
18919
|
+
out += code[i];
|
|
18920
|
+
i += 1;
|
|
18921
|
+
continue;
|
|
18922
|
+
}
|
|
18923
|
+
const label = code.slice(contentStart, closeAt);
|
|
18924
|
+
out += open + (isQuoted(label) ? label : quote(label)) + close;
|
|
18925
|
+
i = closeAt + close.length;
|
|
18926
|
+
}
|
|
18927
|
+
return out;
|
|
18928
|
+
}
|
|
18929
|
+
function quoteEdgeLabels(code) {
|
|
18930
|
+
return code.split("\n").map((line) => {
|
|
18931
|
+
let out = "";
|
|
18932
|
+
let rest = line;
|
|
18933
|
+
for (; ; ) {
|
|
18934
|
+
const open = rest.indexOf("|");
|
|
18935
|
+
if (open === -1) break;
|
|
18936
|
+
const close = rest.indexOf("|", open + 1);
|
|
18937
|
+
if (close === -1) break;
|
|
18938
|
+
const label = rest.slice(open + 1, close);
|
|
18939
|
+
out += rest.slice(0, open + 1) + (isQuoted(label) ? label : quote(label)) + "|";
|
|
18940
|
+
rest = rest.slice(close + 1);
|
|
18941
|
+
}
|
|
18942
|
+
return out + rest;
|
|
18943
|
+
}).join("\n");
|
|
18944
|
+
}
|
|
18945
|
+
function quoteSubgraphTitles(code) {
|
|
18946
|
+
return code.split("\n").map((line) => {
|
|
18947
|
+
const match = /^(\s*subgraph\s+)(.+?)(\s*)$/.exec(line);
|
|
18948
|
+
if (match === null) return line;
|
|
18949
|
+
const [, prefix, title, trailing] = match;
|
|
18950
|
+
if (title === void 0 || prefix === void 0) return line;
|
|
18951
|
+
if (isQuoted(title) || title.includes("[")) return line;
|
|
18952
|
+
return prefix + quote(title) + (trailing ?? "");
|
|
18953
|
+
}).join("\n");
|
|
18954
|
+
}
|
|
18955
|
+
function mermaidRepairCandidates(code) {
|
|
18956
|
+
if (!isFlowchart(code)) return [];
|
|
18957
|
+
const nodes = quoteNodeLabels(code);
|
|
18958
|
+
const nodesAndEdges = quoteEdgeLabels(nodes);
|
|
18959
|
+
const all = quoteSubgraphTitles(nodesAndEdges);
|
|
18960
|
+
const ordered2 = [nodes, nodesAndEdges, all];
|
|
18961
|
+
const seen = /* @__PURE__ */ new Set([code]);
|
|
18962
|
+
const candidates = [];
|
|
18963
|
+
for (const candidate of ordered2) {
|
|
18964
|
+
if (seen.has(candidate)) continue;
|
|
18965
|
+
seen.add(candidate);
|
|
18966
|
+
candidates.push(candidate);
|
|
18967
|
+
}
|
|
18968
|
+
return candidates;
|
|
18969
|
+
}
|
|
18970
|
+
var NODE_SHAPES, FLOWCHART_DIRECTIVE;
|
|
18971
|
+
var init_mermaidSource = __esm({
|
|
18972
|
+
"components/core/molecules/markdown/mermaidSource.ts"() {
|
|
18973
|
+
NODE_SHAPES = [
|
|
18974
|
+
["[[", "]]"],
|
|
18975
|
+
["[(", ")]"],
|
|
18976
|
+
["([", "])"],
|
|
18977
|
+
["((", "))"],
|
|
18978
|
+
["{{", "}}"],
|
|
18979
|
+
["[", "]"],
|
|
18980
|
+
["(", ")"],
|
|
18981
|
+
["{", "}"]
|
|
18982
|
+
];
|
|
18983
|
+
FLOWCHART_DIRECTIVE = /^(?:graph|flowchart)\b/;
|
|
18984
|
+
}
|
|
18985
|
+
});
|
|
18014
18986
|
function loadMermaid() {
|
|
18015
18987
|
mermaidModule ?? (mermaidModule = import('mermaid').then((m) => m.default));
|
|
18016
18988
|
return mermaidModule;
|
|
18017
18989
|
}
|
|
18018
|
-
var mermaidModule, MermaidDiagram;
|
|
18990
|
+
var log7, mermaidModule, MermaidDiagram;
|
|
18019
18991
|
var init_MermaidDiagram = __esm({
|
|
18020
18992
|
"components/core/molecules/markdown/MermaidDiagram.tsx"() {
|
|
18021
18993
|
init_Box();
|
|
18022
18994
|
init_Typography();
|
|
18023
18995
|
init_CodeBlock();
|
|
18996
|
+
init_mermaidSource();
|
|
18024
18997
|
init_cn();
|
|
18998
|
+
log7 = createLogger("almadar:ui:mermaid-diagram");
|
|
18025
18999
|
mermaidModule = null;
|
|
18026
19000
|
MermaidDiagram = React87__default.memo(
|
|
18027
19001
|
({ code, className }) => {
|
|
18028
19002
|
const { resolvedMode } = useTheme();
|
|
19003
|
+
const { t } = useTranslate();
|
|
18029
19004
|
const containerRef = useRef(null);
|
|
18030
|
-
const [
|
|
19005
|
+
const [unrenderable, setUnrenderable] = useState(false);
|
|
18031
19006
|
const reactId = useId();
|
|
18032
19007
|
useEffect(() => {
|
|
18033
19008
|
let active = true;
|
|
18034
19009
|
void (async () => {
|
|
18035
|
-
|
|
18036
|
-
|
|
18037
|
-
|
|
18038
|
-
|
|
18039
|
-
|
|
18040
|
-
|
|
18041
|
-
|
|
18042
|
-
|
|
18043
|
-
|
|
18044
|
-
|
|
18045
|
-
|
|
18046
|
-
|
|
18047
|
-
|
|
18048
|
-
|
|
19010
|
+
const mermaid = await loadMermaid();
|
|
19011
|
+
mermaid.initialize({
|
|
19012
|
+
startOnLoad: false,
|
|
19013
|
+
securityLevel: "strict",
|
|
19014
|
+
theme: resolvedMode === "dark" ? "dark" : "default"
|
|
19015
|
+
});
|
|
19016
|
+
const domId = `mermaid-${reactId.replace(/[^a-zA-Z0-9]/g, "")}`;
|
|
19017
|
+
let firstError = null;
|
|
19018
|
+
for (const [index, source] of [code, ...mermaidRepairCandidates(code)].entries()) {
|
|
19019
|
+
try {
|
|
19020
|
+
const { svg } = await mermaid.render(domId, source);
|
|
19021
|
+
if (!active) return;
|
|
19022
|
+
const container = containerRef.current;
|
|
19023
|
+
if (container === null) return;
|
|
19024
|
+
container.innerHTML = svg;
|
|
19025
|
+
container.dataset.mermaidRepaired = String(index > 0);
|
|
19026
|
+
setUnrenderable(false);
|
|
19027
|
+
if (index > 0) log7.debug("mermaid:repaired", { candidate: index });
|
|
19028
|
+
return;
|
|
19029
|
+
} catch (err) {
|
|
19030
|
+
firstError ?? (firstError = err instanceof Error ? err : new Error(String(err)));
|
|
19031
|
+
}
|
|
18049
19032
|
}
|
|
19033
|
+
if (!active) return;
|
|
19034
|
+
log7.warn("mermaid:unrenderable", { error: firstError?.message ?? "", code });
|
|
19035
|
+
setUnrenderable(true);
|
|
18050
19036
|
})();
|
|
18051
19037
|
return () => {
|
|
18052
19038
|
active = false;
|
|
18053
19039
|
};
|
|
18054
19040
|
}, [code, resolvedMode, reactId]);
|
|
18055
19041
|
return /* @__PURE__ */ jsxs(Box, { className: cn("not-prose my-4", className), children: [
|
|
18056
|
-
|
|
18057
|
-
/* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-
|
|
19042
|
+
unrenderable && /* @__PURE__ */ jsxs(Box, { className: "space-y-2 mb-2", "data-testid": "mermaid-unrenderable", children: [
|
|
19043
|
+
/* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-muted-foreground", children: t("mermaid.unrenderable") }),
|
|
18058
19044
|
/* @__PURE__ */ jsx(CodeBlock, { code, language: "mermaid" })
|
|
18059
19045
|
] }),
|
|
18060
19046
|
/* @__PURE__ */ jsx(
|
|
@@ -18063,7 +19049,7 @@ var init_MermaidDiagram = __esm({
|
|
|
18063
19049
|
ref: containerRef,
|
|
18064
19050
|
"data-testid": "mermaid-diagram",
|
|
18065
19051
|
className: "overflow-x-auto",
|
|
18066
|
-
style:
|
|
19052
|
+
style: unrenderable ? { display: "none" } : void 0
|
|
18067
19053
|
}
|
|
18068
19054
|
)
|
|
18069
19055
|
] });
|
|
@@ -21418,14 +22404,14 @@ function useSafeEventBus2() {
|
|
|
21418
22404
|
} };
|
|
21419
22405
|
}
|
|
21420
22406
|
}
|
|
21421
|
-
var
|
|
22407
|
+
var log8, lookStyles4, ButtonGroup;
|
|
21422
22408
|
var init_ButtonGroup = __esm({
|
|
21423
22409
|
"components/core/molecules/ButtonGroup.tsx"() {
|
|
21424
22410
|
"use client";
|
|
21425
22411
|
init_cn();
|
|
21426
22412
|
init_atoms();
|
|
21427
22413
|
init_useEventBus();
|
|
21428
|
-
|
|
22414
|
+
log8 = createLogger("almadar:ui:button-group");
|
|
21429
22415
|
lookStyles4 = {
|
|
21430
22416
|
"right-aligned-buttons": "",
|
|
21431
22417
|
"floating-bar": "fixed bottom-section left-1/2 -translate-x-1/2 shadow-elevation-toast bg-card p-card-sm rounded-container",
|
|
@@ -21506,7 +22492,7 @@ var init_ButtonGroup = __esm({
|
|
|
21506
22492
|
{
|
|
21507
22493
|
variant: "ghost",
|
|
21508
22494
|
onClick: () => {
|
|
21509
|
-
|
|
22495
|
+
log8.debug("Filter clicked", { field: filter.field });
|
|
21510
22496
|
},
|
|
21511
22497
|
children: filter.label
|
|
21512
22498
|
},
|
|
@@ -24223,6 +25209,12 @@ function commandMatches(command, query) {
|
|
|
24223
25209
|
if (matchesQuery(query, command.label)) return true;
|
|
24224
25210
|
return (command.keywords ?? []).some((keyword) => matchesQuery(query, keyword));
|
|
24225
25211
|
}
|
|
25212
|
+
function dispatchCommandPaletteCommand(command, deps) {
|
|
25213
|
+
if (command.disabled) return;
|
|
25214
|
+
if (command.event) deps.emit(`UI:${command.event}`, { commandId: command.id });
|
|
25215
|
+
if (command.action) deps.emit(`UI:${command.action}`, command.actionPayload ?? {});
|
|
25216
|
+
deps.onSelect?.(command);
|
|
25217
|
+
}
|
|
24226
25218
|
var UNGROUPED, CommandPalette;
|
|
24227
25219
|
var init_CommandPalette = __esm({
|
|
24228
25220
|
"components/core/molecules/CommandPalette.tsx"() {
|
|
@@ -24277,9 +25269,7 @@ var init_CommandPalette = __esm({
|
|
|
24277
25269
|
const handleSelect = useCallback(
|
|
24278
25270
|
(command) => {
|
|
24279
25271
|
if (command.disabled) return;
|
|
24280
|
-
|
|
24281
|
-
if (command.action) eventBus.emit(`UI:${command.action}`, command.actionPayload ?? {});
|
|
24282
|
-
onSelect?.(command);
|
|
25272
|
+
dispatchCommandPaletteCommand(command, { emit: eventBus.emit, onSelect });
|
|
24283
25273
|
handleClose();
|
|
24284
25274
|
},
|
|
24285
25275
|
[eventBus, onSelect, handleClose]
|
|
@@ -29074,9 +30064,9 @@ function debug(...args) {
|
|
|
29074
30064
|
const [first, ...rest] = args;
|
|
29075
30065
|
const message = typeof first === "string" ? first : "<debug>";
|
|
29076
30066
|
if (rest.length === 0 && typeof first === "string") {
|
|
29077
|
-
|
|
30067
|
+
log9.debug(message);
|
|
29078
30068
|
} else {
|
|
29079
|
-
|
|
30069
|
+
log9.debug(message, { args: rest.length > 0 ? formatArgs(rest) : formatArgs([first]) });
|
|
29080
30070
|
}
|
|
29081
30071
|
}
|
|
29082
30072
|
function debugGroup(label) {
|
|
@@ -29104,11 +30094,11 @@ function toLogMetaValue(v) {
|
|
|
29104
30094
|
}
|
|
29105
30095
|
return String(v);
|
|
29106
30096
|
}
|
|
29107
|
-
var NAMESPACE,
|
|
30097
|
+
var NAMESPACE, log9;
|
|
29108
30098
|
var init_debug = __esm({
|
|
29109
30099
|
"lib/debug.ts"() {
|
|
29110
30100
|
NAMESPACE = "almadar:ui:debug";
|
|
29111
|
-
|
|
30101
|
+
log9 = createLogger(NAMESPACE);
|
|
29112
30102
|
createLogger("almadar:ui:debug:input");
|
|
29113
30103
|
createLogger("almadar:ui:debug:collision");
|
|
29114
30104
|
createLogger("almadar:ui:debug:physics");
|
|
@@ -30761,6 +31751,7 @@ var init_MathCanvas = __esm({
|
|
|
30761
31751
|
init_perf();
|
|
30762
31752
|
init_atoms();
|
|
30763
31753
|
init_Stack();
|
|
31754
|
+
init_gameFonts();
|
|
30764
31755
|
init_LearningCanvas();
|
|
30765
31756
|
MathCanvas = ({
|
|
30766
31757
|
className,
|
|
@@ -30780,6 +31771,7 @@ var init_MathCanvas = __esm({
|
|
|
30780
31771
|
showTickLabels = false,
|
|
30781
31772
|
tickLabelFontSize = 10,
|
|
30782
31773
|
labelFontSize = 12,
|
|
31774
|
+
fontFamily: fontFamilyProp,
|
|
30783
31775
|
showCurveLabels = false,
|
|
30784
31776
|
curves = [],
|
|
30785
31777
|
points = [],
|
|
@@ -30802,6 +31794,7 @@ var init_MathCanvas = __esm({
|
|
|
30802
31794
|
error
|
|
30803
31795
|
}) => {
|
|
30804
31796
|
const eventBus = useEventBus();
|
|
31797
|
+
const fontFamily = resolveGameFontFamily(fontFamilyProp);
|
|
30805
31798
|
const keyMapKey = keyMap ? JSON.stringify(keyMap) : null;
|
|
30806
31799
|
const keyUpMapKey = keyUpMap ? JSON.stringify(keyUpMap) : null;
|
|
30807
31800
|
const stableKeyMap = useMemo(() => keyMap, [keyMapKey]);
|
|
@@ -30851,18 +31844,18 @@ var init_MathCanvas = __esm({
|
|
|
30851
31844
|
let kx = 0;
|
|
30852
31845
|
for (let x = Math.ceil(xMin / gridStep) * gridStep; x <= xMax; x += gridStep, kx++) {
|
|
30853
31846
|
if (kx % labelEveryX === 0 && x !== 0) {
|
|
30854
|
-
out.push({ type: "text", x: mapX(x), y: xAxisY + 12, text: formatTick(x), color: "#6b7280", fontSize: tickLabelFontSize, align: "center" });
|
|
31847
|
+
out.push({ type: "text", fontFamily, x: mapX(x), y: xAxisY + 12, text: formatTick(x), color: "#6b7280", fontSize: tickLabelFontSize, align: "center" });
|
|
30855
31848
|
}
|
|
30856
31849
|
}
|
|
30857
31850
|
const labelEveryY = Math.max(1, Math.ceil((yMax - yMin) / gridStep / Math.floor(plotH / 28)));
|
|
30858
31851
|
let ky = 0;
|
|
30859
31852
|
for (let y = Math.ceil(yMin / gridStep) * gridStep; y <= yMax; y += gridStep, ky++) {
|
|
30860
31853
|
if (ky % labelEveryY === 0 && y !== 0) {
|
|
30861
|
-
out.push({ type: "text", x: yAxisX - 6, y: mapY(y), text: formatTick(y), color: "#6b7280", fontSize: tickLabelFontSize, align: "right" });
|
|
31854
|
+
out.push({ type: "text", fontFamily, x: yAxisX - 6, y: mapY(y), text: formatTick(y), color: "#6b7280", fontSize: tickLabelFontSize, align: "right" });
|
|
30862
31855
|
}
|
|
30863
31856
|
}
|
|
30864
31857
|
if (xMin <= 0 && xMax >= 0 && yMin <= 0 && yMax >= 0) {
|
|
30865
|
-
out.push({ type: "text", x: yAxisX - 6, y: xAxisY + 12, text: "0", color: "#6b7280", fontSize: tickLabelFontSize, align: "right" });
|
|
31858
|
+
out.push({ type: "text", fontFamily, x: yAxisX - 6, y: xAxisY + 12, text: "0", color: "#6b7280", fontSize: tickLabelFontSize, align: "right" });
|
|
30866
31859
|
}
|
|
30867
31860
|
}
|
|
30868
31861
|
for (const region of regions) {
|
|
@@ -30889,6 +31882,7 @@ var init_MathCanvas = __esm({
|
|
|
30889
31882
|
const mid = Math.floor(region.samples.length / 2);
|
|
30890
31883
|
out.push({
|
|
30891
31884
|
type: "text",
|
|
31885
|
+
fontFamily,
|
|
30892
31886
|
x: mapX((first.x + last.x) / 2),
|
|
30893
31887
|
y: (mapY(region.samples[mid].y) + mapY(baseline)) / 2,
|
|
30894
31888
|
text: region.label,
|
|
@@ -30925,14 +31919,14 @@ var init_MathCanvas = __esm({
|
|
|
30925
31919
|
const px = mapX(guide.at);
|
|
30926
31920
|
out.push({ type: "line", x1: px, y1: margin, x2: px, y2: height - margin, color, dash });
|
|
30927
31921
|
if (guide.label) {
|
|
30928
|
-
out.push({ type: "text", x: px + 4, y: margin + 10, text: guide.label, color, fontSize: 11 });
|
|
31922
|
+
out.push({ type: "text", fontFamily, x: px + 4, y: margin + 10, text: guide.label, color, fontSize: 11 });
|
|
30929
31923
|
}
|
|
30930
31924
|
} else {
|
|
30931
31925
|
if (guide.at < yMin || guide.at > yMax) continue;
|
|
30932
31926
|
const py = mapY(guide.at);
|
|
30933
31927
|
out.push({ type: "line", x1: margin, y1: py, x2: width - margin, y2: py, color, dash });
|
|
30934
31928
|
if (guide.label) {
|
|
30935
|
-
out.push({ type: "text", x: width - margin - 4, y: py - 8, text: guide.label, color, fontSize: labelFontSize, align: "right" });
|
|
31929
|
+
out.push({ type: "text", fontFamily, x: width - margin - 4, y: py - 8, text: guide.label, color, fontSize: labelFontSize, align: "right" });
|
|
30936
31930
|
}
|
|
30937
31931
|
}
|
|
30938
31932
|
}
|
|
@@ -30971,6 +31965,7 @@ var init_MathCanvas = __esm({
|
|
|
30971
31965
|
if (showCurveLabels && curve.label && lastInRange) {
|
|
30972
31966
|
out.push({
|
|
30973
31967
|
type: "text",
|
|
31968
|
+
fontFamily,
|
|
30974
31969
|
x: mapX(lastInRange.x) + 6,
|
|
30975
31970
|
y: mapY(lastInRange.y) - 6,
|
|
30976
31971
|
text: curve.label,
|
|
@@ -31008,6 +32003,7 @@ var init_MathCanvas = __esm({
|
|
|
31008
32003
|
if (hop.label) {
|
|
31009
32004
|
out.push({
|
|
31010
32005
|
type: "text",
|
|
32006
|
+
fontFamily,
|
|
31011
32007
|
x: (x1 + x2) / 2,
|
|
31012
32008
|
y: xAxisY - peak - 8,
|
|
31013
32009
|
text: hop.label,
|
|
@@ -31035,6 +32031,7 @@ var init_MathCanvas = __esm({
|
|
|
31035
32031
|
const rad = mid * Math.PI / 180;
|
|
31036
32032
|
out.push({
|
|
31037
32033
|
type: "text",
|
|
32034
|
+
fontFamily,
|
|
31038
32035
|
x: mapX(angle.x + 1.35 * radius * Math.cos(rad)),
|
|
31039
32036
|
y: mapY(angle.y + 1.35 * radius * Math.sin(rad)),
|
|
31040
32037
|
text: angle.label,
|
|
@@ -31056,7 +32053,7 @@ var init_MathCanvas = __esm({
|
|
|
31056
32053
|
fill: isOpen ? "#ffffff" : p.color ?? "#dc2626"
|
|
31057
32054
|
});
|
|
31058
32055
|
if (p.label) {
|
|
31059
|
-
out.push({ type: "text", x: mapX(p.x) + 8, y: mapY(p.y) - 8, text: p.label, color: p.color ?? "#111827", fontSize: labelFontSize });
|
|
32056
|
+
out.push({ type: "text", fontFamily, x: mapX(p.x) + 8, y: mapY(p.y) - 8, text: p.label, color: p.color ?? "#111827", fontSize: labelFontSize });
|
|
31060
32057
|
}
|
|
31061
32058
|
}
|
|
31062
32059
|
for (const v of vectors) {
|
|
@@ -31067,7 +32064,7 @@ var init_MathCanvas = __esm({
|
|
|
31067
32064
|
const y2 = mapY(v.y + v.vy);
|
|
31068
32065
|
out.push({ type: "arrow", x1, y1, x2, y2, color: v.color ?? "#7c3aed", lineWidth: 2 });
|
|
31069
32066
|
if (v.label) {
|
|
31070
|
-
out.push({ type: "text", x: x2 + 6, y: y2 - 6, text: v.label, color: v.color ?? "#7c3aed", fontSize: labelFontSize });
|
|
32067
|
+
out.push({ type: "text", fontFamily, x: x2 + 6, y: y2 - 6, text: v.label, color: v.color ?? "#7c3aed", fontSize: labelFontSize });
|
|
31071
32068
|
}
|
|
31072
32069
|
}
|
|
31073
32070
|
out.push(...shapes);
|
|
@@ -31088,6 +32085,7 @@ var init_MathCanvas = __esm({
|
|
|
31088
32085
|
showTickLabels,
|
|
31089
32086
|
tickLabelFontSize,
|
|
31090
32087
|
labelFontSize,
|
|
32088
|
+
fontFamily,
|
|
31091
32089
|
showCurveLabels,
|
|
31092
32090
|
curves,
|
|
31093
32091
|
points,
|
|
@@ -31143,6 +32141,7 @@ var init_MathCanvas = __esm({
|
|
|
31143
32141
|
width,
|
|
31144
32142
|
height,
|
|
31145
32143
|
backgroundColor,
|
|
32144
|
+
fontFamily,
|
|
31146
32145
|
shapes: derivedShapes,
|
|
31147
32146
|
drawables,
|
|
31148
32147
|
projector,
|
|
@@ -32375,13 +33374,13 @@ var init_MapView = __esm({
|
|
|
32375
33374
|
shadowSize: [41, 41]
|
|
32376
33375
|
});
|
|
32377
33376
|
L.Marker.prototype.options.icon = defaultIcon;
|
|
32378
|
-
const { useEffect:
|
|
33377
|
+
const { useEffect: useEffect67, useRef: useRef69, useCallback: useCallback95, useState: useState102 } = React87__default;
|
|
32379
33378
|
const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
|
|
32380
33379
|
const { useEventBus: useEventBus2 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
|
|
32381
33380
|
function MapUpdater({ centerLat, centerLng, zoom }) {
|
|
32382
33381
|
const map = useMap();
|
|
32383
|
-
const prevRef =
|
|
32384
|
-
|
|
33382
|
+
const prevRef = useRef69({ centerLat, centerLng, zoom });
|
|
33383
|
+
useEffect67(() => {
|
|
32385
33384
|
const prev = prevRef.current;
|
|
32386
33385
|
if (prev.centerLat !== centerLat || prev.centerLng !== centerLng || prev.zoom !== zoom) {
|
|
32387
33386
|
map.setView([centerLat, centerLng], zoom);
|
|
@@ -32392,7 +33391,7 @@ var init_MapView = __esm({
|
|
|
32392
33391
|
}
|
|
32393
33392
|
function MapClickHandler({ onMapClick }) {
|
|
32394
33393
|
const map = useMap();
|
|
32395
|
-
|
|
33394
|
+
useEffect67(() => {
|
|
32396
33395
|
if (!onMapClick) return;
|
|
32397
33396
|
const handler = (e) => {
|
|
32398
33397
|
onMapClick(e.latlng.lat, e.latlng.lng);
|
|
@@ -32420,8 +33419,8 @@ var init_MapView = __esm({
|
|
|
32420
33419
|
showAttribution = true
|
|
32421
33420
|
}) {
|
|
32422
33421
|
const eventBus = useEventBus2();
|
|
32423
|
-
const [clickedPosition, setClickedPosition] =
|
|
32424
|
-
const handleMapClick =
|
|
33422
|
+
const [clickedPosition, setClickedPosition] = useState102(null);
|
|
33423
|
+
const handleMapClick = useCallback95((lat, lng) => {
|
|
32425
33424
|
if (showClickedPin) {
|
|
32426
33425
|
setClickedPosition({ lat, lng });
|
|
32427
33426
|
}
|
|
@@ -32430,7 +33429,7 @@ var init_MapView = __esm({
|
|
|
32430
33429
|
eventBus.emit(`UI:${mapClickEvent}`, { latitude: lat, longitude: lng });
|
|
32431
33430
|
}
|
|
32432
33431
|
}, [onMapClick, mapClickEvent, eventBus, showClickedPin]);
|
|
32433
|
-
const handleMarkerClick =
|
|
33432
|
+
const handleMarkerClick = useCallback95((marker) => {
|
|
32434
33433
|
onMarkerClick?.(marker);
|
|
32435
33434
|
if (markerClickEvent) {
|
|
32436
33435
|
eventBus.emit(`UI:${markerClickEvent}`, { ...marker });
|
|
@@ -47010,7 +48009,7 @@ function getAllEvents(traits2) {
|
|
|
47010
48009
|
function EventDispatcherTab({ traits: traits2, schema }) {
|
|
47011
48010
|
const eventBus = useEventBus();
|
|
47012
48011
|
const { t } = useTranslate();
|
|
47013
|
-
const [
|
|
48012
|
+
const [log11, setLog] = React87.useState([]);
|
|
47014
48013
|
const prevStatesRef = React87.useRef(/* @__PURE__ */ new Map());
|
|
47015
48014
|
React87.useEffect(() => {
|
|
47016
48015
|
for (const trait of traits2) {
|
|
@@ -47074,9 +48073,9 @@ function EventDispatcherTab({ traits: traits2, schema }) {
|
|
|
47074
48073
|
/* @__PURE__ */ jsx(Typography, { variant: "small", weight: "medium", className: "text-muted-foreground mb-1", children: t("debug.otherEvents") }),
|
|
47075
48074
|
/* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1", children: unavailableEvents.map((event) => /* @__PURE__ */ jsx(Badge, { variant: "default", size: "sm", className: "opacity-50", children: event }, event)) })
|
|
47076
48075
|
] }),
|
|
47077
|
-
|
|
48076
|
+
log11.length > 0 && /* @__PURE__ */ jsxs("div", { children: [
|
|
47078
48077
|
/* @__PURE__ */ jsx(Typography, { variant: "small", weight: "medium", className: "text-muted-foreground mb-1", children: t("debug.recentTransitions") }),
|
|
47079
|
-
/* @__PURE__ */ jsx(Stack, { gap: "xs", children:
|
|
48078
|
+
/* @__PURE__ */ jsx(Stack, { gap: "xs", children: log11.map((entry, i) => /* @__PURE__ */ jsxs(Typography, { variant: "small", className: "font-mono text-xs", children: [
|
|
47080
48079
|
/* @__PURE__ */ jsx("span", { className: "text-primary", children: entry.traitName }),
|
|
47081
48080
|
" ",
|
|
47082
48081
|
/* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: entry.from }),
|
|
@@ -49267,6 +50266,7 @@ var init_component_registry_generated = __esm({
|
|
|
49267
50266
|
init_FormSection();
|
|
49268
50267
|
init_FormSectionHeader();
|
|
49269
50268
|
init_FxOverlay();
|
|
50269
|
+
init_GameAudioCue();
|
|
49270
50270
|
init_GameAudioToggle();
|
|
49271
50271
|
init_GameHud();
|
|
49272
50272
|
init_GameIcon();
|
|
@@ -49544,6 +50544,7 @@ var init_component_registry_generated = __esm({
|
|
|
49544
50544
|
"FormLayout": FormLayout,
|
|
49545
50545
|
"FormSectionHeader": FormSectionHeader,
|
|
49546
50546
|
"FxOverlay": FxOverlay,
|
|
50547
|
+
"GameAudioCue": GameAudioCue,
|
|
49547
50548
|
"GameAudioToggle": GameAudioToggle,
|
|
49548
50549
|
"GameHud": GameHud,
|
|
49549
50550
|
"GameIcon": GameIcon,
|
|
@@ -49992,7 +50993,9 @@ function UISlotComponentInner({
|
|
|
49992
50993
|
className,
|
|
49993
50994
|
children,
|
|
49994
50995
|
pattern,
|
|
49995
|
-
sourceTrait
|
|
50996
|
+
sourceTrait,
|
|
50997
|
+
fallback,
|
|
50998
|
+
mode = "replace"
|
|
49996
50999
|
}) {
|
|
49997
51000
|
const { slots, clear } = useUISlots();
|
|
49998
51001
|
const eventBus = useEventBus();
|
|
@@ -50044,12 +51047,26 @@ function UISlotComponentInner({
|
|
|
50044
51047
|
);
|
|
50045
51048
|
}
|
|
50046
51049
|
if (!content) {
|
|
51050
|
+
if (fallback !== void 0) {
|
|
51051
|
+
return /* @__PURE__ */ jsx(
|
|
51052
|
+
Box,
|
|
51053
|
+
{
|
|
51054
|
+
id: `slot-${slot}`,
|
|
51055
|
+
className: cn("ui-slot", `ui-slot-${slot}`, className),
|
|
51056
|
+
"data-testid": `ui-slot-${slot}`,
|
|
51057
|
+
"data-slot-mode": "fallback",
|
|
51058
|
+
children: fallback
|
|
51059
|
+
}
|
|
51060
|
+
);
|
|
51061
|
+
}
|
|
50047
51062
|
if (!portal) {
|
|
50048
51063
|
return /* @__PURE__ */ jsx(
|
|
50049
51064
|
Box,
|
|
50050
51065
|
{
|
|
50051
51066
|
id: `slot-${slot}`,
|
|
50052
|
-
className: cn("ui-slot", `ui-slot-${slot}`, className)
|
|
51067
|
+
className: cn("ui-slot", `ui-slot-${slot}`, className),
|
|
51068
|
+
"data-testid": `ui-slot-${slot}`,
|
|
51069
|
+
"data-slot-mode": "empty"
|
|
50053
51070
|
}
|
|
50054
51071
|
);
|
|
50055
51072
|
}
|
|
@@ -50066,29 +51083,51 @@ function UISlotComponentInner({
|
|
|
50066
51083
|
clear(slot);
|
|
50067
51084
|
};
|
|
50068
51085
|
if (portal) {
|
|
50069
|
-
|
|
50070
|
-
|
|
50071
|
-
}
|
|
50072
|
-
return /* @__PURE__ */ jsx(
|
|
50073
|
-
SlotPortal,
|
|
51086
|
+
const inlineFallback = mode === "append" && fallback !== void 0 ? /* @__PURE__ */ jsx(
|
|
51087
|
+
Box,
|
|
50074
51088
|
{
|
|
50075
|
-
slot
|
|
50076
|
-
|
|
50077
|
-
|
|
50078
|
-
|
|
51089
|
+
id: `slot-${slot}-fallback`,
|
|
51090
|
+
className: cn("ui-slot", `ui-slot-${slot}-fallback`, className),
|
|
51091
|
+
"data-testid": `ui-slot-${slot}-fallback`,
|
|
51092
|
+
"data-slot-mode": "append",
|
|
51093
|
+
children: fallback
|
|
50079
51094
|
}
|
|
50080
|
-
);
|
|
51095
|
+
) : null;
|
|
51096
|
+
if (contained) {
|
|
51097
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
51098
|
+
inlineFallback,
|
|
51099
|
+
renderContainedPortal(t, slot, content, handleDismiss)
|
|
51100
|
+
] });
|
|
51101
|
+
}
|
|
51102
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
51103
|
+
inlineFallback,
|
|
51104
|
+
/* @__PURE__ */ jsx(
|
|
51105
|
+
SlotPortal,
|
|
51106
|
+
{
|
|
51107
|
+
slot,
|
|
51108
|
+
content,
|
|
51109
|
+
position,
|
|
51110
|
+
onDismiss: handleDismiss
|
|
51111
|
+
}
|
|
51112
|
+
)
|
|
51113
|
+
] });
|
|
50081
51114
|
}
|
|
50082
51115
|
const slotContent = /* @__PURE__ */ jsx(SlotContentRenderer, { content, onDismiss: handleDismiss });
|
|
50083
51116
|
const wrappedContent = suspenseConfig.enabled ? /* @__PURE__ */ jsx(ErrorBoundary, { children: /* @__PURE__ */ jsx(Suspense, { fallback: getSlotFallback(slot, suspenseConfig), children: slotContent }) }) : /* @__PURE__ */ jsx(ErrorBoundary, { children: slotContent });
|
|
50084
|
-
|
|
51117
|
+
const showFallback = mode === "append" && fallback !== void 0;
|
|
51118
|
+
return /* @__PURE__ */ jsxs(
|
|
50085
51119
|
Box,
|
|
50086
51120
|
{
|
|
50087
51121
|
id: `slot-${slot}`,
|
|
50088
51122
|
className: cn("ui-slot", `ui-slot-${slot}`, className),
|
|
50089
51123
|
"data-pattern": content.pattern,
|
|
50090
51124
|
"data-source-trait": content.sourceTrait,
|
|
50091
|
-
|
|
51125
|
+
"data-testid": `ui-slot-${slot}`,
|
|
51126
|
+
"data-slot-mode": showFallback ? "append" : "content",
|
|
51127
|
+
children: [
|
|
51128
|
+
showFallback ? fallback : null,
|
|
51129
|
+
/* @__PURE__ */ jsx(MaybeTraitScope, { sourceTrait: content.sourceTrait, children: wrappedContent })
|
|
51130
|
+
]
|
|
50092
51131
|
}
|
|
50093
51132
|
);
|
|
50094
51133
|
}
|
|
@@ -51518,7 +52557,7 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
|
|
|
51518
52557
|
};
|
|
51519
52558
|
}, [traitBindings]);
|
|
51520
52559
|
const executeTransitionEffects = useCallback(async (params) => {
|
|
51521
|
-
const { binding, previousState, newState, payload, flushEvent, syncOnly, log:
|
|
52560
|
+
const { binding, previousState, newState, payload, flushEvent, syncOnly, log: log11 } = params;
|
|
51522
52561
|
const traitName = binding.trait.name;
|
|
51523
52562
|
const linkedEntity = binding.linkedEntity || "";
|
|
51524
52563
|
const entityId = payload?.entityId;
|
|
@@ -51647,7 +52686,7 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
|
|
|
51647
52686
|
if (sharedKey !== void 0) {
|
|
51648
52687
|
sharedWrites.push({ field, value });
|
|
51649
52688
|
}
|
|
51650
|
-
|
|
52689
|
+
log11.debug("set:write", {
|
|
51651
52690
|
traitName,
|
|
51652
52691
|
field,
|
|
51653
52692
|
value: JSON.stringify(value),
|
|
@@ -51715,7 +52754,7 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
|
|
|
51715
52754
|
mergeEntityFrame(sharedEntityStore.getSnapshot(sharedKey), sharedWrites)
|
|
51716
52755
|
);
|
|
51717
52756
|
}
|
|
51718
|
-
|
|
52757
|
+
log11.debug("effects:executed", () => ({
|
|
51719
52758
|
traitName,
|
|
51720
52759
|
transition: `${previousState}->${newState}`,
|
|
51721
52760
|
event: flushEvent,
|
|
@@ -51725,7 +52764,7 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
|
|
|
51725
52764
|
slotsTouched: Array.from(pendingSlots.keys()).join(",")
|
|
51726
52765
|
}));
|
|
51727
52766
|
for (const [slot, patterns] of pendingSlots) {
|
|
51728
|
-
|
|
52767
|
+
log11.debug("flush:slot", {
|
|
51729
52768
|
traitName,
|
|
51730
52769
|
slot,
|
|
51731
52770
|
patternCount: patterns.length,
|
|
@@ -51743,7 +52782,7 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
|
|
|
51743
52782
|
publishBindingSnapshot(traitName, liveEntity);
|
|
51744
52783
|
}
|
|
51745
52784
|
} catch (error) {
|
|
51746
|
-
|
|
52785
|
+
log11.error("effects:error", {
|
|
51747
52786
|
traitName,
|
|
51748
52787
|
transition: `${previousState}->${newState}`,
|
|
51749
52788
|
event: flushEvent,
|
|
@@ -52925,11 +53964,20 @@ function BrowserPlayground({
|
|
|
52925
53964
|
mode = "mock",
|
|
52926
53965
|
initialPagePath,
|
|
52927
53966
|
height,
|
|
52928
|
-
className
|
|
53967
|
+
className,
|
|
53968
|
+
paused
|
|
52929
53969
|
}) {
|
|
52930
53970
|
const [runtime] = useState(
|
|
52931
53971
|
() => new OrbitalServerRuntime({ mode, debug: false })
|
|
52932
53972
|
);
|
|
53973
|
+
useEffect(() => {
|
|
53974
|
+
if (paused === void 0) return;
|
|
53975
|
+
if (paused) {
|
|
53976
|
+
runtime.pauseTicks();
|
|
53977
|
+
} else {
|
|
53978
|
+
runtime.resumeTicks();
|
|
53979
|
+
}
|
|
53980
|
+
}, [runtime, paused]);
|
|
52933
53981
|
const registrationReady = useMemo(() => {
|
|
52934
53982
|
const orbitalNames = schema.orbitals.map((o) => o.name);
|
|
52935
53983
|
playgroundLog.debug("register:start", { schema: schema.name, orbitalNames });
|
|
@@ -52984,7 +54032,353 @@ function BrowserPlayground({
|
|
|
52984
54032
|
);
|
|
52985
54033
|
}
|
|
52986
54034
|
|
|
54035
|
+
// runtime/OrbitalPluginHost.tsx
|
|
54036
|
+
init_useEventBus();
|
|
54037
|
+
var log10 = createLogger("almadar:ui:plugin-host");
|
|
54038
|
+
var UI_SLOT_SET = new Set(UI_SLOTS);
|
|
54039
|
+
function isUISlot(value) {
|
|
54040
|
+
return UI_SLOT_SET.has(value);
|
|
54041
|
+
}
|
|
54042
|
+
var OrbitalPluginHostContext = createContext(null);
|
|
54043
|
+
function useOrbitalPluginHostContext(caller) {
|
|
54044
|
+
const ctx = useContext(OrbitalPluginHostContext);
|
|
54045
|
+
if (!ctx) {
|
|
54046
|
+
throw new Error(`${caller} must be used within an <OrbitalPluginHost>.`);
|
|
54047
|
+
}
|
|
54048
|
+
return ctx;
|
|
54049
|
+
}
|
|
54050
|
+
function isTraitState(value) {
|
|
54051
|
+
return value !== void 0 && typeof value.currentState === "string";
|
|
54052
|
+
}
|
|
54053
|
+
function toTraitState(runtime, orbital, trait, stateName, lastEvent) {
|
|
54054
|
+
if (runtime) {
|
|
54055
|
+
const raw = runtime.getState(orbital, trait);
|
|
54056
|
+
if (isTraitState(raw)) return raw;
|
|
54057
|
+
}
|
|
54058
|
+
return { traitName: trait, currentState: stateName, previousState: null, lastEvent, context: {} };
|
|
54059
|
+
}
|
|
54060
|
+
function isKeymapValue(value) {
|
|
54061
|
+
if (value === null || value === void 0 || typeof value !== "object" || Array.isArray(value)) return false;
|
|
54062
|
+
return Object.values(value).every((entry) => Array.isArray(entry) && entry.every((k) => typeof k === "string"));
|
|
54063
|
+
}
|
|
54064
|
+
function findInlineTrait(schema, orbitalName, traitName) {
|
|
54065
|
+
const orbital = schema.orbitals.find((o) => o.name === orbitalName);
|
|
54066
|
+
if (!orbital) return void 0;
|
|
54067
|
+
for (const ref of orbital.traits) {
|
|
54068
|
+
if (typeof ref === "string") continue;
|
|
54069
|
+
if ("ref" in ref) continue;
|
|
54070
|
+
if (ref.name === traitName) return ref;
|
|
54071
|
+
}
|
|
54072
|
+
return void 0;
|
|
54073
|
+
}
|
|
54074
|
+
function readKeymapConfig(trait, knob) {
|
|
54075
|
+
const declared = trait?.config?.[knob]?.default;
|
|
54076
|
+
return isKeymapValue(declared) ? declared : {};
|
|
54077
|
+
}
|
|
54078
|
+
function traitNamesOf(orbital) {
|
|
54079
|
+
const names = [];
|
|
54080
|
+
for (const ref of orbital.traits) {
|
|
54081
|
+
if (typeof ref === "string") {
|
|
54082
|
+
names.push(ref);
|
|
54083
|
+
} else if ("ref" in ref) {
|
|
54084
|
+
if (ref.name) names.push(ref.name);
|
|
54085
|
+
} else {
|
|
54086
|
+
names.push(ref.name);
|
|
54087
|
+
}
|
|
54088
|
+
}
|
|
54089
|
+
return names;
|
|
54090
|
+
}
|
|
54091
|
+
function useDeclaredCaptureTable(opts) {
|
|
54092
|
+
const { pluginId, orbital, trait, keymapKnob = "keymap", target = "shell" } = opts;
|
|
54093
|
+
const ctx = useOrbitalPluginHostContext("useDeclaredCaptureTable");
|
|
54094
|
+
return useMemo(() => {
|
|
54095
|
+
const schema = ctx.getPluginSchema(pluginId);
|
|
54096
|
+
const inlineTrait = schema ? findInlineTrait(schema, orbital, trait) : void 0;
|
|
54097
|
+
const keymap = readKeymapConfig(inlineTrait, keymapKnob);
|
|
54098
|
+
const currentState = ctx.getState(pluginId, orbital, trait)?.currentState;
|
|
54099
|
+
const keys = currentState !== void 0 ? keymap[currentState] ?? [] : [];
|
|
54100
|
+
return { [target]: { mode: currentState ?? "unknown", keys: new Set(keys) } };
|
|
54101
|
+
}, [ctx, pluginId, orbital, trait, keymapKnob, target]);
|
|
54102
|
+
}
|
|
54103
|
+
function useOrbitalPluginHost() {
|
|
54104
|
+
const ctx = useOrbitalPluginHostContext("useOrbitalPluginHost");
|
|
54105
|
+
return { getState: ctx.getState, lastEvent: ctx.lastEvent, errors: ctx.errors };
|
|
54106
|
+
}
|
|
54107
|
+
function buildMockEffectHandlers(opts) {
|
|
54108
|
+
const { pluginId, denySet, slotsRef, navigateRef, notifyRef } = opts;
|
|
54109
|
+
const handlers = {
|
|
54110
|
+
renderUI: (slot, pattern, props, priority) => {
|
|
54111
|
+
if (!isUISlot(slot)) {
|
|
54112
|
+
log10.warn("renderUI:unknown-slot", { pluginId, slot });
|
|
54113
|
+
return;
|
|
54114
|
+
}
|
|
54115
|
+
if (pattern === null) {
|
|
54116
|
+
slotsRef.current.clear(slot);
|
|
54117
|
+
return;
|
|
54118
|
+
}
|
|
54119
|
+
slotsRef.current.render({ target: slot, pattern: pattern.type, props, priority, sourceTrait: pluginId });
|
|
54120
|
+
}
|
|
54121
|
+
};
|
|
54122
|
+
if (denySet.has("navigate")) {
|
|
54123
|
+
handlers.navigate = (path) => log10.warn("navigate:denied", { pluginId, path });
|
|
54124
|
+
} else if (navigateRef.current) {
|
|
54125
|
+
handlers.navigate = (path) => navigateRef.current?.(path);
|
|
54126
|
+
}
|
|
54127
|
+
if (denySet.has("notify")) {
|
|
54128
|
+
handlers.notify = (message, type) => log10.warn("notify:denied", { pluginId, message, type });
|
|
54129
|
+
} else if (notifyRef.current) {
|
|
54130
|
+
handlers.notify = (message, type) => notifyRef.current?.(message, type);
|
|
54131
|
+
}
|
|
54132
|
+
if (denySet.has("persist")) {
|
|
54133
|
+
handlers.persist = async (action, entityType) => {
|
|
54134
|
+
log10.warn("persist:denied", { pluginId, action, entityType });
|
|
54135
|
+
return void 0;
|
|
54136
|
+
};
|
|
54137
|
+
}
|
|
54138
|
+
if (denySet.has("call-service")) {
|
|
54139
|
+
handlers.callService = async (service, action) => {
|
|
54140
|
+
log10.warn("call-service:denied", { pluginId, service, action });
|
|
54141
|
+
return null;
|
|
54142
|
+
};
|
|
54143
|
+
}
|
|
54144
|
+
return handlers;
|
|
54145
|
+
}
|
|
54146
|
+
function PluginRuntimeMount({
|
|
54147
|
+
plugin,
|
|
54148
|
+
mode,
|
|
54149
|
+
transport,
|
|
54150
|
+
deny,
|
|
54151
|
+
navigate,
|
|
54152
|
+
notify,
|
|
54153
|
+
onTransition,
|
|
54154
|
+
onDispatched,
|
|
54155
|
+
onError
|
|
54156
|
+
}) {
|
|
54157
|
+
const bus = useEventBus();
|
|
54158
|
+
const slots = useUISlots();
|
|
54159
|
+
const slotsRef = useRef(slots);
|
|
54160
|
+
slotsRef.current = slots;
|
|
54161
|
+
const navigateRef = useRef(navigate);
|
|
54162
|
+
navigateRef.current = navigate;
|
|
54163
|
+
const notifyRef = useRef(notify);
|
|
54164
|
+
notifyRef.current = notify;
|
|
54165
|
+
const [mockRuntime] = useState(() => {
|
|
54166
|
+
if (mode !== "mock") return void 0;
|
|
54167
|
+
return new OrbitalServerRuntime({
|
|
54168
|
+
mode: "mock",
|
|
54169
|
+
debug: false,
|
|
54170
|
+
effectHandlers: buildMockEffectHandlers({
|
|
54171
|
+
pluginId: plugin.id,
|
|
54172
|
+
denySet: new Set(deny ?? []),
|
|
54173
|
+
slotsRef,
|
|
54174
|
+
navigateRef,
|
|
54175
|
+
notifyRef
|
|
54176
|
+
})
|
|
54177
|
+
});
|
|
54178
|
+
});
|
|
54179
|
+
const registrationReady = useMemo(() => {
|
|
54180
|
+
if (mockRuntime) return mockRuntime.register(plugin.schema);
|
|
54181
|
+
if (mode === "server" && transport) return transport.register(plugin.schema);
|
|
54182
|
+
return Promise.resolve();
|
|
54183
|
+
}, [mockRuntime, transport, mode, plugin.schema]);
|
|
54184
|
+
useEffect(() => {
|
|
54185
|
+
if (mode === "server" && !transport) {
|
|
54186
|
+
onError(plugin.id, 'OrbitalPluginHost: mode "server" requires a transport');
|
|
54187
|
+
}
|
|
54188
|
+
}, [mode, transport, plugin.id, onError]);
|
|
54189
|
+
const teardownTimerRef = useRef(null);
|
|
54190
|
+
useEffect(() => {
|
|
54191
|
+
if (teardownTimerRef.current !== null) {
|
|
54192
|
+
clearTimeout(teardownTimerRef.current);
|
|
54193
|
+
teardownTimerRef.current = null;
|
|
54194
|
+
}
|
|
54195
|
+
return () => {
|
|
54196
|
+
teardownTimerRef.current = setTimeout(() => {
|
|
54197
|
+
teardownTimerRef.current = null;
|
|
54198
|
+
if (mockRuntime) {
|
|
54199
|
+
mockRuntime.unregisterAll();
|
|
54200
|
+
} else if (mode === "server" && transport) {
|
|
54201
|
+
void transport.unregister();
|
|
54202
|
+
}
|
|
54203
|
+
}, 0);
|
|
54204
|
+
};
|
|
54205
|
+
}, [mockRuntime, transport, mode]);
|
|
54206
|
+
const ownOrbitals = useMemo(
|
|
54207
|
+
() => new Set(plugin.schema.orbitals.map((o) => o.name)),
|
|
54208
|
+
[plugin.schema]
|
|
54209
|
+
);
|
|
54210
|
+
const snapshotAllStates = useCallback(() => {
|
|
54211
|
+
const states = [];
|
|
54212
|
+
if (!mockRuntime) return states;
|
|
54213
|
+
for (const orbital of plugin.schema.orbitals) {
|
|
54214
|
+
for (const traitName of traitNamesOf(orbital)) {
|
|
54215
|
+
const raw = mockRuntime.getState(orbital.name, traitName);
|
|
54216
|
+
if (isTraitState(raw)) states.push([orbital.name, traitName, raw]);
|
|
54217
|
+
}
|
|
54218
|
+
}
|
|
54219
|
+
return states;
|
|
54220
|
+
}, [mockRuntime, plugin.schema]);
|
|
54221
|
+
useEffect(() => {
|
|
54222
|
+
let cancelled = false;
|
|
54223
|
+
void registrationReady.then(() => {
|
|
54224
|
+
if (cancelled || !mockRuntime) return;
|
|
54225
|
+
const states = snapshotAllStates();
|
|
54226
|
+
if (states.length > 0) onDispatched(plugin.id, null, states);
|
|
54227
|
+
}).catch((err) => {
|
|
54228
|
+
if (!cancelled) onError(plugin.id, err instanceof Error ? err.message : String(err));
|
|
54229
|
+
});
|
|
54230
|
+
return () => {
|
|
54231
|
+
cancelled = true;
|
|
54232
|
+
};
|
|
54233
|
+
}, [registrationReady, mockRuntime, plugin, onDispatched, onError, snapshotAllStates]);
|
|
54234
|
+
useEffect(() => {
|
|
54235
|
+
if (!mockRuntime) return;
|
|
54236
|
+
let timer = null;
|
|
54237
|
+
const scheduleResync = () => {
|
|
54238
|
+
if (timer !== null) return;
|
|
54239
|
+
timer = setTimeout(() => {
|
|
54240
|
+
timer = null;
|
|
54241
|
+
const states = snapshotAllStates();
|
|
54242
|
+
if (states.length > 0) onDispatched(plugin.id, null, states);
|
|
54243
|
+
}, 0);
|
|
54244
|
+
};
|
|
54245
|
+
const unsub = mockRuntime.getEventBus().onAny(scheduleResync);
|
|
54246
|
+
return () => {
|
|
54247
|
+
unsub();
|
|
54248
|
+
if (timer !== null) clearTimeout(timer);
|
|
54249
|
+
};
|
|
54250
|
+
}, [mockRuntime, plugin.id, onDispatched, snapshotAllStates]);
|
|
54251
|
+
const dispatch = useCallback(
|
|
54252
|
+
async (row, payload) => {
|
|
54253
|
+
await registrationReady;
|
|
54254
|
+
let response;
|
|
54255
|
+
if (mockRuntime) {
|
|
54256
|
+
response = await mockRuntime.processOrbitalEvent(row.orbital, {
|
|
54257
|
+
event: row.trigger,
|
|
54258
|
+
payload,
|
|
54259
|
+
targetTrait: row.trait
|
|
54260
|
+
});
|
|
54261
|
+
} else if (mode === "server" && transport) {
|
|
54262
|
+
response = await transport.sendEvent(row.orbital, row.trigger, payload);
|
|
54263
|
+
} else {
|
|
54264
|
+
return;
|
|
54265
|
+
}
|
|
54266
|
+
if (!response.success) {
|
|
54267
|
+
onError(plugin.id, response.error ?? `${plugin.id}: ${row.trigger} failed`);
|
|
54268
|
+
}
|
|
54269
|
+
for (const entry of response.emittedEvents ?? []) {
|
|
54270
|
+
if (entry.event === row.trigger) continue;
|
|
54271
|
+
const sourceOrbital = entry.source?.orbital;
|
|
54272
|
+
if (sourceOrbital !== void 0 && ownOrbitals.has(sourceOrbital)) {
|
|
54273
|
+
bus.emit(`UI:${entry.event}`, entry.payload, entry.source);
|
|
54274
|
+
}
|
|
54275
|
+
}
|
|
54276
|
+
const states = [];
|
|
54277
|
+
for (const [traitName, stateName] of Object.entries(response.states)) {
|
|
54278
|
+
states.push([row.orbital, traitName, toTraitState(mockRuntime, row.orbital, traitName, stateName, row.trigger)]);
|
|
54279
|
+
onTransition?.(plugin.id, row.orbital, traitName, stateName);
|
|
54280
|
+
}
|
|
54281
|
+
onDispatched(plugin.id, row.trigger, states);
|
|
54282
|
+
},
|
|
54283
|
+
[registrationReady, mockRuntime, mode, transport, plugin, bus, ownOrbitals, onError, onTransition, onDispatched]
|
|
54284
|
+
);
|
|
54285
|
+
useEffect(() => {
|
|
54286
|
+
const unsubs = plugin.inbound.map(
|
|
54287
|
+
(row) => bus.on(`UI:${row.busEvent}`, (event) => {
|
|
54288
|
+
void dispatch(row, event.payload).catch((err) => {
|
|
54289
|
+
onError(plugin.id, err instanceof Error ? err.message : String(err));
|
|
54290
|
+
});
|
|
54291
|
+
})
|
|
54292
|
+
);
|
|
54293
|
+
return () => {
|
|
54294
|
+
for (const unsub of unsubs) unsub();
|
|
54295
|
+
};
|
|
54296
|
+
}, [bus, plugin, dispatch, onError]);
|
|
54297
|
+
return null;
|
|
54298
|
+
}
|
|
54299
|
+
function OrbitalPluginHost({
|
|
54300
|
+
plugins,
|
|
54301
|
+
mode = "mock",
|
|
54302
|
+
transport,
|
|
54303
|
+
deny,
|
|
54304
|
+
navigate,
|
|
54305
|
+
notify,
|
|
54306
|
+
onTransition,
|
|
54307
|
+
children
|
|
54308
|
+
}) {
|
|
54309
|
+
const [tick, setTick] = useState(0);
|
|
54310
|
+
const [errors, setErrors] = useState({});
|
|
54311
|
+
const statesRef = useRef(/* @__PURE__ */ new Map());
|
|
54312
|
+
const lastEventRef = useRef(/* @__PURE__ */ new Map());
|
|
54313
|
+
const pluginSchemas = useMemo(
|
|
54314
|
+
() => new Map(plugins.map((plugin) => [plugin.id, plugin.schema])),
|
|
54315
|
+
[plugins]
|
|
54316
|
+
);
|
|
54317
|
+
const handleDispatched = useCallback(
|
|
54318
|
+
(pluginId, trigger, states) => {
|
|
54319
|
+
if (trigger !== null) {
|
|
54320
|
+
lastEventRef.current.set(pluginId, trigger);
|
|
54321
|
+
}
|
|
54322
|
+
let byKey = statesRef.current.get(pluginId);
|
|
54323
|
+
if (!byKey) {
|
|
54324
|
+
byKey = /* @__PURE__ */ new Map();
|
|
54325
|
+
statesRef.current.set(pluginId, byKey);
|
|
54326
|
+
}
|
|
54327
|
+
for (const [orbital, trait, state] of states) {
|
|
54328
|
+
byKey.set(`${orbital}.${trait}`, state);
|
|
54329
|
+
}
|
|
54330
|
+
setTick((t) => t + 1);
|
|
54331
|
+
},
|
|
54332
|
+
[]
|
|
54333
|
+
);
|
|
54334
|
+
const handleError = useCallback((pluginId, message) => {
|
|
54335
|
+
setErrors((prev) => prev[pluginId] === message ? prev : { ...prev, [pluginId]: message });
|
|
54336
|
+
}, []);
|
|
54337
|
+
const contextValue = useMemo(
|
|
54338
|
+
() => ({
|
|
54339
|
+
getState: (pluginId, orbital, trait) => statesRef.current.get(pluginId)?.get(`${orbital}.${trait}`),
|
|
54340
|
+
lastEvent: (pluginId) => lastEventRef.current.get(pluginId),
|
|
54341
|
+
errors,
|
|
54342
|
+
tick,
|
|
54343
|
+
getPluginSchema: (pluginId) => pluginSchemas.get(pluginId)
|
|
54344
|
+
}),
|
|
54345
|
+
[errors, tick, pluginSchemas]
|
|
54346
|
+
);
|
|
54347
|
+
return /* @__PURE__ */ jsxs(OrbitalPluginHostContext.Provider, { value: contextValue, children: [
|
|
54348
|
+
plugins.map((plugin) => /* @__PURE__ */ jsx(
|
|
54349
|
+
PluginRuntimeMount,
|
|
54350
|
+
{
|
|
54351
|
+
plugin,
|
|
54352
|
+
mode,
|
|
54353
|
+
transport,
|
|
54354
|
+
deny,
|
|
54355
|
+
navigate,
|
|
54356
|
+
notify,
|
|
54357
|
+
onTransition,
|
|
54358
|
+
onDispatched: handleDispatched,
|
|
54359
|
+
onError: handleError
|
|
54360
|
+
},
|
|
54361
|
+
plugin.id
|
|
54362
|
+
)),
|
|
54363
|
+
children ?? null
|
|
54364
|
+
] });
|
|
54365
|
+
}
|
|
54366
|
+
|
|
54367
|
+
// types/slot-host.ts
|
|
54368
|
+
function assertUniqueSlotsPerHost(manifest) {
|
|
54369
|
+
const seenBySlot = /* @__PURE__ */ new Map();
|
|
54370
|
+
for (const [regionId, region] of Object.entries(manifest.regions)) {
|
|
54371
|
+
const existingRegionId = seenBySlot.get(region.slot);
|
|
54372
|
+
if (existingRegionId !== void 0) {
|
|
54373
|
+
throw new Error(
|
|
54374
|
+
`assertUniqueSlotsPerHost: regions "${existingRegionId}" and "${regionId}" both bind slot "${region.slot}" \u2014 a slot may be mounted by only one region per host.`
|
|
54375
|
+
);
|
|
54376
|
+
}
|
|
54377
|
+
seenBySlot.set(region.slot, regionId);
|
|
54378
|
+
}
|
|
54379
|
+
}
|
|
54380
|
+
|
|
52987
54381
|
// runtime/index.ts
|
|
52988
54382
|
init_perf();
|
|
52989
54383
|
|
|
52990
|
-
export { BrowserPlayground, OrbPreview, clearSchemaCache, createClientEffectHandlers, profilerOnRender, usePerfBuffer, useResolvedSchema, useTraitStateMachine };
|
|
54384
|
+
export { BrowserPlayground, OrbPreview, OrbitalPluginHost, assertUniqueSlotsPerHost, clearSchemaCache, createClientEffectHandlers, profilerOnRender, useDeclaredCaptureTable, useOrbitalPluginHost, usePerfBuffer, useResolvedSchema, useTraitStateMachine };
|