@almadar/ui 5.149.0 → 5.151.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/avl/index.cjs +2315 -157
- package/dist/avl/index.js +2315 -157
- package/dist/components/index.cjs +2327 -153
- package/dist/components/index.d.cts +811 -15
- package/dist/components/index.d.ts +811 -15
- package/dist/components/index.js +2329 -155
- package/dist/hooks/index.cjs +1 -1
- package/dist/hooks/index.js +1 -1
- package/dist/lib/drawable/three/index.cjs +1 -1
- package/dist/lib/drawable/three/index.js +1 -1
- package/dist/marketing/index.cjs +1 -1
- package/dist/marketing/index.js +1 -1
- package/dist/providers/index.cjs +2306 -149
- package/dist/providers/index.js +2306 -149
- package/dist/runtime/index.cjs +2315 -157
- package/dist/runtime/index.d.cts +2 -2
- package/dist/runtime/index.d.ts +2 -2
- package/dist/runtime/index.js +2315 -157
- package/package.json +4 -4
package/dist/components/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
2
2
|
import * as React77 from 'react';
|
|
3
|
-
import React77__default, { createContext, useContext, useMemo, useRef, useEffect, useCallback, useState, useLayoutEffect, useId, Suspense,
|
|
3
|
+
import React77__default, { createContext, useContext, useMemo, useRef, useEffect, useCallback, useState, useLayoutEffect, useId, Suspense, lazy, useSyncExternalStore } from 'react';
|
|
4
4
|
import { clsx } from 'clsx';
|
|
5
5
|
import { twMerge } from 'tailwind-merge';
|
|
6
6
|
import { EventBusContext, useTraitScopeChain, useCurrentPagePath, useGameAudioContextOptional, useEntitySchemaOptional, useEntityBindingSnapshot, useTraitScope, TraitScopeProvider } from '@almadar/ui/providers';
|
|
@@ -1081,7 +1081,7 @@ function useEventBus() {
|
|
|
1081
1081
|
return {
|
|
1082
1082
|
...baseBus,
|
|
1083
1083
|
emit: (type, payload, source) => {
|
|
1084
|
-
if (typeof type === "string" && type.startsWith("UI:") && !type.slice(3).includes(".")) {
|
|
1084
|
+
if (typeof type === "string" && type.startsWith("UI:") && !type.slice(3).includes(".") && !source?.trait) {
|
|
1085
1085
|
scopeLog.warn("emit:bare-key-no-scope", { type });
|
|
1086
1086
|
}
|
|
1087
1087
|
baseBus.emit(type, payload, source);
|
|
@@ -8407,6 +8407,14 @@ function shapeBounds(shape) {
|
|
|
8407
8407
|
w: shape.radius * 2 + 8,
|
|
8408
8408
|
h: shape.radius * 2 + 8
|
|
8409
8409
|
};
|
|
8410
|
+
case "ellipse":
|
|
8411
|
+
if (shape.x == null || shape.y == null || shape.width == null || shape.height == null) return null;
|
|
8412
|
+
return {
|
|
8413
|
+
x: shape.x - shape.width / 2 - 4,
|
|
8414
|
+
y: shape.y - shape.height / 2 - 4,
|
|
8415
|
+
w: shape.width + 8,
|
|
8416
|
+
h: shape.height + 8
|
|
8417
|
+
};
|
|
8410
8418
|
case "rect":
|
|
8411
8419
|
if (shape.x == null || shape.y == null || shape.width == null || shape.height == null) return null;
|
|
8412
8420
|
return { x: shape.x - 4, y: shape.y - 4, w: shape.width + 8, h: shape.height + 8 };
|
|
@@ -8440,13 +8448,14 @@ function drawArrowHead(ctx, x1, y1, x2, y2, size) {
|
|
|
8440
8448
|
ctx.closePath();
|
|
8441
8449
|
ctx.fill();
|
|
8442
8450
|
}
|
|
8443
|
-
function drawShape(ctx, shape, width, height) {
|
|
8451
|
+
function drawShape(ctx, shape, width, height, allShapes) {
|
|
8444
8452
|
ctx.save();
|
|
8445
8453
|
const opacity = shape.opacity ?? 1;
|
|
8446
8454
|
ctx.globalAlpha = opacity;
|
|
8447
8455
|
const stroke = resolveColor2(shape.color, ctx, "#333333");
|
|
8448
8456
|
const fill = shape.fill ? resolveColor2(shape.fill, ctx, "#cccccc") : void 0;
|
|
8449
8457
|
ctx.lineWidth = shape.lineWidth ?? 2;
|
|
8458
|
+
if (shape.dash) ctx.setLineDash([...DASH_PATTERNS[shape.dash]]);
|
|
8450
8459
|
switch (shape.type) {
|
|
8451
8460
|
case "grid": {
|
|
8452
8461
|
const step = shape.step ?? 40;
|
|
@@ -8512,6 +8521,20 @@ function drawShape(ctx, shape, width, height) {
|
|
|
8512
8521
|
ctx.stroke();
|
|
8513
8522
|
break;
|
|
8514
8523
|
}
|
|
8524
|
+
case "ellipse": {
|
|
8525
|
+
if (shape.x == null || shape.y == null || shape.width == null || shape.height == null) break;
|
|
8526
|
+
const startAngle = (shape.startAngle ?? 0) * Math.PI / 180;
|
|
8527
|
+
const endAngle = (shape.endAngle ?? 360) * Math.PI / 180;
|
|
8528
|
+
ctx.beginPath();
|
|
8529
|
+
ctx.ellipse(shape.x, shape.y, shape.width / 2, shape.height / 2, 0, startAngle, endAngle);
|
|
8530
|
+
if (fill) {
|
|
8531
|
+
ctx.fillStyle = fill;
|
|
8532
|
+
ctx.fill();
|
|
8533
|
+
}
|
|
8534
|
+
ctx.strokeStyle = stroke;
|
|
8535
|
+
ctx.stroke();
|
|
8536
|
+
break;
|
|
8537
|
+
}
|
|
8515
8538
|
case "rect": {
|
|
8516
8539
|
if (shape.x == null || shape.y == null || shape.width == null || shape.height == null) break;
|
|
8517
8540
|
if (fill) {
|
|
@@ -8558,21 +8581,153 @@ function drawShape(ctx, shape, width, height) {
|
|
|
8558
8581
|
ctx.fillText(shape.text, shape.x, shape.y);
|
|
8559
8582
|
break;
|
|
8560
8583
|
}
|
|
8584
|
+
case "venn-region": {
|
|
8585
|
+
const resolveCircles = (ids) => (ids ?? []).flatMap((id) => {
|
|
8586
|
+
const c = allShapes.find((s) => s.type === "circle" && s.id === id);
|
|
8587
|
+
return c && c.x != null && c.y != null && c.radius != null ? [{ x: c.x, y: c.y, radius: c.radius }] : [];
|
|
8588
|
+
});
|
|
8589
|
+
const inside = resolveCircles(shape.inside);
|
|
8590
|
+
if (inside.length === 0) break;
|
|
8591
|
+
const outside = resolveCircles(shape.outside);
|
|
8592
|
+
const off = document.createElement("canvas");
|
|
8593
|
+
off.width = ctx.canvas.width;
|
|
8594
|
+
off.height = ctx.canvas.height;
|
|
8595
|
+
const octx = off.getContext("2d");
|
|
8596
|
+
if (!octx) break;
|
|
8597
|
+
octx.setTransform(ctx.getTransform());
|
|
8598
|
+
for (const c of inside) {
|
|
8599
|
+
const p = new Path2D();
|
|
8600
|
+
p.arc(c.x, c.y, c.radius, 0, Math.PI * 2);
|
|
8601
|
+
octx.clip(p);
|
|
8602
|
+
}
|
|
8603
|
+
octx.fillStyle = fill ?? stroke;
|
|
8604
|
+
octx.fillRect(0, 0, width, height);
|
|
8605
|
+
octx.globalCompositeOperation = "destination-out";
|
|
8606
|
+
for (const c of outside) {
|
|
8607
|
+
const p = new Path2D();
|
|
8608
|
+
p.arc(c.x, c.y, c.radius, 0, Math.PI * 2);
|
|
8609
|
+
octx.fill(p);
|
|
8610
|
+
}
|
|
8611
|
+
ctx.save();
|
|
8612
|
+
ctx.setTransform(1, 0, 0, 1, 0, 0);
|
|
8613
|
+
ctx.drawImage(off, 0, 0);
|
|
8614
|
+
ctx.restore();
|
|
8615
|
+
break;
|
|
8616
|
+
}
|
|
8561
8617
|
}
|
|
8562
8618
|
ctx.restore();
|
|
8563
8619
|
}
|
|
8564
|
-
|
|
8620
|
+
function readoutShapes(readouts, width) {
|
|
8621
|
+
const out = [];
|
|
8622
|
+
const chipH = 18;
|
|
8623
|
+
const gap = 6;
|
|
8624
|
+
let rightEdge = width - 6;
|
|
8625
|
+
let rowY = 6;
|
|
8626
|
+
for (const readout of readouts) {
|
|
8627
|
+
const text = `${readout.label}: ${String(readout.value)}`;
|
|
8628
|
+
const chipW = Math.min(170, Math.max(34, text.length * 6 + 12));
|
|
8629
|
+
let chipX = rightEdge - chipW;
|
|
8630
|
+
if (chipX < 4) {
|
|
8631
|
+
rowY += chipH + 4;
|
|
8632
|
+
rightEdge = width - 6;
|
|
8633
|
+
chipX = rightEdge - chipW;
|
|
8634
|
+
}
|
|
8635
|
+
const color = readout.color ?? "#334155";
|
|
8636
|
+
out.push({ type: "rect", x: chipX, y: rowY, width: chipW, height: chipH, color, fill: color });
|
|
8637
|
+
out.push({
|
|
8638
|
+
type: "text",
|
|
8639
|
+
x: chipX + chipW / 2,
|
|
8640
|
+
y: rowY + chipH / 2,
|
|
8641
|
+
text,
|
|
8642
|
+
color: "#ffffff",
|
|
8643
|
+
fontSize: 10,
|
|
8644
|
+
align: "center"
|
|
8645
|
+
});
|
|
8646
|
+
rightEdge = chipX - gap;
|
|
8647
|
+
}
|
|
8648
|
+
return out;
|
|
8649
|
+
}
|
|
8650
|
+
function traceShapes(panel, k, width, height) {
|
|
8651
|
+
const w = panel.width ?? Math.round(width * 0.32);
|
|
8652
|
+
const h = panel.height ?? Math.round(height * 0.28);
|
|
8653
|
+
const x = panel.x ?? width - w - 8;
|
|
8654
|
+
const y = panel.y ?? height - h - 8 - k * (h + 8);
|
|
8655
|
+
const allSamples = panel.series.flatMap((series) => series.samples);
|
|
8656
|
+
let xLo = Math.min(...allSamples.map((p) => p.x));
|
|
8657
|
+
let xHi = Math.max(...allSamples.map((p) => p.x));
|
|
8658
|
+
let yLo = Math.min(...allSamples.map((p) => p.y));
|
|
8659
|
+
let yHi = Math.max(...allSamples.map((p) => p.y));
|
|
8660
|
+
if (xLo === xHi) {
|
|
8661
|
+
xLo -= 1;
|
|
8662
|
+
xHi += 1;
|
|
8663
|
+
}
|
|
8664
|
+
if (yLo === yHi) {
|
|
8665
|
+
yLo -= 1;
|
|
8666
|
+
yHi += 1;
|
|
8667
|
+
}
|
|
8668
|
+
const backgroundColor = panel.backgroundColor ?? "#ffffff";
|
|
8669
|
+
const frameColor = panel.frameColor ?? "#94a3b8";
|
|
8670
|
+
const out = [];
|
|
8671
|
+
out.push({
|
|
8672
|
+
type: "rect",
|
|
8673
|
+
x,
|
|
8674
|
+
y,
|
|
8675
|
+
width: w,
|
|
8676
|
+
height: h,
|
|
8677
|
+
color: backgroundColor,
|
|
8678
|
+
fill: backgroundColor,
|
|
8679
|
+
opacity: panel.backgroundOpacity ?? 0.85
|
|
8680
|
+
});
|
|
8681
|
+
out.push({ type: "rect", x, y, width: w, height: h, color: frameColor, lineWidth: 1 });
|
|
8682
|
+
panel.series.forEach((series, j) => {
|
|
8683
|
+
const color = series.color ?? TRACE_SERIES_COLORS[j % TRACE_SERIES_COLORS.length];
|
|
8684
|
+
const mapped = series.samples.map((p) => ({
|
|
8685
|
+
x: x + 4 + (p.x - xLo) / (xHi - xLo) * (w - 8),
|
|
8686
|
+
y: y + h - 4 - (p.y - yLo) / (yHi - yLo) * (h - 8)
|
|
8687
|
+
}));
|
|
8688
|
+
for (let i = 1; i < mapped.length; i++) {
|
|
8689
|
+
out.push({
|
|
8690
|
+
type: "line",
|
|
8691
|
+
x1: mapped[i - 1].x,
|
|
8692
|
+
y1: mapped[i - 1].y,
|
|
8693
|
+
x2: mapped[i].x,
|
|
8694
|
+
y2: mapped[i].y,
|
|
8695
|
+
color,
|
|
8696
|
+
lineWidth: 1.5
|
|
8697
|
+
});
|
|
8698
|
+
}
|
|
8699
|
+
if (mapped.length > 0) {
|
|
8700
|
+
const last = mapped[mapped.length - 1];
|
|
8701
|
+
out.push({ type: "circle", x: last.x, y: last.y, radius: 2, color, fill: color });
|
|
8702
|
+
}
|
|
8703
|
+
if (series.label) {
|
|
8704
|
+
out.push({ type: "text", x: x + 6, y: y + 10 + 11 * j, text: series.label, color, fontSize: 9 });
|
|
8705
|
+
}
|
|
8706
|
+
});
|
|
8707
|
+
if (panel.yLabel) {
|
|
8708
|
+
out.push({ type: "text", x: x + w - 6, y: y + 10, text: panel.yLabel, color: "#6b7280", fontSize: 9, align: "right" });
|
|
8709
|
+
}
|
|
8710
|
+
if (panel.xLabel) {
|
|
8711
|
+
out.push({ type: "text", x: x + w - 6, y: y + h - 6, text: panel.xLabel, color: "#6b7280", fontSize: 9, align: "right" });
|
|
8712
|
+
}
|
|
8713
|
+
return out;
|
|
8714
|
+
}
|
|
8715
|
+
var DASH_PATTERNS, TRACE_SERIES_COLORS, LearningCanvas;
|
|
8565
8716
|
var init_LearningCanvas = __esm({
|
|
8566
8717
|
"components/learning/atoms/LearningCanvas.tsx"() {
|
|
8567
8718
|
"use client";
|
|
8568
8719
|
init_cn();
|
|
8569
8720
|
init_useEventBus();
|
|
8721
|
+
DASH_PATTERNS = { dashed: [6, 4], dotted: [2, 3] };
|
|
8722
|
+
TRACE_SERIES_COLORS = ["#2563eb", "#dc2626", "#16a34a", "#f59e0b"];
|
|
8570
8723
|
LearningCanvas = ({
|
|
8571
8724
|
className,
|
|
8572
8725
|
width = 600,
|
|
8573
8726
|
height = 400,
|
|
8574
8727
|
backgroundColor,
|
|
8575
8728
|
shapes = [],
|
|
8729
|
+
readouts,
|
|
8730
|
+
traces,
|
|
8576
8731
|
interactive = false,
|
|
8577
8732
|
animate = false,
|
|
8578
8733
|
onShapeClick,
|
|
@@ -8598,6 +8753,12 @@ var init_LearningCanvas = __esm({
|
|
|
8598
8753
|
}
|
|
8599
8754
|
return -1;
|
|
8600
8755
|
}, [shapes]);
|
|
8756
|
+
const derivedShapes = useMemo(() => {
|
|
8757
|
+
if (!traces?.length && !readouts?.length) return shapes;
|
|
8758
|
+
const traceOut = (traces ?? []).flatMap((panel, k) => traceShapes(panel, k, width, height));
|
|
8759
|
+
const readoutOut = readouts?.length ? readoutShapes(readouts, width) : [];
|
|
8760
|
+
return [...shapes, ...traceOut, ...readoutOut];
|
|
8761
|
+
}, [shapes, traces, readouts, width, height]);
|
|
8601
8762
|
const draw = useCallback(() => {
|
|
8602
8763
|
const canvas = canvasRef.current;
|
|
8603
8764
|
if (!canvas) return;
|
|
@@ -8614,13 +8775,13 @@ var init_LearningCanvas = __esm({
|
|
|
8614
8775
|
ctx.fillStyle = backgroundColor;
|
|
8615
8776
|
ctx.fillRect(0, 0, width, height);
|
|
8616
8777
|
}
|
|
8617
|
-
for (const shape of
|
|
8618
|
-
if (shape.type !== "text") drawShape(ctx, shape, width, height);
|
|
8778
|
+
for (const shape of derivedShapes) {
|
|
8779
|
+
if (shape.type !== "text") drawShape(ctx, shape, width, height, derivedShapes);
|
|
8619
8780
|
}
|
|
8620
|
-
for (const shape of
|
|
8621
|
-
if (shape.type === "text") drawShape(ctx, shape, width, height);
|
|
8781
|
+
for (const shape of derivedShapes) {
|
|
8782
|
+
if (shape.type === "text") drawShape(ctx, shape, width, height, derivedShapes);
|
|
8622
8783
|
}
|
|
8623
|
-
}, [width, height, backgroundColor,
|
|
8784
|
+
}, [width, height, backgroundColor, derivedShapes]);
|
|
8624
8785
|
useEffect(() => {
|
|
8625
8786
|
draw();
|
|
8626
8787
|
}, [draw]);
|
|
@@ -8688,7 +8849,363 @@ var init_LearningCanvas = __esm({
|
|
|
8688
8849
|
};
|
|
8689
8850
|
}
|
|
8690
8851
|
});
|
|
8691
|
-
|
|
8852
|
+
function layoutCircle(nodes, width, height) {
|
|
8853
|
+
const cx = width / 2;
|
|
8854
|
+
const cy = height / 2;
|
|
8855
|
+
const radius = Math.max(10, Math.min(cx, cy) - 40);
|
|
8856
|
+
const positions = /* @__PURE__ */ new Map();
|
|
8857
|
+
const n = nodes.length;
|
|
8858
|
+
nodes.forEach((node, i) => {
|
|
8859
|
+
const angle = 2 * Math.PI * i / Math.max(n, 1) - Math.PI / 2;
|
|
8860
|
+
positions.set(node.id, { x: cx + radius * Math.cos(angle), y: cy + radius * Math.sin(angle) });
|
|
8861
|
+
});
|
|
8862
|
+
return positions;
|
|
8863
|
+
}
|
|
8864
|
+
function layoutTree(nodes, edges, root, width, height) {
|
|
8865
|
+
const nodeIds = nodes.map((n) => n.id);
|
|
8866
|
+
const idSet = new Set(nodeIds);
|
|
8867
|
+
const childrenOf = /* @__PURE__ */ new Map();
|
|
8868
|
+
const hasIncoming = /* @__PURE__ */ new Set();
|
|
8869
|
+
for (const e of edges) {
|
|
8870
|
+
if (!idSet.has(e.from) || !idSet.has(e.to)) continue;
|
|
8871
|
+
const list = childrenOf.get(e.from) ?? [];
|
|
8872
|
+
list.push(e.to);
|
|
8873
|
+
childrenOf.set(e.from, list);
|
|
8874
|
+
hasIncoming.add(e.to);
|
|
8875
|
+
}
|
|
8876
|
+
const depth = /* @__PURE__ */ new Map();
|
|
8877
|
+
const treeChildren = /* @__PURE__ */ new Map();
|
|
8878
|
+
const visited = /* @__PURE__ */ new Set();
|
|
8879
|
+
const bfsFrom = (start) => {
|
|
8880
|
+
if (visited.has(start)) return;
|
|
8881
|
+
visited.add(start);
|
|
8882
|
+
depth.set(start, 0);
|
|
8883
|
+
const queue = [start];
|
|
8884
|
+
while (queue.length > 0) {
|
|
8885
|
+
const u = queue.shift();
|
|
8886
|
+
for (const v of childrenOf.get(u) ?? []) {
|
|
8887
|
+
if (visited.has(v)) continue;
|
|
8888
|
+
visited.add(v);
|
|
8889
|
+
depth.set(v, (depth.get(u) ?? 0) + 1);
|
|
8890
|
+
const list = treeChildren.get(u) ?? [];
|
|
8891
|
+
list.push(v);
|
|
8892
|
+
treeChildren.set(u, list);
|
|
8893
|
+
queue.push(v);
|
|
8894
|
+
}
|
|
8895
|
+
}
|
|
8896
|
+
};
|
|
8897
|
+
const primaryRoot = root && idSet.has(root) ? root : nodeIds.find((id) => !hasIncoming.has(id)) ?? nodeIds[0];
|
|
8898
|
+
const rootsOrder = [];
|
|
8899
|
+
if (primaryRoot !== void 0) {
|
|
8900
|
+
bfsFrom(primaryRoot);
|
|
8901
|
+
rootsOrder.push(primaryRoot);
|
|
8902
|
+
}
|
|
8903
|
+
for (const id of nodeIds) {
|
|
8904
|
+
if (!visited.has(id)) {
|
|
8905
|
+
bfsFrom(id);
|
|
8906
|
+
rootsOrder.push(id);
|
|
8907
|
+
}
|
|
8908
|
+
}
|
|
8909
|
+
let leafCounter = 0;
|
|
8910
|
+
const xSlot = /* @__PURE__ */ new Map();
|
|
8911
|
+
const assignXSlot = (u) => {
|
|
8912
|
+
const children = treeChildren.get(u) ?? [];
|
|
8913
|
+
if (children.length === 0) {
|
|
8914
|
+
const slot = leafCounter++;
|
|
8915
|
+
xSlot.set(u, slot);
|
|
8916
|
+
return slot;
|
|
8917
|
+
}
|
|
8918
|
+
const childSlots = children.map(assignXSlot);
|
|
8919
|
+
const avg = childSlots.reduce((a, b) => a + b, 0) / childSlots.length;
|
|
8920
|
+
xSlot.set(u, avg);
|
|
8921
|
+
return avg;
|
|
8922
|
+
};
|
|
8923
|
+
for (const r of rootsOrder) assignXSlot(r);
|
|
8924
|
+
let maxDepth = 0;
|
|
8925
|
+
for (const d of depth.values()) maxDepth = Math.max(maxDepth, d);
|
|
8926
|
+
const colWidth = width / Math.max(1, leafCounter);
|
|
8927
|
+
const rowHeight = height / (maxDepth + 1);
|
|
8928
|
+
const positions = /* @__PURE__ */ new Map();
|
|
8929
|
+
for (const id of nodeIds) {
|
|
8930
|
+
const slot = xSlot.get(id) ?? 0;
|
|
8931
|
+
const d = depth.get(id) ?? 0;
|
|
8932
|
+
positions.set(id, { x: slot * colWidth + colWidth / 2, y: d * rowHeight + rowHeight / 2 });
|
|
8933
|
+
}
|
|
8934
|
+
return positions;
|
|
8935
|
+
}
|
|
8936
|
+
function layoutLayered(nodes, edges, width, height) {
|
|
8937
|
+
const nodeIds = nodes.map((n) => n.id);
|
|
8938
|
+
const idSet = new Set(nodeIds);
|
|
8939
|
+
const adj = /* @__PURE__ */ new Map();
|
|
8940
|
+
const remainingIndegree = /* @__PURE__ */ new Map();
|
|
8941
|
+
for (const id of nodeIds) remainingIndegree.set(id, 0);
|
|
8942
|
+
for (const e of edges) {
|
|
8943
|
+
if (!idSet.has(e.from) || !idSet.has(e.to)) continue;
|
|
8944
|
+
const list = adj.get(e.from) ?? [];
|
|
8945
|
+
list.push(e.to);
|
|
8946
|
+
adj.set(e.from, list);
|
|
8947
|
+
remainingIndegree.set(e.to, (remainingIndegree.get(e.to) ?? 0) + 1);
|
|
8948
|
+
}
|
|
8949
|
+
const layer = /* @__PURE__ */ new Map();
|
|
8950
|
+
const dequeued = /* @__PURE__ */ new Set();
|
|
8951
|
+
const queue = [];
|
|
8952
|
+
for (const id of nodeIds) {
|
|
8953
|
+
if ((remainingIndegree.get(id) ?? 0) === 0) {
|
|
8954
|
+
layer.set(id, 0);
|
|
8955
|
+
queue.push(id);
|
|
8956
|
+
}
|
|
8957
|
+
}
|
|
8958
|
+
while (queue.length > 0) {
|
|
8959
|
+
const u = queue.shift();
|
|
8960
|
+
dequeued.add(u);
|
|
8961
|
+
for (const v of adj.get(u) ?? []) {
|
|
8962
|
+
const candidate = (layer.get(u) ?? 0) + 1;
|
|
8963
|
+
layer.set(v, Math.max(layer.get(v) ?? 0, candidate));
|
|
8964
|
+
remainingIndegree.set(v, (remainingIndegree.get(v) ?? 0) - 1);
|
|
8965
|
+
if ((remainingIndegree.get(v) ?? 0) === 0 && !dequeued.has(v)) {
|
|
8966
|
+
queue.push(v);
|
|
8967
|
+
}
|
|
8968
|
+
}
|
|
8969
|
+
}
|
|
8970
|
+
let baseMaxLayer = 0;
|
|
8971
|
+
for (const id of nodeIds) {
|
|
8972
|
+
if (dequeued.has(id)) baseMaxLayer = Math.max(baseMaxLayer, layer.get(id) ?? 0);
|
|
8973
|
+
}
|
|
8974
|
+
const cycleLayer = baseMaxLayer + 1;
|
|
8975
|
+
let maxLayer = baseMaxLayer;
|
|
8976
|
+
for (const id of nodeIds) {
|
|
8977
|
+
if (!dequeued.has(id)) {
|
|
8978
|
+
layer.set(id, cycleLayer);
|
|
8979
|
+
maxLayer = cycleLayer;
|
|
8980
|
+
}
|
|
8981
|
+
}
|
|
8982
|
+
const colWidth = width / Math.max(1, maxLayer + 1);
|
|
8983
|
+
const byLayer = /* @__PURE__ */ new Map();
|
|
8984
|
+
for (const id of nodeIds) {
|
|
8985
|
+
const l = layer.get(id) ?? 0;
|
|
8986
|
+
const list = byLayer.get(l) ?? [];
|
|
8987
|
+
list.push(id);
|
|
8988
|
+
byLayer.set(l, list);
|
|
8989
|
+
}
|
|
8990
|
+
const positions = /* @__PURE__ */ new Map();
|
|
8991
|
+
for (const [l, ids] of byLayer) {
|
|
8992
|
+
const rowHeight = height / ids.length;
|
|
8993
|
+
ids.forEach((id, i) => {
|
|
8994
|
+
positions.set(id, { x: l * colWidth + colWidth / 2, y: i * rowHeight + rowHeight / 2 });
|
|
8995
|
+
});
|
|
8996
|
+
}
|
|
8997
|
+
return positions;
|
|
8998
|
+
}
|
|
8999
|
+
function computePositions(nodes, edges, layout, root, width, height) {
|
|
9000
|
+
switch (layout) {
|
|
9001
|
+
case "circle":
|
|
9002
|
+
return layoutCircle(nodes, width, height);
|
|
9003
|
+
case "tree":
|
|
9004
|
+
return layoutTree(nodes, edges, root, width, height);
|
|
9005
|
+
case "layered":
|
|
9006
|
+
return layoutLayered(nodes, edges, width, height);
|
|
9007
|
+
case "manual":
|
|
9008
|
+
default: {
|
|
9009
|
+
const positions = /* @__PURE__ */ new Map();
|
|
9010
|
+
for (const n of nodes) positions.set(n.id, { x: n.x ?? 0, y: n.y ?? 0 });
|
|
9011
|
+
return positions;
|
|
9012
|
+
}
|
|
9013
|
+
}
|
|
9014
|
+
}
|
|
9015
|
+
var NODE_STATE_COLOR, EDGE_STATE_COLOR, DEFAULT_NODE_RADIUS, AlgoGraphCanvas;
|
|
9016
|
+
var init_AlgoGraphCanvas = __esm({
|
|
9017
|
+
"components/learning/molecules/AlgoGraphCanvas.tsx"() {
|
|
9018
|
+
"use client";
|
|
9019
|
+
init_atoms();
|
|
9020
|
+
init_Stack();
|
|
9021
|
+
init_LearningCanvas();
|
|
9022
|
+
NODE_STATE_COLOR = {
|
|
9023
|
+
unvisited: "#cbd5e1",
|
|
9024
|
+
frontier: "#f59e0b",
|
|
9025
|
+
current: "#ef4444",
|
|
9026
|
+
visited: "#22c55e",
|
|
9027
|
+
goal: "#8b5cf6",
|
|
9028
|
+
path: "#0ea5e9"
|
|
9029
|
+
};
|
|
9030
|
+
EDGE_STATE_COLOR = {
|
|
9031
|
+
default: "#9ca3af",
|
|
9032
|
+
tree: "#16a34a",
|
|
9033
|
+
relaxed: "#f59e0b",
|
|
9034
|
+
candidate: "#38bdf8",
|
|
9035
|
+
path: "#dc2626"
|
|
9036
|
+
};
|
|
9037
|
+
DEFAULT_NODE_RADIUS = 18;
|
|
9038
|
+
AlgoGraphCanvas = ({
|
|
9039
|
+
className,
|
|
9040
|
+
width = 600,
|
|
9041
|
+
height = 400,
|
|
9042
|
+
title,
|
|
9043
|
+
backgroundColor,
|
|
9044
|
+
nodes = [],
|
|
9045
|
+
edges = [],
|
|
9046
|
+
layout = "manual",
|
|
9047
|
+
root,
|
|
9048
|
+
shapes = [],
|
|
9049
|
+
interactive = false,
|
|
9050
|
+
animate = false,
|
|
9051
|
+
onShapeClick,
|
|
9052
|
+
onNodeClick,
|
|
9053
|
+
isLoading,
|
|
9054
|
+
error
|
|
9055
|
+
}) => {
|
|
9056
|
+
const nodeById = useMemo(() => {
|
|
9057
|
+
const m = /* @__PURE__ */ new Map();
|
|
9058
|
+
for (const n of nodes) m.set(n.id, n);
|
|
9059
|
+
return m;
|
|
9060
|
+
}, [nodes]);
|
|
9061
|
+
const nodeIndexById = useMemo(() => {
|
|
9062
|
+
const m = /* @__PURE__ */ new Map();
|
|
9063
|
+
nodes.forEach((n, i) => m.set(n.id, i));
|
|
9064
|
+
return m;
|
|
9065
|
+
}, [nodes]);
|
|
9066
|
+
const derivedShapes = useMemo(() => {
|
|
9067
|
+
const out = [];
|
|
9068
|
+
const positions = computePositions(nodes, edges, layout, root, width, height);
|
|
9069
|
+
const edgeGeoms = [];
|
|
9070
|
+
for (const e of edges) {
|
|
9071
|
+
const a = nodeById.get(e.from);
|
|
9072
|
+
const b = nodeById.get(e.to);
|
|
9073
|
+
const posA = positions.get(e.from);
|
|
9074
|
+
const posB = positions.get(e.to);
|
|
9075
|
+
if (!a || !b || !posA || !posB) continue;
|
|
9076
|
+
const rA = a.radius ?? DEFAULT_NODE_RADIUS;
|
|
9077
|
+
const rB = b.radius ?? DEFAULT_NODE_RADIUS;
|
|
9078
|
+
const dx = posB.x - posA.x;
|
|
9079
|
+
const dy = posB.y - posA.y;
|
|
9080
|
+
const dist = Math.max(1e-6, Math.hypot(dx, dy));
|
|
9081
|
+
const ux = dx / dist;
|
|
9082
|
+
const uy = dy / dist;
|
|
9083
|
+
edgeGeoms.push({
|
|
9084
|
+
directed: e.directed ?? false,
|
|
9085
|
+
x1: posA.x + ux * rA,
|
|
9086
|
+
y1: posA.y + uy * rA,
|
|
9087
|
+
x2: posB.x - ux * rB,
|
|
9088
|
+
y2: posB.y - uy * rB,
|
|
9089
|
+
color: e.color ?? EDGE_STATE_COLOR[e.state ?? "default"],
|
|
9090
|
+
label: e.label ?? (e.weight != null ? String(e.weight) : void 0)
|
|
9091
|
+
});
|
|
9092
|
+
}
|
|
9093
|
+
for (const g of edgeGeoms) {
|
|
9094
|
+
out.push({
|
|
9095
|
+
type: g.directed ? "arrow" : "line",
|
|
9096
|
+
x1: g.x1,
|
|
9097
|
+
y1: g.y1,
|
|
9098
|
+
x2: g.x2,
|
|
9099
|
+
y2: g.y2,
|
|
9100
|
+
color: g.color,
|
|
9101
|
+
lineWidth: 2
|
|
9102
|
+
});
|
|
9103
|
+
}
|
|
9104
|
+
for (const g of edgeGeoms) {
|
|
9105
|
+
if (g.label === void 0) continue;
|
|
9106
|
+
const midX = (g.x1 + g.x2) / 2;
|
|
9107
|
+
const midY = (g.y1 + g.y2) / 2;
|
|
9108
|
+
const dx = g.x2 - g.x1;
|
|
9109
|
+
const dy = g.y2 - g.y1;
|
|
9110
|
+
const dist = Math.max(1e-6, Math.hypot(dx, dy));
|
|
9111
|
+
const perpX = -(dy / dist);
|
|
9112
|
+
const perpY = dx / dist;
|
|
9113
|
+
out.push({
|
|
9114
|
+
type: "text",
|
|
9115
|
+
x: midX + perpX * 10,
|
|
9116
|
+
y: midY + perpY * 10,
|
|
9117
|
+
text: g.label,
|
|
9118
|
+
fontSize: 11,
|
|
9119
|
+
align: "center",
|
|
9120
|
+
color: "#374151"
|
|
9121
|
+
});
|
|
9122
|
+
}
|
|
9123
|
+
const nodeGeoms = [];
|
|
9124
|
+
for (const n of nodes) {
|
|
9125
|
+
const pos = positions.get(n.id);
|
|
9126
|
+
if (!pos) continue;
|
|
9127
|
+
nodeGeoms.push({
|
|
9128
|
+
id: n.id,
|
|
9129
|
+
x: pos.x,
|
|
9130
|
+
y: pos.y,
|
|
9131
|
+
radius: n.radius ?? DEFAULT_NODE_RADIUS,
|
|
9132
|
+
color: n.color ?? NODE_STATE_COLOR[n.state ?? "unvisited"],
|
|
9133
|
+
label: n.label,
|
|
9134
|
+
badge: n.badge
|
|
9135
|
+
});
|
|
9136
|
+
}
|
|
9137
|
+
for (const g of nodeGeoms) {
|
|
9138
|
+
out.push({ type: "circle", id: g.id, x: g.x, y: g.y, radius: g.radius, color: g.color, fill: `${g.color}33` });
|
|
9139
|
+
}
|
|
9140
|
+
const badgeGeoms = [];
|
|
9141
|
+
for (const g of nodeGeoms) {
|
|
9142
|
+
if (!g.badge) continue;
|
|
9143
|
+
const w = Math.min(42, Math.max(18, g.badge.text.length * 6 + 10));
|
|
9144
|
+
badgeGeoms.push({
|
|
9145
|
+
cx: g.x + g.radius * 0.75,
|
|
9146
|
+
cy: g.y - g.radius * 0.75,
|
|
9147
|
+
w,
|
|
9148
|
+
h: 14,
|
|
9149
|
+
// Borderless pill: same color drives both stroke and fill.
|
|
9150
|
+
color: g.badge.color ?? "#1e293b",
|
|
9151
|
+
text: g.badge.text
|
|
9152
|
+
});
|
|
9153
|
+
}
|
|
9154
|
+
for (const b of badgeGeoms) {
|
|
9155
|
+
out.push({ type: "rect", x: b.cx - b.w / 2, y: b.cy - b.h / 2, width: b.w, height: b.h, color: b.color, fill: b.color });
|
|
9156
|
+
}
|
|
9157
|
+
for (const b of badgeGeoms) {
|
|
9158
|
+
out.push({ type: "text", x: b.cx, y: b.cy, text: b.text, fontSize: 9, align: "center", color: "#ffffff" });
|
|
9159
|
+
}
|
|
9160
|
+
for (const g of nodeGeoms) {
|
|
9161
|
+
if (g.label === void 0) continue;
|
|
9162
|
+
out.push({
|
|
9163
|
+
type: "text",
|
|
9164
|
+
x: g.x,
|
|
9165
|
+
y: g.y + g.radius + 14,
|
|
9166
|
+
text: g.label,
|
|
9167
|
+
fontSize: 12,
|
|
9168
|
+
align: "center",
|
|
9169
|
+
color: "#111827"
|
|
9170
|
+
});
|
|
9171
|
+
}
|
|
9172
|
+
out.push(...shapes);
|
|
9173
|
+
return out;
|
|
9174
|
+
}, [nodes, edges, layout, root, width, height, nodeById, shapes]);
|
|
9175
|
+
const handleShapeClick = useCallback(
|
|
9176
|
+
(payload) => {
|
|
9177
|
+
if (payload.type === "circle" && payload.id) {
|
|
9178
|
+
const node = nodeById.get(payload.id);
|
|
9179
|
+
const idx = nodeIndexById.get(payload.id);
|
|
9180
|
+
if (node && idx !== void 0) {
|
|
9181
|
+
onNodeClick?.({ id: node.id, label: node.label, index: idx });
|
|
9182
|
+
}
|
|
9183
|
+
}
|
|
9184
|
+
onShapeClick?.(payload);
|
|
9185
|
+
},
|
|
9186
|
+
[nodeById, nodeIndexById, onNodeClick, onShapeClick]
|
|
9187
|
+
);
|
|
9188
|
+
return /* @__PURE__ */ jsx(Card, { className, children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
|
|
9189
|
+
title ? /* @__PURE__ */ jsx(Typography, { variant: "h4", children: title }) : null,
|
|
9190
|
+
/* @__PURE__ */ jsx(
|
|
9191
|
+
LearningCanvas,
|
|
9192
|
+
{
|
|
9193
|
+
width,
|
|
9194
|
+
height,
|
|
9195
|
+
backgroundColor,
|
|
9196
|
+
shapes: derivedShapes,
|
|
9197
|
+
interactive,
|
|
9198
|
+
animate,
|
|
9199
|
+
onShapeClick: onShapeClick || onNodeClick ? handleShapeClick : void 0,
|
|
9200
|
+
isLoading,
|
|
9201
|
+
error
|
|
9202
|
+
}
|
|
9203
|
+
)
|
|
9204
|
+
] }) });
|
|
9205
|
+
};
|
|
9206
|
+
}
|
|
9207
|
+
});
|
|
9208
|
+
var DEFAULT_BAR_COLOR, DEFAULT_CELL_COLOR, DEFAULT_POINTER_COLOR, POINTER_BAND, TOP_PAD, PANEL_FAMILY_ORDER, RANGE_COLOR_DEFAULT, RANGE_FILL_OPACITY, BRACKET_TOP_OFFSET, BRACKET_ROW_H, BRACKET_TICK_H, BRACKET_LABEL_OFFSET, SLOT_EMPTY_FILL, SLOT_EMPTY_STROKE, SLOT_FILLED_STROKE, SLOT_HIGHLIGHT_DEFAULT, SLOT_VALUE_TEXT_COLOR, FRAME_ACTIVE_COLOR, FRAME_RETURNING_COLOR, FRAME_DONE_COLOR, FRAME_LABEL_COLOR, FRAME_DETAIL_COLOR, FRAME_TWO_LINE_MIN_H, BUCKET_INDEX_FILL, BUCKET_INDEX_STROKE, BUCKET_INDEX_TEXT, BUCKET_ENTRY_TEXT, BUCKET_ENTRY_DEFAULT, BUCKET_ENTRY_HIGHLIGHT, BUCKET_ENTRY_PROBING, BUCKET_ENTRY_MIN_W, BUCKET_ENTRY_MAX_W, AXIS_LABEL_COLOR, AXIS_LABEL_FONT_SIZE, CORNER_TEXT_COLOR, CORNER_FONT_SIZE, CORNER_MIN_CELL, CORNER_INSET_X, CORNER_INSET_Y, AUX_PRIMARY_RATIO, AUX_LABEL_BAND, AUX_BASELINE_PAD, AlgorithmCanvas;
|
|
8692
9209
|
var init_AlgorithmCanvas = __esm({
|
|
8693
9210
|
"components/learning/molecules/AlgorithmCanvas.tsx"() {
|
|
8694
9211
|
"use client";
|
|
@@ -8700,6 +9217,43 @@ var init_AlgorithmCanvas = __esm({
|
|
|
8700
9217
|
DEFAULT_POINTER_COLOR = "#dc2626";
|
|
8701
9218
|
POINTER_BAND = 34;
|
|
8702
9219
|
TOP_PAD = 26;
|
|
9220
|
+
PANEL_FAMILY_ORDER = ["bars", "slots", "cells", "buckets", "frames"];
|
|
9221
|
+
RANGE_COLOR_DEFAULT = "#3b82f6";
|
|
9222
|
+
RANGE_FILL_OPACITY = 0.15;
|
|
9223
|
+
BRACKET_TOP_OFFSET = 16;
|
|
9224
|
+
BRACKET_ROW_H = 14;
|
|
9225
|
+
BRACKET_TICK_H = 6;
|
|
9226
|
+
BRACKET_LABEL_OFFSET = 6;
|
|
9227
|
+
SLOT_EMPTY_FILL = "#f1f5f9";
|
|
9228
|
+
SLOT_EMPTY_STROKE = "#cbd5e1";
|
|
9229
|
+
SLOT_FILLED_STROKE = "#9ca3af";
|
|
9230
|
+
SLOT_HIGHLIGHT_DEFAULT = "#f59e0b";
|
|
9231
|
+
SLOT_VALUE_TEXT_COLOR = "#ffffff";
|
|
9232
|
+
FRAME_ACTIVE_COLOR = "#3b82f6";
|
|
9233
|
+
FRAME_RETURNING_COLOR = "#f59e0b";
|
|
9234
|
+
FRAME_DONE_COLOR = "#94a3b8";
|
|
9235
|
+
FRAME_LABEL_COLOR = "#ffffff";
|
|
9236
|
+
FRAME_DETAIL_COLOR = "#e2e8f0";
|
|
9237
|
+
FRAME_TWO_LINE_MIN_H = 22;
|
|
9238
|
+
BUCKET_INDEX_FILL = "#e2e8f0";
|
|
9239
|
+
BUCKET_INDEX_STROKE = "#9ca3af";
|
|
9240
|
+
BUCKET_INDEX_TEXT = "#374151";
|
|
9241
|
+
BUCKET_ENTRY_TEXT = "#ffffff";
|
|
9242
|
+
BUCKET_ENTRY_DEFAULT = "#3b82f6";
|
|
9243
|
+
BUCKET_ENTRY_HIGHLIGHT = "#f59e0b";
|
|
9244
|
+
BUCKET_ENTRY_PROBING = "#38bdf8";
|
|
9245
|
+
BUCKET_ENTRY_MIN_W = 24;
|
|
9246
|
+
BUCKET_ENTRY_MAX_W = 64;
|
|
9247
|
+
AXIS_LABEL_COLOR = "#6b7280";
|
|
9248
|
+
AXIS_LABEL_FONT_SIZE = 10;
|
|
9249
|
+
CORNER_TEXT_COLOR = "#111827";
|
|
9250
|
+
CORNER_FONT_SIZE = 7;
|
|
9251
|
+
CORNER_MIN_CELL = 28;
|
|
9252
|
+
CORNER_INSET_X = 3;
|
|
9253
|
+
CORNER_INSET_Y = 6;
|
|
9254
|
+
AUX_PRIMARY_RATIO = 0.6;
|
|
9255
|
+
AUX_LABEL_BAND = 18;
|
|
9256
|
+
AUX_BASELINE_PAD = 8;
|
|
8703
9257
|
AlgorithmCanvas = ({
|
|
8704
9258
|
className,
|
|
8705
9259
|
width = 600,
|
|
@@ -8709,6 +9263,14 @@ var init_AlgorithmCanvas = __esm({
|
|
|
8709
9263
|
bars = [],
|
|
8710
9264
|
cells = [],
|
|
8711
9265
|
pointers = [],
|
|
9266
|
+
ranges = [],
|
|
9267
|
+
slots = [],
|
|
9268
|
+
slotOrientation = "horizontal",
|
|
9269
|
+
frames = [],
|
|
9270
|
+
buckets = [],
|
|
9271
|
+
auxBars = [],
|
|
9272
|
+
rowLabels = [],
|
|
9273
|
+
colLabels = [],
|
|
8712
9274
|
shapes = [],
|
|
8713
9275
|
interactive = false,
|
|
8714
9276
|
animate = false,
|
|
@@ -8718,12 +9280,35 @@ var init_AlgorithmCanvas = __esm({
|
|
|
8718
9280
|
}) => {
|
|
8719
9281
|
const derivedShapes = useMemo(() => {
|
|
8720
9282
|
const out = [];
|
|
9283
|
+
const presence = {
|
|
9284
|
+
bars: bars.length > 0,
|
|
9285
|
+
slots: slots.length > 0,
|
|
9286
|
+
cells: cells.length > 0,
|
|
9287
|
+
buckets: buckets.length > 0,
|
|
9288
|
+
frames: frames.length > 0
|
|
9289
|
+
};
|
|
9290
|
+
const panelCount = PANEL_FAMILY_ORDER.filter((f3) => presence[f3]).length;
|
|
9291
|
+
const panelHeight = height / Math.max(1, panelCount);
|
|
9292
|
+
const panelY = { bars: 0, slots: 0, cells: 0, buckets: 0, frames: 0 };
|
|
9293
|
+
let compactIndex = 0;
|
|
9294
|
+
PANEL_FAMILY_ORDER.forEach((f3) => {
|
|
9295
|
+
if (presence[f3]) {
|
|
9296
|
+
panelY[f3] = compactIndex * panelHeight;
|
|
9297
|
+
compactIndex += 1;
|
|
9298
|
+
}
|
|
9299
|
+
});
|
|
8721
9300
|
if (bars.length > 0) {
|
|
9301
|
+
const panelYBars = panelY.bars;
|
|
8722
9302
|
const slot = width / bars.length;
|
|
8723
9303
|
const barW = slot * 0.8;
|
|
8724
9304
|
const gap = slot * 0.1;
|
|
8725
|
-
const
|
|
8726
|
-
const
|
|
9305
|
+
const bracketRanges = ranges.filter((r) => r.kind === "bracket");
|
|
9306
|
+
const bracketCount = bracketRanges.length;
|
|
9307
|
+
const bracketHeadroom = bracketCount > 0 ? BRACKET_TOP_OFFSET + bracketCount * BRACKET_ROW_H : 0;
|
|
9308
|
+
const hasAux = auxBars.length > 0;
|
|
9309
|
+
const primaryH = hasAux ? panelHeight * AUX_PRIMARY_RATIO : panelHeight;
|
|
9310
|
+
const baseline = panelYBars + primaryH - POINTER_BAND;
|
|
9311
|
+
const usableH = baseline - (panelYBars + TOP_PAD + bracketHeadroom);
|
|
8727
9312
|
const maxV = Math.max(1, ...bars.map((b) => Number.isFinite(b.value) ? b.value : 0));
|
|
8728
9313
|
bars.forEach((bar, i) => {
|
|
8729
9314
|
const v = Number.isFinite(bar.value) ? bar.value : 0;
|
|
@@ -8753,6 +9338,89 @@ var init_AlgorithmCanvas = __esm({
|
|
|
8753
9338
|
});
|
|
8754
9339
|
}
|
|
8755
9340
|
});
|
|
9341
|
+
ranges.forEach((r) => {
|
|
9342
|
+
const kind = r.kind ?? "fill";
|
|
9343
|
+
if (kind !== "fill") return;
|
|
9344
|
+
const color = r.color ?? RANGE_COLOR_DEFAULT;
|
|
9345
|
+
out.push({
|
|
9346
|
+
type: "rect",
|
|
9347
|
+
x: r.from * slot,
|
|
9348
|
+
y: panelYBars,
|
|
9349
|
+
width: (r.to - r.from + 1) * slot,
|
|
9350
|
+
height: primaryH,
|
|
9351
|
+
color,
|
|
9352
|
+
fill: color,
|
|
9353
|
+
opacity: RANGE_FILL_OPACITY
|
|
9354
|
+
});
|
|
9355
|
+
if (r.label) {
|
|
9356
|
+
out.push({
|
|
9357
|
+
type: "text",
|
|
9358
|
+
x: r.from * slot + 4,
|
|
9359
|
+
// Sits below the bracket block (if any) so fill and bracket labels never collide.
|
|
9360
|
+
y: panelYBars + 10 + bracketHeadroom,
|
|
9361
|
+
text: r.label,
|
|
9362
|
+
color,
|
|
9363
|
+
fontSize: 10,
|
|
9364
|
+
align: "left"
|
|
9365
|
+
});
|
|
9366
|
+
}
|
|
9367
|
+
});
|
|
9368
|
+
bracketRanges.forEach((r, i) => {
|
|
9369
|
+
const bracketY = panelYBars + BRACKET_TOP_OFFSET + i * BRACKET_ROW_H;
|
|
9370
|
+
const x1 = r.from * slot + slot * 0.1;
|
|
9371
|
+
const x2 = (r.to + 1) * slot - slot * 0.1;
|
|
9372
|
+
const color = r.color ?? RANGE_COLOR_DEFAULT;
|
|
9373
|
+
out.push({ type: "line", x1, y1: bracketY, x2, y2: bracketY, color, lineWidth: 2 });
|
|
9374
|
+
out.push({ type: "line", x1, y1: bracketY, x2: x1, y2: bracketY + BRACKET_TICK_H, color, lineWidth: 2 });
|
|
9375
|
+
out.push({ type: "line", x1: x2, y1: bracketY, x2, y2: bracketY + BRACKET_TICK_H, color, lineWidth: 2 });
|
|
9376
|
+
if (r.label) {
|
|
9377
|
+
out.push({
|
|
9378
|
+
type: "text",
|
|
9379
|
+
x: (x1 + x2) / 2,
|
|
9380
|
+
y: bracketY - BRACKET_LABEL_OFFSET,
|
|
9381
|
+
text: r.label,
|
|
9382
|
+
color,
|
|
9383
|
+
fontSize: 10,
|
|
9384
|
+
align: "center"
|
|
9385
|
+
});
|
|
9386
|
+
}
|
|
9387
|
+
});
|
|
9388
|
+
if (hasAux) {
|
|
9389
|
+
const auxH = panelHeight - primaryH;
|
|
9390
|
+
const slot2 = width / auxBars.length;
|
|
9391
|
+
const auxBaseline = panelYBars + primaryH + auxH - AUX_BASELINE_PAD;
|
|
9392
|
+
const auxUsableH = auxBaseline - (panelYBars + primaryH + AUX_LABEL_BAND);
|
|
9393
|
+
const maxAuxV = Math.max(1, ...auxBars.map((b) => Number.isFinite(b.value) ? b.value : 0));
|
|
9394
|
+
auxBars.forEach((bar, i) => {
|
|
9395
|
+
const v = Number.isFinite(bar.value) ? bar.value : 0;
|
|
9396
|
+
const bh = Math.max(0, v / maxAuxV * auxUsableH);
|
|
9397
|
+
const x = i * slot2 + slot2 * 0.1;
|
|
9398
|
+
const w = slot2 * 0.8;
|
|
9399
|
+
const color = bar.color ?? DEFAULT_BAR_COLOR;
|
|
9400
|
+
out.push({
|
|
9401
|
+
type: "rect",
|
|
9402
|
+
id: `auxbar-${i}`,
|
|
9403
|
+
x,
|
|
9404
|
+
y: auxBaseline - bh,
|
|
9405
|
+
width: w,
|
|
9406
|
+
height: bh,
|
|
9407
|
+
color,
|
|
9408
|
+
fill: color
|
|
9409
|
+
});
|
|
9410
|
+
const label = bar.label ?? (auxBars.length <= 24 ? String(v) : void 0);
|
|
9411
|
+
if (label) {
|
|
9412
|
+
out.push({
|
|
9413
|
+
type: "text",
|
|
9414
|
+
x: x + w / 2,
|
|
9415
|
+
y: auxBaseline - bh - 8,
|
|
9416
|
+
text: label,
|
|
9417
|
+
color: "#374151",
|
|
9418
|
+
fontSize: 11,
|
|
9419
|
+
align: "center"
|
|
9420
|
+
});
|
|
9421
|
+
}
|
|
9422
|
+
});
|
|
9423
|
+
}
|
|
8756
9424
|
pointers.forEach((p) => {
|
|
8757
9425
|
if (p.index < 0 || p.index >= bars.length) return;
|
|
8758
9426
|
const cx = p.index * slot + slot / 2;
|
|
@@ -8760,7 +9428,7 @@ var init_AlgorithmCanvas = __esm({
|
|
|
8760
9428
|
out.push({
|
|
8761
9429
|
type: "arrow",
|
|
8762
9430
|
x1: cx,
|
|
8763
|
-
y1:
|
|
9431
|
+
y1: panelYBars + primaryH - 18,
|
|
8764
9432
|
x2: cx,
|
|
8765
9433
|
y2: baseline + 4,
|
|
8766
9434
|
color,
|
|
@@ -8770,7 +9438,7 @@ var init_AlgorithmCanvas = __esm({
|
|
|
8770
9438
|
out.push({
|
|
8771
9439
|
type: "text",
|
|
8772
9440
|
x: cx,
|
|
8773
|
-
y:
|
|
9441
|
+
y: panelYBars + primaryH - 8,
|
|
8774
9442
|
text: p.label,
|
|
8775
9443
|
color,
|
|
8776
9444
|
fontSize: 11,
|
|
@@ -8779,14 +9447,111 @@ var init_AlgorithmCanvas = __esm({
|
|
|
8779
9447
|
}
|
|
8780
9448
|
});
|
|
8781
9449
|
}
|
|
9450
|
+
if (slots.length > 0) {
|
|
9451
|
+
const panelYSlots = panelY.slots;
|
|
9452
|
+
const n = slots.length;
|
|
9453
|
+
const vertical = slotOrientation === "vertical";
|
|
9454
|
+
const vBoxH = panelHeight / n;
|
|
9455
|
+
const vBoxW = Math.min(width * 0.5, 120);
|
|
9456
|
+
const vBoxX = (width - vBoxW) / 2;
|
|
9457
|
+
const hCellW = width / n;
|
|
9458
|
+
const hBoxW = hCellW * 0.82;
|
|
9459
|
+
const hBoxH = Math.min(panelHeight * 0.6, 48);
|
|
9460
|
+
const hBoxY = panelYSlots + (panelHeight - hBoxH) / 2;
|
|
9461
|
+
const slotBox = (i) => vertical ? { x: vBoxX, y: panelYSlots + panelHeight - (i + 1) * vBoxH, width: vBoxW, height: vBoxH } : { x: i * hCellW + (hCellW - hBoxW) / 2, y: hBoxY, width: hBoxW, height: hBoxH };
|
|
9462
|
+
slots.forEach((s, i) => {
|
|
9463
|
+
const box = slotBox(i);
|
|
9464
|
+
const state = s.state ?? "filled";
|
|
9465
|
+
const fill = state === "empty" ? SLOT_EMPTY_FILL : state === "highlight" ? s.color ?? SLOT_HIGHLIGHT_DEFAULT : s.color ?? DEFAULT_BAR_COLOR;
|
|
9466
|
+
const stroke = state === "empty" ? SLOT_EMPTY_STROKE : SLOT_FILLED_STROKE;
|
|
9467
|
+
out.push({
|
|
9468
|
+
type: "rect",
|
|
9469
|
+
id: `slot-${i}`,
|
|
9470
|
+
x: box.x,
|
|
9471
|
+
y: box.y,
|
|
9472
|
+
width: box.width,
|
|
9473
|
+
height: box.height,
|
|
9474
|
+
color: stroke,
|
|
9475
|
+
fill
|
|
9476
|
+
});
|
|
9477
|
+
if (s.value != null && state !== "empty") {
|
|
9478
|
+
out.push({
|
|
9479
|
+
type: "text",
|
|
9480
|
+
x: box.x + box.width / 2,
|
|
9481
|
+
y: box.y + box.height / 2,
|
|
9482
|
+
text: String(s.value),
|
|
9483
|
+
color: SLOT_VALUE_TEXT_COLOR,
|
|
9484
|
+
fontSize: 12,
|
|
9485
|
+
align: "center"
|
|
9486
|
+
});
|
|
9487
|
+
}
|
|
9488
|
+
});
|
|
9489
|
+
if (bars.length === 0) {
|
|
9490
|
+
pointers.forEach((p) => {
|
|
9491
|
+
if (p.index < 0 || p.index >= slots.length) return;
|
|
9492
|
+
const box = slotBox(p.index);
|
|
9493
|
+
const color = p.color ?? DEFAULT_POINTER_COLOR;
|
|
9494
|
+
if (vertical) {
|
|
9495
|
+
const cy = box.y + box.height / 2;
|
|
9496
|
+
out.push({
|
|
9497
|
+
type: "arrow",
|
|
9498
|
+
x1: box.x + box.width + 34,
|
|
9499
|
+
y1: cy,
|
|
9500
|
+
x2: box.x + box.width + 4,
|
|
9501
|
+
y2: cy,
|
|
9502
|
+
color,
|
|
9503
|
+
lineWidth: 2
|
|
9504
|
+
});
|
|
9505
|
+
if (p.label) {
|
|
9506
|
+
out.push({
|
|
9507
|
+
type: "text",
|
|
9508
|
+
x: box.x + box.width + 38,
|
|
9509
|
+
y: cy,
|
|
9510
|
+
text: p.label,
|
|
9511
|
+
color,
|
|
9512
|
+
fontSize: 11,
|
|
9513
|
+
align: "left"
|
|
9514
|
+
});
|
|
9515
|
+
}
|
|
9516
|
+
} else {
|
|
9517
|
+
const cx = box.x + box.width / 2;
|
|
9518
|
+
out.push({
|
|
9519
|
+
type: "arrow",
|
|
9520
|
+
x1: cx,
|
|
9521
|
+
y1: panelYSlots + panelHeight - 18,
|
|
9522
|
+
x2: cx,
|
|
9523
|
+
y2: box.y + box.height + 4,
|
|
9524
|
+
color,
|
|
9525
|
+
lineWidth: 2
|
|
9526
|
+
});
|
|
9527
|
+
if (p.label) {
|
|
9528
|
+
out.push({
|
|
9529
|
+
type: "text",
|
|
9530
|
+
x: cx,
|
|
9531
|
+
y: panelYSlots + panelHeight - 8,
|
|
9532
|
+
text: p.label,
|
|
9533
|
+
color,
|
|
9534
|
+
fontSize: 11,
|
|
9535
|
+
align: "center"
|
|
9536
|
+
});
|
|
9537
|
+
}
|
|
9538
|
+
}
|
|
9539
|
+
});
|
|
9540
|
+
}
|
|
9541
|
+
}
|
|
8782
9542
|
if (cells.length > 0) {
|
|
9543
|
+
const panelYCells = panelY.cells;
|
|
8783
9544
|
const maxCol = Math.max(0, ...cells.map((c) => c.col)) + 1;
|
|
8784
9545
|
const maxRow = Math.max(0, ...cells.map((c) => c.row)) + 1;
|
|
8785
|
-
const
|
|
8786
|
-
const
|
|
9546
|
+
const colLabelH = colLabels.length > 0 ? 16 : 0;
|
|
9547
|
+
const rowLabelW = rowLabels.length > 0 ? 20 : 0;
|
|
9548
|
+
const gridX0 = rowLabelW;
|
|
9549
|
+
const gridY0 = panelYCells + colLabelH;
|
|
9550
|
+
const cw = (width - rowLabelW) / maxCol;
|
|
9551
|
+
const ch = (panelHeight - colLabelH) / maxRow;
|
|
8787
9552
|
cells.forEach((c, i) => {
|
|
8788
|
-
const x = c.col * cw;
|
|
8789
|
-
const y = c.row * ch;
|
|
9553
|
+
const x = gridX0 + c.col * cw;
|
|
9554
|
+
const y = gridY0 + c.row * ch;
|
|
8790
9555
|
const color = c.color ?? DEFAULT_CELL_COLOR;
|
|
8791
9556
|
out.push({
|
|
8792
9557
|
type: "rect",
|
|
@@ -8810,11 +9575,207 @@ var init_AlgorithmCanvas = __esm({
|
|
|
8810
9575
|
align: "center"
|
|
8811
9576
|
});
|
|
8812
9577
|
}
|
|
9578
|
+
if (c.corner && cw >= CORNER_MIN_CELL && ch >= CORNER_MIN_CELL) {
|
|
9579
|
+
const { tl, tr, bl, br } = c.corner;
|
|
9580
|
+
if (tl) {
|
|
9581
|
+
out.push({
|
|
9582
|
+
type: "text",
|
|
9583
|
+
x: x + CORNER_INSET_X,
|
|
9584
|
+
y: y + CORNER_INSET_Y,
|
|
9585
|
+
text: tl,
|
|
9586
|
+
color: CORNER_TEXT_COLOR,
|
|
9587
|
+
fontSize: CORNER_FONT_SIZE,
|
|
9588
|
+
align: "left"
|
|
9589
|
+
});
|
|
9590
|
+
}
|
|
9591
|
+
if (tr) {
|
|
9592
|
+
out.push({
|
|
9593
|
+
type: "text",
|
|
9594
|
+
x: x + cw - CORNER_INSET_X,
|
|
9595
|
+
y: y + CORNER_INSET_Y,
|
|
9596
|
+
text: tr,
|
|
9597
|
+
color: CORNER_TEXT_COLOR,
|
|
9598
|
+
fontSize: CORNER_FONT_SIZE,
|
|
9599
|
+
align: "right"
|
|
9600
|
+
});
|
|
9601
|
+
}
|
|
9602
|
+
if (bl) {
|
|
9603
|
+
out.push({
|
|
9604
|
+
type: "text",
|
|
9605
|
+
x: x + CORNER_INSET_X,
|
|
9606
|
+
y: y + ch - CORNER_INSET_Y,
|
|
9607
|
+
text: bl,
|
|
9608
|
+
color: CORNER_TEXT_COLOR,
|
|
9609
|
+
fontSize: CORNER_FONT_SIZE,
|
|
9610
|
+
align: "left"
|
|
9611
|
+
});
|
|
9612
|
+
}
|
|
9613
|
+
if (br) {
|
|
9614
|
+
out.push({
|
|
9615
|
+
type: "text",
|
|
9616
|
+
x: x + cw - CORNER_INSET_X,
|
|
9617
|
+
y: y + ch - CORNER_INSET_Y,
|
|
9618
|
+
text: br,
|
|
9619
|
+
color: CORNER_TEXT_COLOR,
|
|
9620
|
+
fontSize: CORNER_FONT_SIZE,
|
|
9621
|
+
align: "right"
|
|
9622
|
+
});
|
|
9623
|
+
}
|
|
9624
|
+
}
|
|
9625
|
+
});
|
|
9626
|
+
colLabels.forEach((l) => {
|
|
9627
|
+
out.push({
|
|
9628
|
+
type: "text",
|
|
9629
|
+
x: gridX0 + l.index * cw + cw / 2,
|
|
9630
|
+
y: panelYCells + colLabelH / 2,
|
|
9631
|
+
text: l.text,
|
|
9632
|
+
color: l.color ?? AXIS_LABEL_COLOR,
|
|
9633
|
+
fontSize: AXIS_LABEL_FONT_SIZE,
|
|
9634
|
+
align: "center"
|
|
9635
|
+
});
|
|
9636
|
+
});
|
|
9637
|
+
rowLabels.forEach((l) => {
|
|
9638
|
+
out.push({
|
|
9639
|
+
type: "text",
|
|
9640
|
+
x: rowLabelW - 6,
|
|
9641
|
+
y: gridY0 + l.index * ch + ch / 2,
|
|
9642
|
+
text: l.text,
|
|
9643
|
+
color: l.color ?? AXIS_LABEL_COLOR,
|
|
9644
|
+
fontSize: AXIS_LABEL_FONT_SIZE,
|
|
9645
|
+
align: "right"
|
|
9646
|
+
});
|
|
9647
|
+
});
|
|
9648
|
+
}
|
|
9649
|
+
if (buckets.length > 0) {
|
|
9650
|
+
const panelYBuckets = panelY.buckets;
|
|
9651
|
+
const bucketCount = Math.max(0, ...buckets.map((b) => b.index)) + 1;
|
|
9652
|
+
const rowH = panelHeight / bucketCount;
|
|
9653
|
+
const indexColW = Math.min(width * 0.12, 40);
|
|
9654
|
+
const maxChainLen = Math.max(1, ...buckets.map((b) => b.entries.length));
|
|
9655
|
+
const entryW = Math.min(BUCKET_ENTRY_MAX_W, Math.max(BUCKET_ENTRY_MIN_W, (width - indexColW - 8) / maxChainLen));
|
|
9656
|
+
const maxVisible = Math.floor((width - indexColW - 4) / entryW);
|
|
9657
|
+
buckets.forEach((b) => {
|
|
9658
|
+
const rowY = panelYBuckets + b.index * rowH;
|
|
9659
|
+
out.push({
|
|
9660
|
+
type: "rect",
|
|
9661
|
+
id: `bucket-index-${b.index}`,
|
|
9662
|
+
x: 2,
|
|
9663
|
+
y: rowY + 2,
|
|
9664
|
+
width: indexColW - 4,
|
|
9665
|
+
height: rowH - 4,
|
|
9666
|
+
color: BUCKET_INDEX_STROKE,
|
|
9667
|
+
fill: BUCKET_INDEX_FILL
|
|
9668
|
+
});
|
|
9669
|
+
out.push({
|
|
9670
|
+
type: "text",
|
|
9671
|
+
x: 2 + (indexColW - 4) / 2,
|
|
9672
|
+
y: rowY + rowH / 2,
|
|
9673
|
+
text: String(b.index),
|
|
9674
|
+
color: BUCKET_INDEX_TEXT,
|
|
9675
|
+
fontSize: 10,
|
|
9676
|
+
align: "center"
|
|
9677
|
+
});
|
|
9678
|
+
const overflow = b.entries.length > maxVisible;
|
|
9679
|
+
const visibleCount = overflow ? Math.max(0, maxVisible - 1) : b.entries.length;
|
|
9680
|
+
for (let j = 0; j < visibleCount; j++) {
|
|
9681
|
+
const entry = b.entries[j];
|
|
9682
|
+
const ex = indexColW + 4 + j * entryW;
|
|
9683
|
+
const state = entry.state ?? "default";
|
|
9684
|
+
const fill = state === "highlight" ? entry.color ?? BUCKET_ENTRY_HIGHLIGHT : state === "probing" ? entry.color ?? BUCKET_ENTRY_PROBING : entry.color ?? BUCKET_ENTRY_DEFAULT;
|
|
9685
|
+
out.push({
|
|
9686
|
+
type: "rect",
|
|
9687
|
+
id: `bucket-${b.index}-${j}`,
|
|
9688
|
+
x: ex,
|
|
9689
|
+
y: rowY + 2,
|
|
9690
|
+
width: entryW - 2,
|
|
9691
|
+
height: rowH - 4,
|
|
9692
|
+
color: fill,
|
|
9693
|
+
fill
|
|
9694
|
+
});
|
|
9695
|
+
if (entryW >= 20 && rowH >= 16) {
|
|
9696
|
+
out.push({
|
|
9697
|
+
type: "text",
|
|
9698
|
+
x: ex + (entryW - 2) / 2,
|
|
9699
|
+
y: rowY + rowH / 2,
|
|
9700
|
+
text: entry.label,
|
|
9701
|
+
color: BUCKET_ENTRY_TEXT,
|
|
9702
|
+
fontSize: 10,
|
|
9703
|
+
align: "center"
|
|
9704
|
+
});
|
|
9705
|
+
}
|
|
9706
|
+
}
|
|
9707
|
+
if (overflow) {
|
|
9708
|
+
const ex = indexColW + 4 + visibleCount * entryW;
|
|
9709
|
+
out.push({
|
|
9710
|
+
type: "rect",
|
|
9711
|
+
id: `bucket-${b.index}-overflow`,
|
|
9712
|
+
x: ex,
|
|
9713
|
+
y: rowY + 2,
|
|
9714
|
+
width: entryW - 2,
|
|
9715
|
+
height: rowH - 4,
|
|
9716
|
+
color: BUCKET_ENTRY_DEFAULT,
|
|
9717
|
+
fill: BUCKET_ENTRY_DEFAULT
|
|
9718
|
+
});
|
|
9719
|
+
out.push({
|
|
9720
|
+
type: "text",
|
|
9721
|
+
x: ex + (entryW - 2) / 2,
|
|
9722
|
+
y: rowY + rowH / 2,
|
|
9723
|
+
text: `+${b.entries.length - visibleCount}`,
|
|
9724
|
+
color: BUCKET_ENTRY_TEXT,
|
|
9725
|
+
fontSize: 10,
|
|
9726
|
+
align: "center"
|
|
9727
|
+
});
|
|
9728
|
+
}
|
|
9729
|
+
});
|
|
9730
|
+
}
|
|
9731
|
+
if (frames.length > 0) {
|
|
9732
|
+
const panelYFrames = panelY.frames;
|
|
9733
|
+
const n = frames.length;
|
|
9734
|
+
const frameH = panelHeight / n;
|
|
9735
|
+
const x = 8;
|
|
9736
|
+
const w = width - 16;
|
|
9737
|
+
frames.forEach((f3, i) => {
|
|
9738
|
+
const y = panelYFrames + panelHeight - (i + 1) * frameH;
|
|
9739
|
+
const state = f3.state ?? "active";
|
|
9740
|
+
const fill = state === "returning" ? f3.color ?? FRAME_RETURNING_COLOR : state === "done" ? f3.color ?? FRAME_DONE_COLOR : f3.color ?? FRAME_ACTIVE_COLOR;
|
|
9741
|
+
out.push({ type: "rect", id: `frame-${i}`, x, y, width: w, height: frameH, color: fill, fill });
|
|
9742
|
+
if (frameH >= FRAME_TWO_LINE_MIN_H) {
|
|
9743
|
+
out.push({
|
|
9744
|
+
type: "text",
|
|
9745
|
+
x: 16,
|
|
9746
|
+
y: y + frameH * 0.35,
|
|
9747
|
+
text: f3.label,
|
|
9748
|
+
color: FRAME_LABEL_COLOR,
|
|
9749
|
+
fontSize: 10,
|
|
9750
|
+
align: "left"
|
|
9751
|
+
});
|
|
9752
|
+
if (f3.detail) {
|
|
9753
|
+
out.push({
|
|
9754
|
+
type: "text",
|
|
9755
|
+
x: 16,
|
|
9756
|
+
y: y + frameH * 0.7,
|
|
9757
|
+
text: f3.detail,
|
|
9758
|
+
color: FRAME_DETAIL_COLOR,
|
|
9759
|
+
fontSize: 10,
|
|
9760
|
+
align: "left"
|
|
9761
|
+
});
|
|
9762
|
+
}
|
|
9763
|
+
} else {
|
|
9764
|
+
out.push({
|
|
9765
|
+
type: "text",
|
|
9766
|
+
x: 16,
|
|
9767
|
+
y: y + frameH / 2,
|
|
9768
|
+
text: f3.label,
|
|
9769
|
+
color: FRAME_LABEL_COLOR,
|
|
9770
|
+
fontSize: 10,
|
|
9771
|
+
align: "left"
|
|
9772
|
+
});
|
|
9773
|
+
}
|
|
8813
9774
|
});
|
|
8814
9775
|
}
|
|
8815
9776
|
out.push(...shapes);
|
|
8816
9777
|
return out;
|
|
8817
|
-
}, [bars, cells, pointers, shapes, width, height]);
|
|
9778
|
+
}, [bars, cells, pointers, ranges, slots, slotOrientation, frames, buckets, auxBars, rowLabels, colLabels, shapes, width, height]);
|
|
8818
9779
|
return /* @__PURE__ */ jsx(Card, { className, children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
|
|
8819
9780
|
title ? /* @__PURE__ */ jsx(Typography, { variant: "h4", children: title }) : null,
|
|
8820
9781
|
/* @__PURE__ */ jsx(
|
|
@@ -9828,6 +10789,12 @@ function LearningScene3D({
|
|
|
9828
10789
|
const unitId = event.payload?.unitId;
|
|
9829
10790
|
if (typeof unitId === "string") onItemClickRef.current?.(unitId);
|
|
9830
10791
|
});
|
|
10792
|
+
if (typeof process !== "undefined" && process.env && process.env.NODE_ENV !== "production" && post?.bloom) {
|
|
10793
|
+
const unknownKeys = Object.keys(post.bloom).filter((k) => !KNOWN_BLOOM_KEYS.has(k));
|
|
10794
|
+
if (unknownKeys.length > 0) {
|
|
10795
|
+
sceneLog.debug("post.bloom has unrecognized keys \u2014 only intensity/threshold/smoothing are read", { unknownKeys });
|
|
10796
|
+
}
|
|
10797
|
+
}
|
|
9831
10798
|
const props3d = {
|
|
9832
10799
|
drawables,
|
|
9833
10800
|
isLoading,
|
|
@@ -9893,7 +10860,7 @@ function cylinderBetween(from, to, radius, color) {
|
|
|
9893
10860
|
material: { color }
|
|
9894
10861
|
};
|
|
9895
10862
|
}
|
|
9896
|
-
function arrowBetween(from, to, color, shaftRadius = 0.08) {
|
|
10863
|
+
function arrowBetween(from, to, color, shaftRadius = 0.08, id) {
|
|
9897
10864
|
const len = segmentLength(from, to);
|
|
9898
10865
|
if (len < 1e-6) return null;
|
|
9899
10866
|
const tipLen = Math.min(shaftRadius * 8, len * 0.35);
|
|
@@ -9921,6 +10888,7 @@ function arrowBetween(from, to, color, shaftRadius = 0.08) {
|
|
|
9921
10888
|
};
|
|
9922
10889
|
return {
|
|
9923
10890
|
type: "draw-group",
|
|
10891
|
+
...id !== void 0 ? { id } : {},
|
|
9924
10892
|
position: { x: from[0], y: from[1], z: from[2] },
|
|
9925
10893
|
items: tipLenActual < 1e-6 ? shaft ? [shaft] : [] : shaft ? [shaft, tip] : [tip]
|
|
9926
10894
|
};
|
|
@@ -9947,20 +10915,228 @@ function get3DClickPayload(onShapeClick, idToIndex) {
|
|
|
9947
10915
|
if (!onShapeClick) return void 0;
|
|
9948
10916
|
return (id) => onShapeClick({ id, index: idToIndex.get(id) ?? -1 });
|
|
9949
10917
|
}
|
|
9950
|
-
|
|
10918
|
+
function polylineTube(points, radius, color, opts) {
|
|
10919
|
+
const maxSegments = opts?.maxSegments ?? 128;
|
|
10920
|
+
let pts = points;
|
|
10921
|
+
if (pts.length - 1 > maxSegments) {
|
|
10922
|
+
const step = (pts.length - 1) / maxSegments;
|
|
10923
|
+
const kept = [pts[0]];
|
|
10924
|
+
for (let s = 1; s < maxSegments; s++) kept.push(pts[Math.round(s * step)]);
|
|
10925
|
+
kept.push(pts[pts.length - 1]);
|
|
10926
|
+
pts = kept;
|
|
10927
|
+
}
|
|
10928
|
+
const out = [];
|
|
10929
|
+
for (let i = 0; i < pts.length - 1; i++) {
|
|
10930
|
+
const seg = cylinderBetween(pts[i], pts[i + 1], radius, color);
|
|
10931
|
+
if (seg) out.push(opts?.opacity !== void 0 ? { ...seg, opacity: opts.opacity } : seg);
|
|
10932
|
+
}
|
|
10933
|
+
return out;
|
|
10934
|
+
}
|
|
10935
|
+
function heightFieldMesh(spec) {
|
|
10936
|
+
const { nx, ny, heights, spacing = 1, x = 0, y = 0 } = spec;
|
|
10937
|
+
const flatShading = spec.flatShading ?? true;
|
|
10938
|
+
const vertices = [];
|
|
10939
|
+
for (let iy = 0; iy < ny; iy++) {
|
|
10940
|
+
for (let ix = 0; ix < nx; ix++) {
|
|
10941
|
+
vertices.push([
|
|
10942
|
+
x + (ix - (nx - 1) / 2) * spacing,
|
|
10943
|
+
y + (iy - (ny - 1) / 2) * spacing,
|
|
10944
|
+
heights[iy * nx + ix] ?? 0
|
|
10945
|
+
]);
|
|
10946
|
+
}
|
|
10947
|
+
}
|
|
10948
|
+
const bands = [...spec.bands ?? []].sort((a, b) => (a.min ?? -Infinity) - (b.min ?? -Infinity));
|
|
10949
|
+
const facesByBand = /* @__PURE__ */ new Map();
|
|
10950
|
+
for (let iy = 0; iy < ny - 1; iy++) {
|
|
10951
|
+
for (let ix = 0; ix < nx - 1; ix++) {
|
|
10952
|
+
const v00 = iy * nx + ix;
|
|
10953
|
+
const v10 = iy * nx + ix + 1;
|
|
10954
|
+
const v01 = (iy + 1) * nx + ix;
|
|
10955
|
+
const v11 = (iy + 1) * nx + ix + 1;
|
|
10956
|
+
for (const face of [[v00, v10, v01], [v10, v11, v01]]) {
|
|
10957
|
+
const centroid = (vertices[face[0]][2] + vertices[face[1]][2] + vertices[face[2]][2]) / 3;
|
|
10958
|
+
let band = null;
|
|
10959
|
+
for (const b of bands) {
|
|
10960
|
+
if ((b.min ?? -Infinity) <= centroid) band = b;
|
|
10961
|
+
}
|
|
10962
|
+
const key = bands.length > 0 ? band : null;
|
|
10963
|
+
const list = facesByBand.get(key) ?? [];
|
|
10964
|
+
list.push(face);
|
|
10965
|
+
facesByBand.set(key, list);
|
|
10966
|
+
}
|
|
10967
|
+
}
|
|
10968
|
+
}
|
|
10969
|
+
const out = [];
|
|
10970
|
+
for (const [band, faces] of facesByBand) {
|
|
10971
|
+
if (faces.length === 0) continue;
|
|
10972
|
+
out.push({
|
|
10973
|
+
type: "draw-mesh",
|
|
10974
|
+
shape: "polyhedron",
|
|
10975
|
+
position: { x: 0, y: 0, z: 0 },
|
|
10976
|
+
vertices,
|
|
10977
|
+
faces,
|
|
10978
|
+
pivot: "center",
|
|
10979
|
+
material: { color: band?.color ?? spec.color ?? "#64748b", flatShading, side: "double" },
|
|
10980
|
+
...spec.opacity !== void 0 ? { opacity: spec.opacity } : {}
|
|
10981
|
+
});
|
|
10982
|
+
}
|
|
10983
|
+
return out;
|
|
10984
|
+
}
|
|
10985
|
+
function arrowField(vectors, opts) {
|
|
10986
|
+
const scale = opts?.scale ?? 1;
|
|
10987
|
+
const out = [];
|
|
10988
|
+
for (const v of vectors) {
|
|
10989
|
+
const to = [
|
|
10990
|
+
v.from[0] + v.delta[0] * scale,
|
|
10991
|
+
v.from[1] + v.delta[1] * scale,
|
|
10992
|
+
v.from[2] + v.delta[2] * scale
|
|
10993
|
+
];
|
|
10994
|
+
const arrow = arrowBetween(v.from, to, v.color ?? "#dc2626", v.width, v.id);
|
|
10995
|
+
if (arrow) out.push(arrow);
|
|
10996
|
+
if (v.label) out.push(billboardLabel(v.label, to[0], to[1], to[2], { color: opts?.labelColor }));
|
|
10997
|
+
}
|
|
10998
|
+
return out;
|
|
10999
|
+
}
|
|
11000
|
+
function helixDrawables(spec, opts) {
|
|
11001
|
+
const count = spec.count ?? spec.rungs?.length ?? 0;
|
|
11002
|
+
const rungs = Array.from({ length: count }, (_, i) => spec.rungs?.[i] ?? {});
|
|
11003
|
+
const radius = spec.radius ?? 1;
|
|
11004
|
+
const rise = spec.rise ?? 0.34;
|
|
11005
|
+
const twistRad = (spec.twistDeg ?? 36) * (Math.PI / 180);
|
|
11006
|
+
const strandAColor = spec.strandAColor ?? "#38bdf8";
|
|
11007
|
+
const strandBColor = spec.strandBColor ?? "#fb923c";
|
|
11008
|
+
const backboneRadius = spec.backboneRadius ?? 0.16;
|
|
11009
|
+
const rungRadius = spec.rungRadius ?? 0.12;
|
|
11010
|
+
const cx = spec.x ?? 0;
|
|
11011
|
+
const cy = spec.y ?? 0;
|
|
11012
|
+
const cz = spec.z ?? 0;
|
|
11013
|
+
const unwoundCount = spec.unwoundCount ?? 0;
|
|
11014
|
+
const unwindSpread = spec.unwindSpread ?? 1.8;
|
|
11015
|
+
const strandA = [];
|
|
11016
|
+
const strandB = [];
|
|
11017
|
+
for (let i = 0; i < count; i++) {
|
|
11018
|
+
const yi = cy + (i - (count - 1) / 2) * rise;
|
|
11019
|
+
const theta = i * twistRad;
|
|
11020
|
+
const s = i < unwoundCount ? unwindSpread : 1;
|
|
11021
|
+
strandA.push([cx + s * radius * Math.cos(theta), yi, cz + s * radius * Math.sin(theta)]);
|
|
11022
|
+
strandB.push([cx + s * radius * Math.cos(theta + Math.PI), yi, cz + s * radius * Math.sin(theta + Math.PI)]);
|
|
11023
|
+
}
|
|
11024
|
+
const out = [];
|
|
11025
|
+
for (let i = 0; i < count; i++) {
|
|
11026
|
+
out.push(meshSphere(`hx-a-${i}`, strandA[i][0], strandA[i][1], strandA[i][2], backboneRadius, strandAColor));
|
|
11027
|
+
out.push(meshSphere(`hx-b-${i}`, strandB[i][0], strandB[i][1], strandB[i][2], backboneRadius, strandBColor));
|
|
11028
|
+
if (i > 0) {
|
|
11029
|
+
const segA = cylinderBetween(strandA[i - 1], strandA[i], backboneRadius, strandAColor);
|
|
11030
|
+
if (segA) out.push(segA);
|
|
11031
|
+
const segB = cylinderBetween(strandB[i - 1], strandB[i], backboneRadius, strandBColor);
|
|
11032
|
+
if (segB) out.push(segB);
|
|
11033
|
+
}
|
|
11034
|
+
const rung = rungs[i];
|
|
11035
|
+
const rungColor = rung.color ?? "#94a3b8";
|
|
11036
|
+
const rod = cylinderBetween(strandA[i], strandB[i], rungRadius, rungColor);
|
|
11037
|
+
if (rod) out.push(rod);
|
|
11038
|
+
const mid = [
|
|
11039
|
+
(strandA[i][0] + strandB[i][0]) / 2,
|
|
11040
|
+
(strandA[i][1] + strandB[i][1]) / 2,
|
|
11041
|
+
(strandA[i][2] + strandB[i][2]) / 2
|
|
11042
|
+
];
|
|
11043
|
+
const markerRadius = rung.radius ?? rungRadius;
|
|
11044
|
+
out.push(meshSphere(rung.id, mid[0], mid[1], mid[2], markerRadius, rungColor));
|
|
11045
|
+
if (rung.label) out.push(billboardLabel(rung.label, mid[0], mid[1], mid[2] + markerRadius, { color: opts?.labelColor }));
|
|
11046
|
+
}
|
|
11047
|
+
return out;
|
|
11048
|
+
}
|
|
11049
|
+
function latticeDrawables(spec, opts) {
|
|
11050
|
+
const nx = spec.nx ?? 2;
|
|
11051
|
+
const ny = spec.ny ?? 2;
|
|
11052
|
+
const nz = spec.nz ?? 2;
|
|
11053
|
+
const latticeConstant = spec.latticeConstant ?? 2;
|
|
11054
|
+
const bondRadius = spec.bondRadius ?? 0.06;
|
|
11055
|
+
const highlightCell = spec.highlightCell ?? false;
|
|
11056
|
+
const dimColor = spec.dimColor ?? "#475569";
|
|
11057
|
+
const showLabels = spec.showLabels ?? false;
|
|
11058
|
+
const selectedColor = spec.selectedColor ?? "#f59e0b";
|
|
11059
|
+
const posByKey = /* @__PURE__ */ new Map();
|
|
11060
|
+
const inCellByKey = /* @__PURE__ */ new Map();
|
|
11061
|
+
const out = [];
|
|
11062
|
+
for (const site of spec.basis) {
|
|
11063
|
+
const snx = site.xEdge ? nx + 1 : nx;
|
|
11064
|
+
const sny = site.yEdge ? ny + 1 : ny;
|
|
11065
|
+
const snz = site.zEdge ? nz + 1 : nz;
|
|
11066
|
+
for (let i = 0; i < snx; i++) {
|
|
11067
|
+
for (let j = 0; j < sny; j++) {
|
|
11068
|
+
for (let k = 0; k < snz; k++) {
|
|
11069
|
+
const key = `${site.key}-${i}-${j}-${k}`;
|
|
11070
|
+
const inCell = i + site.dx <= 1 && j + site.dy <= 1 && k + site.dz <= 1;
|
|
11071
|
+
const pos = [
|
|
11072
|
+
(i + site.dx) * latticeConstant - nx * latticeConstant / 2,
|
|
11073
|
+
(j + site.dy) * latticeConstant - ny * latticeConstant / 2,
|
|
11074
|
+
(k + site.dz) * latticeConstant - nz * latticeConstant / 2
|
|
11075
|
+
];
|
|
11076
|
+
posByKey.set(key, pos);
|
|
11077
|
+
inCellByKey.set(key, inCell);
|
|
11078
|
+
const isSelected = spec.selectedId === `lat-${key}`;
|
|
11079
|
+
const color = isSelected ? selectedColor : highlightCell && !inCell ? dimColor : site.color ?? "#2563eb";
|
|
11080
|
+
const radius = (site.radius ?? 0.3) * (isSelected ? 1.4 : 1);
|
|
11081
|
+
out.push(meshSphere(`lat-${key}`, pos[0], pos[1], pos[2], radius, color));
|
|
11082
|
+
if (showLabels && site.element) {
|
|
11083
|
+
out.push(billboardLabel(site.element, pos[0], pos[1], pos[2] + radius, { color: opts?.labelColor }));
|
|
11084
|
+
}
|
|
11085
|
+
}
|
|
11086
|
+
}
|
|
11087
|
+
}
|
|
11088
|
+
}
|
|
11089
|
+
const basisByKey = new Map(spec.basis.map((s) => [s.key, s]));
|
|
11090
|
+
for (const bond of spec.bonds ?? []) {
|
|
11091
|
+
const fromSite = basisByKey.get(bond.from);
|
|
11092
|
+
const toSite = basisByKey.get(bond.to);
|
|
11093
|
+
if (!fromSite || !toSite) continue;
|
|
11094
|
+
const fnx = fromSite.xEdge ? nx + 1 : nx;
|
|
11095
|
+
const fny = fromSite.yEdge ? ny + 1 : ny;
|
|
11096
|
+
const fnz = fromSite.zEdge ? nz + 1 : nz;
|
|
11097
|
+
const tnx = toSite.xEdge ? nx + 1 : nx;
|
|
11098
|
+
const tny = toSite.yEdge ? ny + 1 : ny;
|
|
11099
|
+
const tnz = toSite.zEdge ? nz + 1 : nz;
|
|
11100
|
+
const bdx = bond.dx ?? 0;
|
|
11101
|
+
const bdy = bond.dy ?? 0;
|
|
11102
|
+
const bdz = bond.dz ?? 0;
|
|
11103
|
+
for (let i = 0; i < fnx; i++) {
|
|
11104
|
+
for (let j = 0; j < fny; j++) {
|
|
11105
|
+
for (let k = 0; k < fnz; k++) {
|
|
11106
|
+
const ti = i + bdx;
|
|
11107
|
+
const tj = j + bdy;
|
|
11108
|
+
const tk = k + bdz;
|
|
11109
|
+
if (ti < 0 || ti >= tnx || tj < 0 || tj >= tny || tk < 0 || tk >= tnz) continue;
|
|
11110
|
+
const fromKey = `${fromSite.key}-${i}-${j}-${k}`;
|
|
11111
|
+
const toKey = `${toSite.key}-${ti}-${tj}-${tk}`;
|
|
11112
|
+
const fromPos = posByKey.get(fromKey);
|
|
11113
|
+
const toPos = posByKey.get(toKey);
|
|
11114
|
+
if (!fromPos || !toPos) continue;
|
|
11115
|
+
const dimmed = highlightCell && !(inCellByKey.get(fromKey) && inCellByKey.get(toKey));
|
|
11116
|
+
const seg = cylinderBetween(fromPos, toPos, bondRadius, dimmed ? dimColor : bond.color ?? "#6b7280");
|
|
11117
|
+
if (seg) out.push(seg);
|
|
11118
|
+
}
|
|
11119
|
+
}
|
|
11120
|
+
}
|
|
11121
|
+
}
|
|
11122
|
+
return out;
|
|
11123
|
+
}
|
|
11124
|
+
var sceneLog, KNOWN_BLOOM_KEYS, Canvas3DHost;
|
|
9951
11125
|
var init_learningScene3D = __esm({
|
|
9952
11126
|
"components/learning/molecules/learningScene3D.tsx"() {
|
|
9953
11127
|
"use client";
|
|
9954
11128
|
init_atoms();
|
|
9955
11129
|
init_Stack();
|
|
9956
11130
|
init_useEventBus();
|
|
11131
|
+
sceneLog = createLogger("almadar:ui:learning-scene-3d");
|
|
11132
|
+
KNOWN_BLOOM_KEYS = /* @__PURE__ */ new Set(["intensity", "threshold", "smoothing"]);
|
|
9957
11133
|
Canvas3DHost = lazy(
|
|
9958
11134
|
() => import('@almadar/ui/components/molecules/game/three').then((m) => ({ default: m.Canvas3DHost }))
|
|
9959
11135
|
);
|
|
9960
11136
|
LearningScene3D.displayName = "LearningScene3D";
|
|
9961
11137
|
}
|
|
9962
11138
|
});
|
|
9963
|
-
var biologyLog, BiologyCanvas;
|
|
11139
|
+
var biologyLog, BIO_BAND_COLORS, BIO_STAGE_FILL, BIO_STAGE_TEXT, BiologyCanvas;
|
|
9964
11140
|
var init_BiologyCanvas = __esm({
|
|
9965
11141
|
"components/learning/molecules/BiologyCanvas.tsx"() {
|
|
9966
11142
|
"use client";
|
|
@@ -9969,6 +11145,17 @@ var init_BiologyCanvas = __esm({
|
|
|
9969
11145
|
init_LearningCanvas();
|
|
9970
11146
|
init_learningScene3D();
|
|
9971
11147
|
biologyLog = createLogger("almadar:ui:biology-canvas");
|
|
11148
|
+
BIO_BAND_COLORS = ["#dcfce7", "#fef9c3", "#fee2e2", "#e0e7ff"];
|
|
11149
|
+
BIO_STAGE_FILL = {
|
|
11150
|
+
pending: "#e2e8f0",
|
|
11151
|
+
active: "#3b82f6",
|
|
11152
|
+
done: "#94a3b8"
|
|
11153
|
+
};
|
|
11154
|
+
BIO_STAGE_TEXT = {
|
|
11155
|
+
pending: "#64748b",
|
|
11156
|
+
active: "#ffffff",
|
|
11157
|
+
done: "#ffffff"
|
|
11158
|
+
};
|
|
9972
11159
|
BiologyCanvas = ({
|
|
9973
11160
|
className,
|
|
9974
11161
|
width = 600,
|
|
@@ -9981,7 +11168,15 @@ var init_BiologyCanvas = __esm({
|
|
|
9981
11168
|
post,
|
|
9982
11169
|
nodes = [],
|
|
9983
11170
|
edges = [],
|
|
11171
|
+
compartments = [],
|
|
11172
|
+
bands = [],
|
|
11173
|
+
stages = [],
|
|
11174
|
+
stageStyle = "timeline",
|
|
11175
|
+
helix,
|
|
11176
|
+
helix3d,
|
|
9984
11177
|
shapes = [],
|
|
11178
|
+
readouts,
|
|
11179
|
+
traces,
|
|
9985
11180
|
showGrid,
|
|
9986
11181
|
shadows,
|
|
9987
11182
|
interactive,
|
|
@@ -9996,19 +11191,148 @@ var init_BiologyCanvas = __esm({
|
|
|
9996
11191
|
for (const n of nodes) {
|
|
9997
11192
|
if (n.id) nodeById.set(n.id, n);
|
|
9998
11193
|
}
|
|
11194
|
+
const bandCount = bands.length;
|
|
11195
|
+
for (let i = 0; i < bandCount; i++) {
|
|
11196
|
+
const band = bands[i];
|
|
11197
|
+
const bandColor = band.color ?? BIO_BAND_COLORS[i % BIO_BAND_COLORS.length];
|
|
11198
|
+
const bandY = i * height / bandCount;
|
|
11199
|
+
const bandH = height / bandCount;
|
|
11200
|
+
out.push({
|
|
11201
|
+
type: "rect",
|
|
11202
|
+
x: 0,
|
|
11203
|
+
y: bandY,
|
|
11204
|
+
width,
|
|
11205
|
+
height: bandH,
|
|
11206
|
+
color: bandColor,
|
|
11207
|
+
fill: bandColor,
|
|
11208
|
+
opacity: 0.45
|
|
11209
|
+
});
|
|
11210
|
+
if (band.label) {
|
|
11211
|
+
out.push({
|
|
11212
|
+
type: "text",
|
|
11213
|
+
x: 8,
|
|
11214
|
+
y: bandY + 14,
|
|
11215
|
+
text: band.label,
|
|
11216
|
+
color: "#6b7280",
|
|
11217
|
+
fontSize: 10
|
|
11218
|
+
});
|
|
11219
|
+
}
|
|
11220
|
+
}
|
|
11221
|
+
for (const c of compartments) {
|
|
11222
|
+
const color = c.color ?? "#16a34a";
|
|
11223
|
+
out.push({
|
|
11224
|
+
type: "ellipse",
|
|
11225
|
+
x: c.x,
|
|
11226
|
+
y: c.y,
|
|
11227
|
+
width: c.width,
|
|
11228
|
+
height: c.height,
|
|
11229
|
+
color,
|
|
11230
|
+
fill: c.fill ?? `${color}1A`,
|
|
11231
|
+
lineWidth: c.lineWidth ?? 2,
|
|
11232
|
+
...c.dash ? { dash: c.dash } : {}
|
|
11233
|
+
});
|
|
11234
|
+
if (c.label) {
|
|
11235
|
+
out.push({
|
|
11236
|
+
type: "text",
|
|
11237
|
+
x: c.x,
|
|
11238
|
+
y: c.y - c.height / 2 + 14,
|
|
11239
|
+
text: c.label,
|
|
11240
|
+
color: "#111827",
|
|
11241
|
+
fontSize: 11,
|
|
11242
|
+
align: "center"
|
|
11243
|
+
});
|
|
11244
|
+
}
|
|
11245
|
+
}
|
|
11246
|
+
if (helix) {
|
|
11247
|
+
const hx = helix.x ?? 24;
|
|
11248
|
+
const hy = helix.y ?? height * 0.25;
|
|
11249
|
+
const hw = helix.width ?? width - 48;
|
|
11250
|
+
const hh = helix.height ?? height * 0.5;
|
|
11251
|
+
const rungs = helix.rungs;
|
|
11252
|
+
const n = rungs.length;
|
|
11253
|
+
const cy = hy + hh / 2;
|
|
11254
|
+
const colorA = helix.colorA ?? "#2563eb";
|
|
11255
|
+
const colorB = helix.colorB ?? "#dc2626";
|
|
11256
|
+
const rungColor = helix.rungColor ?? "#94a3b8";
|
|
11257
|
+
const fork = helix.fork ?? 0;
|
|
11258
|
+
const maxSep = Math.min(hh - 8, 96);
|
|
11259
|
+
const strandA = [];
|
|
11260
|
+
const strandB = [];
|
|
11261
|
+
const rungGeoms = [];
|
|
11262
|
+
for (let i = 0; i < n; i++) {
|
|
11263
|
+
const rx = hx + (i + 0.5) * hw / n;
|
|
11264
|
+
const t = (i + 0.5) / n;
|
|
11265
|
+
const paired = t >= fork;
|
|
11266
|
+
const sep = paired ? 28 : 28 + (maxSep - 28) * ((fork - t) / fork);
|
|
11267
|
+
strandA.push({ x: rx, y: cy - sep / 2 });
|
|
11268
|
+
strandB.push({ x: rx, y: cy + sep / 2 });
|
|
11269
|
+
rungGeoms.push({ rx, sep, rung: rungs[i], paired });
|
|
11270
|
+
}
|
|
11271
|
+
for (let i = 1; i < n; i++) {
|
|
11272
|
+
out.push({ type: "line", x1: strandA[i - 1].x, y1: strandA[i - 1].y, x2: strandA[i].x, y2: strandA[i].y, color: colorA, lineWidth: 3 });
|
|
11273
|
+
}
|
|
11274
|
+
for (let i = 1; i < n; i++) {
|
|
11275
|
+
out.push({ type: "line", x1: strandB[i - 1].x, y1: strandB[i - 1].y, x2: strandB[i].x, y2: strandB[i].y, color: colorB, lineWidth: 3 });
|
|
11276
|
+
}
|
|
11277
|
+
for (const g of rungGeoms) {
|
|
11278
|
+
const rColor = g.rung.color ?? (g.rung.state === "new" ? "#16a34a" : rungColor);
|
|
11279
|
+
const topY = cy - g.sep / 2;
|
|
11280
|
+
const bottomY = cy + g.sep / 2;
|
|
11281
|
+
if (g.paired) {
|
|
11282
|
+
out.push({ type: "line", x1: g.rx, y1: topY, x2: g.rx, y2: bottomY, color: rColor });
|
|
11283
|
+
if (g.rung.a) {
|
|
11284
|
+
out.push({ type: "text", x: g.rx, y: cy - g.sep / 4, text: g.rung.a, fontSize: 9, align: "center", color: "#374151" });
|
|
11285
|
+
}
|
|
11286
|
+
if (g.rung.b) {
|
|
11287
|
+
out.push({ type: "text", x: g.rx, y: cy + g.sep / 4, text: g.rung.b, fontSize: 9, align: "center", color: "#374151" });
|
|
11288
|
+
}
|
|
11289
|
+
} else {
|
|
11290
|
+
const stubTopY = topY + 8;
|
|
11291
|
+
const stubBottomY = bottomY - 8;
|
|
11292
|
+
out.push({ type: "line", x1: g.rx, y1: topY, x2: g.rx, y2: stubTopY, color: rColor });
|
|
11293
|
+
out.push({ type: "line", x1: g.rx, y1: bottomY, x2: g.rx, y2: stubBottomY, color: rColor });
|
|
11294
|
+
if (g.rung.a) {
|
|
11295
|
+
out.push({ type: "text", x: g.rx, y: stubTopY + 6, text: g.rung.a, fontSize: 9, align: "center", color: "#374151" });
|
|
11296
|
+
}
|
|
11297
|
+
if (g.rung.b) {
|
|
11298
|
+
out.push({ type: "text", x: g.rx, y: stubBottomY - 6, text: g.rung.b, fontSize: 9, align: "center", color: "#374151" });
|
|
11299
|
+
}
|
|
11300
|
+
}
|
|
11301
|
+
}
|
|
11302
|
+
}
|
|
9999
11303
|
for (const e of edges) {
|
|
10000
11304
|
const a = nodeById.get(e.from);
|
|
10001
11305
|
const b = nodeById.get(e.to);
|
|
10002
11306
|
if (!a || !b) continue;
|
|
10003
|
-
|
|
10004
|
-
|
|
10005
|
-
|
|
10006
|
-
|
|
10007
|
-
|
|
10008
|
-
|
|
10009
|
-
|
|
10010
|
-
|
|
10011
|
-
|
|
11307
|
+
const color = e.color ?? "#9ca3af";
|
|
11308
|
+
if (e.directed) {
|
|
11309
|
+
const rA = a.radius ?? 16;
|
|
11310
|
+
const rB = b.radius ?? 16;
|
|
11311
|
+
const dx = b.x - a.x;
|
|
11312
|
+
const dy = b.y - a.y;
|
|
11313
|
+
const dist = Math.max(1e-6, Math.hypot(dx, dy));
|
|
11314
|
+
const ux = dx / dist;
|
|
11315
|
+
const uy = dy / dist;
|
|
11316
|
+
out.push({
|
|
11317
|
+
type: "arrow",
|
|
11318
|
+
x1: a.x + ux * rA,
|
|
11319
|
+
y1: a.y + uy * rA,
|
|
11320
|
+
x2: b.x - ux * rB,
|
|
11321
|
+
y2: b.y - uy * rB,
|
|
11322
|
+
color,
|
|
11323
|
+
lineWidth: 2
|
|
11324
|
+
});
|
|
11325
|
+
} else {
|
|
11326
|
+
out.push({
|
|
11327
|
+
type: "line",
|
|
11328
|
+
x1: a.x,
|
|
11329
|
+
y1: a.y,
|
|
11330
|
+
x2: b.x,
|
|
11331
|
+
y2: b.y,
|
|
11332
|
+
color,
|
|
11333
|
+
lineWidth: 2
|
|
11334
|
+
});
|
|
11335
|
+
}
|
|
10012
11336
|
if (e.label) {
|
|
10013
11337
|
out.push({
|
|
10014
11338
|
type: "text",
|
|
@@ -10021,6 +11345,8 @@ var init_BiologyCanvas = __esm({
|
|
|
10021
11345
|
}
|
|
10022
11346
|
}
|
|
10023
11347
|
for (const n of nodes) {
|
|
11348
|
+
const state = n.state ?? "default";
|
|
11349
|
+
const muted = state === "muted";
|
|
10024
11350
|
out.push({
|
|
10025
11351
|
type: "circle",
|
|
10026
11352
|
x: n.x,
|
|
@@ -10028,28 +11354,127 @@ var init_BiologyCanvas = __esm({
|
|
|
10028
11354
|
radius: n.radius ?? 16,
|
|
10029
11355
|
color: n.color ?? "#16a34a",
|
|
10030
11356
|
fill: `${n.color ?? "#16a34a"}33`,
|
|
10031
|
-
id: n.id
|
|
11357
|
+
id: n.id,
|
|
11358
|
+
...muted ? { opacity: 0.35 } : {}
|
|
10032
11359
|
});
|
|
11360
|
+
if (state === "highlight") {
|
|
11361
|
+
out.push({
|
|
11362
|
+
type: "circle",
|
|
11363
|
+
x: n.x,
|
|
11364
|
+
y: n.y,
|
|
11365
|
+
radius: (n.radius ?? 16) + 4,
|
|
11366
|
+
color: "#f59e0b",
|
|
11367
|
+
lineWidth: 2
|
|
11368
|
+
});
|
|
11369
|
+
}
|
|
10033
11370
|
if (n.label) {
|
|
10034
11371
|
out.push({
|
|
10035
11372
|
type: "text",
|
|
10036
11373
|
x: n.x,
|
|
10037
11374
|
y: n.y + (n.radius ?? 16) + 14,
|
|
10038
11375
|
text: n.label,
|
|
11376
|
+
...muted ? { opacity: 0.35 } : {},
|
|
10039
11377
|
color: "#111827",
|
|
10040
11378
|
fontSize: 12,
|
|
10041
11379
|
align: "center"
|
|
10042
11380
|
});
|
|
10043
11381
|
}
|
|
10044
11382
|
}
|
|
11383
|
+
const stageCount = stages.length;
|
|
11384
|
+
if (stageCount > 0) {
|
|
11385
|
+
if (stageStyle === "ring") {
|
|
11386
|
+
const cx = width / 2;
|
|
11387
|
+
const cy = height / 2;
|
|
11388
|
+
const R = Math.min(width, height) / 2 - 48;
|
|
11389
|
+
const ringPoints = [];
|
|
11390
|
+
for (let i = 0; i < stageCount; i++) {
|
|
11391
|
+
const angleRad = (-90 + 360 * i / stageCount) * Math.PI / 180;
|
|
11392
|
+
ringPoints.push({ x: cx + R * Math.cos(angleRad), y: cy + R * Math.sin(angleRad) });
|
|
11393
|
+
}
|
|
11394
|
+
if (stageCount >= 2) {
|
|
11395
|
+
for (let i = 0; i < stageCount - 1; i++) {
|
|
11396
|
+
const p1 = ringPoints[i];
|
|
11397
|
+
const p2 = ringPoints[i + 1];
|
|
11398
|
+
const dx = p2.x - p1.x;
|
|
11399
|
+
const dy = p2.y - p1.y;
|
|
11400
|
+
const dist = Math.max(1e-6, Math.hypot(dx, dy));
|
|
11401
|
+
const ux = dx / dist;
|
|
11402
|
+
const uy = dy / dist;
|
|
11403
|
+
out.push({
|
|
11404
|
+
type: "arrow",
|
|
11405
|
+
x1: p1.x + ux * 46,
|
|
11406
|
+
y1: p1.y + uy * 46,
|
|
11407
|
+
x2: p2.x - ux * 46,
|
|
11408
|
+
y2: p2.y - uy * 46,
|
|
11409
|
+
color: "#94a3b8"
|
|
11410
|
+
});
|
|
11411
|
+
}
|
|
11412
|
+
}
|
|
11413
|
+
for (let i = 0; i < stageCount; i++) {
|
|
11414
|
+
const stage = stages[i];
|
|
11415
|
+
const state = stage.state ?? "pending";
|
|
11416
|
+
const fill = stage.color ?? BIO_STAGE_FILL[state];
|
|
11417
|
+
const w = Math.max(26, Math.min(84, stage.label.length * 6 + 10));
|
|
11418
|
+
const h = 18;
|
|
11419
|
+
const p = ringPoints[i];
|
|
11420
|
+
out.push({ type: "rect", x: p.x - w / 2, y: p.y - h / 2, width: w, height: h, color: fill, fill });
|
|
11421
|
+
out.push({ type: "text", x: p.x, y: p.y, text: stage.label, color: BIO_STAGE_TEXT[state], fontSize: 10, align: "center" });
|
|
11422
|
+
}
|
|
11423
|
+
} else {
|
|
11424
|
+
const stripY = height - 32;
|
|
11425
|
+
const slotW = (width - 16) / stageCount;
|
|
11426
|
+
const chipGeoms = [];
|
|
11427
|
+
for (let i = 0; i < stageCount; i++) {
|
|
11428
|
+
chipGeoms.push({ x: 8 + i * slotW + 5, w: slotW - 10 });
|
|
11429
|
+
}
|
|
11430
|
+
for (let i = 0; i < stageCount - 1; i++) {
|
|
11431
|
+
const midY = stripY + 13;
|
|
11432
|
+
out.push({
|
|
11433
|
+
type: "arrow",
|
|
11434
|
+
x1: chipGeoms[i].x + chipGeoms[i].w,
|
|
11435
|
+
y1: midY,
|
|
11436
|
+
x2: chipGeoms[i + 1].x,
|
|
11437
|
+
y2: midY,
|
|
11438
|
+
color: "#94a3b8"
|
|
11439
|
+
});
|
|
11440
|
+
}
|
|
11441
|
+
for (let i = 0; i < stageCount; i++) {
|
|
11442
|
+
const stage = stages[i];
|
|
11443
|
+
const state = stage.state ?? "pending";
|
|
11444
|
+
const fill = stage.color ?? BIO_STAGE_FILL[state];
|
|
11445
|
+
const g = chipGeoms[i];
|
|
11446
|
+
out.push({ type: "rect", x: g.x, y: stripY, width: g.w, height: 26, color: fill, fill });
|
|
11447
|
+
out.push({
|
|
11448
|
+
type: "text",
|
|
11449
|
+
x: g.x + g.w / 2,
|
|
11450
|
+
y: stripY + 13,
|
|
11451
|
+
text: stage.label,
|
|
11452
|
+
color: BIO_STAGE_TEXT[state],
|
|
11453
|
+
fontSize: 10,
|
|
11454
|
+
align: "center"
|
|
11455
|
+
});
|
|
11456
|
+
}
|
|
11457
|
+
}
|
|
11458
|
+
}
|
|
10045
11459
|
out.push(...shapes);
|
|
10046
11460
|
return out;
|
|
10047
|
-
}, [nodes, edges, shapes]);
|
|
11461
|
+
}, [nodes, edges, compartments, bands, stages, stageStyle, helix, shapes, width, height]);
|
|
10048
11462
|
const drawables3D = useMemo(() => {
|
|
10049
11463
|
if (mode !== "3d") return [];
|
|
10050
11464
|
if (shapes.length > 0) {
|
|
10051
11465
|
biologyLog.debug("shapes ignored in 3D mode (pixel-authored 2D vocabulary)", { count: shapes.length });
|
|
10052
11466
|
}
|
|
11467
|
+
if (compartments.length > 0 || bands.length > 0 || stages.length > 0 || helix) {
|
|
11468
|
+
biologyLog.debug("2D-only families ignored in 3D mode (pixel-authored 2D vocabulary)", {
|
|
11469
|
+
compartments: compartments.length,
|
|
11470
|
+
bands: bands.length,
|
|
11471
|
+
stages: stages.length,
|
|
11472
|
+
helix: helix != null
|
|
11473
|
+
});
|
|
11474
|
+
}
|
|
11475
|
+
if (animate) {
|
|
11476
|
+
biologyLog.debug("animate ignored in 3D mode (motion is entity-state driven)");
|
|
11477
|
+
}
|
|
10053
11478
|
const out = [];
|
|
10054
11479
|
const labelColor = labelColorForBackground(backgroundColor);
|
|
10055
11480
|
const nodeById = /* @__PURE__ */ new Map();
|
|
@@ -10083,15 +11508,21 @@ var init_BiologyCanvas = __esm({
|
|
|
10083
11508
|
out.push(billboardLabel(n.label, n.x, n.y, nz + radius, { color: labelColor }));
|
|
10084
11509
|
}
|
|
10085
11510
|
}
|
|
11511
|
+
if (helix3d) {
|
|
11512
|
+
out.push(...helixDrawables(helix3d, { labelColor }));
|
|
11513
|
+
}
|
|
10086
11514
|
return out;
|
|
10087
|
-
}, [mode, nodes, edges, shapes, backgroundColor]);
|
|
11515
|
+
}, [mode, nodes, edges, shapes, compartments, bands, stages, helix, helix3d, animate, backgroundColor]);
|
|
10088
11516
|
const nodeIndexById = useMemo(() => {
|
|
10089
11517
|
const m = /* @__PURE__ */ new Map();
|
|
11518
|
+
(helix3d?.rungs ?? []).forEach((rung, i) => {
|
|
11519
|
+
if (rung.id) m.set(rung.id, i);
|
|
11520
|
+
});
|
|
10090
11521
|
nodes.forEach((n, i) => {
|
|
10091
11522
|
if (n.id) m.set(n.id, i);
|
|
10092
11523
|
});
|
|
10093
11524
|
return m;
|
|
10094
|
-
}, [nodes]);
|
|
11525
|
+
}, [nodes, helix3d]);
|
|
10095
11526
|
if (mode === "3d") {
|
|
10096
11527
|
return /* @__PURE__ */ jsx(
|
|
10097
11528
|
LearningScene3D,
|
|
@@ -10123,6 +11554,8 @@ var init_BiologyCanvas = __esm({
|
|
|
10123
11554
|
height,
|
|
10124
11555
|
backgroundColor,
|
|
10125
11556
|
shapes: derivedShapes,
|
|
11557
|
+
readouts,
|
|
11558
|
+
traces,
|
|
10126
11559
|
interactive: interactive ?? false,
|
|
10127
11560
|
animate,
|
|
10128
11561
|
onShapeClick,
|
|
@@ -19313,7 +20746,7 @@ function bondPerpendicular(a, b) {
|
|
|
19313
20746
|
if (len < 1e-6) return [1, 0, 0];
|
|
19314
20747
|
return [px / len, py / len, 0];
|
|
19315
20748
|
}
|
|
19316
|
-
var chemistryLog, ChemistryCanvas;
|
|
20749
|
+
var chemistryLog, CHEM_BOND_STATE_COLOR, LONE_PAIR_ANGLES, ChemistryCanvas;
|
|
19317
20750
|
var init_ChemistryCanvas = __esm({
|
|
19318
20751
|
"components/learning/molecules/ChemistryCanvas.tsx"() {
|
|
19319
20752
|
"use client";
|
|
@@ -19322,6 +20755,13 @@ var init_ChemistryCanvas = __esm({
|
|
|
19322
20755
|
init_LearningCanvas();
|
|
19323
20756
|
init_learningScene3D();
|
|
19324
20757
|
chemistryLog = createLogger("almadar:ui:chemistry-canvas");
|
|
20758
|
+
CHEM_BOND_STATE_COLOR = {
|
|
20759
|
+
default: "#6b7280",
|
|
20760
|
+
forming: "#16a34a",
|
|
20761
|
+
breaking: "#dc2626",
|
|
20762
|
+
highlight: "#f59e0b"
|
|
20763
|
+
};
|
|
20764
|
+
LONE_PAIR_ANGLES = [-90, 0, 90, 180];
|
|
19325
20765
|
ChemistryCanvas = ({
|
|
19326
20766
|
className,
|
|
19327
20767
|
width = 600,
|
|
@@ -19335,7 +20775,14 @@ var init_ChemistryCanvas = __esm({
|
|
|
19335
20775
|
atoms = [],
|
|
19336
20776
|
bonds = [],
|
|
19337
20777
|
arrows = [],
|
|
20778
|
+
bondStyle = "thick",
|
|
20779
|
+
containers = [],
|
|
20780
|
+
equation,
|
|
20781
|
+
equationColor,
|
|
20782
|
+
lattice3d,
|
|
19338
20783
|
shapes = [],
|
|
20784
|
+
readouts,
|
|
20785
|
+
traces,
|
|
19339
20786
|
showGrid,
|
|
19340
20787
|
shadows,
|
|
19341
20788
|
interactive,
|
|
@@ -19350,21 +20797,118 @@ var init_ChemistryCanvas = __esm({
|
|
|
19350
20797
|
for (const a of atoms) {
|
|
19351
20798
|
if (a.id) atomById.set(a.id, a);
|
|
19352
20799
|
}
|
|
20800
|
+
for (const c of containers) {
|
|
20801
|
+
const color = c.color ?? "#64748b";
|
|
20802
|
+
if (c.level != null) {
|
|
20803
|
+
const lv = c.level;
|
|
20804
|
+
out.push({
|
|
20805
|
+
type: "rect",
|
|
20806
|
+
x: c.x + 1,
|
|
20807
|
+
y: c.y + c.height * (1 - lv),
|
|
20808
|
+
width: c.width - 2,
|
|
20809
|
+
height: c.height * lv - 1,
|
|
20810
|
+
color: c.levelColor ?? "#60a5fa",
|
|
20811
|
+
fill: c.levelColor ?? "#60a5fa",
|
|
20812
|
+
opacity: 0.5
|
|
20813
|
+
});
|
|
20814
|
+
}
|
|
20815
|
+
out.push({
|
|
20816
|
+
type: "rect",
|
|
20817
|
+
x: c.x,
|
|
20818
|
+
y: c.y,
|
|
20819
|
+
width: c.width,
|
|
20820
|
+
height: c.height,
|
|
20821
|
+
color,
|
|
20822
|
+
fill: c.fill,
|
|
20823
|
+
lineWidth: c.lineWidth ?? 2
|
|
20824
|
+
});
|
|
20825
|
+
const divider = c.divider ?? "none";
|
|
20826
|
+
if (divider !== "none") {
|
|
20827
|
+
out.push({
|
|
20828
|
+
type: "line",
|
|
20829
|
+
x1: c.x + c.width / 2,
|
|
20830
|
+
y1: c.y,
|
|
20831
|
+
x2: c.x + c.width / 2,
|
|
20832
|
+
y2: c.y + c.height,
|
|
20833
|
+
color: c.dividerColor ?? color,
|
|
20834
|
+
...divider === "dashed" || divider === "dotted" ? { dash: divider } : {}
|
|
20835
|
+
});
|
|
20836
|
+
}
|
|
20837
|
+
if (c.leftLabel) {
|
|
20838
|
+
out.push({
|
|
20839
|
+
type: "text",
|
|
20840
|
+
x: c.x + c.width * 0.25,
|
|
20841
|
+
y: c.y + 12,
|
|
20842
|
+
text: c.leftLabel,
|
|
20843
|
+
color: "#374151",
|
|
20844
|
+
fontSize: 11,
|
|
20845
|
+
align: "center"
|
|
20846
|
+
});
|
|
20847
|
+
}
|
|
20848
|
+
if (c.rightLabel) {
|
|
20849
|
+
out.push({
|
|
20850
|
+
type: "text",
|
|
20851
|
+
x: c.x + c.width * 0.75,
|
|
20852
|
+
y: c.y + 12,
|
|
20853
|
+
text: c.rightLabel,
|
|
20854
|
+
color: "#374151",
|
|
20855
|
+
fontSize: 11,
|
|
20856
|
+
align: "center"
|
|
20857
|
+
});
|
|
20858
|
+
}
|
|
20859
|
+
if (c.label) {
|
|
20860
|
+
out.push({
|
|
20861
|
+
type: "text",
|
|
20862
|
+
x: c.x + c.width / 2,
|
|
20863
|
+
y: c.y + c.height + 12,
|
|
20864
|
+
text: c.label,
|
|
20865
|
+
color: "#111827",
|
|
20866
|
+
fontSize: 12,
|
|
20867
|
+
align: "center"
|
|
20868
|
+
});
|
|
20869
|
+
}
|
|
20870
|
+
}
|
|
19353
20871
|
for (const b of bonds) {
|
|
19354
20872
|
const a = atomById.get(b.from);
|
|
19355
20873
|
const c = atomById.get(b.to);
|
|
19356
20874
|
if (!a || !c) continue;
|
|
19357
|
-
const
|
|
19358
|
-
const
|
|
19359
|
-
|
|
19360
|
-
|
|
19361
|
-
|
|
19362
|
-
|
|
19363
|
-
|
|
19364
|
-
|
|
19365
|
-
|
|
19366
|
-
|
|
19367
|
-
|
|
20875
|
+
const state = b.state ?? "default";
|
|
20876
|
+
const color = b.color ?? CHEM_BOND_STATE_COLOR[state];
|
|
20877
|
+
const dash = state === "forming" || state === "breaking" ? "dashed" : void 0;
|
|
20878
|
+
if (bondStyle === "parallel") {
|
|
20879
|
+
const dx = c.x - a.x;
|
|
20880
|
+
const dy = c.y - a.y;
|
|
20881
|
+
const dist = Math.max(1e-6, Math.hypot(dx, dy));
|
|
20882
|
+
const ux = dx / dist;
|
|
20883
|
+
const uy = dy / dist;
|
|
20884
|
+
const px = -uy;
|
|
20885
|
+
const py = ux;
|
|
20886
|
+
const offsets = b.type === "double" ? [-3, 3] : b.type === "triple" ? [-4, 0, 4] : [0];
|
|
20887
|
+
for (const off of offsets) {
|
|
20888
|
+
out.push({
|
|
20889
|
+
type: "line",
|
|
20890
|
+
x1: a.x + px * off,
|
|
20891
|
+
y1: a.y + py * off,
|
|
20892
|
+
x2: c.x + px * off,
|
|
20893
|
+
y2: c.y + py * off,
|
|
20894
|
+
color,
|
|
20895
|
+
lineWidth: 2,
|
|
20896
|
+
...dash ? { dash } : {}
|
|
20897
|
+
});
|
|
20898
|
+
}
|
|
20899
|
+
} else {
|
|
20900
|
+
const strokeWidth = b.type === "double" ? 4 : b.type === "triple" ? 6 : 2;
|
|
20901
|
+
out.push({
|
|
20902
|
+
type: "line",
|
|
20903
|
+
x1: a.x,
|
|
20904
|
+
y1: a.y,
|
|
20905
|
+
x2: c.x,
|
|
20906
|
+
y2: c.y,
|
|
20907
|
+
color,
|
|
20908
|
+
lineWidth: strokeWidth,
|
|
20909
|
+
...dash ? { dash } : {}
|
|
20910
|
+
});
|
|
20911
|
+
}
|
|
19368
20912
|
}
|
|
19369
20913
|
for (const a of arrows) {
|
|
19370
20914
|
const angle = (a.angle ?? 0) * (Math.PI / 180);
|
|
@@ -19413,15 +20957,62 @@ var init_ChemistryCanvas = __esm({
|
|
|
19413
20957
|
align: "center"
|
|
19414
20958
|
});
|
|
19415
20959
|
}
|
|
20960
|
+
const r = a.radius ?? 14;
|
|
20961
|
+
if (a.charge) {
|
|
20962
|
+
out.push({
|
|
20963
|
+
type: "text",
|
|
20964
|
+
x: a.x + r * 0.85,
|
|
20965
|
+
y: a.y - r * 0.85,
|
|
20966
|
+
text: a.charge,
|
|
20967
|
+
color: "#111827",
|
|
20968
|
+
fontSize: 9,
|
|
20969
|
+
align: "left"
|
|
20970
|
+
});
|
|
20971
|
+
}
|
|
20972
|
+
const lonePairs = Math.max(0, Math.min(4, a.lonePairs ?? 0));
|
|
20973
|
+
for (let k = 0; k < lonePairs; k++) {
|
|
20974
|
+
const angleRad = LONE_PAIR_ANGLES[k] * Math.PI / 180;
|
|
20975
|
+
const cx = a.x + (r + 6) * Math.cos(angleRad);
|
|
20976
|
+
const cy = a.y + (r + 6) * Math.sin(angleRad);
|
|
20977
|
+
const perpX = -Math.sin(angleRad);
|
|
20978
|
+
const perpY = Math.cos(angleRad);
|
|
20979
|
+
for (const sign of [1, -1]) {
|
|
20980
|
+
out.push({
|
|
20981
|
+
type: "circle",
|
|
20982
|
+
x: cx + perpX * 2.5 * sign,
|
|
20983
|
+
y: cy + perpY * 2.5 * sign,
|
|
20984
|
+
radius: 1.5,
|
|
20985
|
+
color: "#374151",
|
|
20986
|
+
fill: "#374151"
|
|
20987
|
+
});
|
|
20988
|
+
}
|
|
20989
|
+
}
|
|
20990
|
+
}
|
|
20991
|
+
if (equation) {
|
|
20992
|
+
out.push({
|
|
20993
|
+
type: "text",
|
|
20994
|
+
x: width / 2,
|
|
20995
|
+
y: 14,
|
|
20996
|
+
text: equation,
|
|
20997
|
+
color: equationColor ?? "#111827",
|
|
20998
|
+
fontSize: 13,
|
|
20999
|
+
align: "center"
|
|
21000
|
+
});
|
|
19416
21001
|
}
|
|
19417
21002
|
out.push(...shapes);
|
|
19418
21003
|
return out;
|
|
19419
|
-
}, [atoms, bonds, arrows, shapes]);
|
|
21004
|
+
}, [atoms, bonds, arrows, bondStyle, containers, equation, equationColor, shapes, width]);
|
|
19420
21005
|
const drawables3D = useMemo(() => {
|
|
19421
21006
|
if (mode !== "3d") return [];
|
|
19422
21007
|
if (shapes.length > 0) {
|
|
19423
21008
|
chemistryLog.debug("shapes ignored in 3D mode (pixel-authored 2D vocabulary)", { count: shapes.length });
|
|
19424
21009
|
}
|
|
21010
|
+
if (containers.length > 0) {
|
|
21011
|
+
chemistryLog.debug("containers ignored in 3D mode (pixel-authored 2D vocabulary)", { count: containers.length });
|
|
21012
|
+
}
|
|
21013
|
+
if (animate) {
|
|
21014
|
+
chemistryLog.debug("animate ignored in 3D mode (motion is entity-state driven)");
|
|
21015
|
+
}
|
|
19425
21016
|
const out = [];
|
|
19426
21017
|
const labelColor = labelColorForBackground(backgroundColor);
|
|
19427
21018
|
const atomById = /* @__PURE__ */ new Map();
|
|
@@ -19468,8 +21059,11 @@ var init_ChemistryCanvas = __esm({
|
|
|
19468
21059
|
out.push(billboardLabel(a.element, a.x, a.y, az + radius, { color: labelColor }));
|
|
19469
21060
|
}
|
|
19470
21061
|
}
|
|
21062
|
+
if (lattice3d) {
|
|
21063
|
+
out.push(...latticeDrawables(lattice3d, { labelColor }));
|
|
21064
|
+
}
|
|
19471
21065
|
return out;
|
|
19472
|
-
}, [mode, atoms, bonds, arrows, shapes, backgroundColor]);
|
|
21066
|
+
}, [mode, atoms, bonds, arrows, shapes, containers, lattice3d, animate, backgroundColor]);
|
|
19473
21067
|
const atomIndexById = useMemo(() => {
|
|
19474
21068
|
const m = /* @__PURE__ */ new Map();
|
|
19475
21069
|
atoms.forEach((a, i) => {
|
|
@@ -19508,6 +21102,8 @@ var init_ChemistryCanvas = __esm({
|
|
|
19508
21102
|
height,
|
|
19509
21103
|
backgroundColor,
|
|
19510
21104
|
shapes: derivedShapes,
|
|
21105
|
+
readouts,
|
|
21106
|
+
traces,
|
|
19511
21107
|
interactive: interactive ?? false,
|
|
19512
21108
|
animate,
|
|
19513
21109
|
onShapeClick,
|
|
@@ -29420,6 +31016,10 @@ var init_molecules = __esm({
|
|
|
29420
31016
|
init_GameShell();
|
|
29421
31017
|
}
|
|
29422
31018
|
});
|
|
31019
|
+
function formatTick(v) {
|
|
31020
|
+
if (Number.isInteger(v)) return String(v);
|
|
31021
|
+
return v.toFixed(2).replace(/0+$/, "").replace(/\.$/, "");
|
|
31022
|
+
}
|
|
29423
31023
|
var MathCanvas;
|
|
29424
31024
|
var init_MathCanvas = __esm({
|
|
29425
31025
|
"components/learning/molecules/MathCanvas.tsx"() {
|
|
@@ -29439,10 +31039,19 @@ var init_MathCanvas = __esm({
|
|
|
29439
31039
|
showAxes = true,
|
|
29440
31040
|
showGrid = true,
|
|
29441
31041
|
gridStep = 1,
|
|
31042
|
+
showTickLabels = false,
|
|
31043
|
+
showCurveLabels = false,
|
|
29442
31044
|
curves = [],
|
|
29443
31045
|
points = [],
|
|
29444
31046
|
vectors = [],
|
|
31047
|
+
regions = [],
|
|
31048
|
+
bars = [],
|
|
31049
|
+
guides = [],
|
|
31050
|
+
angles = [],
|
|
31051
|
+
hops = [],
|
|
29445
31052
|
shapes = [],
|
|
31053
|
+
readouts,
|
|
31054
|
+
traces,
|
|
29446
31055
|
interactive = false,
|
|
29447
31056
|
animate = false,
|
|
29448
31057
|
onShapeClick,
|
|
@@ -29456,6 +31065,8 @@ var init_MathCanvas = __esm({
|
|
|
29456
31065
|
const plotH = height - margin * 2;
|
|
29457
31066
|
const mapX = (x) => margin + (x - xMin) / (xMax - xMin) * plotW;
|
|
29458
31067
|
const mapY = (y) => height - (margin + (y - yMin) / (yMax - yMin) * plotH);
|
|
31068
|
+
const xAxisY = Math.max(margin, Math.min(height - margin, mapY(0)));
|
|
31069
|
+
const yAxisX = Math.max(margin, Math.min(width - margin, mapX(0)));
|
|
29459
31070
|
if (showGrid) {
|
|
29460
31071
|
for (let x = Math.ceil(xMin / gridStep) * gridStep; x <= xMax; x += gridStep) {
|
|
29461
31072
|
const px = mapX(x);
|
|
@@ -29466,14 +31077,99 @@ var init_MathCanvas = __esm({
|
|
|
29466
31077
|
out.push({ type: "line", x1: margin, y1: py, x2: width - margin, y2: py, color: "#e5e7eb", lineWidth: 1 });
|
|
29467
31078
|
}
|
|
29468
31079
|
}
|
|
31080
|
+
if (showTickLabels) {
|
|
31081
|
+
const labelEveryX = Math.max(1, Math.ceil((xMax - xMin) / gridStep / Math.floor(plotW / 40)));
|
|
31082
|
+
let kx = 0;
|
|
31083
|
+
for (let x = Math.ceil(xMin / gridStep) * gridStep; x <= xMax; x += gridStep, kx++) {
|
|
31084
|
+
if (kx % labelEveryX === 0 && x !== 0) {
|
|
31085
|
+
out.push({ type: "text", x: mapX(x), y: xAxisY + 12, text: formatTick(x), color: "#6b7280", fontSize: 10, align: "center" });
|
|
31086
|
+
}
|
|
31087
|
+
}
|
|
31088
|
+
const labelEveryY = Math.max(1, Math.ceil((yMax - yMin) / gridStep / Math.floor(plotH / 28)));
|
|
31089
|
+
let ky = 0;
|
|
31090
|
+
for (let y = Math.ceil(yMin / gridStep) * gridStep; y <= yMax; y += gridStep, ky++) {
|
|
31091
|
+
if (ky % labelEveryY === 0 && y !== 0) {
|
|
31092
|
+
out.push({ type: "text", x: yAxisX - 6, y: mapY(y), text: formatTick(y), color: "#6b7280", fontSize: 10, align: "right" });
|
|
31093
|
+
}
|
|
31094
|
+
}
|
|
31095
|
+
if (xMin <= 0 && xMax >= 0 && yMin <= 0 && yMax >= 0) {
|
|
31096
|
+
out.push({ type: "text", x: yAxisX - 6, y: xAxisY + 12, text: "0", color: "#6b7280", fontSize: 10, align: "right" });
|
|
31097
|
+
}
|
|
31098
|
+
}
|
|
31099
|
+
for (const region of regions) {
|
|
31100
|
+
if (!region.samples || region.samples.length === 0) continue;
|
|
31101
|
+
const baseline = region.baseline ?? 0;
|
|
31102
|
+
const clampedPoint = (p) => ({
|
|
31103
|
+
x: mapX(Math.min(xMax, Math.max(xMin, p.x))),
|
|
31104
|
+
y: mapY(Math.min(yMax, Math.max(yMin, p.y)))
|
|
31105
|
+
});
|
|
31106
|
+
const upper = region.samples.map(clampedPoint);
|
|
31107
|
+
const first = region.samples[0];
|
|
31108
|
+
const last = region.samples[region.samples.length - 1];
|
|
31109
|
+
const closing = region.samples2 && region.samples2.length > 0 ? [...region.samples2].reverse().map(clampedPoint) : [clampedPoint({ x: last.x, y: baseline }), clampedPoint({ x: first.x, y: baseline })];
|
|
31110
|
+
const color = region.color ?? "#2563eb";
|
|
31111
|
+
out.push({
|
|
31112
|
+
type: "polygon",
|
|
31113
|
+
points: [...upper, ...closing],
|
|
31114
|
+
fill: color,
|
|
31115
|
+
color,
|
|
31116
|
+
opacity: region.opacity ?? 0.2,
|
|
31117
|
+
lineWidth: 1
|
|
31118
|
+
});
|
|
31119
|
+
if (region.label) {
|
|
31120
|
+
const mid = Math.floor(region.samples.length / 2);
|
|
31121
|
+
out.push({
|
|
31122
|
+
type: "text",
|
|
31123
|
+
x: mapX((first.x + last.x) / 2),
|
|
31124
|
+
y: (mapY(region.samples[mid].y) + mapY(baseline)) / 2,
|
|
31125
|
+
text: region.label,
|
|
31126
|
+
color: "#111827",
|
|
31127
|
+
fontSize: 11
|
|
31128
|
+
});
|
|
31129
|
+
}
|
|
31130
|
+
}
|
|
31131
|
+
for (const bar of bars) {
|
|
31132
|
+
if (bar.x + bar.width < xMin || bar.x > xMax) continue;
|
|
31133
|
+
const y0 = bar.y0 ?? 0;
|
|
31134
|
+
const color = bar.color ?? "#93c5fd";
|
|
31135
|
+
out.push({
|
|
31136
|
+
type: "rect",
|
|
31137
|
+
x: mapX(bar.x),
|
|
31138
|
+
y: mapY(Math.max(y0, bar.y1)),
|
|
31139
|
+
width: mapX(bar.x + bar.width) - mapX(bar.x),
|
|
31140
|
+
height: Math.abs(mapY(bar.y1) - mapY(y0)),
|
|
31141
|
+
color,
|
|
31142
|
+
fill: color,
|
|
31143
|
+
opacity: bar.opacity ?? 0.5,
|
|
31144
|
+
lineWidth: 1
|
|
31145
|
+
});
|
|
31146
|
+
}
|
|
29469
31147
|
if (showAxes) {
|
|
29470
|
-
const xAxisY = Math.max(margin, Math.min(height - margin, mapY(0)));
|
|
29471
|
-
const yAxisX = Math.max(margin, Math.min(width - margin, mapX(0)));
|
|
29472
31148
|
out.push({ type: "line", x1: margin, y1: xAxisY, x2: width - margin, y2: xAxisY, color: "#374151", lineWidth: 2 });
|
|
29473
31149
|
out.push({ type: "line", x1: yAxisX, y1: margin, x2: yAxisX, y2: height - margin, color: "#374151", lineWidth: 2 });
|
|
29474
31150
|
}
|
|
31151
|
+
for (const guide of guides) {
|
|
31152
|
+
const color = guide.color ?? "#9ca3af";
|
|
31153
|
+
const dash = guide.dash ?? "dashed";
|
|
31154
|
+
if (guide.kind === "vline") {
|
|
31155
|
+
if (guide.at < xMin || guide.at > xMax) continue;
|
|
31156
|
+
const px = mapX(guide.at);
|
|
31157
|
+
out.push({ type: "line", x1: px, y1: margin, x2: px, y2: height - margin, color, dash });
|
|
31158
|
+
if (guide.label) {
|
|
31159
|
+
out.push({ type: "text", x: px + 4, y: margin + 10, text: guide.label, color: "#111827", fontSize: 11 });
|
|
31160
|
+
}
|
|
31161
|
+
} else {
|
|
31162
|
+
if (guide.at < yMin || guide.at > yMax) continue;
|
|
31163
|
+
const py = mapY(guide.at);
|
|
31164
|
+
out.push({ type: "line", x1: margin, y1: py, x2: width - margin, y2: py, color, dash });
|
|
31165
|
+
if (guide.label) {
|
|
31166
|
+
out.push({ type: "text", x: width - margin - 4, y: py - 8, text: guide.label, color: "#111827", fontSize: 11, align: "right" });
|
|
31167
|
+
}
|
|
31168
|
+
}
|
|
31169
|
+
}
|
|
29475
31170
|
for (const curve of curves) {
|
|
29476
31171
|
if (!curve.samples || curve.samples.length < 2) continue;
|
|
31172
|
+
let lastInRange;
|
|
29477
31173
|
for (let i = 1; i < curve.samples.length; i++) {
|
|
29478
31174
|
const a = curve.samples[i - 1];
|
|
29479
31175
|
const b = curve.samples[i];
|
|
@@ -29485,19 +31181,97 @@ var init_MathCanvas = __esm({
|
|
|
29485
31181
|
x2: mapX(b.x),
|
|
29486
31182
|
y2: mapY(b.y),
|
|
29487
31183
|
color: curve.color ?? "#2563eb",
|
|
29488
|
-
lineWidth: 2
|
|
31184
|
+
lineWidth: 2,
|
|
31185
|
+
dash: curve.dash
|
|
31186
|
+
});
|
|
31187
|
+
lastInRange = b;
|
|
31188
|
+
}
|
|
31189
|
+
if (showCurveLabels && curve.label && lastInRange) {
|
|
31190
|
+
out.push({
|
|
31191
|
+
type: "text",
|
|
31192
|
+
x: mapX(lastInRange.x) + 6,
|
|
31193
|
+
y: mapY(lastInRange.y) - 6,
|
|
31194
|
+
text: curve.label,
|
|
31195
|
+
color: curve.color ?? "#2563eb",
|
|
31196
|
+
fontSize: 11
|
|
31197
|
+
});
|
|
31198
|
+
}
|
|
31199
|
+
}
|
|
31200
|
+
for (const hop of hops) {
|
|
31201
|
+
const x1 = mapX(hop.from);
|
|
31202
|
+
const x2 = mapX(hop.to);
|
|
31203
|
+
const peak = Math.min(36, plotH * 0.3);
|
|
31204
|
+
const color = hop.color ?? "#7c3aed";
|
|
31205
|
+
out.push({
|
|
31206
|
+
type: "ellipse",
|
|
31207
|
+
x: (x1 + x2) / 2,
|
|
31208
|
+
y: xAxisY,
|
|
31209
|
+
width: Math.abs(x2 - x1),
|
|
31210
|
+
height: 2 * peak,
|
|
31211
|
+
startAngle: 180,
|
|
31212
|
+
endAngle: 360,
|
|
31213
|
+
color
|
|
31214
|
+
});
|
|
31215
|
+
const s = Math.sign(hop.to - hop.from);
|
|
31216
|
+
out.push({
|
|
31217
|
+
type: "polygon",
|
|
31218
|
+
points: [
|
|
31219
|
+
{ x: x2, y: xAxisY },
|
|
31220
|
+
{ x: x2 - 4 * s, y: xAxisY - 7 },
|
|
31221
|
+
{ x: x2 + 2 * s, y: xAxisY - 7 }
|
|
31222
|
+
],
|
|
31223
|
+
fill: color,
|
|
31224
|
+
color
|
|
31225
|
+
});
|
|
31226
|
+
if (hop.label) {
|
|
31227
|
+
out.push({
|
|
31228
|
+
type: "text",
|
|
31229
|
+
x: (x1 + x2) / 2,
|
|
31230
|
+
y: xAxisY - peak - 8,
|
|
31231
|
+
text: hop.label,
|
|
31232
|
+
color: "#111827",
|
|
31233
|
+
fontSize: 10,
|
|
31234
|
+
align: "center"
|
|
31235
|
+
});
|
|
31236
|
+
}
|
|
31237
|
+
}
|
|
31238
|
+
for (const angle of angles) {
|
|
31239
|
+
const radius = angle.radius ?? 0.8;
|
|
31240
|
+
const color = angle.color ?? "#0ea5e9";
|
|
31241
|
+
out.push({
|
|
31242
|
+
type: "ellipse",
|
|
31243
|
+
x: mapX(angle.x),
|
|
31244
|
+
y: mapY(angle.y),
|
|
31245
|
+
width: 2 * radius * plotW / (xMax - xMin),
|
|
31246
|
+
height: 2 * radius * plotH / (yMax - yMin),
|
|
31247
|
+
startAngle: -angle.to,
|
|
31248
|
+
endAngle: -angle.from,
|
|
31249
|
+
color
|
|
31250
|
+
});
|
|
31251
|
+
if (angle.label) {
|
|
31252
|
+
const mid = (angle.from + angle.to) / 2;
|
|
31253
|
+
const rad = mid * Math.PI / 180;
|
|
31254
|
+
out.push({
|
|
31255
|
+
type: "text",
|
|
31256
|
+
x: mapX(angle.x + 1.35 * radius * Math.cos(rad)),
|
|
31257
|
+
y: mapY(angle.y + 1.35 * radius * Math.sin(rad)),
|
|
31258
|
+
text: angle.label,
|
|
31259
|
+
color: "#111827",
|
|
31260
|
+
fontSize: 11,
|
|
31261
|
+
align: "center"
|
|
29489
31262
|
});
|
|
29490
31263
|
}
|
|
29491
31264
|
}
|
|
29492
31265
|
for (const p of points) {
|
|
29493
31266
|
if (p.x < xMin || p.x > xMax || p.y < yMin || p.y > yMax) continue;
|
|
31267
|
+
const isOpen = p.style === "open";
|
|
29494
31268
|
out.push({
|
|
29495
31269
|
type: "circle",
|
|
29496
31270
|
x: mapX(p.x),
|
|
29497
31271
|
y: mapY(p.y),
|
|
29498
31272
|
radius: p.radius ?? 4,
|
|
29499
31273
|
color: p.color ?? "#dc2626",
|
|
29500
|
-
fill: p.color ?? "#dc2626"
|
|
31274
|
+
fill: isOpen ? "#ffffff" : p.color ?? "#dc2626"
|
|
29501
31275
|
});
|
|
29502
31276
|
if (p.label) {
|
|
29503
31277
|
out.push({ type: "text", x: mapX(p.x) + 8, y: mapY(p.y) - 8, text: p.label, color: "#111827", fontSize: 12 });
|
|
@@ -29516,7 +31290,28 @@ var init_MathCanvas = __esm({
|
|
|
29516
31290
|
}
|
|
29517
31291
|
out.push(...shapes);
|
|
29518
31292
|
return out;
|
|
29519
|
-
}, [
|
|
31293
|
+
}, [
|
|
31294
|
+
width,
|
|
31295
|
+
height,
|
|
31296
|
+
xMin,
|
|
31297
|
+
xMax,
|
|
31298
|
+
yMin,
|
|
31299
|
+
yMax,
|
|
31300
|
+
showAxes,
|
|
31301
|
+
showGrid,
|
|
31302
|
+
gridStep,
|
|
31303
|
+
showTickLabels,
|
|
31304
|
+
showCurveLabels,
|
|
31305
|
+
curves,
|
|
31306
|
+
points,
|
|
31307
|
+
vectors,
|
|
31308
|
+
regions,
|
|
31309
|
+
bars,
|
|
31310
|
+
guides,
|
|
31311
|
+
angles,
|
|
31312
|
+
hops,
|
|
31313
|
+
shapes
|
|
31314
|
+
]);
|
|
29520
31315
|
return /* @__PURE__ */ jsx(Card, { className, children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
|
|
29521
31316
|
title ? /* @__PURE__ */ jsx(Typography, { variant: "h4", children: title }) : null,
|
|
29522
31317
|
/* @__PURE__ */ jsx(
|
|
@@ -29525,6 +31320,8 @@ var init_MathCanvas = __esm({
|
|
|
29525
31320
|
width,
|
|
29526
31321
|
height,
|
|
29527
31322
|
shapes: derivedShapes,
|
|
31323
|
+
readouts,
|
|
31324
|
+
traces,
|
|
29528
31325
|
interactive,
|
|
29529
31326
|
animate,
|
|
29530
31327
|
onShapeClick,
|
|
@@ -29536,7 +31333,315 @@ var init_MathCanvas = __esm({
|
|
|
29536
31333
|
};
|
|
29537
31334
|
}
|
|
29538
31335
|
});
|
|
29539
|
-
|
|
31336
|
+
function formatMeterValue(v) {
|
|
31337
|
+
return Number.isInteger(v) ? String(v) : String(Number(v.toFixed(2)));
|
|
31338
|
+
}
|
|
31339
|
+
function sceneObjectShapes(obj, canvasWidth, canvasHeight) {
|
|
31340
|
+
const out = [];
|
|
31341
|
+
const color = obj.color ?? "#334155";
|
|
31342
|
+
switch (obj.kind) {
|
|
31343
|
+
case "ground": {
|
|
31344
|
+
const xStart = obj.x1 ?? 0;
|
|
31345
|
+
const xEnd = obj.x2 ?? canvasWidth;
|
|
31346
|
+
const y = obj.y ?? 0;
|
|
31347
|
+
out.push({ type: "line", x1: xStart, y1: y, x2: xEnd, y2: y, color, lineWidth: 2 });
|
|
31348
|
+
for (let hx = xStart + 7; hx <= xEnd; hx += 14) {
|
|
31349
|
+
out.push({ type: "line", x1: hx, y1: y, x2: hx - 7, y2: y + 7, color, lineWidth: 1 });
|
|
31350
|
+
}
|
|
31351
|
+
if (obj.label) {
|
|
31352
|
+
out.push({
|
|
31353
|
+
type: "text",
|
|
31354
|
+
x: (xStart + xEnd) / 2,
|
|
31355
|
+
y: y - 10,
|
|
31356
|
+
text: obj.label,
|
|
31357
|
+
color: PHYSICS_LABEL_COLOR,
|
|
31358
|
+
fontSize: 11,
|
|
31359
|
+
align: "center"
|
|
31360
|
+
});
|
|
31361
|
+
}
|
|
31362
|
+
break;
|
|
31363
|
+
}
|
|
31364
|
+
case "wall": {
|
|
31365
|
+
const yStart = obj.y1 ?? 0;
|
|
31366
|
+
const yEnd = obj.y2 ?? canvasHeight;
|
|
31367
|
+
const x = obj.x ?? 0;
|
|
31368
|
+
out.push({ type: "line", x1: x, y1: yStart, x2: x, y2: yEnd, color, lineWidth: 2 });
|
|
31369
|
+
for (let hy = yStart + 7; hy <= yEnd; hy += 14) {
|
|
31370
|
+
out.push({ type: "line", x1: x, y1: hy, x2: x - 7, y2: hy + 7, color, lineWidth: 1 });
|
|
31371
|
+
}
|
|
31372
|
+
if (obj.label) {
|
|
31373
|
+
out.push({
|
|
31374
|
+
type: "text",
|
|
31375
|
+
x: x + 12,
|
|
31376
|
+
y: (yStart + yEnd) / 2,
|
|
31377
|
+
text: obj.label,
|
|
31378
|
+
color: PHYSICS_LABEL_COLOR,
|
|
31379
|
+
fontSize: 11,
|
|
31380
|
+
align: "left"
|
|
31381
|
+
});
|
|
31382
|
+
}
|
|
31383
|
+
break;
|
|
31384
|
+
}
|
|
31385
|
+
case "ramp": {
|
|
31386
|
+
const x1 = obj.x1 ?? 0;
|
|
31387
|
+
const y1 = obj.y1 ?? 0;
|
|
31388
|
+
const x2 = obj.x2 ?? canvasWidth;
|
|
31389
|
+
const y2 = obj.y2 ?? canvasHeight;
|
|
31390
|
+
out.push({
|
|
31391
|
+
type: "polygon",
|
|
31392
|
+
points: [
|
|
31393
|
+
{ x: x1, y: y1 },
|
|
31394
|
+
{ x: x2, y: y2 },
|
|
31395
|
+
{ x: x1, y: y2 }
|
|
31396
|
+
],
|
|
31397
|
+
color,
|
|
31398
|
+
fill: obj.fill ?? "#e2e8f0",
|
|
31399
|
+
lineWidth: 2
|
|
31400
|
+
});
|
|
31401
|
+
if (obj.label) {
|
|
31402
|
+
out.push({
|
|
31403
|
+
type: "text",
|
|
31404
|
+
x: (2 * x1 + x2) / 3,
|
|
31405
|
+
y: (y1 + 2 * y2) / 3,
|
|
31406
|
+
text: obj.label,
|
|
31407
|
+
color: PHYSICS_LABEL_COLOR,
|
|
31408
|
+
fontSize: 11,
|
|
31409
|
+
align: "center"
|
|
31410
|
+
});
|
|
31411
|
+
}
|
|
31412
|
+
break;
|
|
31413
|
+
}
|
|
31414
|
+
case "box": {
|
|
31415
|
+
const x = obj.x ?? 0;
|
|
31416
|
+
const y = obj.y ?? 0;
|
|
31417
|
+
const w = obj.width ?? 40;
|
|
31418
|
+
const h = obj.height ?? 40;
|
|
31419
|
+
out.push({ type: "rect", x, y, width: w, height: h, color, fill: obj.fill, lineWidth: 2 });
|
|
31420
|
+
if (obj.label) {
|
|
31421
|
+
out.push({
|
|
31422
|
+
type: "text",
|
|
31423
|
+
x: x + w / 2,
|
|
31424
|
+
y: y + h / 2,
|
|
31425
|
+
text: obj.label,
|
|
31426
|
+
color: PHYSICS_LABEL_COLOR,
|
|
31427
|
+
fontSize: 11,
|
|
31428
|
+
align: "center"
|
|
31429
|
+
});
|
|
31430
|
+
}
|
|
31431
|
+
break;
|
|
31432
|
+
}
|
|
31433
|
+
case "pivot": {
|
|
31434
|
+
const x = obj.x ?? 0;
|
|
31435
|
+
const y = obj.y ?? 0;
|
|
31436
|
+
out.push({ type: "circle", x, y, radius: 5, color, fill: color });
|
|
31437
|
+
out.push({ type: "line", x1: x - 14, y1: y - 8, x2: x + 14, y2: y - 8, color, lineWidth: 1 });
|
|
31438
|
+
for (let k = 0; k < 5; k++) {
|
|
31439
|
+
const hx = x - 14 + 7 * k;
|
|
31440
|
+
out.push({ type: "line", x1: hx, y1: y - 8, x2: hx - 6, y2: y - 14, color, lineWidth: 1 });
|
|
31441
|
+
}
|
|
31442
|
+
if (obj.label) {
|
|
31443
|
+
out.push({
|
|
31444
|
+
type: "text",
|
|
31445
|
+
x,
|
|
31446
|
+
y: y - 20,
|
|
31447
|
+
text: obj.label,
|
|
31448
|
+
color: PHYSICS_LABEL_COLOR,
|
|
31449
|
+
fontSize: 11,
|
|
31450
|
+
align: "center"
|
|
31451
|
+
});
|
|
31452
|
+
}
|
|
31453
|
+
break;
|
|
31454
|
+
}
|
|
31455
|
+
}
|
|
31456
|
+
return out;
|
|
31457
|
+
}
|
|
31458
|
+
function trailShapes(trail) {
|
|
31459
|
+
const n = trail.points.length;
|
|
31460
|
+
if (n < 2) return [];
|
|
31461
|
+
const color = trail.color ?? "#94a3b8";
|
|
31462
|
+
const lineWidth = trail.width ?? 2;
|
|
31463
|
+
const fade = trail.fade ?? true;
|
|
31464
|
+
const globalOpacity = trail.opacity ?? 1;
|
|
31465
|
+
const out = [];
|
|
31466
|
+
for (let i = 0; i < n - 1; i++) {
|
|
31467
|
+
const a = trail.points[i];
|
|
31468
|
+
const b = trail.points[i + 1];
|
|
31469
|
+
const segmentOpacity = fade ? 0.12 + 0.68 * i / (n - 1) : 0.6;
|
|
31470
|
+
out.push({
|
|
31471
|
+
type: "line",
|
|
31472
|
+
x1: a.x,
|
|
31473
|
+
y1: a.y,
|
|
31474
|
+
x2: b.x,
|
|
31475
|
+
y2: b.y,
|
|
31476
|
+
color,
|
|
31477
|
+
lineWidth,
|
|
31478
|
+
opacity: segmentOpacity * globalOpacity
|
|
31479
|
+
});
|
|
31480
|
+
}
|
|
31481
|
+
return out;
|
|
31482
|
+
}
|
|
31483
|
+
function constraintShapes(c, a, b) {
|
|
31484
|
+
const color = c.color ?? "#9ca3af";
|
|
31485
|
+
const kind = c.kind ?? "rod";
|
|
31486
|
+
if (kind === "rod") {
|
|
31487
|
+
return [{ type: "line", x1: a.x, y1: a.y, x2: b.x, y2: b.y, color, lineWidth: 2 }];
|
|
31488
|
+
}
|
|
31489
|
+
if (kind === "string") {
|
|
31490
|
+
return [{ type: "line", x1: a.x, y1: a.y, x2: b.x, y2: b.y, color, lineWidth: 2, dash: "dashed" }];
|
|
31491
|
+
}
|
|
31492
|
+
const COILS = 8;
|
|
31493
|
+
const AMP = 7;
|
|
31494
|
+
const LEAD = 10;
|
|
31495
|
+
const dx = b.x - a.x;
|
|
31496
|
+
const dy = b.y - a.y;
|
|
31497
|
+
const dist = Math.max(1e-6, Math.hypot(dx, dy));
|
|
31498
|
+
const ux = dx / dist;
|
|
31499
|
+
const uy = dy / dist;
|
|
31500
|
+
const perpX = -uy;
|
|
31501
|
+
const perpY = ux;
|
|
31502
|
+
const aPrime = { x: a.x + LEAD * ux, y: a.y + LEAD * uy };
|
|
31503
|
+
const bPrime = { x: b.x - LEAD * ux, y: b.y - LEAD * uy };
|
|
31504
|
+
const m = 2 * COILS;
|
|
31505
|
+
const polyline = [{ x: a.x, y: a.y }, aPrime];
|
|
31506
|
+
for (let j = 1; j <= m; j++) {
|
|
31507
|
+
const t = j / (m + 1);
|
|
31508
|
+
const baseX = aPrime.x + t * (bPrime.x - aPrime.x);
|
|
31509
|
+
const baseY = aPrime.y + t * (bPrime.y - aPrime.y);
|
|
31510
|
+
const sign = j % 2 === 0 ? 1 : -1;
|
|
31511
|
+
polyline.push({ x: baseX + sign * AMP * perpX, y: baseY + sign * AMP * perpY });
|
|
31512
|
+
}
|
|
31513
|
+
polyline.push(bPrime, { x: b.x, y: b.y });
|
|
31514
|
+
const out = [];
|
|
31515
|
+
for (let i = 1; i < polyline.length; i++) {
|
|
31516
|
+
out.push({
|
|
31517
|
+
type: "line",
|
|
31518
|
+
x1: polyline[i - 1].x,
|
|
31519
|
+
y1: polyline[i - 1].y,
|
|
31520
|
+
x2: polyline[i].x,
|
|
31521
|
+
y2: polyline[i].y,
|
|
31522
|
+
color,
|
|
31523
|
+
lineWidth: 2
|
|
31524
|
+
});
|
|
31525
|
+
}
|
|
31526
|
+
return out;
|
|
31527
|
+
}
|
|
31528
|
+
function vectorShapes(v, bodyById) {
|
|
31529
|
+
let ax;
|
|
31530
|
+
let ay;
|
|
31531
|
+
if (v.body) {
|
|
31532
|
+
const anchor = bodyById.get(v.body);
|
|
31533
|
+
if (!anchor) return [];
|
|
31534
|
+
ax = anchor.x;
|
|
31535
|
+
ay = anchor.y;
|
|
31536
|
+
} else {
|
|
31537
|
+
ax = v.x ?? 0;
|
|
31538
|
+
ay = v.y ?? 0;
|
|
31539
|
+
}
|
|
31540
|
+
const scale = v.scale ?? 1;
|
|
31541
|
+
const color = v.color ?? "#dc2626";
|
|
31542
|
+
const tx = ax + v.dx * scale;
|
|
31543
|
+
const ty = ay + v.dy * scale;
|
|
31544
|
+
const out = [{ type: "arrow", x1: ax, y1: ay, x2: tx, y2: ty, color, lineWidth: 2, dash: v.dash }];
|
|
31545
|
+
if (v.label) {
|
|
31546
|
+
const dist = Math.max(1e-6, Math.hypot(tx - ax, ty - ay));
|
|
31547
|
+
const ux = (tx - ax) / dist;
|
|
31548
|
+
const uy = (ty - ay) / dist;
|
|
31549
|
+
out.push({
|
|
31550
|
+
type: "text",
|
|
31551
|
+
x: tx + 8 * ux,
|
|
31552
|
+
y: ty + 8 * uy,
|
|
31553
|
+
text: v.label,
|
|
31554
|
+
color,
|
|
31555
|
+
fontSize: 11,
|
|
31556
|
+
align: "center"
|
|
31557
|
+
});
|
|
31558
|
+
}
|
|
31559
|
+
return out;
|
|
31560
|
+
}
|
|
31561
|
+
function angleMarkerShapes(a) {
|
|
31562
|
+
const radius = a.radius ?? 26;
|
|
31563
|
+
const color = a.color ?? "#0ea5e9";
|
|
31564
|
+
const out = [
|
|
31565
|
+
{
|
|
31566
|
+
type: "ellipse",
|
|
31567
|
+
x: a.x,
|
|
31568
|
+
y: a.y,
|
|
31569
|
+
width: radius * 2,
|
|
31570
|
+
height: radius * 2,
|
|
31571
|
+
startAngle: a.from,
|
|
31572
|
+
endAngle: a.to,
|
|
31573
|
+
color,
|
|
31574
|
+
lineWidth: 2
|
|
31575
|
+
}
|
|
31576
|
+
];
|
|
31577
|
+
if (a.label) {
|
|
31578
|
+
const mid = (a.from + a.to) / 2 * (Math.PI / 180);
|
|
31579
|
+
out.push({
|
|
31580
|
+
type: "text",
|
|
31581
|
+
x: a.x + (radius + 13) * Math.cos(mid),
|
|
31582
|
+
y: a.y + (radius + 13) * Math.sin(mid),
|
|
31583
|
+
text: a.label,
|
|
31584
|
+
color,
|
|
31585
|
+
fontSize: 11,
|
|
31586
|
+
align: "center"
|
|
31587
|
+
});
|
|
31588
|
+
}
|
|
31589
|
+
return out;
|
|
31590
|
+
}
|
|
31591
|
+
function fieldShapes(field, canvasWidth, canvasHeight) {
|
|
31592
|
+
const spacing = field.spacing ?? 48;
|
|
31593
|
+
const size = field.size ?? 14;
|
|
31594
|
+
const color = field.color ?? "#94a3b8";
|
|
31595
|
+
const regionX = field.x ?? 0;
|
|
31596
|
+
const regionY = field.y ?? 0;
|
|
31597
|
+
const regionW = field.width ?? canvasWidth;
|
|
31598
|
+
const regionH = field.height ?? canvasHeight;
|
|
31599
|
+
const out = [];
|
|
31600
|
+
for (let gx = regionX + spacing / 2; gx < regionX + regionW; gx += spacing) {
|
|
31601
|
+
for (let gy = regionY + spacing / 2; gy < regionY + regionH; gy += spacing) {
|
|
31602
|
+
if (field.kind === "arrows") {
|
|
31603
|
+
const rad = (field.angle ?? 0) * Math.PI / 180;
|
|
31604
|
+
const hx = Math.cos(rad) * size / 2;
|
|
31605
|
+
const hy = Math.sin(rad) * size / 2;
|
|
31606
|
+
out.push({ type: "arrow", x1: gx - hx, y1: gy - hy, x2: gx + hx, y2: gy + hy, color, lineWidth: 2 });
|
|
31607
|
+
} else if (field.kind === "into") {
|
|
31608
|
+
const r = size / 3;
|
|
31609
|
+
const d = 0.6 * r * Math.SQRT1_2;
|
|
31610
|
+
out.push({ type: "circle", x: gx, y: gy, radius: r, color });
|
|
31611
|
+
out.push({ type: "line", x1: gx - d, y1: gy - d, x2: gx + d, y2: gy + d, color, lineWidth: 1 });
|
|
31612
|
+
out.push({ type: "line", x1: gx - d, y1: gy + d, x2: gx + d, y2: gy - d, color, lineWidth: 1 });
|
|
31613
|
+
} else {
|
|
31614
|
+
const r = size / 3;
|
|
31615
|
+
out.push({ type: "circle", x: gx, y: gy, radius: r, color });
|
|
31616
|
+
out.push({ type: "circle", x: gx, y: gy, radius: 1.5, color, fill: color });
|
|
31617
|
+
}
|
|
31618
|
+
}
|
|
31619
|
+
}
|
|
31620
|
+
return out;
|
|
31621
|
+
}
|
|
31622
|
+
function meterShapes(meters, canvasHeight) {
|
|
31623
|
+
const n = meters.length;
|
|
31624
|
+
const out = [];
|
|
31625
|
+
const sharedMax = Math.max(1e-6, ...meters.map((m) => m.value));
|
|
31626
|
+
meters.forEach((meter, i) => {
|
|
31627
|
+
const rowY = canvasHeight - 10 - 16 * (n - i);
|
|
31628
|
+
const color = meter.color ?? "#3b82f6";
|
|
31629
|
+
const M = meter.max ?? sharedMax;
|
|
31630
|
+
const w = Math.round(Math.min(1, Math.max(0, meter.value / M)) * 110);
|
|
31631
|
+
out.push({ type: "text", x: 8, y: rowY + 8, text: meter.label, color: PHYSICS_LABEL_COLOR, fontSize: 10 });
|
|
31632
|
+
out.push({ type: "rect", x: 52, y: rowY, width: w, height: 10, color, fill: color });
|
|
31633
|
+
out.push({
|
|
31634
|
+
type: "text",
|
|
31635
|
+
x: 166,
|
|
31636
|
+
y: rowY + 8,
|
|
31637
|
+
text: formatMeterValue(meter.value),
|
|
31638
|
+
color: "#6b7280",
|
|
31639
|
+
fontSize: 9
|
|
31640
|
+
});
|
|
31641
|
+
});
|
|
31642
|
+
return out;
|
|
31643
|
+
}
|
|
31644
|
+
var physicsLog2, PHYSICS_LABEL_COLOR, PhysicsCanvas;
|
|
29540
31645
|
var init_PhysicsCanvas = __esm({
|
|
29541
31646
|
"components/learning/molecules/PhysicsCanvas.tsx"() {
|
|
29542
31647
|
"use client";
|
|
@@ -29545,6 +31650,7 @@ var init_PhysicsCanvas = __esm({
|
|
|
29545
31650
|
init_LearningCanvas();
|
|
29546
31651
|
init_learningScene3D();
|
|
29547
31652
|
physicsLog2 = createLogger("almadar:ui:physics-canvas");
|
|
31653
|
+
PHYSICS_LABEL_COLOR = "#374151";
|
|
29548
31654
|
PhysicsCanvas = ({
|
|
29549
31655
|
className,
|
|
29550
31656
|
width = 600,
|
|
@@ -29561,7 +31667,18 @@ var init_PhysicsCanvas = __esm({
|
|
|
29561
31667
|
showForces = false,
|
|
29562
31668
|
velocityScale = 20,
|
|
29563
31669
|
forceScale = 20,
|
|
31670
|
+
sceneObjects = [],
|
|
31671
|
+
trails = [],
|
|
31672
|
+
vectors = [],
|
|
31673
|
+
surface3d,
|
|
31674
|
+
vectors3d = [],
|
|
31675
|
+
vectorScale = 1,
|
|
31676
|
+
angles = [],
|
|
31677
|
+
field,
|
|
31678
|
+
meters = [],
|
|
29564
31679
|
shapes = [],
|
|
31680
|
+
readouts,
|
|
31681
|
+
traces,
|
|
29565
31682
|
showGrid,
|
|
29566
31683
|
shadows,
|
|
29567
31684
|
interactive,
|
|
@@ -29576,19 +31693,14 @@ var init_PhysicsCanvas = __esm({
|
|
|
29576
31693
|
for (const b of bodies) {
|
|
29577
31694
|
if (b.id) bodyById.set(b.id, b);
|
|
29578
31695
|
}
|
|
31696
|
+
if (field) out.push(...fieldShapes(field, width, height));
|
|
31697
|
+
for (const obj of sceneObjects) out.push(...sceneObjectShapes(obj, width, height));
|
|
31698
|
+
for (const trail of trails) out.push(...trailShapes(trail));
|
|
29579
31699
|
for (const c of constraints) {
|
|
29580
31700
|
const a = bodyById.get(c.from);
|
|
29581
31701
|
const b = bodyById.get(c.to);
|
|
29582
31702
|
if (!a || !b) continue;
|
|
29583
|
-
out.push(
|
|
29584
|
-
type: "line",
|
|
29585
|
-
x1: a.x,
|
|
29586
|
-
y1: a.y,
|
|
29587
|
-
x2: b.x,
|
|
29588
|
-
y2: b.y,
|
|
29589
|
-
color: c.color ?? "#9ca3af",
|
|
29590
|
-
lineWidth: 2
|
|
29591
|
-
});
|
|
31703
|
+
out.push(...constraintShapes(c, a, b));
|
|
29592
31704
|
}
|
|
29593
31705
|
for (const b of bodies) {
|
|
29594
31706
|
out.push({
|
|
@@ -29633,14 +31745,51 @@ var init_PhysicsCanvas = __esm({
|
|
|
29633
31745
|
});
|
|
29634
31746
|
}
|
|
29635
31747
|
}
|
|
31748
|
+
for (const v of vectors) out.push(...vectorShapes(v, bodyById));
|
|
31749
|
+
for (const a of angles) out.push(...angleMarkerShapes(a));
|
|
31750
|
+
if (meters.length > 0) out.push(...meterShapes(meters, height));
|
|
29636
31751
|
out.push(...shapes);
|
|
29637
31752
|
return out;
|
|
29638
|
-
}, [
|
|
31753
|
+
}, [
|
|
31754
|
+
bodies,
|
|
31755
|
+
constraints,
|
|
31756
|
+
showVelocity,
|
|
31757
|
+
showForces,
|
|
31758
|
+
velocityScale,
|
|
31759
|
+
forceScale,
|
|
31760
|
+
sceneObjects,
|
|
31761
|
+
trails,
|
|
31762
|
+
vectors,
|
|
31763
|
+
angles,
|
|
31764
|
+
field,
|
|
31765
|
+
meters,
|
|
31766
|
+
shapes,
|
|
31767
|
+
width,
|
|
31768
|
+
height
|
|
31769
|
+
]);
|
|
29639
31770
|
const drawables3D = useMemo(() => {
|
|
29640
31771
|
if (mode !== "3d") return [];
|
|
29641
31772
|
if (shapes.length > 0) {
|
|
29642
31773
|
physicsLog2.debug("shapes ignored in 3D mode (pixel-authored 2D vocabulary)", { count: shapes.length });
|
|
29643
31774
|
}
|
|
31775
|
+
if (sceneObjects.length > 0) {
|
|
31776
|
+
physicsLog2.debug("sceneObjects ignored in 3D mode (pixel-authored 2D vocabulary)", { count: sceneObjects.length });
|
|
31777
|
+
}
|
|
31778
|
+
if (vectors.length > 0) {
|
|
31779
|
+
physicsLog2.debug("vectors ignored in 3D mode (pixel-authored 2D vocabulary)", { count: vectors.length });
|
|
31780
|
+
}
|
|
31781
|
+
if (angles.length > 0) {
|
|
31782
|
+
physicsLog2.debug("angles ignored in 3D mode (pixel-authored 2D vocabulary)", { count: angles.length });
|
|
31783
|
+
}
|
|
31784
|
+
if (field) {
|
|
31785
|
+
physicsLog2.debug("field ignored in 3D mode (pixel-authored 2D vocabulary)");
|
|
31786
|
+
}
|
|
31787
|
+
if (meters.length > 0) {
|
|
31788
|
+
physicsLog2.debug("meters ignored in 3D mode (pixel-authored 2D vocabulary)", { count: meters.length });
|
|
31789
|
+
}
|
|
31790
|
+
if (animate) {
|
|
31791
|
+
physicsLog2.debug("animate ignored in 3D mode (motion is entity-state driven)");
|
|
31792
|
+
}
|
|
29644
31793
|
const out = [];
|
|
29645
31794
|
const labelColor = labelColorForBackground(backgroundColor);
|
|
29646
31795
|
const bodyById = /* @__PURE__ */ new Map();
|
|
@@ -29688,15 +31837,67 @@ var init_PhysicsCanvas = __esm({
|
|
|
29688
31837
|
if (arrow) out.push(arrow);
|
|
29689
31838
|
}
|
|
29690
31839
|
}
|
|
31840
|
+
for (const trail of trails) {
|
|
31841
|
+
if (trail.fade !== void 0) {
|
|
31842
|
+
physicsLog2.debug("trail.fade ignored in 3D mode (2D-only fade curve \u2014 3D draws an opaque tube)", { id: trail.id });
|
|
31843
|
+
}
|
|
31844
|
+
const points = trail.points.map((p) => [p.x, p.y, p.z ?? 0]);
|
|
31845
|
+
out.push(
|
|
31846
|
+
...polylineTube(points, trail.width ?? 0.05, trail.color ?? "#94a3b8", {
|
|
31847
|
+
...trail.opacity !== void 0 ? { opacity: trail.opacity } : {}
|
|
31848
|
+
})
|
|
31849
|
+
);
|
|
31850
|
+
}
|
|
31851
|
+
if (surface3d) {
|
|
31852
|
+
out.push(...heightFieldMesh(surface3d));
|
|
31853
|
+
}
|
|
31854
|
+
if (vectors3d.length > 0) {
|
|
31855
|
+
out.push(
|
|
31856
|
+
...arrowField(
|
|
31857
|
+
vectors3d.map((v) => ({
|
|
31858
|
+
id: v.id,
|
|
31859
|
+
from: [v.x, v.y, v.z ?? 0],
|
|
31860
|
+
delta: [v.dx, v.dy, v.dz ?? 0],
|
|
31861
|
+
color: v.color,
|
|
31862
|
+
label: v.label,
|
|
31863
|
+
width: v.width
|
|
31864
|
+
})),
|
|
31865
|
+
{ scale: vectorScale, labelColor }
|
|
31866
|
+
)
|
|
31867
|
+
);
|
|
31868
|
+
}
|
|
29691
31869
|
return out;
|
|
29692
|
-
}, [
|
|
31870
|
+
}, [
|
|
31871
|
+
mode,
|
|
31872
|
+
bodies,
|
|
31873
|
+
constraints,
|
|
31874
|
+
showVelocity,
|
|
31875
|
+
showForces,
|
|
31876
|
+
velocityScale,
|
|
31877
|
+
forceScale,
|
|
31878
|
+
shapes,
|
|
31879
|
+
sceneObjects,
|
|
31880
|
+
trails,
|
|
31881
|
+
vectors,
|
|
31882
|
+
surface3d,
|
|
31883
|
+
vectors3d,
|
|
31884
|
+
vectorScale,
|
|
31885
|
+
angles,
|
|
31886
|
+
field,
|
|
31887
|
+
meters,
|
|
31888
|
+
animate,
|
|
31889
|
+
backgroundColor
|
|
31890
|
+
]);
|
|
29693
31891
|
const bodyIndexById = useMemo(() => {
|
|
29694
31892
|
const m = /* @__PURE__ */ new Map();
|
|
31893
|
+
vectors3d.forEach((v, i) => {
|
|
31894
|
+
if (v.id) m.set(v.id, i);
|
|
31895
|
+
});
|
|
29695
31896
|
bodies.forEach((b, i) => {
|
|
29696
31897
|
if (b.id) m.set(b.id, i);
|
|
29697
31898
|
});
|
|
29698
31899
|
return m;
|
|
29699
|
-
}, [bodies]);
|
|
31900
|
+
}, [bodies, vectors3d]);
|
|
29700
31901
|
if (mode === "3d") {
|
|
29701
31902
|
return /* @__PURE__ */ jsx(
|
|
29702
31903
|
LearningScene3D,
|
|
@@ -29728,6 +31929,8 @@ var init_PhysicsCanvas = __esm({
|
|
|
29728
31929
|
height,
|
|
29729
31930
|
backgroundColor,
|
|
29730
31931
|
shapes: derivedShapes,
|
|
31932
|
+
readouts,
|
|
31933
|
+
traces,
|
|
29731
31934
|
interactive: interactive ?? false,
|
|
29732
31935
|
animate,
|
|
29733
31936
|
onShapeClick,
|
|
@@ -29878,7 +32081,7 @@ function layoutFlow(nodeIds, adjacency, roots, width, height, margin) {
|
|
|
29878
32081
|
}
|
|
29879
32082
|
return nodeIds.map((id) => positions.get(id));
|
|
29880
32083
|
}
|
|
29881
|
-
function
|
|
32084
|
+
function layoutTree2(nodeIds, adjacency, roots, width, height, margin) {
|
|
29882
32085
|
const effectiveRoots = roots.length > 0 ? roots : [nodeIds[0]];
|
|
29883
32086
|
const layers = assignLayers(nodeIds, adjacency, effectiveRoots);
|
|
29884
32087
|
const maxLayer = Math.max(...Array.from(layers.values()));
|
|
@@ -29934,7 +32137,7 @@ function computeStaticLayout(mode, input) {
|
|
|
29934
32137
|
const adjacency = buildAdjacency(nodeIds, edges);
|
|
29935
32138
|
const roots = findRoots(nodeIds, adjacency);
|
|
29936
32139
|
if (mode === "flow") return layoutFlow(nodeIds, adjacency, roots, width, height, margin);
|
|
29937
|
-
if (mode === "tree") return
|
|
32140
|
+
if (mode === "tree") return layoutTree2(nodeIds, adjacency, roots, width, height, margin);
|
|
29938
32141
|
return layoutRadial(nodeIds, adjacency, roots, width, height, margin);
|
|
29939
32142
|
}
|
|
29940
32143
|
var init_graphViewLayouts = __esm({
|
|
@@ -30346,7 +32549,7 @@ var init_MapView = __esm({
|
|
|
30346
32549
|
shadowSize: [41, 41]
|
|
30347
32550
|
});
|
|
30348
32551
|
L.Marker.prototype.options.icon = defaultIcon;
|
|
30349
|
-
const { useEffect: useEffect66, useRef: useRef65, useCallback:
|
|
32552
|
+
const { useEffect: useEffect66, useRef: useRef65, useCallback: useCallback108, useState: useState104 } = React77__default;
|
|
30350
32553
|
const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
|
|
30351
32554
|
const { useEventBus: useEventBus2 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
|
|
30352
32555
|
function MapUpdater({ centerLat, centerLng, zoom }) {
|
|
@@ -30392,7 +32595,7 @@ var init_MapView = __esm({
|
|
|
30392
32595
|
}) {
|
|
30393
32596
|
const eventBus = useEventBus2();
|
|
30394
32597
|
const [clickedPosition, setClickedPosition] = useState104(null);
|
|
30395
|
-
const handleMapClick =
|
|
32598
|
+
const handleMapClick = useCallback108((lat, lng) => {
|
|
30396
32599
|
if (showClickedPin) {
|
|
30397
32600
|
setClickedPosition({ lat, lng });
|
|
30398
32601
|
}
|
|
@@ -30401,7 +32604,7 @@ var init_MapView = __esm({
|
|
|
30401
32604
|
eventBus.emit(`UI:${mapClickEvent}`, { latitude: lat, longitude: lng });
|
|
30402
32605
|
}
|
|
30403
32606
|
}, [onMapClick, mapClickEvent, eventBus, showClickedPin]);
|
|
30404
|
-
const handleMarkerClick =
|
|
32607
|
+
const handleMarkerClick = useCallback108((marker) => {
|
|
30405
32608
|
onMarkerClick?.(marker);
|
|
30406
32609
|
if (markerClickEvent) {
|
|
30407
32610
|
eventBus.emit(`UI:${markerClickEvent}`, { ...marker });
|
|
@@ -31177,26 +33380,6 @@ var init_Lightbox = __esm({
|
|
|
31177
33380
|
Lightbox.displayName = "Lightbox";
|
|
31178
33381
|
}
|
|
31179
33382
|
});
|
|
31180
|
-
function useMediaQuery(query) {
|
|
31181
|
-
const subscribe = useCallback(
|
|
31182
|
-
(onChange) => {
|
|
31183
|
-
const mql = window.matchMedia(query);
|
|
31184
|
-
mql.addEventListener("change", onChange);
|
|
31185
|
-
return () => mql.removeEventListener("change", onChange);
|
|
31186
|
-
},
|
|
31187
|
-
[query]
|
|
31188
|
-
);
|
|
31189
|
-
return useSyncExternalStore(
|
|
31190
|
-
subscribe,
|
|
31191
|
-
() => window.matchMedia(query).matches,
|
|
31192
|
-
() => false
|
|
31193
|
-
);
|
|
31194
|
-
}
|
|
31195
|
-
var init_useMediaQuery = __esm({
|
|
31196
|
-
"hooks/useMediaQuery.ts"() {
|
|
31197
|
-
"use client";
|
|
31198
|
-
}
|
|
31199
|
-
});
|
|
31200
33383
|
function renderIconInput3(icon, props) {
|
|
31201
33384
|
return typeof icon === "string" ? /* @__PURE__ */ jsx(Icon, { name: icon, ...props }) : /* @__PURE__ */ jsx(Icon, { icon, ...props });
|
|
31202
33385
|
}
|
|
@@ -31235,8 +33418,8 @@ function TableView({
|
|
|
31235
33418
|
columns,
|
|
31236
33419
|
fields,
|
|
31237
33420
|
itemActions,
|
|
31238
|
-
maxInlineActions,
|
|
31239
|
-
itemClickEvent,
|
|
33421
|
+
maxInlineActions: _maxInlineActions,
|
|
33422
|
+
itemClickEvent = "",
|
|
31240
33423
|
selectable = false,
|
|
31241
33424
|
selectEvent,
|
|
31242
33425
|
selectedIds,
|
|
@@ -31286,7 +33469,6 @@ function TableView({
|
|
|
31286
33469
|
const hasMore = pageSize > 0 && visibleCount < ordered2.length;
|
|
31287
33470
|
const hasRenderProp = typeof children === "function";
|
|
31288
33471
|
const idField = dndItemIdField ?? "id";
|
|
31289
|
-
const isCoarsePointer = useMediaQuery("(pointer: coarse)");
|
|
31290
33472
|
React77__default.useEffect(() => {
|
|
31291
33473
|
tableViewLog.debug("render", {
|
|
31292
33474
|
rowCount: data.length,
|
|
@@ -31328,21 +33510,14 @@ function TableView({
|
|
|
31328
33510
|
const dir = sortColumn === (col.field ?? col.key) && sortDirection === "asc" ? "desc" : "asc";
|
|
31329
33511
|
eventBus.emit(`UI:${sortEvent}`, { column: col.field ?? col.key, direction: dir });
|
|
31330
33512
|
};
|
|
31331
|
-
const
|
|
31332
|
-
e.stopPropagation();
|
|
31333
|
-
const payload = {
|
|
31334
|
-
id: row.id,
|
|
31335
|
-
row
|
|
31336
|
-
};
|
|
31337
|
-
eventBus.emit(`UI:${action.event}`, payload);
|
|
31338
|
-
};
|
|
33513
|
+
const rowClickEvent = itemClickEvent || actionDefs.find((a) => a.variant !== "danger")?.event;
|
|
31339
33514
|
const handleRowClick = (row) => () => {
|
|
31340
|
-
if (!
|
|
33515
|
+
if (!rowClickEvent) return;
|
|
31341
33516
|
const payload = {
|
|
31342
33517
|
id: row.id,
|
|
31343
33518
|
row
|
|
31344
33519
|
};
|
|
31345
|
-
eventBus.emit(`UI:${
|
|
33520
|
+
eventBus.emit(`UI:${rowClickEvent}`, payload);
|
|
31346
33521
|
};
|
|
31347
33522
|
const colFloors = React77__default.useMemo(
|
|
31348
33523
|
() => colDefs.map((col) => {
|
|
@@ -31358,10 +33533,7 @@ function TableView({
|
|
|
31358
33533
|
const statusNode = isLoading ? /* @__PURE__ */ jsx(Box, { className: "text-center py-8", children: /* @__PURE__ */ jsx(Typography, { variant: "body", color: "secondary", children: t("loading.items") }) }) : error ? /* @__PURE__ */ jsx(Box, { className: "text-center py-8", children: /* @__PURE__ */ jsx(Typography, { variant: "body", color: "error", children: error.message }) }) : data.length === 0 ? /* @__PURE__ */ jsx(Box, { className: "text-center py-12", children: /* @__PURE__ */ jsx(Typography, { variant: "body", color: "secondary", children: emptyMessage || t("empty.noItems") }) }) : null;
|
|
31359
33534
|
const lk = LOOKS[look];
|
|
31360
33535
|
const hasActions = actionDefs.length > 0;
|
|
31361
|
-
const
|
|
31362
|
-
const inlineActionCount = hasActions ? effectiveMaxInline != null ? Math.min(actionDefs.length, effectiveMaxInline) : actionDefs.length : 0;
|
|
31363
|
-
const hasOverflowActions = hasActions && effectiveMaxInline != null && actionDefs.length > effectiveMaxInline;
|
|
31364
|
-
const actionsTrack = hasActions ? `${inlineActionCount * 6 + (hasOverflowActions ? 3 : 0)}rem` : null;
|
|
33536
|
+
const actionsTrack = hasActions ? "3rem" : null;
|
|
31365
33537
|
const gridTemplateColumns = [
|
|
31366
33538
|
selectable ? "auto" : null,
|
|
31367
33539
|
...colDefs.map((c, i) => c.width ?? `minmax(${colFloors[i]}ch, 1fr)`),
|
|
@@ -31408,7 +33580,7 @@ function TableView({
|
|
|
31408
33580
|
col.key
|
|
31409
33581
|
);
|
|
31410
33582
|
}),
|
|
31411
|
-
hasActions && /* @__PURE__ */ jsx(Box, { "aria-hidden": true, className: "sticky right-0 bg-[var(--color-surface-subtle)]" })
|
|
33583
|
+
hasActions && /* @__PURE__ */ jsx(Box, { "aria-hidden": true, className: "sticky right-0 bg-[var(--color-surface-subtle)] border-l border-[var(--color-border)] h-full" })
|
|
31412
33584
|
]
|
|
31413
33585
|
}
|
|
31414
33586
|
);
|
|
@@ -31420,12 +33592,12 @@ function TableView({
|
|
|
31420
33592
|
role: "row",
|
|
31421
33593
|
"data-entity-row": true,
|
|
31422
33594
|
"data-entity-id": id,
|
|
31423
|
-
onClick:
|
|
33595
|
+
onClick: rowClickEvent ? handleRowClick(row) : void 0,
|
|
31424
33596
|
style: !hasRenderProp ? { gridTemplateColumns } : void 0,
|
|
31425
33597
|
className: cn(
|
|
31426
33598
|
"group items-center gap-3 transition-colors duration-fast",
|
|
31427
33599
|
hasRenderProp ? "flex" : "grid",
|
|
31428
|
-
|
|
33600
|
+
rowClickEvent && "cursor-pointer",
|
|
31429
33601
|
lk.rowPad,
|
|
31430
33602
|
lk.divider && "border-b border-[var(--color-border)]",
|
|
31431
33603
|
lk.striped && index % 2 === 1 && "bg-[var(--color-surface-subtle)]",
|
|
@@ -31433,7 +33605,7 @@ function TableView({
|
|
|
31433
33605
|
look === "bordered" && "[&>*]:border-r [&>*]:border-[var(--color-border)] [&>*:last-child]:border-r-0"
|
|
31434
33606
|
),
|
|
31435
33607
|
children: [
|
|
31436
|
-
selectable && /* @__PURE__ */ jsx(Box, { className: "flex items-center", onClick:
|
|
33608
|
+
selectable && /* @__PURE__ */ jsx(Box, { className: "flex items-center", onClick: rowClickEvent ? (e) => e.stopPropagation() : void 0, children: /* @__PURE__ */ jsx(
|
|
31437
33609
|
Checkbox,
|
|
31438
33610
|
{
|
|
31439
33611
|
checked: selected.has(id),
|
|
@@ -31454,53 +33626,37 @@ function TableView({
|
|
|
31454
33626
|
}
|
|
31455
33627
|
return /* @__PURE__ */ jsx(Box, { role: "cell", className: cellBase, children: /* @__PURE__ */ jsx("span", { className: "truncate text-foreground", children: formatCell(raw, col.format) }) }, col.key);
|
|
31456
33628
|
}),
|
|
31457
|
-
hasActions && /* @__PURE__ */
|
|
33629
|
+
hasActions && /* @__PURE__ */ jsx(
|
|
31458
33630
|
HStack,
|
|
31459
33631
|
{
|
|
31460
33632
|
gap: "xs",
|
|
31461
|
-
onClick:
|
|
33633
|
+
onClick: rowClickEvent ? (e) => e.stopPropagation() : void 0,
|
|
31462
33634
|
className: cn(
|
|
31463
33635
|
// Pinned: the fixed column tracks routinely overflow the caller's
|
|
31464
|
-
// scroll container, which
|
|
31465
|
-
// Opaque so
|
|
33636
|
+
// scroll container, which would leave the kebab off-screen.
|
|
33637
|
+
// Opaque + hairline edge so it reads as a pinned column, not a
|
|
33638
|
+
// floating control, while scrolled cells pass underneath.
|
|
31466
33639
|
"justify-end flex-shrink-0 sticky right-0 z-[1] transition-colors",
|
|
33640
|
+
"border-l border-[var(--color-border)]",
|
|
31467
33641
|
lk.striped && index % 2 === 1 ? "bg-[var(--color-surface-subtle)]" : "bg-[var(--color-card)] group-hover:bg-[var(--color-surface-subtle)]"
|
|
31468
33642
|
),
|
|
31469
|
-
children:
|
|
31470
|
-
|
|
31471
|
-
|
|
31472
|
-
|
|
31473
|
-
|
|
31474
|
-
|
|
31475
|
-
|
|
31476
|
-
|
|
31477
|
-
|
|
31478
|
-
|
|
31479
|
-
|
|
31480
|
-
|
|
31481
|
-
|
|
31482
|
-
|
|
31483
|
-
}
|
|
31484
|
-
|
|
31485
|
-
|
|
31486
|
-
effectiveMaxInline != null && actionDefs.length > effectiveMaxInline && /* @__PURE__ */ jsx(
|
|
31487
|
-
Menu,
|
|
31488
|
-
{
|
|
31489
|
-
position: "bottom-end",
|
|
31490
|
-
trigger: /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", "aria-label": t("common.actions"), "data-testid": "action-overflow", children: /* @__PURE__ */ jsx(Icon, { name: "more-horizontal", size: "xs" }) }),
|
|
31491
|
-
items: actionDefs.slice(effectiveMaxInline).map((action) => ({
|
|
31492
|
-
label: action.label,
|
|
31493
|
-
icon: action.icon,
|
|
31494
|
-
event: action.event,
|
|
31495
|
-
variant: action.variant === "danger" ? "danger" : "default",
|
|
31496
|
-
onClick: () => eventBus.emit(`UI:${action.event}`, {
|
|
31497
|
-
id: row.id,
|
|
31498
|
-
row
|
|
31499
|
-
})
|
|
31500
|
-
}))
|
|
31501
|
-
}
|
|
31502
|
-
)
|
|
31503
|
-
]
|
|
33643
|
+
children: /* @__PURE__ */ jsx(
|
|
33644
|
+
Menu,
|
|
33645
|
+
{
|
|
33646
|
+
position: "bottom-end",
|
|
33647
|
+
trigger: /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", "aria-label": t("common.actions"), "data-testid": "action-overflow", "data-row-id": String(row.id), children: /* @__PURE__ */ jsx(Icon, { name: "more-horizontal", size: "xs" }) }),
|
|
33648
|
+
items: actionDefs.map((action) => ({
|
|
33649
|
+
label: action.label,
|
|
33650
|
+
icon: action.icon,
|
|
33651
|
+
event: action.event,
|
|
33652
|
+
variant: action.variant === "danger" ? "danger" : "default",
|
|
33653
|
+
onClick: () => eventBus.emit(`UI:${action.event}`, {
|
|
33654
|
+
id: row.id,
|
|
33655
|
+
row
|
|
33656
|
+
})
|
|
33657
|
+
}))
|
|
33658
|
+
}
|
|
33659
|
+
)
|
|
31504
33660
|
}
|
|
31505
33661
|
)
|
|
31506
33662
|
]
|
|
@@ -31543,7 +33699,6 @@ var init_TableView = __esm({
|
|
|
31543
33699
|
init_format();
|
|
31544
33700
|
init_getNestedValue();
|
|
31545
33701
|
init_useEventBus();
|
|
31546
|
-
init_useMediaQuery();
|
|
31547
33702
|
init_Box();
|
|
31548
33703
|
init_Stack();
|
|
31549
33704
|
init_Typography();
|
|
@@ -40143,6 +42298,7 @@ var init_molecules2 = __esm({
|
|
|
40143
42298
|
init_BiologyCanvas();
|
|
40144
42299
|
init_ChemistryCanvas();
|
|
40145
42300
|
init_AlgorithmCanvas();
|
|
42301
|
+
init_AlgoGraphCanvas();
|
|
40146
42302
|
init_learningScene3D();
|
|
40147
42303
|
init_GraphView();
|
|
40148
42304
|
init_MapView();
|
|
@@ -46825,6 +48981,7 @@ var init_component_registry_generated = __esm({
|
|
|
46825
48981
|
init_ActionTile();
|
|
46826
48982
|
init_ActivationBlock();
|
|
46827
48983
|
init_ComponentPatterns();
|
|
48984
|
+
init_AlgoGraphCanvas();
|
|
46828
48985
|
init_AlgorithmCanvas();
|
|
46829
48986
|
init_AnimatedCounter();
|
|
46830
48987
|
init_AnimatedGraphic();
|
|
@@ -47088,6 +49245,7 @@ var init_component_registry_generated = __esm({
|
|
|
47088
49245
|
"ActivationBlock": ActivationBlock,
|
|
47089
49246
|
"Alert": AlertPattern,
|
|
47090
49247
|
"AlertPattern": AlertPattern,
|
|
49248
|
+
"AlgoGraphCanvas": AlgoGraphCanvas,
|
|
47091
49249
|
"AlgorithmCanvas": AlgorithmCanvas,
|
|
47092
49250
|
"AnimatedCounter": AnimatedCounter,
|
|
47093
49251
|
"AnimatedGraphic": AnimatedGraphic,
|
|
@@ -50560,7 +52718,23 @@ init_useAuthContext();
|
|
|
50560
52718
|
init_useSwipeGesture();
|
|
50561
52719
|
init_useLongPress();
|
|
50562
52720
|
init_useDragReorder();
|
|
50563
|
-
|
|
52721
|
+
function useMediaQuery(query) {
|
|
52722
|
+
const subscribe = useCallback(
|
|
52723
|
+
(onChange) => {
|
|
52724
|
+
const mql = window.matchMedia(query);
|
|
52725
|
+
mql.addEventListener("change", onChange);
|
|
52726
|
+
return () => mql.removeEventListener("change", onChange);
|
|
52727
|
+
},
|
|
52728
|
+
[query]
|
|
52729
|
+
);
|
|
52730
|
+
return useSyncExternalStore(
|
|
52731
|
+
subscribe,
|
|
52732
|
+
() => window.matchMedia(query).matches,
|
|
52733
|
+
() => false
|
|
52734
|
+
);
|
|
52735
|
+
}
|
|
52736
|
+
|
|
52737
|
+
// hooks/index.ts
|
|
50564
52738
|
init_useInfiniteScroll();
|
|
50565
52739
|
init_usePullToRefresh();
|
|
50566
52740
|
init_useCanvasGestures();
|
|
@@ -50806,4 +52980,4 @@ function useGitHubBranches(owner, repo, enabled = true) {
|
|
|
50806
52980
|
});
|
|
50807
52981
|
}
|
|
50808
52982
|
|
|
50809
|
-
export { ALL_PRESETS, ALMADAR_DND_MIME, AR_BOOK_FIELDS, AboutPageTemplate, Accordion, Card2 as ActionCard, ActionPalette, ActionTile, ActivationBlock, Alert, AlgorithmCanvas, AnimatedCounter, AnimatedGraphic, AnimatedReveal, ArticleSection, Aside, AssetPicker, AtlasImage, AtlasPanel, AuthLayout, Avatar, Badge, BehaviorView, BiologyCanvas, BloomQuizBlock, BookChapterView, BookCoverPage, BookNavBar, BookTableOfContents, BookViewer, Box, BranchingLogicBuilder, Breadcrumb, Button, ButtonGroup, CTABanner, CalendarGrid, Canvas, Canvas2D, Card, CardBody, CardContent, CardFooter, CardGrid, CardHeader, CardTitle, Carousel, CaseStudyCard, CaseStudyOrganism, Center, Chart, ChartLegend, ChatBar, Checkbox, ChemistryCanvas, ChoiceButton, Coachmark, CodeBlock, CodeRunnerPanel, CollapsibleSection, CommunityLinks, ConditionalWrapper, ConfettiEffect, ConfirmDialog, ConnectionBlock, Container, ContentRenderer, ContentSection, ControlButton, ControlGrid, CounterTemplate, DEFAULT_LIKERT_OPTIONS, DEFAULT_MATRIX_COLUMNS, DEFAULT_SLOTS, DIAMOND_TOP_Y, DashboardGrid, DashboardLayout, DataGrid, DataList, DataTable, DateRangePicker, DateRangeSelector, DayCell, DetailPanel, Dialog, DialogueBubble, Divider, DocBreadcrumb, DocPagination, DocSearch, DocSidebar, DocTOC, DocumentViewer, StateMachineView as DomStateMachineVisualizer, Drawer, DrawerSlot, ELEMENT_SELECTED_EVENT, EdgeDecoration, EditorCheckbox, EditorSelect, EditorSlider, EditorTextInput, EditorToolbar, EmojiPicker, EmptyState, EntityDisplayEvents, ErrorBoundary, ErrorState, FEATURE_COLORS, FEATURE_TYPES, FLOOR_HEIGHT, FeatureCard, FeatureDetailPageTemplate, FeatureGrid, FeatureGridOrganism, FileTree, FilterGroup, FilterPill, Flex, FlipCard, FlipContainer, FloatingActionButton, Form, FormActions, FormField, FormLayout, FormSection, FormSectionHeader, GameAudioToggle, GameHud, GameIcon, GameMenu, GameShell, GenericAppTemplate, GeometricPattern, GradientDivider, GraphCanvas, GraphView, Grid, GridPicker, HStack, Header, HealthBar, HeroOrganism, HeroSection, I18nProvider, IDENTITY_BOOK_FIELDS, Icon, IconPicker, ImportPreviewTree, ImportProgress, ImportSourcePicker, InfiniteScrollSentinel, Input, InputGroup, InstallBox, JazariStateMachine, JsonTreeEditor, Label, LandingPageTemplate, LawReferenceTooltip, LearningCanvas, LearningScene3D, Lightbox, LikertScale, LineChart2 as LineChart, List3 as List, LoadingState, MapView, MarkdownContent, MarketingFooter, MarketingStatCard, MasterDetail, MasterDetailLayout, MathCanvas, MatrixQuestion, MediaGallery, Menu, Meter, Modal, ModalSlot, ModuleCard, Navigation, NodeSlotEditor, NotifyListener, NumberStepper, OnboardingSpotlight, OptionConstraintGroup, StateMachineView as OrbitalStateMachineView, OrbitalVisualization, Overlay, PageHeader, PageTransition, Pagination, PatternTile, PhysicsCanvas, Popover, PositionedCanvas, Presence, PricingCard, PricingGrid, PricingOrganism, PricingPageTemplate, ProgressBar, ProgressDots, PropertyInspector, PullQuote, PullToRefresh, QrScanner, QuizBlock, Radio, RangeSlider, ReflectionBlock, RelationSelect, RepeatableFormSection, ReplyTree, RichBlockEditor, RuntimeDebugger, SHEET_COLUMNS, SPRITE_SHEET_LAYOUT, ScaledDiagram, ScoreDisplay, SearchInput, Section, SectionHeader, SegmentRenderer, Select, SequenceBar, ServiceCatalog, SharedEntityStoreContext, ShowcaseCard, ShowcaseOrganism, SidePanel, Sidebar, SignaturePad, SimpleGrid, Skeleton, SlotContentRenderer, SocialProof, SortableList, Spacer, Sparkline, Spinner, Split, SplitPane, SplitSection, Stack, StarRating, StatBadge, StatCard, StatDisplay, StateGraph, StateJsonView, StateMachineView, StateNode2 as StateNode, StatsGrid, StatsOrganism, StatusBar, StatusDot, StepFlow, StepFlowOrganism, SubagentTracePanel, SvgBranch, SvgConnection, SvgFlow, SvgGrid, SvgLobe, SvgMesh, SvgMorph, SvgNode, SvgPulse, SvgRing, SvgShield, SvgStack, SwipeableRow, Switch, TERRAIN_COLORS, TILE_HEIGHT, TILE_WIDTH, TabbedContainer, TableView, Tabs, TagCloud, TagInput, TeamCard, TeamOrganism, TerrainPalette, TextHighlight, Textarea, ThemeToggle, TimeSlotCell, Timeline, TimerDisplay, Toast, ToastSlot, Tooltip, TraitFrame, TraitSlot, TraitStateViewer, TransitionArrow, TrendIndicator, TypewriterText, Typography, UISlotComponent, UISlotRenderer, UploadDropZone, VStack, VersionDiff, ViolationAlert, VoteStack, WizardContainer, WizardNavigation, WizardProgress, arrowBetween, billboardLabel, boardEntity, bool, calculateAttackTargets, calculateValidMoves, cn, createInitialGameState, createSharedEntityStore, createTranslate, createUnitAnimationState, cylinderBetween, get3DClickPayload, getCurrentFrame, getTileDimensions, inferDirection, isoToScreen, makeAsset, makeAssetMap, mapBookData, meshSphere, num, objAvailableActions, objAvailableEvents, objCurrentState, objIcon, objId, objMaxRules, objName, objRules, objStates, parseEditFocus, parseLessonSegments, parseMarkdownWithCodeBlocks, parseQueryBinding, pendulum, projectileMotion, registerCodeLanguageLoader, resolveFieldMap, resolveFrame, resolveSheetDirection, rows, runTickFrame, screenToIso, springOscillator, str, tickAnimationState, toCodeLanguage, transitionAnimation, unitHealth, unitPosition, unitTeam, useAgentChat, useAnchorRect, useAtlasSliceDataUrl, useAuthContext, useCamera, useCanvasGestures, useCompile, useConnectGitHub, useDeepAgentGeneration, useDisconnectGitHub, useDragReorder, useDraggable, useDropZone, useEmitEvent, useEventBus, useEventListener, useExtensions, useFileEditor, useFileSystem, useGameAudio, useGitHubBranches, useGitHubRepo, useGitHubRepos, useGitHubStatus, useImageCache, useInfiniteScroll, useLongPress, useMediaQuery, useOrbitalHistory, usePresence, usePreview, usePullToRefresh, useQuerySingleton, useRenderInterpolation, useSharedEntitySnapshot, useSharedEntityStore, useSharedEntityStoreContext, useSwipeGesture, useTapReveal, useTraitListens, useTranslate114 as useTranslate, useUIEvents, useUISlotManager, useUnitSpriteAtlas, useValidation, vec2 };
|
|
52983
|
+
export { ALL_PRESETS, ALMADAR_DND_MIME, AR_BOOK_FIELDS, AboutPageTemplate, Accordion, Card2 as ActionCard, ActionPalette, ActionTile, ActivationBlock, Alert, AlgoGraphCanvas, AlgorithmCanvas, AnimatedCounter, AnimatedGraphic, AnimatedReveal, ArticleSection, Aside, AssetPicker, AtlasImage, AtlasPanel, AuthLayout, Avatar, Badge, BehaviorView, BiologyCanvas, BloomQuizBlock, BookChapterView, BookCoverPage, BookNavBar, BookTableOfContents, BookViewer, Box, BranchingLogicBuilder, Breadcrumb, Button, ButtonGroup, CTABanner, CalendarGrid, Canvas, Canvas2D, Card, CardBody, CardContent, CardFooter, CardGrid, CardHeader, CardTitle, Carousel, CaseStudyCard, CaseStudyOrganism, Center, Chart, ChartLegend, ChatBar, Checkbox, ChemistryCanvas, ChoiceButton, Coachmark, CodeBlock, CodeRunnerPanel, CollapsibleSection, CommunityLinks, ConditionalWrapper, ConfettiEffect, ConfirmDialog, ConnectionBlock, Container, ContentRenderer, ContentSection, ControlButton, ControlGrid, CounterTemplate, DEFAULT_LIKERT_OPTIONS, DEFAULT_MATRIX_COLUMNS, DEFAULT_SLOTS, DIAMOND_TOP_Y, DashboardGrid, DashboardLayout, DataGrid, DataList, DataTable, DateRangePicker, DateRangeSelector, DayCell, DetailPanel, Dialog, DialogueBubble, Divider, DocBreadcrumb, DocPagination, DocSearch, DocSidebar, DocTOC, DocumentViewer, StateMachineView as DomStateMachineVisualizer, Drawer, DrawerSlot, ELEMENT_SELECTED_EVENT, EdgeDecoration, EditorCheckbox, EditorSelect, EditorSlider, EditorTextInput, EditorToolbar, EmojiPicker, EmptyState, EntityDisplayEvents, ErrorBoundary, ErrorState, FEATURE_COLORS, FEATURE_TYPES, FLOOR_HEIGHT, FeatureCard, FeatureDetailPageTemplate, FeatureGrid, FeatureGridOrganism, FileTree, FilterGroup, FilterPill, Flex, FlipCard, FlipContainer, FloatingActionButton, Form, FormActions, FormField, FormLayout, FormSection, FormSectionHeader, GameAudioToggle, GameHud, GameIcon, GameMenu, GameShell, GenericAppTemplate, GeometricPattern, GradientDivider, GraphCanvas, GraphView, Grid, GridPicker, HStack, Header, HealthBar, HeroOrganism, HeroSection, I18nProvider, IDENTITY_BOOK_FIELDS, Icon, IconPicker, ImportPreviewTree, ImportProgress, ImportSourcePicker, InfiniteScrollSentinel, Input, InputGroup, InstallBox, JazariStateMachine, JsonTreeEditor, Label, LandingPageTemplate, LawReferenceTooltip, LearningCanvas, LearningScene3D, Lightbox, LikertScale, LineChart2 as LineChart, List3 as List, LoadingState, MapView, MarkdownContent, MarketingFooter, MarketingStatCard, MasterDetail, MasterDetailLayout, MathCanvas, MatrixQuestion, MediaGallery, Menu, Meter, Modal, ModalSlot, ModuleCard, Navigation, NodeSlotEditor, NotifyListener, NumberStepper, OnboardingSpotlight, OptionConstraintGroup, StateMachineView as OrbitalStateMachineView, OrbitalVisualization, Overlay, PageHeader, PageTransition, Pagination, PatternTile, PhysicsCanvas, Popover, PositionedCanvas, Presence, PricingCard, PricingGrid, PricingOrganism, PricingPageTemplate, ProgressBar, ProgressDots, PropertyInspector, PullQuote, PullToRefresh, QrScanner, QuizBlock, Radio, RangeSlider, ReflectionBlock, RelationSelect, RepeatableFormSection, ReplyTree, RichBlockEditor, RuntimeDebugger, SHEET_COLUMNS, SPRITE_SHEET_LAYOUT, ScaledDiagram, ScoreDisplay, SearchInput, Section, SectionHeader, SegmentRenderer, Select, SequenceBar, ServiceCatalog, SharedEntityStoreContext, ShowcaseCard, ShowcaseOrganism, SidePanel, Sidebar, SignaturePad, SimpleGrid, Skeleton, SlotContentRenderer, SocialProof, SortableList, Spacer, Sparkline, Spinner, Split, SplitPane, SplitSection, Stack, StarRating, StatBadge, StatCard, StatDisplay, StateGraph, StateJsonView, StateMachineView, StateNode2 as StateNode, StatsGrid, StatsOrganism, StatusBar, StatusDot, StepFlow, StepFlowOrganism, SubagentTracePanel, SvgBranch, SvgConnection, SvgFlow, SvgGrid, SvgLobe, SvgMesh, SvgMorph, SvgNode, SvgPulse, SvgRing, SvgShield, SvgStack, SwipeableRow, Switch, TERRAIN_COLORS, TILE_HEIGHT, TILE_WIDTH, TabbedContainer, TableView, Tabs, TagCloud, TagInput, TeamCard, TeamOrganism, TerrainPalette, TextHighlight, Textarea, ThemeToggle, TimeSlotCell, Timeline, TimerDisplay, Toast, ToastSlot, Tooltip, TraitFrame, TraitSlot, TraitStateViewer, TransitionArrow, TrendIndicator, TypewriterText, Typography, UISlotComponent, UISlotRenderer, UploadDropZone, VStack, VersionDiff, ViolationAlert, VoteStack, WizardContainer, WizardNavigation, WizardProgress, arrowBetween, billboardLabel, boardEntity, bool, calculateAttackTargets, calculateValidMoves, cn, createInitialGameState, createSharedEntityStore, createTranslate, createUnitAnimationState, cylinderBetween, get3DClickPayload, getCurrentFrame, getTileDimensions, inferDirection, isoToScreen, makeAsset, makeAssetMap, mapBookData, meshSphere, num, objAvailableActions, objAvailableEvents, objCurrentState, objIcon, objId, objMaxRules, objName, objRules, objStates, parseEditFocus, parseLessonSegments, parseMarkdownWithCodeBlocks, parseQueryBinding, pendulum, projectileMotion, registerCodeLanguageLoader, resolveFieldMap, resolveFrame, resolveSheetDirection, rows, runTickFrame, screenToIso, springOscillator, str, tickAnimationState, toCodeLanguage, transitionAnimation, unitHealth, unitPosition, unitTeam, useAgentChat, useAnchorRect, useAtlasSliceDataUrl, useAuthContext, useCamera, useCanvasGestures, useCompile, useConnectGitHub, useDeepAgentGeneration, useDisconnectGitHub, useDragReorder, useDraggable, useDropZone, useEmitEvent, useEventBus, useEventListener, useExtensions, useFileEditor, useFileSystem, useGameAudio, useGitHubBranches, useGitHubRepo, useGitHubRepos, useGitHubStatus, useImageCache, useInfiniteScroll, useLongPress, useMediaQuery, useOrbitalHistory, usePresence, usePreview, usePullToRefresh, useQuerySingleton, useRenderInterpolation, useSharedEntitySnapshot, useSharedEntityStore, useSharedEntityStoreContext, useSwipeGesture, useTapReveal, useTraitListens, useTranslate114 as useTranslate, useUIEvents, useUISlotManager, useUnitSpriteAtlas, useValidation, vec2 };
|