@almadar/ui 6.5.0 → 6.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{EntityBindingContext-0Evn_LcT.d.cts → EntityBindingContext-Bn3ePJQC.d.cts} +1 -1
- package/dist/{EntityBindingContext-0Evn_LcT.d.ts → EntityBindingContext-Bn3ePJQC.d.ts} +1 -1
- package/dist/{GameAudioProvider-B48iXwz3.d.ts → GameAudioProvider-Ctceau1s.d.ts} +6 -18
- package/dist/{GameAudioProvider-CQAdPreB.d.cts → GameAudioProvider-Dk_Y3LN3.d.cts} +6 -18
- package/dist/avl/index.cjs +1216 -206
- package/dist/avl/index.js +1217 -207
- package/dist/{avl-schema-parser-dvJKXn-o.d.ts → avl-schema-parser-DncJLEn9.d.ts} +18 -0
- package/dist/{avl-schema-parser-Bt4NzAam.d.cts → avl-schema-parser-FQ1474v8.d.cts} +18 -0
- package/dist/{cn-DJSBBm5W.d.cts → cn-CCjFspAo.d.cts} +25 -1
- package/dist/{cn-BbhJq_nr.d.ts → cn-sgpqpN0U.d.ts} +25 -1
- package/dist/components/index.cjs +1025 -235
- package/dist/components/index.d.cts +176 -23
- package/dist/components/index.d.ts +176 -23
- package/dist/components/index.js +1023 -236
- package/dist/context/index.cjs +10 -13
- package/dist/context/index.js +10 -13
- package/dist/hooks/index.cjs +63 -13
- package/dist/hooks/index.d.cts +2 -2
- package/dist/hooks/index.d.ts +2 -2
- package/dist/hooks/index.js +64 -15
- package/dist/lib/drawable/three/index.cjs +126 -13
- package/dist/lib/drawable/three/index.d.cts +2 -2
- package/dist/lib/drawable/three/index.d.ts +2 -2
- package/dist/lib/drawable/three/index.js +127 -14
- package/dist/lib/index.cjs +180 -0
- package/dist/lib/index.d.cts +1 -1
- package/dist/lib/index.d.ts +1 -1
- package/dist/lib/index.js +178 -1
- package/dist/locales/index.cjs +6 -3
- package/dist/locales/index.js +6 -3
- package/dist/providers/index.cjs +922 -188
- package/dist/providers/index.d.cts +2 -2
- package/dist/providers/index.d.ts +2 -2
- package/dist/providers/index.js +922 -188
- package/dist/runtime/index.cjs +1505 -182
- package/dist/runtime/index.d.cts +78 -6
- package/dist/runtime/index.d.ts +78 -6
- package/dist/runtime/index.js +1504 -184
- package/dist/{useEventBus-CQWyAWpK.d.ts → useKeyboardRouter-D84cNWmA.d.ts} +31 -1
- package/dist/{useEventBus-Ckr4wqW3.d.cts → useKeyboardRouter-DQEE_9mq.d.cts} +31 -1
- package/locales/ar.json +2 -1
- package/locales/en.json +2 -1
- package/locales/sl.json +2 -1
- package/package.json +4 -4
- package/themes/comic.css +437 -0
- package/themes/index.css +1 -0
package/dist/avl/index.cjs
CHANGED
|
@@ -13787,7 +13787,7 @@ function collectDrawnItems(nodes) {
|
|
|
13787
13787
|
for (const n of nodes) {
|
|
13788
13788
|
switch (n.type) {
|
|
13789
13789
|
case "draw-sprite":
|
|
13790
|
-
if (isValidScenePos(n.position)) out.push({ pos: n.position, id: n.id, anchor: n.anchor, width: n.width, height: n.height });
|
|
13790
|
+
if (isValidScenePos(n.position)) out.push({ pos: n.position, id: n.id, anchor: n.anchor, width: n.width, height: n.height, rotation: n.rotation });
|
|
13791
13791
|
break;
|
|
13792
13792
|
case "draw-shape":
|
|
13793
13793
|
case "draw-text":
|
|
@@ -13797,7 +13797,7 @@ function collectDrawnItems(nodes) {
|
|
|
13797
13797
|
break;
|
|
13798
13798
|
case "draw-sprite-layer":
|
|
13799
13799
|
for (const it of n.items) {
|
|
13800
|
-
if (isValidScenePos(it.position)) out.push({ pos: it.position, id: it.id, anchor: it.anchor, width: it.width, height: it.height });
|
|
13800
|
+
if (isValidScenePos(it.position)) out.push({ pos: it.position, id: it.id, anchor: it.anchor, width: it.width, height: it.height, rotation: it.rotation });
|
|
13801
13801
|
}
|
|
13802
13802
|
break;
|
|
13803
13803
|
case "draw-shape-layer":
|
|
@@ -13817,15 +13817,39 @@ function buildHitIndex(items) {
|
|
|
13817
13817
|
}
|
|
13818
13818
|
return m;
|
|
13819
13819
|
}
|
|
13820
|
+
function withPreviewPosition(nodes, id, pos) {
|
|
13821
|
+
return nodes.map((n) => {
|
|
13822
|
+
if (n.type === "draw-sprite-layer" || n.type === "draw-shape-layer" || n.type === "draw-text-layer") {
|
|
13823
|
+
if (!n.items.some((it) => it.id === id)) return n;
|
|
13824
|
+
return { ...n, items: n.items.map((it) => it.id === id ? { ...it, position: pos } : it) };
|
|
13825
|
+
}
|
|
13826
|
+
if (n.type === "draw-group") {
|
|
13827
|
+
if (!Array.isArray(n.items)) return n;
|
|
13828
|
+
return { ...n, items: withPreviewPosition(n.items, id, pos) };
|
|
13829
|
+
}
|
|
13830
|
+
if (n.id === id && "position" in n) {
|
|
13831
|
+
return { ...n, position: pos };
|
|
13832
|
+
}
|
|
13833
|
+
return n;
|
|
13834
|
+
});
|
|
13835
|
+
}
|
|
13820
13836
|
function hitTestSprites(items, projector, point) {
|
|
13821
13837
|
for (let i = items.length - 1; i >= 0; i--) {
|
|
13822
13838
|
const it = items[i];
|
|
13823
13839
|
if (it.id === void 0) continue;
|
|
13824
13840
|
const r2 = spriteRect(projector, { position: it.pos, anchor: it.anchor, width: it.width, height: it.height });
|
|
13825
|
-
|
|
13841
|
+
const test = it.rotation ? rotatePoint(point, { x: r2.x + r2.w / 2, y: r2.y + r2.h / 2 }, -it.rotation) : point;
|
|
13842
|
+
if (test.x >= r2.x && test.x <= r2.x + r2.w && test.y >= r2.y && test.y <= r2.y + r2.h) return it.id;
|
|
13826
13843
|
}
|
|
13827
13844
|
return void 0;
|
|
13828
13845
|
}
|
|
13846
|
+
function rotatePoint(p, center, radians) {
|
|
13847
|
+
const cos = Math.cos(radians);
|
|
13848
|
+
const sin = Math.sin(radians);
|
|
13849
|
+
const dx = p.x - center.x;
|
|
13850
|
+
const dy = p.y - center.y;
|
|
13851
|
+
return { x: center.x + dx * cos - dy * sin, y: center.y + dx * sin + dy * cos };
|
|
13852
|
+
}
|
|
13829
13853
|
var init_hitTest = __esm({
|
|
13830
13854
|
"lib/drawable/hitTest.ts"() {
|
|
13831
13855
|
init_contract();
|
|
@@ -13834,6 +13858,41 @@ var init_hitTest = __esm({
|
|
|
13834
13858
|
function normalizeBackdrop(bg) {
|
|
13835
13859
|
return typeof bg === "string" ? { url: bg, role: "decoration", category: "background" } : bg;
|
|
13836
13860
|
}
|
|
13861
|
+
function selectionOverlayNodes(projector, item) {
|
|
13862
|
+
const r2 = spriteRect(projector, { position: item.pos, anchor: item.anchor, width: item.width, height: item.height });
|
|
13863
|
+
const tw = projector.tileWidth;
|
|
13864
|
+
const cellTopLeft = projector.anchorPoint(item.pos, "top-left");
|
|
13865
|
+
const offsetX = (r2.x - cellTopLeft.x) / tw;
|
|
13866
|
+
const offsetY = (r2.y - cellTopLeft.y) / tw;
|
|
13867
|
+
const width = r2.w / tw;
|
|
13868
|
+
const height = r2.h / tw;
|
|
13869
|
+
const handle = EDIT_HANDLE_SIZE_PX / tw;
|
|
13870
|
+
const ring = {
|
|
13871
|
+
type: "draw-shape",
|
|
13872
|
+
shape: "rect",
|
|
13873
|
+
position: item.pos,
|
|
13874
|
+
anchor: "top-left",
|
|
13875
|
+
offsetX,
|
|
13876
|
+
offsetY,
|
|
13877
|
+
width,
|
|
13878
|
+
height,
|
|
13879
|
+
stroke: EDIT_SELECTION_COLOR,
|
|
13880
|
+
strokeWidth: 2,
|
|
13881
|
+
fill: "none"
|
|
13882
|
+
};
|
|
13883
|
+
const handles = EDIT_SELECTION_CORNERS.map(([cx, cy]) => ({
|
|
13884
|
+
type: "draw-shape",
|
|
13885
|
+
shape: "rect",
|
|
13886
|
+
position: item.pos,
|
|
13887
|
+
anchor: "top-left",
|
|
13888
|
+
offsetX: offsetX + cx * width - handle / 2,
|
|
13889
|
+
offsetY: offsetY + cy * height - handle / 2,
|
|
13890
|
+
width: handle,
|
|
13891
|
+
height: handle,
|
|
13892
|
+
fill: EDIT_SELECTION_COLOR
|
|
13893
|
+
}));
|
|
13894
|
+
return [ring, ...handles];
|
|
13895
|
+
}
|
|
13837
13896
|
function Canvas2D({
|
|
13838
13897
|
className,
|
|
13839
13898
|
isLoading = false,
|
|
@@ -13847,6 +13906,12 @@ function Canvas2D({
|
|
|
13847
13906
|
tileLeaveEvent,
|
|
13848
13907
|
keyMap,
|
|
13849
13908
|
keyUpMap,
|
|
13909
|
+
editable = false,
|
|
13910
|
+
selectedId = null,
|
|
13911
|
+
onSelect,
|
|
13912
|
+
onMove,
|
|
13913
|
+
selectEvent,
|
|
13914
|
+
moveEvent,
|
|
13850
13915
|
camera = "pan-zoom",
|
|
13851
13916
|
scale = 0.4,
|
|
13852
13917
|
tileWidth,
|
|
@@ -14104,10 +14169,22 @@ function Canvas2D({
|
|
|
14104
14169
|
painter.scale(cam.zoom, cam.zoom);
|
|
14105
14170
|
painter.translate(-viewportSize.width / 2 - cam.x, -viewportSize.height / 2 - cam.y);
|
|
14106
14171
|
const dctx = { projector, time: timeMs, invalidate: bumpAtlas };
|
|
14107
|
-
|
|
14172
|
+
let paintNodes = drawables;
|
|
14173
|
+
if (editable) {
|
|
14174
|
+
const drag = editDragRef.current;
|
|
14175
|
+
if (drag && drag.moved) {
|
|
14176
|
+
paintNodes = withPreviewPosition(paintNodes, drag.id, { x: drag.previewX, y: drag.previewY });
|
|
14177
|
+
}
|
|
14178
|
+
const selectedItem = selectedId != null ? drawnItems.find((it) => it.id === selectedId) : void 0;
|
|
14179
|
+
if (selectedItem) {
|
|
14180
|
+
const overlaySource = drag && drag.moved && drag.id === selectedId ? { ...selectedItem, pos: { x: drag.previewX, y: drag.previewY } } : selectedItem;
|
|
14181
|
+
paintNodes = [...paintNodes, ...selectionOverlayNodes(projector, overlaySource)];
|
|
14182
|
+
}
|
|
14183
|
+
}
|
|
14184
|
+
for (const node of paintNodes) paintDrawable(painter, node, dctx);
|
|
14108
14185
|
painter.restore();
|
|
14109
|
-
scheduleAnimation(
|
|
14110
|
-
}, [viewportSize, backgroundImage, bgColor, drawables, projector, cameraRef, bumpAtlas, getImage, cameraPos, defaultGridFocus, camera, dragDistance]);
|
|
14186
|
+
scheduleAnimation(paintNodes);
|
|
14187
|
+
}, [viewportSize, backgroundImage, bgColor, drawables, projector, cameraRef, bumpAtlas, getImage, cameraPos, defaultGridFocus, camera, dragDistance, editable, selectedId, drawnItems]);
|
|
14111
14188
|
React96.useEffect(() => {
|
|
14112
14189
|
drawTimeRef.current = draw;
|
|
14113
14190
|
}, [draw]);
|
|
@@ -14152,23 +14229,83 @@ function Canvas2D({
|
|
|
14152
14229
|
};
|
|
14153
14230
|
}, [camera, followTarget, lerpToTarget, draw]);
|
|
14154
14231
|
const singlePointerActiveRef = React96.useRef(false);
|
|
14232
|
+
const editDragRef = React96.useRef(null);
|
|
14233
|
+
const pointerToScene = React96.useCallback((clientX, clientY) => {
|
|
14234
|
+
const canvas = canvasRef.current;
|
|
14235
|
+
if (!canvas) return { x: 0, y: 0 };
|
|
14236
|
+
const world = screenToWorld(clientX, clientY, canvas, viewportSize);
|
|
14237
|
+
const adjustedX = world.x - scaledTileWidth / 2;
|
|
14238
|
+
const adjustedY = squareGrid ? world.y - scaledTileWidth / 2 : world.y - scaledDiamondTopY - scaledFloorHeight / 2;
|
|
14239
|
+
return unproject(adjustedX, adjustedY);
|
|
14240
|
+
}, [screenToWorld, viewportSize, scaledTileWidth, squareGrid, scaledDiamondTopY, scaledFloorHeight, unproject]);
|
|
14155
14241
|
const handleCanvasPointerDown = React96.useCallback((e) => {
|
|
14156
14242
|
singlePointerActiveRef.current = true;
|
|
14243
|
+
if (editable) {
|
|
14244
|
+
if (!canvasRef.current) return;
|
|
14245
|
+
const world = screenToWorld(e.clientX, e.clientY, canvasRef.current, viewportSize);
|
|
14246
|
+
const hitId = hitTestSprites(drawnItems, projector, world);
|
|
14247
|
+
if (hitId === void 0) return;
|
|
14248
|
+
const item = [...drawnItems].reverse().find((it) => it.id === hitId);
|
|
14249
|
+
if (!item) return;
|
|
14250
|
+
editDragRef.current = {
|
|
14251
|
+
id: hitId,
|
|
14252
|
+
pointerId: e.pointerId,
|
|
14253
|
+
startClientX: e.clientX,
|
|
14254
|
+
startClientY: e.clientY,
|
|
14255
|
+
startSceneX: item.pos.x,
|
|
14256
|
+
startSceneY: item.pos.y,
|
|
14257
|
+
moved: false,
|
|
14258
|
+
previewX: item.pos.x,
|
|
14259
|
+
previewY: item.pos.y
|
|
14260
|
+
};
|
|
14261
|
+
return;
|
|
14262
|
+
}
|
|
14157
14263
|
if (enableCamera) handlePointerDown(e);
|
|
14158
|
-
}, [enableCamera, handlePointerDown]);
|
|
14264
|
+
}, [editable, screenToWorld, viewportSize, drawnItems, projector, enableCamera, handlePointerDown]);
|
|
14159
14265
|
const handleCanvasPointerMove = React96.useCallback((e) => {
|
|
14266
|
+
if (editable) {
|
|
14267
|
+
const drag = editDragRef.current;
|
|
14268
|
+
if (!drag || drag.pointerId !== e.pointerId) return;
|
|
14269
|
+
const dxPx = e.clientX - drag.startClientX;
|
|
14270
|
+
const dyPx = e.clientY - drag.startClientY;
|
|
14271
|
+
if (!drag.moved && Math.abs(dxPx) + Math.abs(dyPx) <= 5) return;
|
|
14272
|
+
drag.moved = true;
|
|
14273
|
+
const nowScene = pointerToScene(e.clientX, e.clientY);
|
|
14274
|
+
const startScene = pointerToScene(drag.startClientX, drag.startClientY);
|
|
14275
|
+
drag.previewX = drag.startSceneX + (nowScene.x - startScene.x);
|
|
14276
|
+
drag.previewY = drag.startSceneY + (nowScene.y - startScene.y);
|
|
14277
|
+
draw();
|
|
14278
|
+
return;
|
|
14279
|
+
}
|
|
14160
14280
|
if (enableCamera) handlePointerMove(e, () => draw());
|
|
14161
|
-
}, [
|
|
14281
|
+
}, [editable, pointerToScene, draw, enableCamera, handlePointerMove]);
|
|
14162
14282
|
const handleCanvasHover = React96.useCallback((e) => {
|
|
14163
14283
|
if (singlePointerActiveRef.current) return;
|
|
14164
14284
|
if (!tileHoverEvent || !canvasRef.current) return;
|
|
14165
|
-
const
|
|
14166
|
-
const adjustedX = world.x - scaledTileWidth / 2;
|
|
14167
|
-
const adjustedY = squareGrid ? world.y - scaledTileWidth / 2 : world.y - scaledDiamondTopY - scaledFloorHeight / 2;
|
|
14168
|
-
const isoPos = unproject(adjustedX, adjustedY);
|
|
14285
|
+
const isoPos = pointerToScene(e.clientX, e.clientY);
|
|
14169
14286
|
eventBus.emit(`UI:${tileHoverEvent}`, { x: isoPos.x, y: isoPos.y });
|
|
14170
|
-
}, [
|
|
14287
|
+
}, [pointerToScene, tileHoverEvent, eventBus]);
|
|
14171
14288
|
const handleCanvasPointerUp = React96.useCallback((e) => {
|
|
14289
|
+
if (editable) {
|
|
14290
|
+
singlePointerActiveRef.current = false;
|
|
14291
|
+
const drag = editDragRef.current;
|
|
14292
|
+
if (drag && drag.pointerId === e.pointerId) {
|
|
14293
|
+
editDragRef.current = null;
|
|
14294
|
+
if (drag.moved) {
|
|
14295
|
+
onMove?.(drag.id, drag.previewX, drag.previewY);
|
|
14296
|
+
if (moveEvent) eventBus.emit(`UI:${moveEvent}`, { id: drag.id, x: drag.previewX, y: drag.previewY });
|
|
14297
|
+
draw();
|
|
14298
|
+
return;
|
|
14299
|
+
}
|
|
14300
|
+
const next = selectedId === drag.id ? null : drag.id;
|
|
14301
|
+
onSelect?.(next);
|
|
14302
|
+
if (selectEvent) eventBus.emit(`UI:${selectEvent}`, { id: next });
|
|
14303
|
+
return;
|
|
14304
|
+
}
|
|
14305
|
+
onSelect?.(null);
|
|
14306
|
+
if (selectEvent) eventBus.emit(`UI:${selectEvent}`, { id: null });
|
|
14307
|
+
return;
|
|
14308
|
+
}
|
|
14172
14309
|
singlePointerActiveRef.current = false;
|
|
14173
14310
|
if (enableCamera) handlePointerUp();
|
|
14174
14311
|
if (dragDistance() > 5) return;
|
|
@@ -14179,16 +14316,14 @@ function Canvas2D({
|
|
|
14179
14316
|
eventBus.emit(`UI:${unitClickEvent}`, { unitId: spriteHit });
|
|
14180
14317
|
return;
|
|
14181
14318
|
}
|
|
14182
|
-
const
|
|
14183
|
-
const adjustedY = squareGrid ? world.y - scaledTileWidth / 2 : world.y - scaledDiamondTopY - scaledFloorHeight / 2;
|
|
14184
|
-
const isoPos = unproject(adjustedX, adjustedY);
|
|
14319
|
+
const isoPos = pointerToScene(e.clientX, e.clientY);
|
|
14185
14320
|
const hitId = hitIndex.get(`${isoPos.x},${isoPos.y}`);
|
|
14186
14321
|
if (hitId !== void 0 && unitClickEvent) {
|
|
14187
14322
|
eventBus.emit(`UI:${unitClickEvent}`, { unitId: hitId });
|
|
14188
14323
|
} else if (tileClickEvent) {
|
|
14189
14324
|
eventBus.emit(`UI:${tileClickEvent}`, { x: isoPos.x, y: isoPos.y });
|
|
14190
14325
|
}
|
|
14191
|
-
}, [
|
|
14326
|
+
}, [editable, selectedId, onMove, moveEvent, onSelect, selectEvent, eventBus, draw, enableCamera, handlePointerUp, dragDistance, screenToWorld, viewportSize, drawnItems, projector, tileClickEvent, unitClickEvent, hitIndex, pointerToScene]);
|
|
14192
14327
|
const handleCanvasPointerLeave = React96.useCallback(() => {
|
|
14193
14328
|
handleMouseLeave();
|
|
14194
14329
|
if (tileLeaveEvent) eventBus.emit(`UI:${tileLeaveEvent}`, {});
|
|
@@ -14207,7 +14342,7 @@ function Canvas2D({
|
|
|
14207
14342
|
}, [enableCamera, handlePointerUp]);
|
|
14208
14343
|
const gestureHandlers = useCanvasGestures({
|
|
14209
14344
|
canvasRef,
|
|
14210
|
-
enabled: enableCamera || !!tileHoverEvent || !!tileClickEvent || !!unitClickEvent,
|
|
14345
|
+
enabled: enableCamera || !!tileHoverEvent || !!tileClickEvent || !!unitClickEvent || editable,
|
|
14211
14346
|
onPointerDown: handleCanvasPointerDown,
|
|
14212
14347
|
onPointerMove: handleCanvasPointerMove,
|
|
14213
14348
|
onPointerUp: handleCanvasPointerUp,
|
|
@@ -14339,7 +14474,7 @@ function Canvas2D({
|
|
|
14339
14474
|
}
|
|
14340
14475
|
) });
|
|
14341
14476
|
}
|
|
14342
|
-
var canvas2DLog;
|
|
14477
|
+
var canvas2DLog, EDIT_SELECTION_COLOR, EDIT_HANDLE_SIZE_PX, EDIT_SELECTION_CORNERS;
|
|
14343
14478
|
var init_Canvas2D = __esm({
|
|
14344
14479
|
"components/game/molecules/Canvas2D.tsx"() {
|
|
14345
14480
|
"use client";
|
|
@@ -14364,8 +14499,12 @@ var init_Canvas2D = __esm({
|
|
|
14364
14499
|
init_DrawGroup();
|
|
14365
14500
|
init_registry();
|
|
14366
14501
|
init_hitTest();
|
|
14502
|
+
init_contract();
|
|
14367
14503
|
init_isometric();
|
|
14368
14504
|
canvas2DLog = logger.createLogger("almadar:ui:game-canvas");
|
|
14505
|
+
EDIT_SELECTION_COLOR = "#3b82f6";
|
|
14506
|
+
EDIT_HANDLE_SIZE_PX = 8;
|
|
14507
|
+
EDIT_SELECTION_CORNERS = [[0, 0], [1, 0], [0, 1], [1, 1]];
|
|
14369
14508
|
Canvas2D.displayName = "Canvas2D";
|
|
14370
14509
|
}
|
|
14371
14510
|
});
|
|
@@ -14407,6 +14546,12 @@ function Canvas({
|
|
|
14407
14546
|
featureClickEvent,
|
|
14408
14547
|
keyMap,
|
|
14409
14548
|
keyUpMap,
|
|
14549
|
+
editable,
|
|
14550
|
+
selectedId,
|
|
14551
|
+
onSelect,
|
|
14552
|
+
onMove,
|
|
14553
|
+
selectEvent,
|
|
14554
|
+
moveEvent,
|
|
14410
14555
|
children
|
|
14411
14556
|
}) {
|
|
14412
14557
|
canvasLog.debug("Canvas render", { mode, drawablesCount: drawables?.length, projection, camera: camera ? JSON.stringify(camera) : void 0 });
|
|
@@ -14484,6 +14629,12 @@ function Canvas({
|
|
|
14484
14629
|
tileLeaveEvent,
|
|
14485
14630
|
keyMap,
|
|
14486
14631
|
keyUpMap,
|
|
14632
|
+
editable,
|
|
14633
|
+
selectedId,
|
|
14634
|
+
onSelect,
|
|
14635
|
+
onMove,
|
|
14636
|
+
selectEvent,
|
|
14637
|
+
moveEvent,
|
|
14487
14638
|
...children !== void 0 ? { children } : {}
|
|
14488
14639
|
}
|
|
14489
14640
|
);
|
|
@@ -14505,15 +14656,19 @@ function GameAudioToggle({
|
|
|
14505
14656
|
size = "sm",
|
|
14506
14657
|
className,
|
|
14507
14658
|
onAsset,
|
|
14508
|
-
offAsset
|
|
14659
|
+
offAsset,
|
|
14660
|
+
toggleEvent
|
|
14509
14661
|
}) {
|
|
14510
14662
|
const ctx = providers.useGameAudioContextOptional();
|
|
14511
14663
|
const [localMuted, setLocalMuted] = React96.useState(false);
|
|
14512
14664
|
const muted = ctx ? ctx.muted : localMuted;
|
|
14513
14665
|
const setMuted = ctx ? ctx.setMuted : setLocalMuted;
|
|
14666
|
+
const eventBus = useEventBus();
|
|
14514
14667
|
const handleToggle = React96.useCallback(() => {
|
|
14515
|
-
|
|
14516
|
-
|
|
14668
|
+
const next = !muted;
|
|
14669
|
+
setMuted(next);
|
|
14670
|
+
if (toggleEvent) eventBus.emit(`UI:${toggleEvent}`, { muted: next });
|
|
14671
|
+
}, [muted, setMuted, toggleEvent, eventBus]);
|
|
14517
14672
|
const activeAsset = muted ? offAsset : onAsset;
|
|
14518
14673
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
14519
14674
|
Button,
|
|
@@ -14532,10 +14687,319 @@ var init_GameAudioToggle = __esm({
|
|
|
14532
14687
|
"use client";
|
|
14533
14688
|
init_atoms();
|
|
14534
14689
|
init_cn();
|
|
14690
|
+
init_useEventBus();
|
|
14535
14691
|
init_GameIcon();
|
|
14536
14692
|
GameAudioToggle.displayName = "GameAudioToggle";
|
|
14537
14693
|
}
|
|
14538
14694
|
});
|
|
14695
|
+
function pickPath(entry) {
|
|
14696
|
+
if (Array.isArray(entry.path)) {
|
|
14697
|
+
return entry.path[Math.floor(Math.random() * entry.path.length)];
|
|
14698
|
+
}
|
|
14699
|
+
return entry.path;
|
|
14700
|
+
}
|
|
14701
|
+
function useGameAudio({
|
|
14702
|
+
manifest,
|
|
14703
|
+
baseUrl = "",
|
|
14704
|
+
initialMuted = false,
|
|
14705
|
+
initialVolume = 1
|
|
14706
|
+
}) {
|
|
14707
|
+
const [muted, setMutedState] = React96.useState(initialMuted);
|
|
14708
|
+
const [masterVolume, setMasterVolumeState] = React96.useState(initialVolume);
|
|
14709
|
+
const mutedRef = React96.useRef(muted);
|
|
14710
|
+
const volumeRef = React96.useRef(masterVolume);
|
|
14711
|
+
const manifestRef = React96.useRef(manifest);
|
|
14712
|
+
mutedRef.current = muted;
|
|
14713
|
+
volumeRef.current = masterVolume;
|
|
14714
|
+
manifestRef.current = manifest;
|
|
14715
|
+
const poolsRef = React96.useRef(/* @__PURE__ */ new Map());
|
|
14716
|
+
const getOrCreateElement = React96.useCallback((key) => {
|
|
14717
|
+
const entry = manifestRef.current[key];
|
|
14718
|
+
if (!entry) return null;
|
|
14719
|
+
let pool = poolsRef.current.get(key);
|
|
14720
|
+
if (!pool) {
|
|
14721
|
+
pool = [];
|
|
14722
|
+
poolsRef.current.set(key, pool);
|
|
14723
|
+
}
|
|
14724
|
+
const maxSize = entry.poolSize ?? 1;
|
|
14725
|
+
for (const audio of pool) {
|
|
14726
|
+
if (audio.paused && (audio.ended || audio.currentTime === 0)) {
|
|
14727
|
+
return audio;
|
|
14728
|
+
}
|
|
14729
|
+
}
|
|
14730
|
+
if (pool.length < maxSize) {
|
|
14731
|
+
const src = baseUrl + pickPath(entry);
|
|
14732
|
+
const audio = new Audio(src);
|
|
14733
|
+
audio.loop = entry.loop ?? false;
|
|
14734
|
+
pool.push(audio);
|
|
14735
|
+
return audio;
|
|
14736
|
+
}
|
|
14737
|
+
if (!entry.loop) {
|
|
14738
|
+
let oldest = pool[0];
|
|
14739
|
+
for (const audio of pool) {
|
|
14740
|
+
if (audio.currentTime > oldest.currentTime) {
|
|
14741
|
+
oldest = audio;
|
|
14742
|
+
}
|
|
14743
|
+
}
|
|
14744
|
+
oldest.pause();
|
|
14745
|
+
oldest.currentTime = 0;
|
|
14746
|
+
return oldest;
|
|
14747
|
+
}
|
|
14748
|
+
return null;
|
|
14749
|
+
}, [baseUrl]);
|
|
14750
|
+
const play = React96.useCallback((key) => {
|
|
14751
|
+
if (mutedRef.current) return;
|
|
14752
|
+
const entry = manifestRef.current[key];
|
|
14753
|
+
if (!entry) return;
|
|
14754
|
+
const audio = getOrCreateElement(key);
|
|
14755
|
+
if (!audio) return;
|
|
14756
|
+
audio.volume = Math.min(1, (entry.volume ?? 1) * volumeRef.current);
|
|
14757
|
+
if (!entry.loop) {
|
|
14758
|
+
audio.currentTime = 0;
|
|
14759
|
+
}
|
|
14760
|
+
const promise = audio.play();
|
|
14761
|
+
if (promise) {
|
|
14762
|
+
promise.catch(() => {
|
|
14763
|
+
});
|
|
14764
|
+
}
|
|
14765
|
+
}, [getOrCreateElement]);
|
|
14766
|
+
const stop = React96.useCallback((key) => {
|
|
14767
|
+
const pool = poolsRef.current.get(key);
|
|
14768
|
+
if (!pool) return;
|
|
14769
|
+
for (const audio of pool) {
|
|
14770
|
+
audio.pause();
|
|
14771
|
+
audio.currentTime = 0;
|
|
14772
|
+
}
|
|
14773
|
+
}, []);
|
|
14774
|
+
const currentMusicKeyRef = React96.useRef(null);
|
|
14775
|
+
const currentMusicElRef = React96.useRef(null);
|
|
14776
|
+
const musicFadeRef = React96.useRef(null);
|
|
14777
|
+
const pendingMusicKeyRef = React96.useRef(null);
|
|
14778
|
+
const clearMusicFade = React96.useCallback(() => {
|
|
14779
|
+
if (musicFadeRef.current) {
|
|
14780
|
+
clearInterval(musicFadeRef.current);
|
|
14781
|
+
musicFadeRef.current = null;
|
|
14782
|
+
}
|
|
14783
|
+
}, []);
|
|
14784
|
+
const playMusic = React96.useCallback((key) => {
|
|
14785
|
+
if (key === currentMusicKeyRef.current) return;
|
|
14786
|
+
pendingMusicKeyRef.current = key;
|
|
14787
|
+
const entry = manifestRef.current[key];
|
|
14788
|
+
if (!entry) return;
|
|
14789
|
+
const fadeDurationMs = entry.crossfadeDurationMs ?? 1500;
|
|
14790
|
+
const stepMs = 50;
|
|
14791
|
+
const totalSteps = Math.max(1, fadeDurationMs / stepMs);
|
|
14792
|
+
const targetVolume = Math.min(1, (entry.volume ?? 1) * volumeRef.current);
|
|
14793
|
+
clearMusicFade();
|
|
14794
|
+
const src = baseUrl + (Array.isArray(entry.path) ? entry.path[0] : entry.path);
|
|
14795
|
+
const incoming = new Audio(src);
|
|
14796
|
+
incoming.loop = true;
|
|
14797
|
+
incoming.volume = 0;
|
|
14798
|
+
const outgoing = currentMusicElRef.current;
|
|
14799
|
+
const outgoingStartVol = outgoing?.volume ?? 0;
|
|
14800
|
+
currentMusicKeyRef.current = key;
|
|
14801
|
+
currentMusicElRef.current = incoming;
|
|
14802
|
+
if (!mutedRef.current) {
|
|
14803
|
+
incoming.play().catch(() => {
|
|
14804
|
+
currentMusicKeyRef.current = null;
|
|
14805
|
+
currentMusicElRef.current = outgoing;
|
|
14806
|
+
});
|
|
14807
|
+
}
|
|
14808
|
+
let step = 0;
|
|
14809
|
+
musicFadeRef.current = setInterval(() => {
|
|
14810
|
+
step++;
|
|
14811
|
+
const progress = Math.min(step / totalSteps, 1);
|
|
14812
|
+
incoming.volume = Math.min(1, targetVolume * progress);
|
|
14813
|
+
if (outgoing) {
|
|
14814
|
+
outgoing.volume = Math.max(0, outgoingStartVol * (1 - progress));
|
|
14815
|
+
}
|
|
14816
|
+
if (progress >= 1) {
|
|
14817
|
+
clearMusicFade();
|
|
14818
|
+
if (outgoing) {
|
|
14819
|
+
outgoing.pause();
|
|
14820
|
+
outgoing.src = "";
|
|
14821
|
+
}
|
|
14822
|
+
}
|
|
14823
|
+
}, stepMs);
|
|
14824
|
+
}, [baseUrl, clearMusicFade]);
|
|
14825
|
+
const stopMusic = React96.useCallback((fadeDurationMs = 1e3) => {
|
|
14826
|
+
const outgoing = currentMusicElRef.current;
|
|
14827
|
+
if (!outgoing) return;
|
|
14828
|
+
currentMusicKeyRef.current = null;
|
|
14829
|
+
currentMusicElRef.current = null;
|
|
14830
|
+
pendingMusicKeyRef.current = null;
|
|
14831
|
+
clearMusicFade();
|
|
14832
|
+
const startVolume = outgoing.volume;
|
|
14833
|
+
const stepMs = 50;
|
|
14834
|
+
const totalSteps = Math.max(1, fadeDurationMs / stepMs);
|
|
14835
|
+
let step = 0;
|
|
14836
|
+
musicFadeRef.current = setInterval(() => {
|
|
14837
|
+
step++;
|
|
14838
|
+
const progress = step / totalSteps;
|
|
14839
|
+
outgoing.volume = Math.max(0, startVolume * (1 - progress));
|
|
14840
|
+
if (progress >= 1) {
|
|
14841
|
+
clearMusicFade();
|
|
14842
|
+
outgoing.pause();
|
|
14843
|
+
outgoing.src = "";
|
|
14844
|
+
}
|
|
14845
|
+
}, stepMs);
|
|
14846
|
+
}, [clearMusicFade]);
|
|
14847
|
+
const stopAll = React96.useCallback(() => {
|
|
14848
|
+
for (const pool of poolsRef.current.values()) {
|
|
14849
|
+
for (const audio of pool) {
|
|
14850
|
+
audio.pause();
|
|
14851
|
+
audio.currentTime = 0;
|
|
14852
|
+
}
|
|
14853
|
+
}
|
|
14854
|
+
stopMusic(0);
|
|
14855
|
+
}, [stopMusic]);
|
|
14856
|
+
const setMuted = React96.useCallback((value) => {
|
|
14857
|
+
setMutedState(value);
|
|
14858
|
+
if (value) {
|
|
14859
|
+
for (const [key, pool] of poolsRef.current.entries()) {
|
|
14860
|
+
if (manifestRef.current[key]?.loop) {
|
|
14861
|
+
for (const audio of pool) {
|
|
14862
|
+
if (!audio.paused) audio.pause();
|
|
14863
|
+
}
|
|
14864
|
+
}
|
|
14865
|
+
}
|
|
14866
|
+
currentMusicElRef.current?.pause();
|
|
14867
|
+
} else {
|
|
14868
|
+
for (const [key, pool] of poolsRef.current.entries()) {
|
|
14869
|
+
const entry = manifestRef.current[key];
|
|
14870
|
+
if (entry?.loop && entry?.autostart) {
|
|
14871
|
+
for (const audio of pool) {
|
|
14872
|
+
if (audio.paused) audio.play().catch(() => {
|
|
14873
|
+
});
|
|
14874
|
+
}
|
|
14875
|
+
}
|
|
14876
|
+
}
|
|
14877
|
+
const musicEl = currentMusicElRef.current;
|
|
14878
|
+
if (musicEl) {
|
|
14879
|
+
musicEl.play().catch(() => {
|
|
14880
|
+
});
|
|
14881
|
+
}
|
|
14882
|
+
}
|
|
14883
|
+
}, []);
|
|
14884
|
+
const setMasterVolume = React96.useCallback((volume) => {
|
|
14885
|
+
const clamped = Math.max(0, Math.min(1, volume));
|
|
14886
|
+
setMasterVolumeState(clamped);
|
|
14887
|
+
for (const [key, pool] of poolsRef.current.entries()) {
|
|
14888
|
+
const entryVol = manifestRef.current[key]?.volume ?? 1;
|
|
14889
|
+
for (const audio of pool) {
|
|
14890
|
+
audio.volume = Math.min(1, entryVol * clamped);
|
|
14891
|
+
}
|
|
14892
|
+
}
|
|
14893
|
+
if (!musicFadeRef.current && currentMusicElRef.current) {
|
|
14894
|
+
const key = currentMusicKeyRef.current;
|
|
14895
|
+
const entryVol = key ? manifestRef.current[key]?.volume ?? 1 : 1;
|
|
14896
|
+
currentMusicElRef.current.volume = Math.min(1, entryVol * clamped);
|
|
14897
|
+
}
|
|
14898
|
+
}, []);
|
|
14899
|
+
const unlockedRef = React96.useRef(false);
|
|
14900
|
+
React96.useEffect(() => {
|
|
14901
|
+
const autoKeys = Object.keys(manifest).filter((k) => manifest[k].autostart);
|
|
14902
|
+
const hasPendingMusic = () => pendingMusicKeyRef.current !== null;
|
|
14903
|
+
const hasAutoStart = autoKeys.length > 0;
|
|
14904
|
+
if (!hasAutoStart && !hasPendingMusic()) return;
|
|
14905
|
+
const unlock = () => {
|
|
14906
|
+
if (unlockedRef.current) return;
|
|
14907
|
+
unlockedRef.current = true;
|
|
14908
|
+
if (!mutedRef.current) {
|
|
14909
|
+
for (const key of autoKeys) {
|
|
14910
|
+
play(key);
|
|
14911
|
+
}
|
|
14912
|
+
const pending = pendingMusicKeyRef.current;
|
|
14913
|
+
if (pending && pending !== currentMusicKeyRef.current) {
|
|
14914
|
+
playMusic(pending);
|
|
14915
|
+
}
|
|
14916
|
+
}
|
|
14917
|
+
};
|
|
14918
|
+
document.addEventListener("click", unlock, { once: true });
|
|
14919
|
+
document.addEventListener("keydown", unlock, { once: true });
|
|
14920
|
+
document.addEventListener("touchstart", unlock, { once: true });
|
|
14921
|
+
return () => {
|
|
14922
|
+
document.removeEventListener("click", unlock);
|
|
14923
|
+
document.removeEventListener("keydown", unlock);
|
|
14924
|
+
document.removeEventListener("touchstart", unlock);
|
|
14925
|
+
};
|
|
14926
|
+
}, [manifest, play, playMusic]);
|
|
14927
|
+
React96.useEffect(() => {
|
|
14928
|
+
return () => {
|
|
14929
|
+
clearMusicFade();
|
|
14930
|
+
for (const pool of poolsRef.current.values()) {
|
|
14931
|
+
for (const audio of pool) {
|
|
14932
|
+
audio.pause();
|
|
14933
|
+
audio.src = "";
|
|
14934
|
+
}
|
|
14935
|
+
}
|
|
14936
|
+
poolsRef.current.clear();
|
|
14937
|
+
if (currentMusicElRef.current) {
|
|
14938
|
+
currentMusicElRef.current.pause();
|
|
14939
|
+
currentMusicElRef.current.src = "";
|
|
14940
|
+
currentMusicElRef.current = null;
|
|
14941
|
+
}
|
|
14942
|
+
};
|
|
14943
|
+
}, [clearMusicFade]);
|
|
14944
|
+
return {
|
|
14945
|
+
play,
|
|
14946
|
+
stop,
|
|
14947
|
+
stopAll,
|
|
14948
|
+
playMusic,
|
|
14949
|
+
stopMusic,
|
|
14950
|
+
muted,
|
|
14951
|
+
setMuted,
|
|
14952
|
+
masterVolume,
|
|
14953
|
+
setMasterVolume
|
|
14954
|
+
};
|
|
14955
|
+
}
|
|
14956
|
+
var init_useGameAudio = __esm({
|
|
14957
|
+
"hooks/useGameAudio.ts"() {
|
|
14958
|
+
"use client";
|
|
14959
|
+
useGameAudio.displayName = "useGameAudio";
|
|
14960
|
+
}
|
|
14961
|
+
});
|
|
14962
|
+
function GameAudioCue({
|
|
14963
|
+
cue,
|
|
14964
|
+
cueSeq,
|
|
14965
|
+
music,
|
|
14966
|
+
muted,
|
|
14967
|
+
volume,
|
|
14968
|
+
manifest,
|
|
14969
|
+
baseUrl
|
|
14970
|
+
}) {
|
|
14971
|
+
const { play, playMusic, stopMusic, setMuted, setMasterVolume } = useGameAudio({
|
|
14972
|
+
manifest,
|
|
14973
|
+
baseUrl,
|
|
14974
|
+
initialMuted: muted,
|
|
14975
|
+
initialVolume: volume
|
|
14976
|
+
});
|
|
14977
|
+
const prevCueSeqRef = React96.useRef(cueSeq);
|
|
14978
|
+
React96.useEffect(() => {
|
|
14979
|
+
if (cue && cueSeq !== void 0 && cueSeq !== prevCueSeqRef.current) {
|
|
14980
|
+
play(cue);
|
|
14981
|
+
}
|
|
14982
|
+
prevCueSeqRef.current = cueSeq;
|
|
14983
|
+
}, [cue, cueSeq, play]);
|
|
14984
|
+
React96.useEffect(() => {
|
|
14985
|
+
if (music) playMusic(music);
|
|
14986
|
+
else stopMusic();
|
|
14987
|
+
}, [music, playMusic, stopMusic]);
|
|
14988
|
+
React96.useEffect(() => {
|
|
14989
|
+
if (muted !== void 0) setMuted(muted);
|
|
14990
|
+
}, [muted, setMuted]);
|
|
14991
|
+
React96.useEffect(() => {
|
|
14992
|
+
if (volume !== void 0) setMasterVolume(volume);
|
|
14993
|
+
}, [volume, setMasterVolume]);
|
|
14994
|
+
return null;
|
|
14995
|
+
}
|
|
14996
|
+
var init_GameAudioCue = __esm({
|
|
14997
|
+
"components/game/atoms/GameAudioCue.tsx"() {
|
|
14998
|
+
"use client";
|
|
14999
|
+
init_useGameAudio();
|
|
15000
|
+
GameAudioCue.displayName = "GameAudioCue";
|
|
15001
|
+
}
|
|
15002
|
+
});
|
|
14539
15003
|
function isKnownState(s) {
|
|
14540
15004
|
return s in DEFAULT_STATE_STYLES;
|
|
14541
15005
|
}
|
|
@@ -15234,15 +15698,18 @@ var init_StateJsonView = __esm({
|
|
|
15234
15698
|
StateJsonView.displayName = "StateJsonView";
|
|
15235
15699
|
}
|
|
15236
15700
|
});
|
|
15237
|
-
|
|
15238
|
-
|
|
15239
|
-
|
|
15240
|
-
|
|
15241
|
-
|
|
15242
|
-
|
|
15243
|
-
|
|
15244
|
-
|
|
15245
|
-
|
|
15701
|
+
|
|
15702
|
+
// lib/gameFonts.ts
|
|
15703
|
+
function resolveGameFontFamily(input) {
|
|
15704
|
+
if (!input) return void 0;
|
|
15705
|
+
const resolved = GAME_FONT_KEYS[input];
|
|
15706
|
+
if (resolved) return `'${resolved}', ui-sans-serif, system-ui, sans-serif`;
|
|
15707
|
+
return input;
|
|
15708
|
+
}
|
|
15709
|
+
var GAME_FONT_KEYS;
|
|
15710
|
+
var init_gameFonts = __esm({
|
|
15711
|
+
"lib/gameFonts.ts"() {
|
|
15712
|
+
GAME_FONT_KEYS = {
|
|
15246
15713
|
fredoka: "Fredoka",
|
|
15247
15714
|
future: "Kenney Future",
|
|
15248
15715
|
"future-narrow": "Kenney Future Narrow",
|
|
@@ -15250,6 +15717,17 @@ var init_GameShell = __esm({
|
|
|
15250
15717
|
blocks: "Kenney Blocks",
|
|
15251
15718
|
mini: "Kenney Mini"
|
|
15252
15719
|
};
|
|
15720
|
+
}
|
|
15721
|
+
});
|
|
15722
|
+
var GameShell;
|
|
15723
|
+
var init_GameShell = __esm({
|
|
15724
|
+
"components/game/templates/GameShell.tsx"() {
|
|
15725
|
+
init_cn();
|
|
15726
|
+
init_gameFonts();
|
|
15727
|
+
init_Box();
|
|
15728
|
+
init_Card();
|
|
15729
|
+
init_Typography();
|
|
15730
|
+
init_AtlasImage();
|
|
15253
15731
|
GameShell = ({
|
|
15254
15732
|
appName = "Game",
|
|
15255
15733
|
hud,
|
|
@@ -15263,7 +15741,7 @@ var init_GameShell = __esm({
|
|
|
15263
15741
|
fontFamily,
|
|
15264
15742
|
"data-theme": dataTheme
|
|
15265
15743
|
}) => {
|
|
15266
|
-
const
|
|
15744
|
+
const displayFont = resolveGameFontFamily(fontFamily);
|
|
15267
15745
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
15268
15746
|
Box,
|
|
15269
15747
|
{
|
|
@@ -15282,7 +15760,7 @@ var init_GameShell = __esm({
|
|
|
15282
15760
|
// passed — an always-on inline stamp would shadow the orbital's
|
|
15283
15761
|
// inline-theme font-family-display token (inline style beats the
|
|
15284
15762
|
// theme provider's vars for the whole shell subtree).
|
|
15285
|
-
...
|
|
15763
|
+
...displayFont ? { "--font-family-display": displayFont } : {}
|
|
15286
15764
|
},
|
|
15287
15765
|
children: [
|
|
15288
15766
|
backgroundAsset && /* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -15493,14 +15971,15 @@ function drawArrowHead(ctx, x1, y1, x2, y2, size) {
|
|
|
15493
15971
|
ctx.closePath();
|
|
15494
15972
|
ctx.fill();
|
|
15495
15973
|
}
|
|
15496
|
-
function drawShape(ctx, shape, width, height, allShapes) {
|
|
15974
|
+
function drawShape(ctx, shape, width, height, allShapes, fontFamily) {
|
|
15497
15975
|
ctx.save();
|
|
15498
15976
|
const opacity = shape.opacity ?? 1;
|
|
15499
15977
|
ctx.globalAlpha = opacity;
|
|
15500
15978
|
const stroke = resolveColor2(shape.color, ctx, "#333333");
|
|
15501
15979
|
const fill = shape.fill ? resolveColor2(shape.fill, ctx, "#cccccc") : void 0;
|
|
15502
15980
|
ctx.lineWidth = shape.lineWidth ?? 2;
|
|
15503
|
-
|
|
15981
|
+
const dashPattern = shape.dash ? DASH_PATTERNS[shape.dash] : void 0;
|
|
15982
|
+
if (dashPattern) ctx.setLineDash([...dashPattern]);
|
|
15504
15983
|
switch (shape.type) {
|
|
15505
15984
|
case "grid": {
|
|
15506
15985
|
const step = shape.step ?? 40;
|
|
@@ -15622,7 +16101,7 @@ function drawShape(ctx, shape, width, height, allShapes) {
|
|
|
15622
16101
|
case "text": {
|
|
15623
16102
|
if (shape.x == null || shape.y == null || !shape.text) break;
|
|
15624
16103
|
ctx.fillStyle = stroke;
|
|
15625
|
-
ctx.font = `${shape.fontSize ?? 14}px ${themeBodyFont(ctx.canvas)}`;
|
|
16104
|
+
ctx.font = `${shape.fontSize ?? 14}px ${shape.fontFamily ?? fontFamily ?? themeBodyFont(ctx.canvas)}`;
|
|
15626
16105
|
ctx.textAlign = shape.align ?? "left";
|
|
15627
16106
|
ctx.textBaseline = "middle";
|
|
15628
16107
|
ctx.fillText(shape.text, shape.x, shape.y);
|
|
@@ -15664,7 +16143,7 @@ function drawShape(ctx, shape, width, height, allShapes) {
|
|
|
15664
16143
|
}
|
|
15665
16144
|
ctx.restore();
|
|
15666
16145
|
}
|
|
15667
|
-
function readoutShapes(readouts, width) {
|
|
16146
|
+
function readoutShapes(readouts, width, fontFamily) {
|
|
15668
16147
|
const out = [];
|
|
15669
16148
|
const chipH = 18;
|
|
15670
16149
|
const gap = 6;
|
|
@@ -15688,13 +16167,14 @@ function readoutShapes(readouts, width) {
|
|
|
15688
16167
|
text,
|
|
15689
16168
|
color: "#ffffff",
|
|
15690
16169
|
fontSize: 10,
|
|
15691
|
-
align: "center"
|
|
16170
|
+
align: "center",
|
|
16171
|
+
fontFamily
|
|
15692
16172
|
});
|
|
15693
16173
|
rightEdge = chipX - gap;
|
|
15694
16174
|
}
|
|
15695
16175
|
return out;
|
|
15696
16176
|
}
|
|
15697
|
-
function traceShapes(panel, k, width, height) {
|
|
16177
|
+
function traceShapes(panel, k, width, height, fontFamily) {
|
|
15698
16178
|
const w = panel.width ?? Math.round(width * 0.32);
|
|
15699
16179
|
const h = panel.height ?? Math.round(height * 0.28);
|
|
15700
16180
|
const x = panel.x ?? width - w - 8;
|
|
@@ -15748,14 +16228,14 @@ function traceShapes(panel, k, width, height) {
|
|
|
15748
16228
|
out.push({ type: "circle", x: last.x, y: last.y, radius: 2, color, fill: color });
|
|
15749
16229
|
}
|
|
15750
16230
|
if (series.label) {
|
|
15751
|
-
out.push({ type: "text", x: x + 6, y: y + 10 + 11 * j, text: series.label, color, fontSize: 9 });
|
|
16231
|
+
out.push({ type: "text", x: x + 6, y: y + 10 + 11 * j, text: series.label, color, fontSize: 9, fontFamily });
|
|
15752
16232
|
}
|
|
15753
16233
|
});
|
|
15754
16234
|
if (panel.yLabel) {
|
|
15755
|
-
out.push({ type: "text", x: x + w - 6, y: y + 10, text: panel.yLabel, color: "#6b7280", fontSize: 9, align: "right" });
|
|
16235
|
+
out.push({ type: "text", x: x + w - 6, y: y + 10, text: panel.yLabel, color: "#6b7280", fontSize: 9, align: "right", fontFamily });
|
|
15756
16236
|
}
|
|
15757
16237
|
if (panel.xLabel) {
|
|
15758
|
-
out.push({ type: "text", x: x + w - 6, y: y + h - 6, text: panel.xLabel, color: "#6b7280", fontSize: 9, align: "right" });
|
|
16238
|
+
out.push({ type: "text", x: x + w - 6, y: y + h - 6, text: panel.xLabel, color: "#6b7280", fontSize: 9, align: "right", fontFamily });
|
|
15759
16239
|
}
|
|
15760
16240
|
return out;
|
|
15761
16241
|
}
|
|
@@ -15778,13 +16258,14 @@ var init_LearningCanvas = __esm({
|
|
|
15778
16258
|
init_useEventBus();
|
|
15779
16259
|
init_webPainter2d();
|
|
15780
16260
|
init_paintDispatch();
|
|
15781
|
-
DASH_PATTERNS = { dashed: [6, 4], dotted: [2, 3] };
|
|
16261
|
+
DASH_PATTERNS = { solid: [], dashed: [6, 4], dotted: [2, 3] };
|
|
15782
16262
|
TRACE_SERIES_COLORS = ["#2563eb", "#dc2626", "#16a34a", "#f59e0b"];
|
|
15783
16263
|
LearningCanvas = ({
|
|
15784
16264
|
className,
|
|
15785
16265
|
width = 600,
|
|
15786
16266
|
height = 400,
|
|
15787
16267
|
backgroundColor,
|
|
16268
|
+
fontFamily,
|
|
15788
16269
|
shapes = [],
|
|
15789
16270
|
drawables,
|
|
15790
16271
|
projector,
|
|
@@ -15820,10 +16301,10 @@ var init_LearningCanvas = __esm({
|
|
|
15820
16301
|
}, [shapes]);
|
|
15821
16302
|
const derivedShapes = React96.useMemo(() => {
|
|
15822
16303
|
if (!traces?.length && !readouts?.length) return shapes;
|
|
15823
|
-
const traceOut = (traces ?? []).flatMap((panel, k) => traceShapes(panel, k, width, height));
|
|
15824
|
-
const readoutOut = readouts?.length ? readoutShapes(readouts, width) : [];
|
|
16304
|
+
const traceOut = (traces ?? []).flatMap((panel, k) => traceShapes(panel, k, width, height, fontFamily));
|
|
16305
|
+
const readoutOut = readouts?.length ? readoutShapes(readouts, width, fontFamily) : [];
|
|
15825
16306
|
return [...shapes, ...traceOut, ...readoutOut];
|
|
15826
|
-
}, [shapes, traces, readouts, width, height]);
|
|
16307
|
+
}, [shapes, traces, readouts, width, height, fontFamily]);
|
|
15827
16308
|
const draw = React96.useCallback(() => {
|
|
15828
16309
|
const _perfT = ui.perfStart("learningcanvas:paint");
|
|
15829
16310
|
const canvas = canvasRef.current;
|
|
@@ -15842,15 +16323,15 @@ var init_LearningCanvas = __esm({
|
|
|
15842
16323
|
ctx.fillRect(0, 0, width, height);
|
|
15843
16324
|
}
|
|
15844
16325
|
for (const shape of derivedShapes) {
|
|
15845
|
-
if (shape.type !== "text") drawShape(ctx, shape, width, height, derivedShapes);
|
|
16326
|
+
if (shape.type !== "text") drawShape(ctx, shape, width, height, derivedShapes, fontFamily);
|
|
15846
16327
|
}
|
|
15847
16328
|
for (const shape of derivedShapes) {
|
|
15848
|
-
if (shape.type === "text") drawShape(ctx, shape, width, height, derivedShapes);
|
|
16329
|
+
if (shape.type === "text") drawShape(ctx, shape, width, height, derivedShapes, fontFamily);
|
|
15849
16330
|
}
|
|
15850
16331
|
if (drawables?.length && projector) {
|
|
15851
16332
|
const painter = createWebPainter(ctx, invalidateRef.current);
|
|
15852
16333
|
const timeMs = needsAnim && typeof performance !== "undefined" ? performance.now() : 0;
|
|
15853
|
-
const dctx = { projector, time: timeMs, invalidate: invalidateRef.current, fontFamily: themeBodyFont(canvas) };
|
|
16334
|
+
const dctx = { projector, time: timeMs, invalidate: invalidateRef.current, fontFamily: fontFamily || themeBodyFont(canvas) };
|
|
15854
16335
|
for (const node of drawables) {
|
|
15855
16336
|
paintDrawable(painter, node, dctx);
|
|
15856
16337
|
}
|
|
@@ -19622,6 +20103,250 @@ var init_EmptyState = __esm({
|
|
|
19622
20103
|
EmptyState.displayName = "EmptyState";
|
|
19623
20104
|
}
|
|
19624
20105
|
});
|
|
20106
|
+
|
|
20107
|
+
// lib/editorMotions.ts
|
|
20108
|
+
function clamp(value, min, max) {
|
|
20109
|
+
return Math.max(min, Math.min(max, value));
|
|
20110
|
+
}
|
|
20111
|
+
function computeLines(text) {
|
|
20112
|
+
const lines = [];
|
|
20113
|
+
let start = 0;
|
|
20114
|
+
for (let i = 0; i <= text.length; i++) {
|
|
20115
|
+
if (i === text.length || text[i] === "\n") {
|
|
20116
|
+
lines.push({ start, end: i });
|
|
20117
|
+
start = i + 1;
|
|
20118
|
+
}
|
|
20119
|
+
}
|
|
20120
|
+
return lines;
|
|
20121
|
+
}
|
|
20122
|
+
function lineIndexAt(lines, pos) {
|
|
20123
|
+
for (let i = 0; i < lines.length; i++) {
|
|
20124
|
+
if (pos <= lines[i].end) return i;
|
|
20125
|
+
}
|
|
20126
|
+
return lines.length - 1;
|
|
20127
|
+
}
|
|
20128
|
+
function findWords(text) {
|
|
20129
|
+
const words = [];
|
|
20130
|
+
const re = /\w+|\S+/g;
|
|
20131
|
+
let m;
|
|
20132
|
+
while ((m = re.exec(text)) !== null) {
|
|
20133
|
+
words.push({ start: m.index, end: m.index + m[0].length });
|
|
20134
|
+
}
|
|
20135
|
+
return words;
|
|
20136
|
+
}
|
|
20137
|
+
function nextWordStart(text, pos) {
|
|
20138
|
+
for (const w of findWords(text)) {
|
|
20139
|
+
if (w.start > pos) return w.start;
|
|
20140
|
+
}
|
|
20141
|
+
return text.length;
|
|
20142
|
+
}
|
|
20143
|
+
function prevWordStart(text, pos) {
|
|
20144
|
+
let result = 0;
|
|
20145
|
+
for (const w of findWords(text)) {
|
|
20146
|
+
if (w.start < pos) result = w.start;
|
|
20147
|
+
else break;
|
|
20148
|
+
}
|
|
20149
|
+
return result;
|
|
20150
|
+
}
|
|
20151
|
+
function nextWordEnd(text, pos) {
|
|
20152
|
+
for (const w of findWords(text)) {
|
|
20153
|
+
const lastChar = w.end - 1;
|
|
20154
|
+
if (lastChar > pos) return lastChar;
|
|
20155
|
+
}
|
|
20156
|
+
return text.length > 0 ? text.length - 1 : 0;
|
|
20157
|
+
}
|
|
20158
|
+
function firstNonBlank(text, line) {
|
|
20159
|
+
let i = line.start;
|
|
20160
|
+
while (i < line.end && (text[i] === " " || text[i] === " ")) i++;
|
|
20161
|
+
return i;
|
|
20162
|
+
}
|
|
20163
|
+
function nextParagraphBoundary(lines, fromLineIdx, textLength) {
|
|
20164
|
+
for (let i = fromLineIdx + 1; i < lines.length; i++) {
|
|
20165
|
+
if (lines[i].start === lines[i].end) return lines[i].start;
|
|
20166
|
+
}
|
|
20167
|
+
return textLength;
|
|
20168
|
+
}
|
|
20169
|
+
function prevParagraphBoundary(lines, fromLineIdx) {
|
|
20170
|
+
for (let i = fromLineIdx - 1; i >= 0; i--) {
|
|
20171
|
+
if (lines[i].start === lines[i].end) return lines[i].start;
|
|
20172
|
+
}
|
|
20173
|
+
return 0;
|
|
20174
|
+
}
|
|
20175
|
+
function applyMotion(text, caret, motion, count) {
|
|
20176
|
+
const n = Math.max(1, count);
|
|
20177
|
+
const lines = computeLines(text);
|
|
20178
|
+
const lineIdx = lineIndexAt(lines, caret);
|
|
20179
|
+
const line = lines[lineIdx];
|
|
20180
|
+
switch (motion) {
|
|
20181
|
+
case "left":
|
|
20182
|
+
return clamp(caret - n, line.start, line.end);
|
|
20183
|
+
case "right":
|
|
20184
|
+
return clamp(caret + n, line.start, line.end);
|
|
20185
|
+
case "up": {
|
|
20186
|
+
const col = caret - line.start;
|
|
20187
|
+
const targetIdx = clamp(lineIdx - n, 0, lines.length - 1);
|
|
20188
|
+
const target = lines[targetIdx];
|
|
20189
|
+
return clamp(target.start + col, target.start, target.end);
|
|
20190
|
+
}
|
|
20191
|
+
case "down": {
|
|
20192
|
+
const col = caret - line.start;
|
|
20193
|
+
const targetIdx = clamp(lineIdx + n, 0, lines.length - 1);
|
|
20194
|
+
const target = lines[targetIdx];
|
|
20195
|
+
return clamp(target.start + col, target.start, target.end);
|
|
20196
|
+
}
|
|
20197
|
+
case "word-forward": {
|
|
20198
|
+
let pos = caret;
|
|
20199
|
+
for (let i = 0; i < n; i++) pos = nextWordStart(text, pos);
|
|
20200
|
+
return pos;
|
|
20201
|
+
}
|
|
20202
|
+
case "word-back": {
|
|
20203
|
+
let pos = caret;
|
|
20204
|
+
for (let i = 0; i < n; i++) pos = prevWordStart(text, pos);
|
|
20205
|
+
return pos;
|
|
20206
|
+
}
|
|
20207
|
+
case "word-end": {
|
|
20208
|
+
let pos = caret;
|
|
20209
|
+
for (let i = 0; i < n; i++) pos = nextWordEnd(text, pos);
|
|
20210
|
+
return pos;
|
|
20211
|
+
}
|
|
20212
|
+
case "line-start":
|
|
20213
|
+
return line.start;
|
|
20214
|
+
case "line-end":
|
|
20215
|
+
return line.end > line.start ? line.end - 1 : line.start;
|
|
20216
|
+
case "first-nonblank":
|
|
20217
|
+
return firstNonBlank(text, line);
|
|
20218
|
+
case "doc-start":
|
|
20219
|
+
return 0;
|
|
20220
|
+
case "doc-end":
|
|
20221
|
+
return text.length;
|
|
20222
|
+
case "paragraph-forward": {
|
|
20223
|
+
let pos = caret;
|
|
20224
|
+
for (let i = 0; i < n; i++) {
|
|
20225
|
+
pos = nextParagraphBoundary(lines, lineIndexAt(lines, pos), text.length);
|
|
20226
|
+
}
|
|
20227
|
+
return pos;
|
|
20228
|
+
}
|
|
20229
|
+
case "paragraph-back": {
|
|
20230
|
+
let pos = caret;
|
|
20231
|
+
for (let i = 0; i < n; i++) {
|
|
20232
|
+
pos = prevParagraphBoundary(lines, lineIndexAt(lines, pos));
|
|
20233
|
+
}
|
|
20234
|
+
return pos;
|
|
20235
|
+
}
|
|
20236
|
+
case "line":
|
|
20237
|
+
case "selection":
|
|
20238
|
+
return caret;
|
|
20239
|
+
default: {
|
|
20240
|
+
const _exhaustive = motion;
|
|
20241
|
+
return _exhaustive;
|
|
20242
|
+
}
|
|
20243
|
+
}
|
|
20244
|
+
}
|
|
20245
|
+
function motionRange(text, caret, motion, count, selection) {
|
|
20246
|
+
if (motion === "selection") {
|
|
20247
|
+
if (!selection) return [caret, caret];
|
|
20248
|
+
return [Math.min(selection[0], selection[1]), Math.max(selection[0], selection[1])];
|
|
20249
|
+
}
|
|
20250
|
+
const lines = computeLines(text);
|
|
20251
|
+
if (motion === "line") {
|
|
20252
|
+
const n = Math.max(1, count);
|
|
20253
|
+
const startIdx = lineIndexAt(lines, caret);
|
|
20254
|
+
const endIdx = clamp(startIdx + n - 1, 0, lines.length - 1);
|
|
20255
|
+
const start2 = lines[startIdx].start;
|
|
20256
|
+
const rawEnd = lines[endIdx].end;
|
|
20257
|
+
const end2 = rawEnd < text.length ? rawEnd + 1 : rawEnd;
|
|
20258
|
+
return [start2, end2];
|
|
20259
|
+
}
|
|
20260
|
+
const newCaret = applyMotion(text, caret, motion, count);
|
|
20261
|
+
let start = Math.min(caret, newCaret);
|
|
20262
|
+
let end = Math.max(caret, newCaret);
|
|
20263
|
+
if (motion === "word-end" || motion === "line-end") {
|
|
20264
|
+
end = Math.min(Math.max(start, newCaret) + 1, text.length);
|
|
20265
|
+
} else if (motion === "word-forward" && newCaret > caret) {
|
|
20266
|
+
const startLine = lineIndexAt(lines, caret);
|
|
20267
|
+
const endLine = lineIndexAt(lines, newCaret);
|
|
20268
|
+
if (endLine !== startLine) {
|
|
20269
|
+
end = lines[startLine].end;
|
|
20270
|
+
}
|
|
20271
|
+
}
|
|
20272
|
+
return [start, end];
|
|
20273
|
+
}
|
|
20274
|
+
function applyOperator(text, range, operator, register) {
|
|
20275
|
+
const start = clamp(range[0], 0, text.length);
|
|
20276
|
+
const end = clamp(range[1], start, text.length);
|
|
20277
|
+
const removed = text.slice(start, end);
|
|
20278
|
+
if (operator === "yank") {
|
|
20279
|
+
return { text, caret: start, register: removed };
|
|
20280
|
+
}
|
|
20281
|
+
return { text: text.slice(0, start) + text.slice(end), caret: start, register: removed };
|
|
20282
|
+
}
|
|
20283
|
+
var init_editorMotions = __esm({
|
|
20284
|
+
"lib/editorMotions.ts"() {
|
|
20285
|
+
}
|
|
20286
|
+
});
|
|
20287
|
+
function isMotionPayload(payload) {
|
|
20288
|
+
return !!payload && typeof payload.editorId === "string" && typeof payload.motion === "string" && typeof payload.count === "number";
|
|
20289
|
+
}
|
|
20290
|
+
function isOperatePayload(payload) {
|
|
20291
|
+
return !!payload && typeof payload.editorId === "string" && typeof payload.operator === "string" && typeof payload.motion === "string" && typeof payload.count === "number";
|
|
20292
|
+
}
|
|
20293
|
+
function isInsertTextPayload(payload) {
|
|
20294
|
+
return !!payload && typeof payload.editorId === "string" && typeof payload.text === "string";
|
|
20295
|
+
}
|
|
20296
|
+
function isSetModePayload(payload) {
|
|
20297
|
+
return !!payload && typeof payload.editorId === "string" && typeof payload.mode === "string" && typeof payload.caret === "string";
|
|
20298
|
+
}
|
|
20299
|
+
function useEditorCapabilities(args) {
|
|
20300
|
+
const [caretMode, setCaretMode] = React96.useState("bar");
|
|
20301
|
+
const registerRef = React96.useRef("");
|
|
20302
|
+
useEventListener(`UI:${args.events.onMotion}`, (evt) => {
|
|
20303
|
+
if (!args.editorId || !isMotionPayload(evt.payload) || evt.payload.editorId !== args.editorId) return;
|
|
20304
|
+
const ta = args.textareaRef.current;
|
|
20305
|
+
if (!ta) return;
|
|
20306
|
+
const { motion, count } = evt.payload;
|
|
20307
|
+
const newCaret = applyMotion(ta.value, ta.selectionStart, motion, count);
|
|
20308
|
+
if (ta.selectionStart !== ta.selectionEnd) {
|
|
20309
|
+
ta.setSelectionRange(ta.selectionStart, newCaret);
|
|
20310
|
+
} else {
|
|
20311
|
+
ta.setSelectionRange(newCaret, newCaret);
|
|
20312
|
+
}
|
|
20313
|
+
});
|
|
20314
|
+
useEventListener(`UI:${args.events.onOperate}`, (evt) => {
|
|
20315
|
+
if (!args.editorId || !isOperatePayload(evt.payload) || evt.payload.editorId !== args.editorId) return;
|
|
20316
|
+
const ta = args.textareaRef.current;
|
|
20317
|
+
if (!ta) return;
|
|
20318
|
+
const { operator, motion, count } = evt.payload;
|
|
20319
|
+
const selection = motion === "selection" ? [ta.selectionStart, ta.selectionEnd] : void 0;
|
|
20320
|
+
const range = motionRange(ta.value, ta.selectionStart, motion, count, selection);
|
|
20321
|
+
const result = applyOperator(ta.value, range, operator, registerRef.current);
|
|
20322
|
+
registerRef.current = result.register;
|
|
20323
|
+
if (operator === "yank") {
|
|
20324
|
+
ta.setSelectionRange(range[0], range[0]);
|
|
20325
|
+
} else {
|
|
20326
|
+
ta.setRangeText("", range[0], range[1], "end");
|
|
20327
|
+
}
|
|
20328
|
+
args.applyChange(ta.value);
|
|
20329
|
+
});
|
|
20330
|
+
useEventListener(`UI:${args.events.onInsertText}`, (evt) => {
|
|
20331
|
+
if (!args.editorId || !isInsertTextPayload(evt.payload) || evt.payload.editorId !== args.editorId) return;
|
|
20332
|
+
const ta = args.textareaRef.current;
|
|
20333
|
+
if (!ta) return;
|
|
20334
|
+
ta.setRangeText(evt.payload.text, ta.selectionStart, ta.selectionEnd, "end");
|
|
20335
|
+
args.applyChange(ta.value);
|
|
20336
|
+
});
|
|
20337
|
+
useEventListener(`UI:${args.events.onSetMode}`, (evt) => {
|
|
20338
|
+
if (!args.editorId || !isSetModePayload(evt.payload) || evt.payload.editorId !== args.editorId) return;
|
|
20339
|
+
setCaretMode(evt.payload.caret);
|
|
20340
|
+
});
|
|
20341
|
+
return { caretMode };
|
|
20342
|
+
}
|
|
20343
|
+
var init_useEditorCapabilities = __esm({
|
|
20344
|
+
"components/core/molecules/markdown/useEditorCapabilities.ts"() {
|
|
20345
|
+
"use client";
|
|
20346
|
+
init_useEventBus();
|
|
20347
|
+
init_editorMotions();
|
|
20348
|
+
}
|
|
20349
|
+
});
|
|
19625
20350
|
function isLanguageRegistered(lang) {
|
|
19626
20351
|
return CODE_LANGUAGE_SET.has(lang) || dynamicallyLoaded.has(lang);
|
|
19627
20352
|
}
|
|
@@ -19707,7 +20432,36 @@ function useLanguageReady(language) {
|
|
|
19707
20432
|
}, [language]);
|
|
19708
20433
|
return ready;
|
|
19709
20434
|
}
|
|
19710
|
-
|
|
20435
|
+
function resolveHighlightStyle(lang) {
|
|
20436
|
+
if (lang === "orb") return orbStyle;
|
|
20437
|
+
if (lang === "lolo") return loloStyle;
|
|
20438
|
+
return dark__default.default;
|
|
20439
|
+
}
|
|
20440
|
+
function plainCodeColorOf(style) {
|
|
20441
|
+
return style['code[class*="language-"]']?.color ?? "#d4d4d4";
|
|
20442
|
+
}
|
|
20443
|
+
function buildLineProps(errorLines, extraClassName) {
|
|
20444
|
+
return (lineNumber) => {
|
|
20445
|
+
const base = {
|
|
20446
|
+
"data-line": String(lineNumber - 1),
|
|
20447
|
+
...extraClassName ? { className: extraClassName } : {}
|
|
20448
|
+
};
|
|
20449
|
+
const severity = errorLines?.get(lineNumber);
|
|
20450
|
+
if (!severity) return base;
|
|
20451
|
+
return {
|
|
20452
|
+
...base,
|
|
20453
|
+
style: {
|
|
20454
|
+
display: "block",
|
|
20455
|
+
backgroundColor: severity === "error" ? "rgba(248, 113, 113, 0.18)" : "rgba(251, 191, 36, 0.18)",
|
|
20456
|
+
// amber-400 @ 18%
|
|
20457
|
+
borderLeft: `3px solid ${severity === "error" ? "#ef4444" : "#f59e0b"}`,
|
|
20458
|
+
paddingLeft: "0.5rem",
|
|
20459
|
+
marginLeft: "-0.5rem"
|
|
20460
|
+
}
|
|
20461
|
+
};
|
|
20462
|
+
};
|
|
20463
|
+
}
|
|
20464
|
+
var dynamicallyLoaded, codeLanguageLoader, orbStyleOverrides, orbStyle, loloStyleOverrides, loloStyle, log4, 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;
|
|
19711
20465
|
var init_CodeBlock = __esm({
|
|
19712
20466
|
"components/core/molecules/markdown/CodeBlock.tsx"() {
|
|
19713
20467
|
init_cn();
|
|
@@ -19723,6 +20477,7 @@ var init_CodeBlock = __esm({
|
|
|
19723
20477
|
init_Textarea();
|
|
19724
20478
|
init_Icon();
|
|
19725
20479
|
init_useEventBus();
|
|
20480
|
+
init_useEditorCapabilities();
|
|
19726
20481
|
SyntaxHighlighter__default.default.registerLanguage("json", langJson__default.default);
|
|
19727
20482
|
SyntaxHighlighter__default.default.registerLanguage("javascript", langJavascript__default.default);
|
|
19728
20483
|
SyntaxHighlighter__default.default.registerLanguage("js", langJavascript__default.default);
|
|
@@ -19941,6 +20696,15 @@ var init_CodeBlock = __esm({
|
|
|
19941
20696
|
LINE_PROPS_FN = (n) => ({ "data-line": String(n - 1) });
|
|
19942
20697
|
HIDDEN_LINE_NUMBERS = { display: "none" };
|
|
19943
20698
|
HIGHLIGHT_CAPACITY_BYTES = 512 * 1024;
|
|
20699
|
+
MONO_FONT_FAMILY = 'ui-monospace, SFMono-Regular, Menlo, Monaco, "Cascadia Mono", "Courier New", monospace';
|
|
20700
|
+
VIEWER_LINE_NUMBER_STYLE = {
|
|
20701
|
+
minWidth: "2.5em",
|
|
20702
|
+
paddingRight: "1rem",
|
|
20703
|
+
textAlign: "right",
|
|
20704
|
+
userSelect: "none",
|
|
20705
|
+
opacity: 0.5,
|
|
20706
|
+
fontVariantNumeric: "tabular-nums"
|
|
20707
|
+
};
|
|
19944
20708
|
CodeBlock = React96__namespace.default.memo(
|
|
19945
20709
|
({
|
|
19946
20710
|
code: rawCode,
|
|
@@ -19965,15 +20729,39 @@ var init_CodeBlock = __esm({
|
|
|
19965
20729
|
actions,
|
|
19966
20730
|
isLoading = false,
|
|
19967
20731
|
error,
|
|
19968
|
-
showCopy
|
|
20732
|
+
showCopy,
|
|
20733
|
+
// editor capability surface — P1 wires these
|
|
20734
|
+
editorId,
|
|
20735
|
+
onEditorFocus = "EDITOR_FOCUS",
|
|
20736
|
+
onEditorBlur = "EDITOR_BLUR",
|
|
20737
|
+
onMotion = "MOTION",
|
|
20738
|
+
onOperate = "OPERATE",
|
|
20739
|
+
onInsertText = "INSERT_TEXT",
|
|
20740
|
+
onSetMode = "SET_MODE",
|
|
20741
|
+
motions = [
|
|
20742
|
+
"left",
|
|
20743
|
+
"right",
|
|
20744
|
+
"up",
|
|
20745
|
+
"down",
|
|
20746
|
+
"word-forward",
|
|
20747
|
+
"word-back",
|
|
20748
|
+
"word-end",
|
|
20749
|
+
"line-start",
|
|
20750
|
+
"line-end",
|
|
20751
|
+
"first-nonblank",
|
|
20752
|
+
"doc-start",
|
|
20753
|
+
"doc-end",
|
|
20754
|
+
"paragraph-forward",
|
|
20755
|
+
"paragraph-back",
|
|
20756
|
+
"line",
|
|
20757
|
+
"selection"
|
|
20758
|
+
],
|
|
20759
|
+
operators = ["delete", "yank", "change"]
|
|
19969
20760
|
}) => {
|
|
19970
20761
|
const code = typeof rawCode === "string" ? rawCode : String(rawCode ?? "");
|
|
19971
|
-
const
|
|
19972
|
-
const isLolo = language === "lolo";
|
|
19973
|
-
const activeStyle = isOrb ? orbStyle : isLolo ? loloStyle : dark__default.default;
|
|
20762
|
+
const activeStyle = resolveHighlightStyle(language);
|
|
19974
20763
|
const overCapacity = code.length > HIGHLIGHT_CAPACITY_BYTES;
|
|
19975
|
-
const plainCodeColor = activeStyle
|
|
19976
|
-
const languageReady = useLanguageReady(language);
|
|
20764
|
+
const plainCodeColor = plainCodeColorOf(activeStyle);
|
|
19977
20765
|
const eventBus = useEventBus();
|
|
19978
20766
|
const { t } = hooks.useTranslate();
|
|
19979
20767
|
const scrollRef = React96.useRef(null);
|
|
@@ -19985,6 +20773,9 @@ var init_CodeBlock = __esm({
|
|
|
19985
20773
|
const activeFile = files?.[activeFileIndex];
|
|
19986
20774
|
const activeCode = activeFile?.code ?? code;
|
|
19987
20775
|
const activeLanguage = activeFile?.language ?? language;
|
|
20776
|
+
const languageReady = useLanguageReady(activeLanguage);
|
|
20777
|
+
const viewerStyle = resolveHighlightStyle(activeLanguage);
|
|
20778
|
+
const viewerPlainCodeColor = plainCodeColorOf(viewerStyle);
|
|
19988
20779
|
const diffLines = React96.useMemo(() => {
|
|
19989
20780
|
if (propDiff) return propDiff;
|
|
19990
20781
|
if (mode === "diff" && oldValue !== void 0 && newValue !== void 0) {
|
|
@@ -20014,28 +20805,28 @@ var init_CodeBlock = __esm({
|
|
|
20014
20805
|
ov.scrollLeft = ta.scrollLeft;
|
|
20015
20806
|
}
|
|
20016
20807
|
}, []);
|
|
20017
|
-
const
|
|
20018
|
-
|
|
20019
|
-
|
|
20020
|
-
|
|
20021
|
-
|
|
20022
|
-
|
|
20023
|
-
|
|
20024
|
-
|
|
20025
|
-
|
|
20026
|
-
|
|
20027
|
-
|
|
20028
|
-
|
|
20029
|
-
|
|
20030
|
-
|
|
20031
|
-
|
|
20032
|
-
|
|
20033
|
-
|
|
20034
|
-
|
|
20035
|
-
|
|
20036
|
-
|
|
20037
|
-
|
|
20038
|
-
|
|
20808
|
+
const handleEditableChange = React96.useCallback((v) => {
|
|
20809
|
+
lastPropCodeRef.current = v;
|
|
20810
|
+
setEditableValue(v);
|
|
20811
|
+
onChange?.(v);
|
|
20812
|
+
}, [onChange]);
|
|
20813
|
+
const { caretMode } = useEditorCapabilities({
|
|
20814
|
+
editorId: editable ? editorId : void 0,
|
|
20815
|
+
textareaRef: editableTextareaRef,
|
|
20816
|
+
events: { onMotion, onOperate, onInsertText, onSetMode },
|
|
20817
|
+
applyChange: handleEditableChange
|
|
20818
|
+
});
|
|
20819
|
+
const [caretIndex, setCaretIndex] = React96.useState(0);
|
|
20820
|
+
const caretRowCol = React96.useMemo(() => {
|
|
20821
|
+
const before = editableValue.slice(0, Math.min(caretIndex, editableValue.length));
|
|
20822
|
+
const lines = before.split("\n");
|
|
20823
|
+
return { row: lines.length - 1, col: lines[lines.length - 1].length };
|
|
20824
|
+
}, [editableValue, caretIndex]);
|
|
20825
|
+
const errorLineProps = React96.useMemo(() => buildLineProps(errorLines), [errorLines]);
|
|
20826
|
+
const viewerLineProps = React96.useMemo(
|
|
20827
|
+
() => buildLineProps(errorLines, "px-4 py-0.5 hover:bg-muted/50"),
|
|
20828
|
+
[errorLines]
|
|
20829
|
+
);
|
|
20039
20830
|
const isFoldable = foldableProp ?? true;
|
|
20040
20831
|
const [collapsed, setCollapsed] = React96.useState(() => /* @__PURE__ */ new Set());
|
|
20041
20832
|
const foldRegions = React96.useMemo(
|
|
@@ -20158,6 +20949,110 @@ var init_CodeBlock = __esm({
|
|
|
20158
20949
|
),
|
|
20159
20950
|
[code, overCapacity, plainCodeColor, language, activeStyle, languageReady]
|
|
20160
20951
|
);
|
|
20952
|
+
const viewerOverCapacity = activeCode.length > HIGHLIGHT_CAPACITY_BYTES;
|
|
20953
|
+
const viewerHighlightedElement = React96.useMemo(
|
|
20954
|
+
() => viewerOverCapacity ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
20955
|
+
"div",
|
|
20956
|
+
{
|
|
20957
|
+
className: "px-4 py-0.5",
|
|
20958
|
+
style: {
|
|
20959
|
+
margin: 0,
|
|
20960
|
+
whiteSpace: wrap ? "pre-wrap" : "pre",
|
|
20961
|
+
wordBreak: wrap ? "break-all" : "normal",
|
|
20962
|
+
color: viewerPlainCodeColor,
|
|
20963
|
+
fontFamily: MONO_FONT_FAMILY,
|
|
20964
|
+
fontSize: "12px",
|
|
20965
|
+
lineHeight: "1.6"
|
|
20966
|
+
},
|
|
20967
|
+
children: activeCode
|
|
20968
|
+
}
|
|
20969
|
+
) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
20970
|
+
SyntaxHighlighter__default.default,
|
|
20971
|
+
{
|
|
20972
|
+
PreTag: "div",
|
|
20973
|
+
language: activeLanguage,
|
|
20974
|
+
style: viewerStyle,
|
|
20975
|
+
wrapLines: true,
|
|
20976
|
+
wrapLongLines: wrap,
|
|
20977
|
+
showLineNumbers,
|
|
20978
|
+
lineNumberStyle: VIEWER_LINE_NUMBER_STYLE,
|
|
20979
|
+
lineProps: viewerLineProps,
|
|
20980
|
+
customStyle: {
|
|
20981
|
+
backgroundColor: "transparent",
|
|
20982
|
+
borderRadius: 0,
|
|
20983
|
+
padding: "0.25rem 0",
|
|
20984
|
+
margin: 0,
|
|
20985
|
+
whiteSpace: wrap ? "pre-wrap" : "pre",
|
|
20986
|
+
wordBreak: wrap ? "break-all" : "normal",
|
|
20987
|
+
fontFamily: MONO_FONT_FAMILY,
|
|
20988
|
+
fontSize: "12px",
|
|
20989
|
+
lineHeight: "1.6"
|
|
20990
|
+
},
|
|
20991
|
+
codeTagProps: { style: { fontFamily: MONO_FONT_FAMILY, fontSize: "12px", lineHeight: "1.6" } },
|
|
20992
|
+
children: activeCode
|
|
20993
|
+
}
|
|
20994
|
+
),
|
|
20995
|
+
[activeCode, viewerOverCapacity, viewerPlainCodeColor, activeLanguage, viewerStyle, wrap, showLineNumbers, viewerLineProps, languageReady]
|
|
20996
|
+
);
|
|
20997
|
+
const diffOverCapacity = React96.useMemo(
|
|
20998
|
+
() => !!diffLines && diffLines.reduce((n, l) => n + l.content.length + 1, 0) > HIGHLIGHT_CAPACITY_BYTES,
|
|
20999
|
+
[diffLines]
|
|
21000
|
+
);
|
|
21001
|
+
const diffRowElements = React96.useMemo(() => {
|
|
21002
|
+
if (!diffLines) return null;
|
|
21003
|
+
return diffLines.map((line, idx) => {
|
|
21004
|
+
const style = DIFF_STYLES[line.type] ?? DIFF_STYLE_FALLBACK;
|
|
21005
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "none", align: "start", className: cn(style.bg, "px-4 py-0.5"), children: [
|
|
21006
|
+
showLineNumbers && /* @__PURE__ */ jsxRuntime.jsx(
|
|
21007
|
+
Typography,
|
|
21008
|
+
{
|
|
21009
|
+
variant: "caption",
|
|
21010
|
+
color: "secondary",
|
|
21011
|
+
className: "w-8 text-right mr-3 select-none tabular-nums flex-shrink-0",
|
|
21012
|
+
children: line.lineNumber ?? ""
|
|
21013
|
+
}
|
|
21014
|
+
),
|
|
21015
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
21016
|
+
Typography,
|
|
21017
|
+
{
|
|
21018
|
+
variant: "caption",
|
|
21019
|
+
className: cn("font-mono flex-1 min-w-0", style.text, wrap ? "whitespace-pre-wrap break-all" : "whitespace-pre"),
|
|
21020
|
+
children: [
|
|
21021
|
+
/* @__PURE__ */ jsxRuntime.jsx(Box, { as: "span", className: "select-none opacity-50 mr-2", children: style.prefix }),
|
|
21022
|
+
diffOverCapacity ? line.content || " " : /* @__PURE__ */ jsxRuntime.jsx(
|
|
21023
|
+
SyntaxHighlighter__default.default,
|
|
21024
|
+
{
|
|
21025
|
+
PreTag: "span",
|
|
21026
|
+
CodeTag: "span",
|
|
21027
|
+
language: activeLanguage,
|
|
21028
|
+
style: viewerStyle,
|
|
21029
|
+
customStyle: {
|
|
21030
|
+
display: "inline",
|
|
21031
|
+
background: "transparent",
|
|
21032
|
+
padding: 0,
|
|
21033
|
+
margin: 0,
|
|
21034
|
+
whiteSpace: wrap ? "pre-wrap" : "pre",
|
|
21035
|
+
wordBreak: wrap ? "break-all" : "normal",
|
|
21036
|
+
fontFamily: "inherit",
|
|
21037
|
+
fontSize: "inherit",
|
|
21038
|
+
lineHeight: "inherit"
|
|
21039
|
+
},
|
|
21040
|
+
codeTagProps: {
|
|
21041
|
+
style: {
|
|
21042
|
+
whiteSpace: wrap ? "pre-wrap" : "pre",
|
|
21043
|
+
fontFamily: "inherit",
|
|
21044
|
+
fontSize: "inherit"
|
|
21045
|
+
}
|
|
21046
|
+
},
|
|
21047
|
+
children: line.content || " "
|
|
21048
|
+
}
|
|
21049
|
+
)
|
|
21050
|
+
]
|
|
21051
|
+
}
|
|
21052
|
+
)
|
|
21053
|
+
] }, idx);
|
|
21054
|
+
});
|
|
21055
|
+
}, [diffLines, showLineNumbers, wrap, diffOverCapacity, activeLanguage, viewerStyle, languageReady]);
|
|
20161
21056
|
React96.useLayoutEffect(() => {
|
|
20162
21057
|
const container = codeRef.current;
|
|
20163
21058
|
if (!container) return;
|
|
@@ -20301,7 +21196,6 @@ var init_CodeBlock = __esm({
|
|
|
20301
21196
|
label: file.label,
|
|
20302
21197
|
content: null
|
|
20303
21198
|
}));
|
|
20304
|
-
const lines = activeCode.split("\n");
|
|
20305
21199
|
return /* @__PURE__ */ jsxRuntime.jsx(Card, { className: cn("overflow-hidden", className), children: /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", flexDirection: "column" }, children: [
|
|
20306
21200
|
tabItems && tabItems.length > 1 && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "border-b border-border", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
20307
21201
|
Tabs,
|
|
@@ -20364,49 +21258,7 @@ var init_CodeBlock = __esm({
|
|
|
20364
21258
|
]
|
|
20365
21259
|
}
|
|
20366
21260
|
),
|
|
20367
|
-
/* @__PURE__ */ jsxRuntime.jsx(Box, { className: "overflow-auto bg-muted/20", style: { maxHeight }, children: diffLines ? /* @__PURE__ */ jsxRuntime.jsx("div", { style: { display: "flex", flexDirection: "column" }, className: "font-mono text-xs", children:
|
|
20368
|
-
const style = DIFF_STYLES[line.type] ?? DIFF_STYLE_FALLBACK;
|
|
20369
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "none", align: "start", className: cn(style.bg, "px-4 py-0.5"), children: [
|
|
20370
|
-
showLineNumbers && /* @__PURE__ */ jsxRuntime.jsx(
|
|
20371
|
-
Typography,
|
|
20372
|
-
{
|
|
20373
|
-
variant: "caption",
|
|
20374
|
-
color: "secondary",
|
|
20375
|
-
className: "w-8 text-right mr-3 select-none tabular-nums flex-shrink-0",
|
|
20376
|
-
children: line.lineNumber ?? ""
|
|
20377
|
-
}
|
|
20378
|
-
),
|
|
20379
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
20380
|
-
Typography,
|
|
20381
|
-
{
|
|
20382
|
-
variant: "caption",
|
|
20383
|
-
className: cn("font-mono flex-1 min-w-0", style.text, wrap ? "whitespace-pre-wrap break-all" : "whitespace-pre"),
|
|
20384
|
-
children: [
|
|
20385
|
-
/* @__PURE__ */ jsxRuntime.jsx(Box, { as: "span", className: "select-none opacity-50 mr-2", children: style.prefix }),
|
|
20386
|
-
line.content
|
|
20387
|
-
]
|
|
20388
|
-
}
|
|
20389
|
-
)
|
|
20390
|
-
] }, idx);
|
|
20391
|
-
}) }) : /* @__PURE__ */ jsxRuntime.jsx("div", { style: { display: "flex", flexDirection: "column" }, className: "font-mono text-xs", children: lines.map((line, idx) => /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "none", align: "start", className: "px-4 py-0.5 hover:bg-muted/50", children: [
|
|
20392
|
-
showLineNumbers && /* @__PURE__ */ jsxRuntime.jsx(
|
|
20393
|
-
Typography,
|
|
20394
|
-
{
|
|
20395
|
-
variant: "caption",
|
|
20396
|
-
color: "secondary",
|
|
20397
|
-
className: "w-8 text-right mr-4 select-none tabular-nums flex-shrink-0",
|
|
20398
|
-
children: idx + 1
|
|
20399
|
-
}
|
|
20400
|
-
),
|
|
20401
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
20402
|
-
Typography,
|
|
20403
|
-
{
|
|
20404
|
-
variant: "caption",
|
|
20405
|
-
className: cn("font-mono flex-1 min-w-0", wrap ? "whitespace-pre-wrap break-all" : "whitespace-pre"),
|
|
20406
|
-
children: line || " "
|
|
20407
|
-
}
|
|
20408
|
-
)
|
|
20409
|
-
] }, idx)) }) })
|
|
21261
|
+
/* @__PURE__ */ jsxRuntime.jsx(Box, { className: "overflow-auto bg-muted/20", style: { maxHeight }, children: diffLines ? /* @__PURE__ */ jsxRuntime.jsx("div", { style: { display: "flex", flexDirection: "column" }, className: "font-mono text-xs", children: diffRowElements }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "font-mono text-xs", children: viewerHighlightedElement }) })
|
|
20410
21262
|
] }) });
|
|
20411
21263
|
}
|
|
20412
21264
|
const hasHeader = showLanguageBadge || effectiveCopy;
|
|
@@ -20493,13 +21345,11 @@ var init_CodeBlock = __esm({
|
|
|
20493
21345
|
{
|
|
20494
21346
|
ref: editableTextareaRef,
|
|
20495
21347
|
defaultValue: code,
|
|
20496
|
-
onChange: (e) =>
|
|
20497
|
-
const v = e.target.value;
|
|
20498
|
-
lastPropCodeRef.current = v;
|
|
20499
|
-
setEditableValue(v);
|
|
20500
|
-
onChange?.(v);
|
|
20501
|
-
},
|
|
21348
|
+
onChange: (e) => handleEditableChange(e.target.value),
|
|
20502
21349
|
onScroll: handleEditableScroll,
|
|
21350
|
+
onSelect: (e) => setCaretIndex(e.currentTarget.selectionStart),
|
|
21351
|
+
onFocus: editorId ? () => eventBus.emit(`UI:${onEditorFocus}`, { editorId }) : void 0,
|
|
21352
|
+
onBlur: editorId ? () => eventBus.emit(`UI:${onEditorBlur}`, { editorId }) : void 0,
|
|
20503
21353
|
spellCheck: false,
|
|
20504
21354
|
style: {
|
|
20505
21355
|
position: "absolute",
|
|
@@ -20514,7 +21364,7 @@ var init_CodeBlock = __esm({
|
|
|
20514
21364
|
resize: "none",
|
|
20515
21365
|
backgroundColor: "transparent",
|
|
20516
21366
|
color: "transparent",
|
|
20517
|
-
caretColor: "#e6e6e6",
|
|
21367
|
+
caretColor: caretMode === "block" ? "transparent" : "#e6e6e6",
|
|
20518
21368
|
WebkitTextFillColor: "transparent",
|
|
20519
21369
|
fontFamily: 'ui-monospace, SFMono-Regular, Menlo, Monaco, "Cascadia Mono", "Courier New", monospace',
|
|
20520
21370
|
fontSize: "13px",
|
|
@@ -20525,6 +21375,22 @@ var init_CodeBlock = __esm({
|
|
|
20525
21375
|
}
|
|
20526
21376
|
},
|
|
20527
21377
|
editableTextareaKey
|
|
21378
|
+
),
|
|
21379
|
+
caretMode !== "bar" && /* @__PURE__ */ jsxRuntime.jsx(
|
|
21380
|
+
"span",
|
|
21381
|
+
{
|
|
21382
|
+
"aria-hidden": true,
|
|
21383
|
+
style: {
|
|
21384
|
+
position: "absolute",
|
|
21385
|
+
top: `calc(1rem + ${caretRowCol.row * 19.5}px)`,
|
|
21386
|
+
left: `calc(1rem + ${caretRowCol.col}ch)`,
|
|
21387
|
+
width: "1ch",
|
|
21388
|
+
height: caretMode === "block" ? "19.5px" : "2px",
|
|
21389
|
+
backgroundColor: caretMode === "block" ? "rgba(230, 230, 230, 0.5)" : void 0,
|
|
21390
|
+
borderBottom: caretMode === "underline" ? "2px solid #e6e6e6" : void 0,
|
|
21391
|
+
pointerEvents: "none"
|
|
21392
|
+
}
|
|
21393
|
+
}
|
|
20528
21394
|
)
|
|
20529
21395
|
]
|
|
20530
21396
|
}
|
|
@@ -20552,55 +21418,175 @@ var init_CodeBlock = __esm({
|
|
|
20552
21418
|
)
|
|
20553
21419
|
] });
|
|
20554
21420
|
},
|
|
20555
|
-
(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
|
|
21421
|
+
(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
|
|
20556
21422
|
);
|
|
20557
21423
|
CodeBlock.displayName = "CodeBlock";
|
|
20558
21424
|
}
|
|
20559
21425
|
});
|
|
21426
|
+
|
|
21427
|
+
// components/core/molecules/markdown/mermaidSource.ts
|
|
21428
|
+
function isQuoted(label) {
|
|
21429
|
+
const trimmed = label.trim();
|
|
21430
|
+
return trimmed.length === 0 || trimmed.startsWith('"') && trimmed.endsWith('"') && trimmed.length > 1;
|
|
21431
|
+
}
|
|
21432
|
+
function quote(label) {
|
|
21433
|
+
return `"${label.replace(/"/g, "#quot;")}"`;
|
|
21434
|
+
}
|
|
21435
|
+
function isIdentifierChar(ch) {
|
|
21436
|
+
return /[A-Za-z0-9_\-.]/.test(ch);
|
|
21437
|
+
}
|
|
21438
|
+
function declaredType(code) {
|
|
21439
|
+
for (const line of code.split("\n")) {
|
|
21440
|
+
const trimmed = line.trim();
|
|
21441
|
+
if (trimmed.length === 0 || trimmed.startsWith("%%")) continue;
|
|
21442
|
+
return trimmed;
|
|
21443
|
+
}
|
|
21444
|
+
return "";
|
|
21445
|
+
}
|
|
21446
|
+
function isFlowchart(code) {
|
|
21447
|
+
return FLOWCHART_DIRECTIVE.test(declaredType(code));
|
|
21448
|
+
}
|
|
21449
|
+
function quoteNodeLabels(code) {
|
|
21450
|
+
let out = "";
|
|
21451
|
+
let i = 0;
|
|
21452
|
+
while (i < code.length) {
|
|
21453
|
+
const shape = NODE_SHAPES.find(([open2]) => code.startsWith(open2, i));
|
|
21454
|
+
const precededByIdentifier = i > 0 && isIdentifierChar(code[i - 1] ?? "");
|
|
21455
|
+
if (shape === void 0 || !precededByIdentifier) {
|
|
21456
|
+
out += code[i];
|
|
21457
|
+
i += 1;
|
|
21458
|
+
continue;
|
|
21459
|
+
}
|
|
21460
|
+
const [open, close] = shape;
|
|
21461
|
+
const contentStart = i + open.length;
|
|
21462
|
+
const closeAt = code.indexOf(close, contentStart);
|
|
21463
|
+
const newlineAt = code.indexOf("\n", contentStart);
|
|
21464
|
+
if (closeAt === -1 || newlineAt !== -1 && newlineAt < closeAt) {
|
|
21465
|
+
out += code[i];
|
|
21466
|
+
i += 1;
|
|
21467
|
+
continue;
|
|
21468
|
+
}
|
|
21469
|
+
const label = code.slice(contentStart, closeAt);
|
|
21470
|
+
out += open + (isQuoted(label) ? label : quote(label)) + close;
|
|
21471
|
+
i = closeAt + close.length;
|
|
21472
|
+
}
|
|
21473
|
+
return out;
|
|
21474
|
+
}
|
|
21475
|
+
function quoteEdgeLabels(code) {
|
|
21476
|
+
return code.split("\n").map((line) => {
|
|
21477
|
+
let out = "";
|
|
21478
|
+
let rest = line;
|
|
21479
|
+
for (; ; ) {
|
|
21480
|
+
const open = rest.indexOf("|");
|
|
21481
|
+
if (open === -1) break;
|
|
21482
|
+
const close = rest.indexOf("|", open + 1);
|
|
21483
|
+
if (close === -1) break;
|
|
21484
|
+
const label = rest.slice(open + 1, close);
|
|
21485
|
+
out += rest.slice(0, open + 1) + (isQuoted(label) ? label : quote(label)) + "|";
|
|
21486
|
+
rest = rest.slice(close + 1);
|
|
21487
|
+
}
|
|
21488
|
+
return out + rest;
|
|
21489
|
+
}).join("\n");
|
|
21490
|
+
}
|
|
21491
|
+
function quoteSubgraphTitles(code) {
|
|
21492
|
+
return code.split("\n").map((line) => {
|
|
21493
|
+
const match = /^(\s*subgraph\s+)(.+?)(\s*)$/.exec(line);
|
|
21494
|
+
if (match === null) return line;
|
|
21495
|
+
const [, prefix, title, trailing] = match;
|
|
21496
|
+
if (title === void 0 || prefix === void 0) return line;
|
|
21497
|
+
if (isQuoted(title) || title.includes("[")) return line;
|
|
21498
|
+
return prefix + quote(title) + (trailing ?? "");
|
|
21499
|
+
}).join("\n");
|
|
21500
|
+
}
|
|
21501
|
+
function mermaidRepairCandidates(code) {
|
|
21502
|
+
if (!isFlowchart(code)) return [];
|
|
21503
|
+
const nodes = quoteNodeLabels(code);
|
|
21504
|
+
const nodesAndEdges = quoteEdgeLabels(nodes);
|
|
21505
|
+
const all = quoteSubgraphTitles(nodesAndEdges);
|
|
21506
|
+
const ordered2 = [nodes, nodesAndEdges, all];
|
|
21507
|
+
const seen = /* @__PURE__ */ new Set([code]);
|
|
21508
|
+
const candidates = [];
|
|
21509
|
+
for (const candidate of ordered2) {
|
|
21510
|
+
if (seen.has(candidate)) continue;
|
|
21511
|
+
seen.add(candidate);
|
|
21512
|
+
candidates.push(candidate);
|
|
21513
|
+
}
|
|
21514
|
+
return candidates;
|
|
21515
|
+
}
|
|
21516
|
+
var NODE_SHAPES, FLOWCHART_DIRECTIVE;
|
|
21517
|
+
var init_mermaidSource = __esm({
|
|
21518
|
+
"components/core/molecules/markdown/mermaidSource.ts"() {
|
|
21519
|
+
NODE_SHAPES = [
|
|
21520
|
+
["[[", "]]"],
|
|
21521
|
+
["[(", ")]"],
|
|
21522
|
+
["([", "])"],
|
|
21523
|
+
["((", "))"],
|
|
21524
|
+
["{{", "}}"],
|
|
21525
|
+
["[", "]"],
|
|
21526
|
+
["(", ")"],
|
|
21527
|
+
["{", "}"]
|
|
21528
|
+
];
|
|
21529
|
+
FLOWCHART_DIRECTIVE = /^(?:graph|flowchart)\b/;
|
|
21530
|
+
}
|
|
21531
|
+
});
|
|
20560
21532
|
function loadMermaid() {
|
|
20561
21533
|
mermaidModule ?? (mermaidModule = import('mermaid').then((m) => m.default));
|
|
20562
21534
|
return mermaidModule;
|
|
20563
21535
|
}
|
|
20564
|
-
var mermaidModule, MermaidDiagram;
|
|
21536
|
+
var log5, mermaidModule, MermaidDiagram;
|
|
20565
21537
|
var init_MermaidDiagram = __esm({
|
|
20566
21538
|
"components/core/molecules/markdown/MermaidDiagram.tsx"() {
|
|
20567
21539
|
init_Box();
|
|
20568
21540
|
init_Typography();
|
|
20569
21541
|
init_CodeBlock();
|
|
21542
|
+
init_mermaidSource();
|
|
20570
21543
|
init_cn();
|
|
21544
|
+
log5 = logger.createLogger("almadar:ui:mermaid-diagram");
|
|
20571
21545
|
mermaidModule = null;
|
|
20572
21546
|
MermaidDiagram = React96__namespace.default.memo(
|
|
20573
21547
|
({ code, className }) => {
|
|
20574
21548
|
const { resolvedMode } = context.useTheme();
|
|
21549
|
+
const { t } = hooks.useTranslate();
|
|
20575
21550
|
const containerRef = React96.useRef(null);
|
|
20576
|
-
const [
|
|
21551
|
+
const [unrenderable, setUnrenderable] = React96.useState(false);
|
|
20577
21552
|
const reactId = React96.useId();
|
|
20578
21553
|
React96.useEffect(() => {
|
|
20579
21554
|
let active = true;
|
|
20580
21555
|
void (async () => {
|
|
20581
|
-
|
|
20582
|
-
|
|
20583
|
-
|
|
20584
|
-
|
|
20585
|
-
|
|
20586
|
-
|
|
20587
|
-
|
|
20588
|
-
|
|
20589
|
-
|
|
20590
|
-
|
|
20591
|
-
|
|
20592
|
-
|
|
20593
|
-
|
|
20594
|
-
|
|
21556
|
+
const mermaid = await loadMermaid();
|
|
21557
|
+
mermaid.initialize({
|
|
21558
|
+
startOnLoad: false,
|
|
21559
|
+
securityLevel: "strict",
|
|
21560
|
+
theme: resolvedMode === "dark" ? "dark" : "default"
|
|
21561
|
+
});
|
|
21562
|
+
const domId = `mermaid-${reactId.replace(/[^a-zA-Z0-9]/g, "")}`;
|
|
21563
|
+
let firstError = null;
|
|
21564
|
+
for (const [index, source] of [code, ...mermaidRepairCandidates(code)].entries()) {
|
|
21565
|
+
try {
|
|
21566
|
+
const { svg } = await mermaid.render(domId, source);
|
|
21567
|
+
if (!active) return;
|
|
21568
|
+
const container = containerRef.current;
|
|
21569
|
+
if (container === null) return;
|
|
21570
|
+
container.innerHTML = svg;
|
|
21571
|
+
container.dataset.mermaidRepaired = String(index > 0);
|
|
21572
|
+
setUnrenderable(false);
|
|
21573
|
+
if (index > 0) log5.debug("mermaid:repaired", { candidate: index });
|
|
21574
|
+
return;
|
|
21575
|
+
} catch (err) {
|
|
21576
|
+
firstError ?? (firstError = err instanceof Error ? err : new Error(String(err)));
|
|
21577
|
+
}
|
|
20595
21578
|
}
|
|
21579
|
+
if (!active) return;
|
|
21580
|
+
log5.warn("mermaid:unrenderable", { error: firstError?.message ?? "", code });
|
|
21581
|
+
setUnrenderable(true);
|
|
20596
21582
|
})();
|
|
20597
21583
|
return () => {
|
|
20598
21584
|
active = false;
|
|
20599
21585
|
};
|
|
20600
21586
|
}, [code, resolvedMode, reactId]);
|
|
20601
21587
|
return /* @__PURE__ */ jsxRuntime.jsxs(Box, { className: cn("not-prose my-4", className), children: [
|
|
20602
|
-
|
|
20603
|
-
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", className: "text-
|
|
21588
|
+
unrenderable && /* @__PURE__ */ jsxRuntime.jsxs(Box, { className: "space-y-2 mb-2", "data-testid": "mermaid-unrenderable", children: [
|
|
21589
|
+
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", className: "text-muted-foreground", children: t("mermaid.unrenderable") }),
|
|
20604
21590
|
/* @__PURE__ */ jsxRuntime.jsx(CodeBlock, { code, language: "mermaid" })
|
|
20605
21591
|
] }),
|
|
20606
21592
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -20609,7 +21595,7 @@ var init_MermaidDiagram = __esm({
|
|
|
20609
21595
|
ref: containerRef,
|
|
20610
21596
|
"data-testid": "mermaid-diagram",
|
|
20611
21597
|
className: "overflow-x-auto",
|
|
20612
|
-
style:
|
|
21598
|
+
style: unrenderable ? { display: "none" } : void 0
|
|
20613
21599
|
}
|
|
20614
21600
|
)
|
|
20615
21601
|
] });
|
|
@@ -24014,14 +25000,14 @@ function useSafeEventBus2() {
|
|
|
24014
25000
|
} };
|
|
24015
25001
|
}
|
|
24016
25002
|
}
|
|
24017
|
-
var
|
|
25003
|
+
var log6, lookStyles4, ButtonGroup;
|
|
24018
25004
|
var init_ButtonGroup = __esm({
|
|
24019
25005
|
"components/core/molecules/ButtonGroup.tsx"() {
|
|
24020
25006
|
"use client";
|
|
24021
25007
|
init_cn();
|
|
24022
25008
|
init_atoms();
|
|
24023
25009
|
init_useEventBus();
|
|
24024
|
-
|
|
25010
|
+
log6 = logger.createLogger("almadar:ui:button-group");
|
|
24025
25011
|
lookStyles4 = {
|
|
24026
25012
|
"right-aligned-buttons": "",
|
|
24027
25013
|
"floating-bar": "fixed bottom-section left-1/2 -translate-x-1/2 shadow-elevation-toast bg-card p-card-sm rounded-container",
|
|
@@ -24102,7 +25088,7 @@ var init_ButtonGroup = __esm({
|
|
|
24102
25088
|
{
|
|
24103
25089
|
variant: "ghost",
|
|
24104
25090
|
onClick: () => {
|
|
24105
|
-
|
|
25091
|
+
log6.debug("Filter clicked", { field: filter.field });
|
|
24106
25092
|
},
|
|
24107
25093
|
children: filter.label
|
|
24108
25094
|
},
|
|
@@ -26886,6 +27872,12 @@ function commandMatches(command, query) {
|
|
|
26886
27872
|
if (matchesQuery(query, command.label)) return true;
|
|
26887
27873
|
return (command.keywords ?? []).some((keyword) => matchesQuery(query, keyword));
|
|
26888
27874
|
}
|
|
27875
|
+
function dispatchCommandPaletteCommand(command, deps) {
|
|
27876
|
+
if (command.disabled) return;
|
|
27877
|
+
if (command.event) deps.emit(`UI:${command.event}`, { commandId: command.id });
|
|
27878
|
+
if (command.action) deps.emit(`UI:${command.action}`, command.actionPayload ?? {});
|
|
27879
|
+
deps.onSelect?.(command);
|
|
27880
|
+
}
|
|
26889
27881
|
var UNGROUPED, CommandPalette;
|
|
26890
27882
|
var init_CommandPalette = __esm({
|
|
26891
27883
|
"components/core/molecules/CommandPalette.tsx"() {
|
|
@@ -26940,9 +27932,7 @@ var init_CommandPalette = __esm({
|
|
|
26940
27932
|
const handleSelect = React96.useCallback(
|
|
26941
27933
|
(command) => {
|
|
26942
27934
|
if (command.disabled) return;
|
|
26943
|
-
|
|
26944
|
-
if (command.action) eventBus.emit(`UI:${command.action}`, command.actionPayload ?? {});
|
|
26945
|
-
onSelect?.(command);
|
|
27935
|
+
dispatchCommandPaletteCommand(command, { emit: eventBus.emit, onSelect });
|
|
26946
27936
|
handleClose();
|
|
26947
27937
|
},
|
|
26948
27938
|
[eventBus, onSelect, handleClose]
|
|
@@ -31921,9 +32911,9 @@ function debug(...args) {
|
|
|
31921
32911
|
const [first, ...rest] = args;
|
|
31922
32912
|
const message = typeof first === "string" ? first : "<debug>";
|
|
31923
32913
|
if (rest.length === 0 && typeof first === "string") {
|
|
31924
|
-
|
|
32914
|
+
log7.debug(message);
|
|
31925
32915
|
} else {
|
|
31926
|
-
|
|
32916
|
+
log7.debug(message, { args: rest.length > 0 ? formatArgs(rest) : formatArgs([first]) });
|
|
31927
32917
|
}
|
|
31928
32918
|
}
|
|
31929
32919
|
function debugGroup(label) {
|
|
@@ -31951,11 +32941,11 @@ function toLogMetaValue(v) {
|
|
|
31951
32941
|
}
|
|
31952
32942
|
return String(v);
|
|
31953
32943
|
}
|
|
31954
|
-
var NAMESPACE,
|
|
32944
|
+
var NAMESPACE, log7;
|
|
31955
32945
|
var init_debug = __esm({
|
|
31956
32946
|
"lib/debug.ts"() {
|
|
31957
32947
|
NAMESPACE = "almadar:ui:debug";
|
|
31958
|
-
|
|
32948
|
+
log7 = logger.createLogger(NAMESPACE);
|
|
31959
32949
|
logger.createLogger("almadar:ui:debug:input");
|
|
31960
32950
|
logger.createLogger("almadar:ui:debug:collision");
|
|
31961
32951
|
logger.createLogger("almadar:ui:debug:physics");
|
|
@@ -33608,6 +34598,7 @@ var init_MathCanvas = __esm({
|
|
|
33608
34598
|
init_perf();
|
|
33609
34599
|
init_atoms();
|
|
33610
34600
|
init_Stack();
|
|
34601
|
+
init_gameFonts();
|
|
33611
34602
|
init_LearningCanvas();
|
|
33612
34603
|
MathCanvas = ({
|
|
33613
34604
|
className,
|
|
@@ -33627,6 +34618,7 @@ var init_MathCanvas = __esm({
|
|
|
33627
34618
|
showTickLabels = false,
|
|
33628
34619
|
tickLabelFontSize = 10,
|
|
33629
34620
|
labelFontSize = 12,
|
|
34621
|
+
fontFamily: fontFamilyProp,
|
|
33630
34622
|
showCurveLabels = false,
|
|
33631
34623
|
curves = [],
|
|
33632
34624
|
points = [],
|
|
@@ -33649,6 +34641,7 @@ var init_MathCanvas = __esm({
|
|
|
33649
34641
|
error
|
|
33650
34642
|
}) => {
|
|
33651
34643
|
const eventBus = useEventBus();
|
|
34644
|
+
const fontFamily = resolveGameFontFamily(fontFamilyProp);
|
|
33652
34645
|
const keyMapKey = keyMap ? JSON.stringify(keyMap) : null;
|
|
33653
34646
|
const keyUpMapKey = keyUpMap ? JSON.stringify(keyUpMap) : null;
|
|
33654
34647
|
const stableKeyMap = React96.useMemo(() => keyMap, [keyMapKey]);
|
|
@@ -33698,18 +34691,18 @@ var init_MathCanvas = __esm({
|
|
|
33698
34691
|
let kx = 0;
|
|
33699
34692
|
for (let x = Math.ceil(xMin / gridStep) * gridStep; x <= xMax; x += gridStep, kx++) {
|
|
33700
34693
|
if (kx % labelEveryX === 0 && x !== 0) {
|
|
33701
|
-
out.push({ type: "text", x: mapX(x), y: xAxisY + 12, text: formatTick(x), color: "#6b7280", fontSize: tickLabelFontSize, align: "center" });
|
|
34694
|
+
out.push({ type: "text", fontFamily, x: mapX(x), y: xAxisY + 12, text: formatTick(x), color: "#6b7280", fontSize: tickLabelFontSize, align: "center" });
|
|
33702
34695
|
}
|
|
33703
34696
|
}
|
|
33704
34697
|
const labelEveryY = Math.max(1, Math.ceil((yMax - yMin) / gridStep / Math.floor(plotH / 28)));
|
|
33705
34698
|
let ky = 0;
|
|
33706
34699
|
for (let y = Math.ceil(yMin / gridStep) * gridStep; y <= yMax; y += gridStep, ky++) {
|
|
33707
34700
|
if (ky % labelEveryY === 0 && y !== 0) {
|
|
33708
|
-
out.push({ type: "text", x: yAxisX - 6, y: mapY(y), text: formatTick(y), color: "#6b7280", fontSize: tickLabelFontSize, align: "right" });
|
|
34701
|
+
out.push({ type: "text", fontFamily, x: yAxisX - 6, y: mapY(y), text: formatTick(y), color: "#6b7280", fontSize: tickLabelFontSize, align: "right" });
|
|
33709
34702
|
}
|
|
33710
34703
|
}
|
|
33711
34704
|
if (xMin <= 0 && xMax >= 0 && yMin <= 0 && yMax >= 0) {
|
|
33712
|
-
out.push({ type: "text", x: yAxisX - 6, y: xAxisY + 12, text: "0", color: "#6b7280", fontSize: tickLabelFontSize, align: "right" });
|
|
34705
|
+
out.push({ type: "text", fontFamily, x: yAxisX - 6, y: xAxisY + 12, text: "0", color: "#6b7280", fontSize: tickLabelFontSize, align: "right" });
|
|
33713
34706
|
}
|
|
33714
34707
|
}
|
|
33715
34708
|
for (const region of regions) {
|
|
@@ -33736,6 +34729,7 @@ var init_MathCanvas = __esm({
|
|
|
33736
34729
|
const mid = Math.floor(region.samples.length / 2);
|
|
33737
34730
|
out.push({
|
|
33738
34731
|
type: "text",
|
|
34732
|
+
fontFamily,
|
|
33739
34733
|
x: mapX((first.x + last.x) / 2),
|
|
33740
34734
|
y: (mapY(region.samples[mid].y) + mapY(baseline)) / 2,
|
|
33741
34735
|
text: region.label,
|
|
@@ -33772,14 +34766,14 @@ var init_MathCanvas = __esm({
|
|
|
33772
34766
|
const px = mapX(guide.at);
|
|
33773
34767
|
out.push({ type: "line", x1: px, y1: margin, x2: px, y2: height - margin, color, dash });
|
|
33774
34768
|
if (guide.label) {
|
|
33775
|
-
out.push({ type: "text", x: px + 4, y: margin + 10, text: guide.label, color, fontSize: 11 });
|
|
34769
|
+
out.push({ type: "text", fontFamily, x: px + 4, y: margin + 10, text: guide.label, color, fontSize: 11 });
|
|
33776
34770
|
}
|
|
33777
34771
|
} else {
|
|
33778
34772
|
if (guide.at < yMin || guide.at > yMax) continue;
|
|
33779
34773
|
const py = mapY(guide.at);
|
|
33780
34774
|
out.push({ type: "line", x1: margin, y1: py, x2: width - margin, y2: py, color, dash });
|
|
33781
34775
|
if (guide.label) {
|
|
33782
|
-
out.push({ type: "text", x: width - margin - 4, y: py - 8, text: guide.label, color, fontSize: labelFontSize, align: "right" });
|
|
34776
|
+
out.push({ type: "text", fontFamily, x: width - margin - 4, y: py - 8, text: guide.label, color, fontSize: labelFontSize, align: "right" });
|
|
33783
34777
|
}
|
|
33784
34778
|
}
|
|
33785
34779
|
}
|
|
@@ -33818,6 +34812,7 @@ var init_MathCanvas = __esm({
|
|
|
33818
34812
|
if (showCurveLabels && curve.label && lastInRange) {
|
|
33819
34813
|
out.push({
|
|
33820
34814
|
type: "text",
|
|
34815
|
+
fontFamily,
|
|
33821
34816
|
x: mapX(lastInRange.x) + 6,
|
|
33822
34817
|
y: mapY(lastInRange.y) - 6,
|
|
33823
34818
|
text: curve.label,
|
|
@@ -33855,6 +34850,7 @@ var init_MathCanvas = __esm({
|
|
|
33855
34850
|
if (hop.label) {
|
|
33856
34851
|
out.push({
|
|
33857
34852
|
type: "text",
|
|
34853
|
+
fontFamily,
|
|
33858
34854
|
x: (x1 + x2) / 2,
|
|
33859
34855
|
y: xAxisY - peak - 8,
|
|
33860
34856
|
text: hop.label,
|
|
@@ -33882,6 +34878,7 @@ var init_MathCanvas = __esm({
|
|
|
33882
34878
|
const rad = mid * Math.PI / 180;
|
|
33883
34879
|
out.push({
|
|
33884
34880
|
type: "text",
|
|
34881
|
+
fontFamily,
|
|
33885
34882
|
x: mapX(angle.x + 1.35 * radius * Math.cos(rad)),
|
|
33886
34883
|
y: mapY(angle.y + 1.35 * radius * Math.sin(rad)),
|
|
33887
34884
|
text: angle.label,
|
|
@@ -33903,7 +34900,7 @@ var init_MathCanvas = __esm({
|
|
|
33903
34900
|
fill: isOpen ? "#ffffff" : p.color ?? "#dc2626"
|
|
33904
34901
|
});
|
|
33905
34902
|
if (p.label) {
|
|
33906
|
-
out.push({ type: "text", x: mapX(p.x) + 8, y: mapY(p.y) - 8, text: p.label, color: p.color ?? "#111827", fontSize: labelFontSize });
|
|
34903
|
+
out.push({ type: "text", fontFamily, x: mapX(p.x) + 8, y: mapY(p.y) - 8, text: p.label, color: p.color ?? "#111827", fontSize: labelFontSize });
|
|
33907
34904
|
}
|
|
33908
34905
|
}
|
|
33909
34906
|
for (const v of vectors) {
|
|
@@ -33914,7 +34911,7 @@ var init_MathCanvas = __esm({
|
|
|
33914
34911
|
const y2 = mapY(v.y + v.vy);
|
|
33915
34912
|
out.push({ type: "arrow", x1, y1, x2, y2, color: v.color ?? "#7c3aed", lineWidth: 2 });
|
|
33916
34913
|
if (v.label) {
|
|
33917
|
-
out.push({ type: "text", x: x2 + 6, y: y2 - 6, text: v.label, color: v.color ?? "#7c3aed", fontSize: labelFontSize });
|
|
34914
|
+
out.push({ type: "text", fontFamily, x: x2 + 6, y: y2 - 6, text: v.label, color: v.color ?? "#7c3aed", fontSize: labelFontSize });
|
|
33918
34915
|
}
|
|
33919
34916
|
}
|
|
33920
34917
|
out.push(...shapes);
|
|
@@ -33935,6 +34932,7 @@ var init_MathCanvas = __esm({
|
|
|
33935
34932
|
showTickLabels,
|
|
33936
34933
|
tickLabelFontSize,
|
|
33937
34934
|
labelFontSize,
|
|
34935
|
+
fontFamily,
|
|
33938
34936
|
showCurveLabels,
|
|
33939
34937
|
curves,
|
|
33940
34938
|
points,
|
|
@@ -33990,6 +34988,7 @@ var init_MathCanvas = __esm({
|
|
|
33990
34988
|
width,
|
|
33991
34989
|
height,
|
|
33992
34990
|
backgroundColor,
|
|
34991
|
+
fontFamily,
|
|
33993
34992
|
shapes: derivedShapes,
|
|
33994
34993
|
drawables,
|
|
33995
34994
|
projector,
|
|
@@ -35222,13 +36221,13 @@ var init_MapView = __esm({
|
|
|
35222
36221
|
shadowSize: [41, 41]
|
|
35223
36222
|
});
|
|
35224
36223
|
L.Marker.prototype.options.icon = defaultIcon;
|
|
35225
|
-
const { useEffect:
|
|
36224
|
+
const { useEffect: useEffect70, useRef: useRef70, useCallback: useCallback100, useState: useState109 } = React96__namespace.default;
|
|
35226
36225
|
const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
|
|
35227
36226
|
const { useEventBus: useEventBus2 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
|
|
35228
36227
|
function MapUpdater({ centerLat, centerLng, zoom }) {
|
|
35229
36228
|
const map = useMap();
|
|
35230
|
-
const prevRef =
|
|
35231
|
-
|
|
36229
|
+
const prevRef = useRef70({ centerLat, centerLng, zoom });
|
|
36230
|
+
useEffect70(() => {
|
|
35232
36231
|
const prev = prevRef.current;
|
|
35233
36232
|
if (prev.centerLat !== centerLat || prev.centerLng !== centerLng || prev.zoom !== zoom) {
|
|
35234
36233
|
map.setView([centerLat, centerLng], zoom);
|
|
@@ -35239,7 +36238,7 @@ var init_MapView = __esm({
|
|
|
35239
36238
|
}
|
|
35240
36239
|
function MapClickHandler({ onMapClick }) {
|
|
35241
36240
|
const map = useMap();
|
|
35242
|
-
|
|
36241
|
+
useEffect70(() => {
|
|
35243
36242
|
if (!onMapClick) return;
|
|
35244
36243
|
const handler = (e) => {
|
|
35245
36244
|
onMapClick(e.latlng.lat, e.latlng.lng);
|
|
@@ -35267,8 +36266,8 @@ var init_MapView = __esm({
|
|
|
35267
36266
|
showAttribution = true
|
|
35268
36267
|
}) {
|
|
35269
36268
|
const eventBus = useEventBus2();
|
|
35270
|
-
const [clickedPosition, setClickedPosition] =
|
|
35271
|
-
const handleMapClick =
|
|
36269
|
+
const [clickedPosition, setClickedPosition] = useState109(null);
|
|
36270
|
+
const handleMapClick = useCallback100((lat, lng) => {
|
|
35272
36271
|
if (showClickedPin) {
|
|
35273
36272
|
setClickedPosition({ lat, lng });
|
|
35274
36273
|
}
|
|
@@ -35277,7 +36276,7 @@ var init_MapView = __esm({
|
|
|
35277
36276
|
eventBus.emit(`UI:${mapClickEvent}`, { latitude: lat, longitude: lng });
|
|
35278
36277
|
}
|
|
35279
36278
|
}, [onMapClick, mapClickEvent, eventBus, showClickedPin]);
|
|
35280
|
-
const handleMarkerClick =
|
|
36279
|
+
const handleMarkerClick = useCallback100((marker) => {
|
|
35281
36280
|
onMarkerClick?.(marker);
|
|
35282
36281
|
if (markerClickEvent) {
|
|
35283
36282
|
eventBus.emit(`UI:${markerClickEvent}`, { ...marker });
|
|
@@ -49586,7 +50585,7 @@ function getAllEvents(traits2) {
|
|
|
49586
50585
|
function EventDispatcherTab({ traits: traits2, schema }) {
|
|
49587
50586
|
const eventBus = useEventBus();
|
|
49588
50587
|
const { t } = hooks.useTranslate();
|
|
49589
|
-
const [
|
|
50588
|
+
const [log12, setLog] = React96__namespace.useState([]);
|
|
49590
50589
|
const prevStatesRef = React96__namespace.useRef(/* @__PURE__ */ new Map());
|
|
49591
50590
|
React96__namespace.useEffect(() => {
|
|
49592
50591
|
for (const trait of traits2) {
|
|
@@ -49650,9 +50649,9 @@ function EventDispatcherTab({ traits: traits2, schema }) {
|
|
|
49650
50649
|
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", weight: "medium", className: "text-muted-foreground mb-1", children: t("debug.otherEvents") }),
|
|
49651
50650
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-wrap gap-1", children: unavailableEvents.map((event) => /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: "default", size: "sm", className: "opacity-50", children: event }, event)) })
|
|
49652
50651
|
] }),
|
|
49653
|
-
|
|
50652
|
+
log12.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
49654
50653
|
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", weight: "medium", className: "text-muted-foreground mb-1", children: t("debug.recentTransitions") }),
|
|
49655
|
-
/* @__PURE__ */ jsxRuntime.jsx(Stack, { gap: "xs", children:
|
|
50654
|
+
/* @__PURE__ */ jsxRuntime.jsx(Stack, { gap: "xs", children: log12.map((entry, i) => /* @__PURE__ */ jsxRuntime.jsxs(Typography, { variant: "small", className: "font-mono text-xs", children: [
|
|
49656
50655
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-primary", children: entry.traitName }),
|
|
49657
50656
|
" ",
|
|
49658
50657
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: entry.from }),
|
|
@@ -51843,6 +52842,7 @@ var init_component_registry_generated = __esm({
|
|
|
51843
52842
|
init_FormSection();
|
|
51844
52843
|
init_FormSectionHeader();
|
|
51845
52844
|
init_FxOverlay();
|
|
52845
|
+
init_GameAudioCue();
|
|
51846
52846
|
init_GameAudioToggle();
|
|
51847
52847
|
init_GameHud();
|
|
51848
52848
|
init_GameIcon();
|
|
@@ -52120,6 +53120,7 @@ var init_component_registry_generated = __esm({
|
|
|
52120
53120
|
"FormLayout": FormLayout,
|
|
52121
53121
|
"FormSectionHeader": FormSectionHeader,
|
|
52122
53122
|
"FxOverlay": FxOverlay,
|
|
53123
|
+
"GameAudioCue": GameAudioCue,
|
|
52123
53124
|
"GameAudioToggle": GameAudioToggle,
|
|
52124
53125
|
"GameHud": GameHud,
|
|
52125
53126
|
"GameIcon": GameIcon,
|
|
@@ -56916,7 +57917,7 @@ function convertFnFormLambdasInProps(props) {
|
|
|
56916
57917
|
|
|
56917
57918
|
// hooks/index.ts
|
|
56918
57919
|
init_useEventBus();
|
|
56919
|
-
var
|
|
57920
|
+
var log8 = logger.createLogger("almadar:ui:shared-entity-store");
|
|
56920
57921
|
var EMPTY_ENTITY_STATE = {};
|
|
56921
57922
|
function createSharedEntityStore() {
|
|
56922
57923
|
const states = /* @__PURE__ */ new Map();
|
|
@@ -56946,7 +57947,7 @@ function createSharedEntityStore() {
|
|
|
56946
57947
|
try {
|
|
56947
57948
|
callback();
|
|
56948
57949
|
} catch (error) {
|
|
56949
|
-
|
|
57950
|
+
log8.error("Shared entity subscriber error", {
|
|
56950
57951
|
entityId,
|
|
56951
57952
|
error: error instanceof Error ? error : String(error)
|
|
56952
57953
|
});
|
|
@@ -56977,7 +57978,7 @@ function runTickFrame(entityId, orderedWriters, store) {
|
|
|
56977
57978
|
store.commit(entityId, scratch);
|
|
56978
57979
|
return scratch;
|
|
56979
57980
|
}
|
|
56980
|
-
var
|
|
57981
|
+
var log9 = logger.createLogger("almadar:ui:effects:client-handlers");
|
|
56981
57982
|
function createClientEffectHandlers(options) {
|
|
56982
57983
|
const { eventBus, slotSetter, navigate, navigateBack, notify, callService, liveEntity } = options;
|
|
56983
57984
|
return {
|
|
@@ -56986,12 +57987,12 @@ function createClientEffectHandlers(options) {
|
|
|
56986
57987
|
eventBus.emit(prefixedEvent, payload, source);
|
|
56987
57988
|
},
|
|
56988
57989
|
persist: async () => {
|
|
56989
|
-
|
|
57990
|
+
log9.warn("persist is server-side only, ignored on client");
|
|
56990
57991
|
},
|
|
56991
57992
|
// @almadar/runtime EffectHandlers.set types value:unknown — should be FieldValue (upstream fix queued)
|
|
56992
57993
|
set: ((_entityId, field, value) => {
|
|
56993
57994
|
if (!liveEntity) {
|
|
56994
|
-
|
|
57995
|
+
log9.warn("set is server-side only, ignored on client (no live entity)");
|
|
56995
57996
|
return;
|
|
56996
57997
|
}
|
|
56997
57998
|
liveEntity[field] = value;
|
|
@@ -57027,13 +58028,13 @@ function createClientEffectHandlers(options) {
|
|
|
57027
58028
|
window.location.href = path;
|
|
57028
58029
|
return;
|
|
57029
58030
|
}
|
|
57030
|
-
|
|
58031
|
+
log9.warn("No navigate handler, ignoring", { path });
|
|
57031
58032
|
}),
|
|
57032
58033
|
navigateBack: navigateBack ?? (() => {
|
|
57033
|
-
|
|
58034
|
+
log9.warn("No navigate-back handler, ignoring");
|
|
57034
58035
|
}),
|
|
57035
58036
|
notify: notify ?? ((msg, type) => {
|
|
57036
|
-
|
|
58037
|
+
log9.debug("notify", { type, message: msg });
|
|
57037
58038
|
})
|
|
57038
58039
|
};
|
|
57039
58040
|
}
|
|
@@ -57468,7 +58469,7 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
|
|
|
57468
58469
|
};
|
|
57469
58470
|
}, [traitBindings]);
|
|
57470
58471
|
const executeTransitionEffects = React96.useCallback(async (params) => {
|
|
57471
|
-
const { binding, previousState, newState, payload, flushEvent, syncOnly, log:
|
|
58472
|
+
const { binding, previousState, newState, payload, flushEvent, syncOnly, log: log12 } = params;
|
|
57472
58473
|
const traitName = binding.trait.name;
|
|
57473
58474
|
const linkedEntity = binding.linkedEntity || "";
|
|
57474
58475
|
const entityId = payload?.entityId;
|
|
@@ -57597,7 +58598,7 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
|
|
|
57597
58598
|
if (sharedKey !== void 0) {
|
|
57598
58599
|
sharedWrites.push({ field, value });
|
|
57599
58600
|
}
|
|
57600
|
-
|
|
58601
|
+
log12.debug("set:write", {
|
|
57601
58602
|
traitName,
|
|
57602
58603
|
field,
|
|
57603
58604
|
value: JSON.stringify(value),
|
|
@@ -57665,7 +58666,7 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
|
|
|
57665
58666
|
core.mergeEntityFrame(sharedEntityStore.getSnapshot(sharedKey), sharedWrites)
|
|
57666
58667
|
);
|
|
57667
58668
|
}
|
|
57668
|
-
|
|
58669
|
+
log12.debug("effects:executed", () => ({
|
|
57669
58670
|
traitName,
|
|
57670
58671
|
transition: `${previousState}->${newState}`,
|
|
57671
58672
|
event: flushEvent,
|
|
@@ -57675,7 +58676,7 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
|
|
|
57675
58676
|
slotsTouched: Array.from(pendingSlots.keys()).join(",")
|
|
57676
58677
|
}));
|
|
57677
58678
|
for (const [slot, patterns] of pendingSlots) {
|
|
57678
|
-
|
|
58679
|
+
log12.debug("flush:slot", {
|
|
57679
58680
|
traitName,
|
|
57680
58681
|
slot,
|
|
57681
58682
|
patternCount: patterns.length,
|
|
@@ -57693,7 +58694,7 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
|
|
|
57693
58694
|
publishBindingSnapshot(traitName, liveEntity);
|
|
57694
58695
|
}
|
|
57695
58696
|
} catch (error) {
|
|
57696
|
-
|
|
58697
|
+
log12.error("effects:error", {
|
|
57697
58698
|
traitName,
|
|
57698
58699
|
transition: `${previousState}->${newState}`,
|
|
57699
58700
|
event: flushEvent,
|
|
@@ -58772,11 +59773,20 @@ function BrowserPlayground({
|
|
|
58772
59773
|
mode = "mock",
|
|
58773
59774
|
initialPagePath,
|
|
58774
59775
|
height,
|
|
58775
|
-
className
|
|
59776
|
+
className,
|
|
59777
|
+
paused
|
|
58776
59778
|
}) {
|
|
58777
59779
|
const [runtime] = React96.useState(
|
|
58778
59780
|
() => new OrbitalServerRuntime.OrbitalServerRuntime({ mode, debug: false })
|
|
58779
59781
|
);
|
|
59782
|
+
React96.useEffect(() => {
|
|
59783
|
+
if (paused === void 0) return;
|
|
59784
|
+
if (paused) {
|
|
59785
|
+
runtime.pauseTicks();
|
|
59786
|
+
} else {
|
|
59787
|
+
runtime.resumeTicks();
|
|
59788
|
+
}
|
|
59789
|
+
}, [runtime, paused]);
|
|
58780
59790
|
const registrationReady = React96.useMemo(() => {
|
|
58781
59791
|
const orbitalNames = schema.orbitals.map((o) => o.name);
|
|
58782
59792
|
playgroundLog.debug("register:start", { schema: schema.name, orbitalNames });
|
|
@@ -58837,7 +59847,7 @@ init_useEventBus();
|
|
|
58837
59847
|
// components/avl/hooks/useCanvasDnd.tsx
|
|
58838
59848
|
init_useEventBus();
|
|
58839
59849
|
init_useAlmadarDndCollision();
|
|
58840
|
-
var
|
|
59850
|
+
var log10 = logger.createLogger("almadar:ui:canvas-dnd");
|
|
58841
59851
|
function useCanvasDraggable({
|
|
58842
59852
|
id,
|
|
58843
59853
|
payload,
|
|
@@ -58875,7 +59885,7 @@ function defaultEmit(eventBus, drop) {
|
|
|
58875
59885
|
if (payload.kind === "pattern") {
|
|
58876
59886
|
const patternType = payload.data["type"];
|
|
58877
59887
|
if (typeof patternType !== "string") {
|
|
58878
|
-
|
|
59888
|
+
log10.warn("default-emit:pattern:missing-type");
|
|
58879
59889
|
return;
|
|
58880
59890
|
}
|
|
58881
59891
|
const out = { patternType, containerNode: target.containerNode };
|
|
@@ -58884,26 +59894,26 @@ function defaultEmit(eventBus, drop) {
|
|
|
58884
59894
|
out.index = resolved.index;
|
|
58885
59895
|
}
|
|
58886
59896
|
eventBus.emit("UI:PATTERN_DROP", out);
|
|
58887
|
-
|
|
59897
|
+
log10.info("default-emit:pattern", { patternType, level: target.level });
|
|
58888
59898
|
return;
|
|
58889
59899
|
}
|
|
58890
59900
|
if (payload.kind === "behavior") {
|
|
58891
59901
|
const behaviorName = payload.data["name"];
|
|
58892
59902
|
if (typeof behaviorName !== "string") {
|
|
58893
|
-
|
|
59903
|
+
log10.warn("default-emit:behavior:missing-name");
|
|
58894
59904
|
return;
|
|
58895
59905
|
}
|
|
58896
59906
|
eventBus.emit("UI:BEHAVIOR_DROP", {
|
|
58897
59907
|
behaviorName,
|
|
58898
59908
|
containerNode: target.containerNode
|
|
58899
59909
|
});
|
|
58900
|
-
|
|
59910
|
+
log10.info("default-emit:behavior", { behaviorName, level: target.level });
|
|
58901
59911
|
return;
|
|
58902
59912
|
}
|
|
58903
59913
|
if (payload.kind === "pattern-instance") {
|
|
58904
59914
|
const fromPath = payload.data["fromPath"];
|
|
58905
59915
|
if (typeof fromPath !== "string") {
|
|
58906
|
-
|
|
59916
|
+
log10.warn("default-emit:pattern-instance:missing-fromPath");
|
|
58907
59917
|
return;
|
|
58908
59918
|
}
|
|
58909
59919
|
const out = { fromPath, loc: payload.data["loc"] };
|
|
@@ -58912,10 +59922,10 @@ function defaultEmit(eventBus, drop) {
|
|
|
58912
59922
|
out.toIndex = resolved.index;
|
|
58913
59923
|
}
|
|
58914
59924
|
eventBus.emit("UI:PATTERN_MOVE", out);
|
|
58915
|
-
|
|
59925
|
+
log10.info("default-emit:pattern-instance", { fromPath, level: target.level });
|
|
58916
59926
|
return;
|
|
58917
59927
|
}
|
|
58918
|
-
|
|
59928
|
+
log10.debug("default-emit:unhandled-kind", { kind: payload.kind });
|
|
58919
59929
|
}
|
|
58920
59930
|
function CanvasDndProvider({
|
|
58921
59931
|
children,
|
|
@@ -58938,9 +59948,9 @@ function CanvasDndProvider({
|
|
|
58938
59948
|
if (payload) {
|
|
58939
59949
|
setActivePayload(payload);
|
|
58940
59950
|
eventBus.emit("UI:DRAG_START", { kind: payload.kind, data: payload.data });
|
|
58941
|
-
|
|
59951
|
+
log10.info("dragStart", { id: e.active.id, kind: payload.kind });
|
|
58942
59952
|
} else {
|
|
58943
|
-
|
|
59953
|
+
log10.warn("dragStart:missing-payload", { id: e.active.id });
|
|
58944
59954
|
}
|
|
58945
59955
|
}, [eventBus, trackPointer]);
|
|
58946
59956
|
const handleDragEnd = React96__namespace.default.useCallback((e) => {
|
|
@@ -58951,7 +59961,7 @@ function CanvasDndProvider({
|
|
|
58951
59961
|
const overData = e.over?.data.current;
|
|
58952
59962
|
const target = overData?.target;
|
|
58953
59963
|
const accepts = overData?.accepts;
|
|
58954
|
-
|
|
59964
|
+
log10.info("dragEnd", {
|
|
58955
59965
|
activeId: e.active.id,
|
|
58956
59966
|
overId: e.over?.id,
|
|
58957
59967
|
hasPayload: !!payload,
|
|
@@ -58963,12 +59973,12 @@ function CanvasDndProvider({
|
|
|
58963
59973
|
}
|
|
58964
59974
|
if (!payload || !target) return;
|
|
58965
59975
|
if (accepts && !accepts.includes(payload.kind)) {
|
|
58966
|
-
|
|
59976
|
+
log10.debug("dragEnd:rejected:kind", { kind: payload.kind, accepts: [...accepts] });
|
|
58967
59977
|
return;
|
|
58968
59978
|
}
|
|
58969
59979
|
const cursor = lastPointerRef.current;
|
|
58970
59980
|
const resolved = target.resolvePath && cursor ? target.resolvePath(cursor) : null;
|
|
58971
|
-
|
|
59981
|
+
log10.debug("dragEnd:resolve", {
|
|
58972
59982
|
hasResolver: !!target.resolvePath,
|
|
58973
59983
|
cursorX: cursor?.x,
|
|
58974
59984
|
cursorY: cursor?.y,
|
|
@@ -58982,7 +59992,7 @@ function CanvasDndProvider({
|
|
|
58982
59992
|
const handleDragCancel = React96__namespace.default.useCallback(() => {
|
|
58983
59993
|
setActivePayload(null);
|
|
58984
59994
|
document.removeEventListener("pointermove", trackPointer);
|
|
58985
|
-
|
|
59995
|
+
log10.info("dragCancel");
|
|
58986
59996
|
}, [trackPointer]);
|
|
58987
59997
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
58988
59998
|
core$1.DndContext,
|
|
@@ -61042,7 +62052,7 @@ init_AvlTransitionLane();
|
|
|
61042
62052
|
init_AvlSwimLane();
|
|
61043
62053
|
init_avl_atom_types();
|
|
61044
62054
|
init_avl_elk_layout();
|
|
61045
|
-
var
|
|
62055
|
+
var log11 = logger.createLogger("almadar:ui:avl:trait-scene");
|
|
61046
62056
|
var SWIM_GUTTER2 = 120;
|
|
61047
62057
|
var CENTER_W2 = 360;
|
|
61048
62058
|
var AvlTraitScene = ({
|
|
@@ -61055,7 +62065,7 @@ var AvlTraitScene = ({
|
|
|
61055
62065
|
const dataKey = React96.useMemo(() => JSON.stringify(data), [data]);
|
|
61056
62066
|
React96.useEffect(() => {
|
|
61057
62067
|
computeTraitLayout(data).then(setLayout).catch((error) => {
|
|
61058
|
-
|
|
62068
|
+
log11.error("computeTraitLayout failed", { error: error instanceof Error ? error : String(error) });
|
|
61059
62069
|
});
|
|
61060
62070
|
}, [dataKey]);
|
|
61061
62071
|
if (!layout) {
|