@almadar/ui 5.76.7 → 5.78.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 +1121 -535
- package/dist/avl/index.css +3 -3
- package/dist/avl/index.js +1122 -536
- package/dist/components/core/atoms/FlipContainer.d.ts +1 -1
- package/dist/components/core/atoms/index.d.ts +2 -1
- package/dist/components/core/molecules/Card.d.ts +1 -1
- package/dist/components/core/molecules/DataGrid.d.ts +1 -1
- package/dist/components/core/molecules/DataList.d.ts +1 -1
- package/dist/components/core/molecules/DocPagination.d.ts +1 -8
- package/dist/components/core/molecules/DocSearch.d.ts +1 -8
- package/dist/components/core/molecules/FlipCard.d.ts +1 -1
- package/dist/components/core/molecules/MapView.d.ts +1 -18
- package/dist/components/core/molecules/SortableList.d.ts +1 -1
- package/dist/components/core/molecules/TableView.d.ts +1 -1
- package/dist/components/core/molecules/index.d.ts +4 -0
- package/dist/components/core/organisms/DataTable.d.ts +1 -1
- package/dist/components/core/organisms/debug/RuntimeDebugger.d.ts +1 -10
- package/dist/components/core/organisms/debug/tabs/EntitiesTab.d.ts +1 -2
- package/dist/components/core/organisms/debug/tabs/EventDispatcherTab.d.ts +1 -2
- package/dist/components/core/organisms/debug/tabs/EventFlowTab.d.ts +1 -6
- package/dist/components/core/organisms/debug/tabs/GuardsPanel.d.ts +1 -6
- package/dist/components/core/organisms/debug/tabs/ServerBridgeTab.d.ts +1 -12
- package/dist/components/core/organisms/debug/tabs/TicksTab.d.ts +1 -2
- package/dist/components/core/organisms/debug/tabs/TraitsTab.d.ts +1 -2
- package/dist/components/core/organisms/debug/tabs/TransitionTimeline.d.ts +1 -11
- package/dist/components/core/organisms/debug/tabs/VerificationTab.d.ts +1 -8
- package/dist/components/game/2d/atoms/ActionButton.d.ts +1 -2
- package/dist/components/game/2d/atoms/ChoiceButton.d.ts +1 -2
- package/dist/components/game/2d/atoms/ComboCounter.d.ts +1 -2
- package/dist/components/game/2d/atoms/ControlButton.d.ts +1 -2
- package/dist/components/game/2d/atoms/DamageNumber.d.ts +1 -2
- package/dist/components/game/2d/atoms/DialogueBubble.d.ts +1 -2
- package/dist/components/game/2d/atoms/GameIcon.d.ts +1 -2
- package/dist/components/game/2d/atoms/HealthBar.d.ts +1 -2
- package/dist/components/game/2d/atoms/ItemSlot.d.ts +1 -2
- package/dist/components/game/2d/atoms/MiniMap.d.ts +1 -2
- package/dist/components/game/2d/atoms/ResourceCounter.d.ts +1 -2
- package/dist/components/game/2d/atoms/ScoreDisplay.d.ts +1 -2
- package/dist/components/game/2d/atoms/StatusEffect.d.ts +1 -2
- package/dist/components/game/2d/atoms/TimerDisplay.d.ts +1 -2
- package/dist/components/game/2d/atoms/TurnIndicator.d.ts +1 -2
- package/dist/components/game/2d/atoms/WaypointMarker.d.ts +1 -2
- package/dist/components/game/2d/molecules/GameHud.d.ts +1 -2
- package/dist/components/game/2d/molecules/GameMenu.d.ts +1 -2
- package/dist/components/game/2d/molecules/InventoryGrid.d.ts +1 -2
- package/dist/components/game/2d/molecules/ResourceBar.d.ts +1 -2
- package/dist/components/game/2d/molecules/StatBadge.d.ts +1 -2
- package/dist/components/game/shared/lib/editorUtils.d.ts +8 -8
- package/dist/components/index.cjs +2334 -1653
- package/dist/components/index.css +3 -3
- package/dist/components/index.js +1124 -443
- package/dist/components/learning/atoms/LearningCanvas.d.ts +80 -0
- package/dist/components/learning/molecules/BiologyCanvas.d.ts +47 -0
- package/dist/components/learning/molecules/ChemistryCanvas.d.ts +55 -0
- package/dist/components/learning/molecules/MathCanvas.d.ts +61 -0
- package/dist/components/learning/molecules/PhysicsCanvas.d.ts +54 -0
- package/dist/components/marketing/atoms/MarketingStatCard.d.ts +2 -2
- package/dist/hooks/useGitHub.d.ts +7 -7
- package/dist/marketing/index.cjs +2 -2
- package/dist/marketing/index.js +2 -2
- package/dist/providers/EventBusProvider.d.ts +1 -1
- package/dist/providers/SelectionProvider.d.ts +1 -1
- package/dist/providers/ServerBridge.d.ts +1 -1
- package/dist/providers/index.cjs +1117 -531
- package/dist/providers/index.css +3 -3
- package/dist/providers/index.js +1118 -532
- package/dist/runtime/index.cjs +1117 -531
- package/dist/runtime/index.css +3 -3
- package/dist/runtime/index.js +1118 -532
- package/package.json +2 -2
package/dist/runtime/index.cjs
CHANGED
|
@@ -6225,6 +6225,66 @@ var init_RangeSlider = __esm({
|
|
|
6225
6225
|
RangeSlider.displayName = "RangeSlider";
|
|
6226
6226
|
}
|
|
6227
6227
|
});
|
|
6228
|
+
function easeOut(t) {
|
|
6229
|
+
return t * (2 - t);
|
|
6230
|
+
}
|
|
6231
|
+
var AnimatedCounter;
|
|
6232
|
+
var init_AnimatedCounter = __esm({
|
|
6233
|
+
"components/marketing/atoms/AnimatedCounter.tsx"() {
|
|
6234
|
+
"use client";
|
|
6235
|
+
init_cn();
|
|
6236
|
+
init_Typography();
|
|
6237
|
+
AnimatedCounter = ({
|
|
6238
|
+
value: rawValue,
|
|
6239
|
+
duration = 600,
|
|
6240
|
+
prefix,
|
|
6241
|
+
suffix,
|
|
6242
|
+
className
|
|
6243
|
+
}) => {
|
|
6244
|
+
const numericRaw = typeof rawValue === "number" ? rawValue : Number.parseFloat(String(rawValue ?? ""));
|
|
6245
|
+
const value = !Number.isNaN(numericRaw) ? numericRaw : 0;
|
|
6246
|
+
const [displayValue, setDisplayValue] = React105.useState(value);
|
|
6247
|
+
const previousValueRef = React105.useRef(value);
|
|
6248
|
+
const animationFrameRef = React105.useRef(null);
|
|
6249
|
+
React105.useEffect(() => {
|
|
6250
|
+
const from = previousValueRef.current;
|
|
6251
|
+
const to = value;
|
|
6252
|
+
previousValueRef.current = value;
|
|
6253
|
+
if (from === to) {
|
|
6254
|
+
setDisplayValue(to);
|
|
6255
|
+
return;
|
|
6256
|
+
}
|
|
6257
|
+
const startTime = performance.now();
|
|
6258
|
+
const diff = to - from;
|
|
6259
|
+
function animate(currentTime) {
|
|
6260
|
+
const elapsed = currentTime - startTime;
|
|
6261
|
+
const progress = Math.min(elapsed / duration, 1);
|
|
6262
|
+
const easedProgress = easeOut(progress);
|
|
6263
|
+
setDisplayValue(from + diff * easedProgress);
|
|
6264
|
+
if (progress < 1) {
|
|
6265
|
+
animationFrameRef.current = requestAnimationFrame(animate);
|
|
6266
|
+
} else {
|
|
6267
|
+
setDisplayValue(to);
|
|
6268
|
+
}
|
|
6269
|
+
}
|
|
6270
|
+
animationFrameRef.current = requestAnimationFrame(animate);
|
|
6271
|
+
return () => {
|
|
6272
|
+
if (animationFrameRef.current !== null) {
|
|
6273
|
+
cancelAnimationFrame(animationFrameRef.current);
|
|
6274
|
+
}
|
|
6275
|
+
};
|
|
6276
|
+
}, [value, duration]);
|
|
6277
|
+
const decimalPlaces = Number.isInteger(value) ? 0 : String(value).split(".")[1]?.length ?? 0;
|
|
6278
|
+
const formattedValue = displayValue.toFixed(decimalPlaces);
|
|
6279
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(Typography, { variant: "h3", className: cn("tabular-nums", className), children: [
|
|
6280
|
+
prefix,
|
|
6281
|
+
formattedValue,
|
|
6282
|
+
suffix
|
|
6283
|
+
] });
|
|
6284
|
+
};
|
|
6285
|
+
AnimatedCounter.displayName = "AnimatedCounter";
|
|
6286
|
+
}
|
|
6287
|
+
});
|
|
6228
6288
|
var InfiniteScrollSentinel;
|
|
6229
6289
|
var init_InfiniteScrollSentinel = __esm({
|
|
6230
6290
|
"components/core/atoms/InfiniteScrollSentinel.tsx"() {
|
|
@@ -6486,6 +6546,34 @@ var init_SectionHeader = __esm({
|
|
|
6486
6546
|
SectionHeader.displayName = "SectionHeader";
|
|
6487
6547
|
}
|
|
6488
6548
|
});
|
|
6549
|
+
var sizeClasses6, MarketingStatCard;
|
|
6550
|
+
var init_MarketingStatCard = __esm({
|
|
6551
|
+
"components/marketing/atoms/MarketingStatCard.tsx"() {
|
|
6552
|
+
init_cn();
|
|
6553
|
+
init_Stack();
|
|
6554
|
+
init_Typography();
|
|
6555
|
+
sizeClasses6 = {
|
|
6556
|
+
sm: "text-2xl",
|
|
6557
|
+
md: "text-4xl",
|
|
6558
|
+
lg: "text-5xl"
|
|
6559
|
+
};
|
|
6560
|
+
MarketingStatCard = ({ value, label, size = "md", className }) => {
|
|
6561
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "xs", align: "center", className: cn(className), children: [
|
|
6562
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
6563
|
+
Typography,
|
|
6564
|
+
{
|
|
6565
|
+
weight: "bold",
|
|
6566
|
+
color: "primary",
|
|
6567
|
+
className: cn(sizeClasses6[size]),
|
|
6568
|
+
children: value
|
|
6569
|
+
}
|
|
6570
|
+
),
|
|
6571
|
+
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", color: "muted", children: label })
|
|
6572
|
+
] });
|
|
6573
|
+
};
|
|
6574
|
+
MarketingStatCard.displayName = "MarketingStatCard";
|
|
6575
|
+
}
|
|
6576
|
+
});
|
|
6489
6577
|
var backgroundClasses, paddingClasses, ContentSection;
|
|
6490
6578
|
var init_ContentSection = __esm({
|
|
6491
6579
|
"components/marketing/atoms/ContentSection.tsx"() {
|
|
@@ -12500,318 +12588,14 @@ var init_useCanvasEffects = __esm({
|
|
|
12500
12588
|
"use client";
|
|
12501
12589
|
}
|
|
12502
12590
|
});
|
|
12503
|
-
function pickPath(entry) {
|
|
12504
|
-
if (Array.isArray(entry.path)) {
|
|
12505
|
-
return entry.path[Math.floor(Math.random() * entry.path.length)];
|
|
12506
|
-
}
|
|
12507
|
-
return entry.path;
|
|
12508
|
-
}
|
|
12509
|
-
function useGameAudio({
|
|
12510
|
-
manifest,
|
|
12511
|
-
baseUrl = "",
|
|
12512
|
-
initialMuted = false,
|
|
12513
|
-
initialVolume = 1
|
|
12514
|
-
}) {
|
|
12515
|
-
const [muted, setMutedState] = React105.useState(initialMuted);
|
|
12516
|
-
const [masterVolume, setMasterVolumeState] = React105.useState(initialVolume);
|
|
12517
|
-
const mutedRef = React105.useRef(muted);
|
|
12518
|
-
const volumeRef = React105.useRef(masterVolume);
|
|
12519
|
-
const manifestRef = React105.useRef(manifest);
|
|
12520
|
-
mutedRef.current = muted;
|
|
12521
|
-
volumeRef.current = masterVolume;
|
|
12522
|
-
manifestRef.current = manifest;
|
|
12523
|
-
const poolsRef = React105.useRef(/* @__PURE__ */ new Map());
|
|
12524
|
-
const getOrCreateElement = React105.useCallback((key) => {
|
|
12525
|
-
const entry = manifestRef.current[key];
|
|
12526
|
-
if (!entry) return null;
|
|
12527
|
-
let pool = poolsRef.current.get(key);
|
|
12528
|
-
if (!pool) {
|
|
12529
|
-
pool = [];
|
|
12530
|
-
poolsRef.current.set(key, pool);
|
|
12531
|
-
}
|
|
12532
|
-
const maxSize = entry.poolSize ?? 1;
|
|
12533
|
-
for (const audio of pool) {
|
|
12534
|
-
if (audio.paused && (audio.ended || audio.currentTime === 0)) {
|
|
12535
|
-
return audio;
|
|
12536
|
-
}
|
|
12537
|
-
}
|
|
12538
|
-
if (pool.length < maxSize) {
|
|
12539
|
-
const src = baseUrl + pickPath(entry);
|
|
12540
|
-
const audio = new Audio(src);
|
|
12541
|
-
audio.loop = entry.loop ?? false;
|
|
12542
|
-
pool.push(audio);
|
|
12543
|
-
return audio;
|
|
12544
|
-
}
|
|
12545
|
-
if (!entry.loop) {
|
|
12546
|
-
let oldest = pool[0];
|
|
12547
|
-
for (const audio of pool) {
|
|
12548
|
-
if (audio.currentTime > oldest.currentTime) {
|
|
12549
|
-
oldest = audio;
|
|
12550
|
-
}
|
|
12551
|
-
}
|
|
12552
|
-
oldest.pause();
|
|
12553
|
-
oldest.currentTime = 0;
|
|
12554
|
-
return oldest;
|
|
12555
|
-
}
|
|
12556
|
-
return null;
|
|
12557
|
-
}, [baseUrl]);
|
|
12558
|
-
const play = React105.useCallback((key) => {
|
|
12559
|
-
if (mutedRef.current) return;
|
|
12560
|
-
const entry = manifestRef.current[key];
|
|
12561
|
-
if (!entry) return;
|
|
12562
|
-
const audio = getOrCreateElement(key);
|
|
12563
|
-
if (!audio) return;
|
|
12564
|
-
audio.volume = Math.min(1, (entry.volume ?? 1) * volumeRef.current);
|
|
12565
|
-
if (!entry.loop) {
|
|
12566
|
-
audio.currentTime = 0;
|
|
12567
|
-
}
|
|
12568
|
-
const promise = audio.play();
|
|
12569
|
-
if (promise) {
|
|
12570
|
-
promise.catch(() => {
|
|
12571
|
-
});
|
|
12572
|
-
}
|
|
12573
|
-
}, [getOrCreateElement]);
|
|
12574
|
-
const stop = React105.useCallback((key) => {
|
|
12575
|
-
const pool = poolsRef.current.get(key);
|
|
12576
|
-
if (!pool) return;
|
|
12577
|
-
for (const audio of pool) {
|
|
12578
|
-
audio.pause();
|
|
12579
|
-
audio.currentTime = 0;
|
|
12580
|
-
}
|
|
12581
|
-
}, []);
|
|
12582
|
-
const currentMusicKeyRef = React105.useRef(null);
|
|
12583
|
-
const currentMusicElRef = React105.useRef(null);
|
|
12584
|
-
const musicFadeRef = React105.useRef(null);
|
|
12585
|
-
const pendingMusicKeyRef = React105.useRef(null);
|
|
12586
|
-
const clearMusicFade = React105.useCallback(() => {
|
|
12587
|
-
if (musicFadeRef.current) {
|
|
12588
|
-
clearInterval(musicFadeRef.current);
|
|
12589
|
-
musicFadeRef.current = null;
|
|
12590
|
-
}
|
|
12591
|
-
}, []);
|
|
12592
|
-
const playMusic = React105.useCallback((key) => {
|
|
12593
|
-
if (key === currentMusicKeyRef.current) return;
|
|
12594
|
-
pendingMusicKeyRef.current = key;
|
|
12595
|
-
const entry = manifestRef.current[key];
|
|
12596
|
-
if (!entry) return;
|
|
12597
|
-
const fadeDurationMs = entry.crossfadeDurationMs ?? 1500;
|
|
12598
|
-
const stepMs = 50;
|
|
12599
|
-
const totalSteps = Math.max(1, fadeDurationMs / stepMs);
|
|
12600
|
-
const targetVolume = Math.min(1, (entry.volume ?? 1) * volumeRef.current);
|
|
12601
|
-
clearMusicFade();
|
|
12602
|
-
const src = baseUrl + (Array.isArray(entry.path) ? entry.path[0] : entry.path);
|
|
12603
|
-
const incoming = new Audio(src);
|
|
12604
|
-
incoming.loop = true;
|
|
12605
|
-
incoming.volume = 0;
|
|
12606
|
-
const outgoing = currentMusicElRef.current;
|
|
12607
|
-
const outgoingStartVol = outgoing?.volume ?? 0;
|
|
12608
|
-
currentMusicKeyRef.current = key;
|
|
12609
|
-
currentMusicElRef.current = incoming;
|
|
12610
|
-
if (!mutedRef.current) {
|
|
12611
|
-
incoming.play().catch(() => {
|
|
12612
|
-
currentMusicKeyRef.current = null;
|
|
12613
|
-
currentMusicElRef.current = outgoing;
|
|
12614
|
-
});
|
|
12615
|
-
}
|
|
12616
|
-
let step = 0;
|
|
12617
|
-
musicFadeRef.current = setInterval(() => {
|
|
12618
|
-
step++;
|
|
12619
|
-
const progress = Math.min(step / totalSteps, 1);
|
|
12620
|
-
incoming.volume = Math.min(1, targetVolume * progress);
|
|
12621
|
-
if (outgoing) {
|
|
12622
|
-
outgoing.volume = Math.max(0, outgoingStartVol * (1 - progress));
|
|
12623
|
-
}
|
|
12624
|
-
if (progress >= 1) {
|
|
12625
|
-
clearMusicFade();
|
|
12626
|
-
if (outgoing) {
|
|
12627
|
-
outgoing.pause();
|
|
12628
|
-
outgoing.src = "";
|
|
12629
|
-
}
|
|
12630
|
-
}
|
|
12631
|
-
}, stepMs);
|
|
12632
|
-
}, [baseUrl, clearMusicFade]);
|
|
12633
|
-
const stopMusic = React105.useCallback((fadeDurationMs = 1e3) => {
|
|
12634
|
-
const outgoing = currentMusicElRef.current;
|
|
12635
|
-
if (!outgoing) return;
|
|
12636
|
-
currentMusicKeyRef.current = null;
|
|
12637
|
-
currentMusicElRef.current = null;
|
|
12638
|
-
pendingMusicKeyRef.current = null;
|
|
12639
|
-
clearMusicFade();
|
|
12640
|
-
const startVolume = outgoing.volume;
|
|
12641
|
-
const stepMs = 50;
|
|
12642
|
-
const totalSteps = Math.max(1, fadeDurationMs / stepMs);
|
|
12643
|
-
let step = 0;
|
|
12644
|
-
musicFadeRef.current = setInterval(() => {
|
|
12645
|
-
step++;
|
|
12646
|
-
const progress = step / totalSteps;
|
|
12647
|
-
outgoing.volume = Math.max(0, startVolume * (1 - progress));
|
|
12648
|
-
if (progress >= 1) {
|
|
12649
|
-
clearMusicFade();
|
|
12650
|
-
outgoing.pause();
|
|
12651
|
-
outgoing.src = "";
|
|
12652
|
-
}
|
|
12653
|
-
}, stepMs);
|
|
12654
|
-
}, [clearMusicFade]);
|
|
12655
|
-
const stopAll = React105.useCallback(() => {
|
|
12656
|
-
for (const pool of poolsRef.current.values()) {
|
|
12657
|
-
for (const audio of pool) {
|
|
12658
|
-
audio.pause();
|
|
12659
|
-
audio.currentTime = 0;
|
|
12660
|
-
}
|
|
12661
|
-
}
|
|
12662
|
-
stopMusic(0);
|
|
12663
|
-
}, [stopMusic]);
|
|
12664
|
-
const setMuted = React105.useCallback((value) => {
|
|
12665
|
-
setMutedState(value);
|
|
12666
|
-
if (value) {
|
|
12667
|
-
for (const [key, pool] of poolsRef.current.entries()) {
|
|
12668
|
-
if (manifestRef.current[key]?.loop) {
|
|
12669
|
-
for (const audio of pool) {
|
|
12670
|
-
if (!audio.paused) audio.pause();
|
|
12671
|
-
}
|
|
12672
|
-
}
|
|
12673
|
-
}
|
|
12674
|
-
currentMusicElRef.current?.pause();
|
|
12675
|
-
} else {
|
|
12676
|
-
for (const [key, pool] of poolsRef.current.entries()) {
|
|
12677
|
-
const entry = manifestRef.current[key];
|
|
12678
|
-
if (entry?.loop && entry?.autostart) {
|
|
12679
|
-
for (const audio of pool) {
|
|
12680
|
-
if (audio.paused) audio.play().catch(() => {
|
|
12681
|
-
});
|
|
12682
|
-
}
|
|
12683
|
-
}
|
|
12684
|
-
}
|
|
12685
|
-
const musicEl = currentMusicElRef.current;
|
|
12686
|
-
if (musicEl) {
|
|
12687
|
-
musicEl.play().catch(() => {
|
|
12688
|
-
});
|
|
12689
|
-
}
|
|
12690
|
-
}
|
|
12691
|
-
}, []);
|
|
12692
|
-
const setMasterVolume = React105.useCallback((volume) => {
|
|
12693
|
-
const clamped = Math.max(0, Math.min(1, volume));
|
|
12694
|
-
setMasterVolumeState(clamped);
|
|
12695
|
-
for (const [key, pool] of poolsRef.current.entries()) {
|
|
12696
|
-
const entryVol = manifestRef.current[key]?.volume ?? 1;
|
|
12697
|
-
for (const audio of pool) {
|
|
12698
|
-
audio.volume = Math.min(1, entryVol * clamped);
|
|
12699
|
-
}
|
|
12700
|
-
}
|
|
12701
|
-
if (!musicFadeRef.current && currentMusicElRef.current) {
|
|
12702
|
-
const key = currentMusicKeyRef.current;
|
|
12703
|
-
const entryVol = key ? manifestRef.current[key]?.volume ?? 1 : 1;
|
|
12704
|
-
currentMusicElRef.current.volume = Math.min(1, entryVol * clamped);
|
|
12705
|
-
}
|
|
12706
|
-
}, []);
|
|
12707
|
-
const unlockedRef = React105.useRef(false);
|
|
12708
|
-
React105.useEffect(() => {
|
|
12709
|
-
const autoKeys = Object.keys(manifest).filter((k) => manifest[k].autostart);
|
|
12710
|
-
const hasPendingMusic = () => pendingMusicKeyRef.current !== null;
|
|
12711
|
-
const hasAutoStart = autoKeys.length > 0;
|
|
12712
|
-
if (!hasAutoStart && !hasPendingMusic()) return;
|
|
12713
|
-
const unlock = () => {
|
|
12714
|
-
if (unlockedRef.current) return;
|
|
12715
|
-
unlockedRef.current = true;
|
|
12716
|
-
if (!mutedRef.current) {
|
|
12717
|
-
for (const key of autoKeys) {
|
|
12718
|
-
play(key);
|
|
12719
|
-
}
|
|
12720
|
-
const pending = pendingMusicKeyRef.current;
|
|
12721
|
-
if (pending && pending !== currentMusicKeyRef.current) {
|
|
12722
|
-
playMusic(pending);
|
|
12723
|
-
}
|
|
12724
|
-
}
|
|
12725
|
-
};
|
|
12726
|
-
document.addEventListener("click", unlock, { once: true });
|
|
12727
|
-
document.addEventListener("keydown", unlock, { once: true });
|
|
12728
|
-
document.addEventListener("touchstart", unlock, { once: true });
|
|
12729
|
-
return () => {
|
|
12730
|
-
document.removeEventListener("click", unlock);
|
|
12731
|
-
document.removeEventListener("keydown", unlock);
|
|
12732
|
-
document.removeEventListener("touchstart", unlock);
|
|
12733
|
-
};
|
|
12734
|
-
}, [manifest, play, playMusic]);
|
|
12735
|
-
React105.useEffect(() => {
|
|
12736
|
-
return () => {
|
|
12737
|
-
clearMusicFade();
|
|
12738
|
-
for (const pool of poolsRef.current.values()) {
|
|
12739
|
-
for (const audio of pool) {
|
|
12740
|
-
audio.pause();
|
|
12741
|
-
audio.src = "";
|
|
12742
|
-
}
|
|
12743
|
-
}
|
|
12744
|
-
poolsRef.current.clear();
|
|
12745
|
-
if (currentMusicElRef.current) {
|
|
12746
|
-
currentMusicElRef.current.pause();
|
|
12747
|
-
currentMusicElRef.current.src = "";
|
|
12748
|
-
currentMusicElRef.current = null;
|
|
12749
|
-
}
|
|
12750
|
-
};
|
|
12751
|
-
}, [clearMusicFade]);
|
|
12752
|
-
return {
|
|
12753
|
-
play,
|
|
12754
|
-
stop,
|
|
12755
|
-
stopAll,
|
|
12756
|
-
playMusic,
|
|
12757
|
-
stopMusic,
|
|
12758
|
-
muted,
|
|
12759
|
-
setMuted,
|
|
12760
|
-
masterVolume,
|
|
12761
|
-
setMasterVolume
|
|
12762
|
-
};
|
|
12763
|
-
}
|
|
12764
12591
|
var init_useGameAudio = __esm({
|
|
12765
12592
|
"components/game/shared/hooks/useGameAudio.ts"() {
|
|
12766
12593
|
"use client";
|
|
12767
|
-
useGameAudio.displayName = "useGameAudio";
|
|
12768
12594
|
}
|
|
12769
12595
|
});
|
|
12770
12596
|
function useGameAudioContextOptional() {
|
|
12771
12597
|
return React105.useContext(GameAudioContext);
|
|
12772
12598
|
}
|
|
12773
|
-
function GameAudioProvider({
|
|
12774
|
-
manifest,
|
|
12775
|
-
baseUrl = "",
|
|
12776
|
-
children,
|
|
12777
|
-
initialMuted = false
|
|
12778
|
-
}) {
|
|
12779
|
-
const eventBus = useEventBus();
|
|
12780
|
-
const { play, stop, stopAll, playMusic, stopMusic, muted, setMuted, masterVolume, setMasterVolume } = useGameAudio({ manifest, baseUrl, initialMuted });
|
|
12781
|
-
React105.useEffect(() => {
|
|
12782
|
-
const unsubPlay = eventBus.on("UI:PLAY_SOUND", (event) => {
|
|
12783
|
-
const key = event.payload?.key;
|
|
12784
|
-
if (key) play(key);
|
|
12785
|
-
});
|
|
12786
|
-
const unsubStop = eventBus.on("UI:STOP_SOUND", (event) => {
|
|
12787
|
-
const key = event.payload?.key;
|
|
12788
|
-
if (key) {
|
|
12789
|
-
stop(key);
|
|
12790
|
-
} else {
|
|
12791
|
-
stopAll();
|
|
12792
|
-
}
|
|
12793
|
-
});
|
|
12794
|
-
const unsubChangeMusic = eventBus.on("UI:CHANGE_MUSIC", (event) => {
|
|
12795
|
-
const key = event.payload?.key;
|
|
12796
|
-
if (key) {
|
|
12797
|
-
playMusic(key);
|
|
12798
|
-
} else {
|
|
12799
|
-
stopMusic();
|
|
12800
|
-
}
|
|
12801
|
-
});
|
|
12802
|
-
const unsubStopMusic = eventBus.on("UI:STOP_MUSIC", () => {
|
|
12803
|
-
stopMusic();
|
|
12804
|
-
});
|
|
12805
|
-
return () => {
|
|
12806
|
-
unsubPlay();
|
|
12807
|
-
unsubStop();
|
|
12808
|
-
unsubChangeMusic();
|
|
12809
|
-
unsubStopMusic();
|
|
12810
|
-
};
|
|
12811
|
-
}, [eventBus, play, stop, stopAll, playMusic, stopMusic]);
|
|
12812
|
-
const value = { muted, setMuted, masterVolume, setMasterVolume, play, playMusic, stopMusic };
|
|
12813
|
-
return /* @__PURE__ */ jsxRuntime.jsx(GameAudioContext.Provider, { value, children });
|
|
12814
|
-
}
|
|
12815
12599
|
var GameAudioContext;
|
|
12816
12600
|
var init_GameAudioProvider = __esm({
|
|
12817
12601
|
"components/game/shared/providers/GameAudioProvider.tsx"() {
|
|
@@ -12820,7 +12604,6 @@ var init_GameAudioProvider = __esm({
|
|
|
12820
12604
|
init_useGameAudio();
|
|
12821
12605
|
GameAudioContext = React105.createContext(null);
|
|
12822
12606
|
GameAudioContext.displayName = "GameAudioContext";
|
|
12823
|
-
GameAudioProvider.displayName = "GameAudioProvider";
|
|
12824
12607
|
}
|
|
12825
12608
|
});
|
|
12826
12609
|
function GameAudioToggle({
|
|
@@ -20456,6 +20239,315 @@ var init_molecules = __esm({
|
|
|
20456
20239
|
init_puzzleObject();
|
|
20457
20240
|
}
|
|
20458
20241
|
});
|
|
20242
|
+
function resolveColor2(color, ctx, fallback) {
|
|
20243
|
+
if (!color) return fallback;
|
|
20244
|
+
if (color.startsWith("var(")) {
|
|
20245
|
+
ctx.canvas.style;
|
|
20246
|
+
const m = /^var\((--[^,)]+)(?:,\s*([^)]+))?\)$/.exec(color);
|
|
20247
|
+
if (m) {
|
|
20248
|
+
const computed = getComputedStyle(ctx.canvas).getPropertyValue(m[1]).trim();
|
|
20249
|
+
return computed || m[2]?.trim() || fallback;
|
|
20250
|
+
}
|
|
20251
|
+
}
|
|
20252
|
+
return color;
|
|
20253
|
+
}
|
|
20254
|
+
function shapeBounds(shape) {
|
|
20255
|
+
switch (shape.type) {
|
|
20256
|
+
case "line":
|
|
20257
|
+
case "arrow":
|
|
20258
|
+
if (shape.x1 == null || shape.y1 == null || shape.x2 == null || shape.y2 == null) return null;
|
|
20259
|
+
return {
|
|
20260
|
+
x: Math.min(shape.x1, shape.x2) - 6,
|
|
20261
|
+
y: Math.min(shape.y1, shape.y2) - 6,
|
|
20262
|
+
w: Math.abs(shape.x2 - shape.x1) + 12,
|
|
20263
|
+
h: Math.abs(shape.y2 - shape.y1) + 12
|
|
20264
|
+
};
|
|
20265
|
+
case "circle":
|
|
20266
|
+
if (shape.x == null || shape.y == null || shape.radius == null) return null;
|
|
20267
|
+
return {
|
|
20268
|
+
x: shape.x - shape.radius - 4,
|
|
20269
|
+
y: shape.y - shape.radius - 4,
|
|
20270
|
+
w: shape.radius * 2 + 8,
|
|
20271
|
+
h: shape.radius * 2 + 8
|
|
20272
|
+
};
|
|
20273
|
+
case "rect":
|
|
20274
|
+
if (shape.x == null || shape.y == null || shape.width == null || shape.height == null) return null;
|
|
20275
|
+
return { x: shape.x - 4, y: shape.y - 4, w: shape.width + 8, h: shape.height + 8 };
|
|
20276
|
+
case "polygon":
|
|
20277
|
+
if (!shape.points || shape.points.length === 0) return null;
|
|
20278
|
+
{
|
|
20279
|
+
const xs = shape.points.map((p2) => p2.x);
|
|
20280
|
+
const ys = shape.points.map((p2) => p2.y);
|
|
20281
|
+
const minX = Math.min(...xs);
|
|
20282
|
+
const minY = Math.min(...ys);
|
|
20283
|
+
return {
|
|
20284
|
+
x: minX - 4,
|
|
20285
|
+
y: minY - 4,
|
|
20286
|
+
w: Math.max(...xs) - minX + 8,
|
|
20287
|
+
h: Math.max(...ys) - minY + 8
|
|
20288
|
+
};
|
|
20289
|
+
}
|
|
20290
|
+
case "text":
|
|
20291
|
+
if (shape.x == null || shape.y == null) return null;
|
|
20292
|
+
return { x: shape.x - 4, y: shape.y - (shape.fontSize ?? 14) - 4, w: 120, h: (shape.fontSize ?? 14) + 8 };
|
|
20293
|
+
default:
|
|
20294
|
+
return null;
|
|
20295
|
+
}
|
|
20296
|
+
}
|
|
20297
|
+
function drawArrowHead(ctx, x1, y1, x2, y2, size) {
|
|
20298
|
+
const angle = Math.atan2(y2 - y1, x2 - x1);
|
|
20299
|
+
ctx.beginPath();
|
|
20300
|
+
ctx.moveTo(x2, y2);
|
|
20301
|
+
ctx.lineTo(x2 - size * Math.cos(angle - Math.PI / 6), y2 - size * Math.sin(angle - Math.PI / 6));
|
|
20302
|
+
ctx.lineTo(x2 - size * Math.cos(angle + Math.PI / 6), y2 - size * Math.sin(angle + Math.PI / 6));
|
|
20303
|
+
ctx.closePath();
|
|
20304
|
+
ctx.fill();
|
|
20305
|
+
}
|
|
20306
|
+
function drawShape(ctx, shape, width, height) {
|
|
20307
|
+
ctx.save();
|
|
20308
|
+
const opacity = shape.opacity ?? 1;
|
|
20309
|
+
ctx.globalAlpha = opacity;
|
|
20310
|
+
const stroke = resolveColor2(shape.color, ctx, "#333333");
|
|
20311
|
+
const fill = shape.fill ? resolveColor2(shape.fill, ctx, "#cccccc") : void 0;
|
|
20312
|
+
ctx.lineWidth = shape.lineWidth ?? 2;
|
|
20313
|
+
switch (shape.type) {
|
|
20314
|
+
case "grid": {
|
|
20315
|
+
const step = shape.step ?? 40;
|
|
20316
|
+
ctx.strokeStyle = stroke;
|
|
20317
|
+
ctx.globalAlpha = opacity * 0.25;
|
|
20318
|
+
ctx.lineWidth = 1;
|
|
20319
|
+
ctx.beginPath();
|
|
20320
|
+
for (let x = 0; x <= width; x += step) {
|
|
20321
|
+
ctx.moveTo(x, 0);
|
|
20322
|
+
ctx.lineTo(x, height);
|
|
20323
|
+
}
|
|
20324
|
+
for (let y = 0; y <= height; y += step) {
|
|
20325
|
+
ctx.moveTo(0, y);
|
|
20326
|
+
ctx.lineTo(width, y);
|
|
20327
|
+
}
|
|
20328
|
+
ctx.stroke();
|
|
20329
|
+
break;
|
|
20330
|
+
}
|
|
20331
|
+
case "axis": {
|
|
20332
|
+
const axis = shape.axis ?? "x";
|
|
20333
|
+
ctx.strokeStyle = stroke;
|
|
20334
|
+
ctx.lineWidth = shape.lineWidth ?? 2;
|
|
20335
|
+
ctx.beginPath();
|
|
20336
|
+
if (axis === "x") {
|
|
20337
|
+
ctx.moveTo(0, height / 2);
|
|
20338
|
+
ctx.lineTo(width, height / 2);
|
|
20339
|
+
} else {
|
|
20340
|
+
ctx.moveTo(width / 2, 0);
|
|
20341
|
+
ctx.lineTo(width / 2, height);
|
|
20342
|
+
}
|
|
20343
|
+
ctx.stroke();
|
|
20344
|
+
break;
|
|
20345
|
+
}
|
|
20346
|
+
case "line": {
|
|
20347
|
+
if (shape.x1 == null || shape.y1 == null || shape.x2 == null || shape.y2 == null) break;
|
|
20348
|
+
ctx.strokeStyle = stroke;
|
|
20349
|
+
ctx.beginPath();
|
|
20350
|
+
ctx.moveTo(shape.x1, shape.y1);
|
|
20351
|
+
ctx.lineTo(shape.x2, shape.y2);
|
|
20352
|
+
ctx.stroke();
|
|
20353
|
+
break;
|
|
20354
|
+
}
|
|
20355
|
+
case "arrow": {
|
|
20356
|
+
if (shape.x1 == null || shape.y1 == null || shape.x2 == null || shape.y2 == null) break;
|
|
20357
|
+
ctx.strokeStyle = stroke;
|
|
20358
|
+
ctx.fillStyle = stroke;
|
|
20359
|
+
ctx.beginPath();
|
|
20360
|
+
ctx.moveTo(shape.x1, shape.y1);
|
|
20361
|
+
ctx.lineTo(shape.x2, shape.y2);
|
|
20362
|
+
ctx.stroke();
|
|
20363
|
+
drawArrowHead(ctx, shape.x1, shape.y1, shape.x2, shape.y2, 10);
|
|
20364
|
+
break;
|
|
20365
|
+
}
|
|
20366
|
+
case "circle": {
|
|
20367
|
+
if (shape.x == null || shape.y == null || shape.radius == null) break;
|
|
20368
|
+
ctx.beginPath();
|
|
20369
|
+
ctx.arc(shape.x, shape.y, shape.radius, 0, Math.PI * 2);
|
|
20370
|
+
if (fill) {
|
|
20371
|
+
ctx.fillStyle = fill;
|
|
20372
|
+
ctx.fill();
|
|
20373
|
+
}
|
|
20374
|
+
ctx.strokeStyle = stroke;
|
|
20375
|
+
ctx.stroke();
|
|
20376
|
+
break;
|
|
20377
|
+
}
|
|
20378
|
+
case "rect": {
|
|
20379
|
+
if (shape.x == null || shape.y == null || shape.width == null || shape.height == null) break;
|
|
20380
|
+
if (fill) {
|
|
20381
|
+
ctx.fillStyle = fill;
|
|
20382
|
+
ctx.fillRect(shape.x, shape.y, shape.width, shape.height);
|
|
20383
|
+
}
|
|
20384
|
+
ctx.strokeStyle = stroke;
|
|
20385
|
+
ctx.strokeRect(shape.x, shape.y, shape.width, shape.height);
|
|
20386
|
+
break;
|
|
20387
|
+
}
|
|
20388
|
+
case "polygon": {
|
|
20389
|
+
if (!shape.points || shape.points.length < 2) break;
|
|
20390
|
+
ctx.beginPath();
|
|
20391
|
+
ctx.moveTo(shape.points[0].x, shape.points[0].y);
|
|
20392
|
+
for (let i = 1; i < shape.points.length; i++) {
|
|
20393
|
+
ctx.lineTo(shape.points[i].x, shape.points[i].y);
|
|
20394
|
+
}
|
|
20395
|
+
ctx.closePath();
|
|
20396
|
+
if (fill) {
|
|
20397
|
+
ctx.fillStyle = fill;
|
|
20398
|
+
ctx.fill();
|
|
20399
|
+
}
|
|
20400
|
+
ctx.strokeStyle = stroke;
|
|
20401
|
+
ctx.stroke();
|
|
20402
|
+
break;
|
|
20403
|
+
}
|
|
20404
|
+
case "path": {
|
|
20405
|
+
if (!shape.path) break;
|
|
20406
|
+
const p2 = new Path2D(shape.path);
|
|
20407
|
+
if (fill) {
|
|
20408
|
+
ctx.fillStyle = fill;
|
|
20409
|
+
ctx.fill(p2);
|
|
20410
|
+
}
|
|
20411
|
+
ctx.strokeStyle = stroke;
|
|
20412
|
+
ctx.stroke(p2);
|
|
20413
|
+
break;
|
|
20414
|
+
}
|
|
20415
|
+
case "text": {
|
|
20416
|
+
if (shape.x == null || shape.y == null || !shape.text) break;
|
|
20417
|
+
ctx.fillStyle = stroke;
|
|
20418
|
+
ctx.font = `${shape.fontSize ?? 14}px system-ui, sans-serif`;
|
|
20419
|
+
ctx.textAlign = shape.align ?? "left";
|
|
20420
|
+
ctx.textBaseline = "middle";
|
|
20421
|
+
ctx.fillText(shape.text, shape.x, shape.y);
|
|
20422
|
+
break;
|
|
20423
|
+
}
|
|
20424
|
+
}
|
|
20425
|
+
ctx.restore();
|
|
20426
|
+
}
|
|
20427
|
+
var LearningCanvas;
|
|
20428
|
+
var init_LearningCanvas = __esm({
|
|
20429
|
+
"components/learning/atoms/LearningCanvas.tsx"() {
|
|
20430
|
+
"use client";
|
|
20431
|
+
init_cn();
|
|
20432
|
+
init_useEventBus();
|
|
20433
|
+
LearningCanvas = ({
|
|
20434
|
+
className,
|
|
20435
|
+
width = 600,
|
|
20436
|
+
height = 400,
|
|
20437
|
+
backgroundColor,
|
|
20438
|
+
shapes = [],
|
|
20439
|
+
interactive = false,
|
|
20440
|
+
animate = false,
|
|
20441
|
+
onShapeClick,
|
|
20442
|
+
onShapeHover,
|
|
20443
|
+
isLoading,
|
|
20444
|
+
error
|
|
20445
|
+
}) => {
|
|
20446
|
+
const canvasRef = React105.useRef(null);
|
|
20447
|
+
const eventBus = useEventBus();
|
|
20448
|
+
const animRef = React105.useRef(0);
|
|
20449
|
+
const hoverIndexRef = React105.useRef(-1);
|
|
20450
|
+
const findShapeAt = React105.useCallback((clientX, clientY) => {
|
|
20451
|
+
const canvas = canvasRef.current;
|
|
20452
|
+
if (!canvas) return -1;
|
|
20453
|
+
const rect = canvas.getBoundingClientRect();
|
|
20454
|
+
const x = clientX - rect.left;
|
|
20455
|
+
const y = clientY - rect.top;
|
|
20456
|
+
for (let i = shapes.length - 1; i >= 0; i--) {
|
|
20457
|
+
const b = shapeBounds(shapes[i]);
|
|
20458
|
+
if (b && x >= b.x && x <= b.x + b.w && y >= b.y && y <= b.y + b.h) {
|
|
20459
|
+
return i;
|
|
20460
|
+
}
|
|
20461
|
+
}
|
|
20462
|
+
return -1;
|
|
20463
|
+
}, [shapes]);
|
|
20464
|
+
const draw = React105.useCallback(() => {
|
|
20465
|
+
const canvas = canvasRef.current;
|
|
20466
|
+
if (!canvas) return;
|
|
20467
|
+
const ctx = canvas.getContext("2d");
|
|
20468
|
+
if (!ctx) return;
|
|
20469
|
+
const dpr = typeof window !== "undefined" ? window.devicePixelRatio || 1 : 1;
|
|
20470
|
+
canvas.width = Math.max(1, Math.floor(width * dpr));
|
|
20471
|
+
canvas.height = Math.max(1, Math.floor(height * dpr));
|
|
20472
|
+
canvas.style.width = `${width}px`;
|
|
20473
|
+
canvas.style.height = `${height}px`;
|
|
20474
|
+
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
|
|
20475
|
+
ctx.clearRect(0, 0, width, height);
|
|
20476
|
+
if (backgroundColor) {
|
|
20477
|
+
ctx.fillStyle = backgroundColor;
|
|
20478
|
+
ctx.fillRect(0, 0, width, height);
|
|
20479
|
+
}
|
|
20480
|
+
for (const shape of shapes) {
|
|
20481
|
+
drawShape(ctx, shape, width, height);
|
|
20482
|
+
}
|
|
20483
|
+
}, [width, height, backgroundColor, shapes]);
|
|
20484
|
+
React105.useEffect(() => {
|
|
20485
|
+
draw();
|
|
20486
|
+
}, [draw]);
|
|
20487
|
+
React105.useEffect(() => {
|
|
20488
|
+
if (!animate) return;
|
|
20489
|
+
const loop = () => {
|
|
20490
|
+
draw();
|
|
20491
|
+
animRef.current = requestAnimationFrame(loop);
|
|
20492
|
+
};
|
|
20493
|
+
animRef.current = requestAnimationFrame(loop);
|
|
20494
|
+
return () => cancelAnimationFrame(animRef.current);
|
|
20495
|
+
}, [animate, draw]);
|
|
20496
|
+
const handlePointerMove = React105.useCallback(
|
|
20497
|
+
(e) => {
|
|
20498
|
+
if (!interactive) return;
|
|
20499
|
+
const idx = findShapeAt(e.clientX, e.clientY);
|
|
20500
|
+
if (idx !== hoverIndexRef.current) {
|
|
20501
|
+
hoverIndexRef.current = idx;
|
|
20502
|
+
if (idx >= 0) {
|
|
20503
|
+
const shape = shapes[idx];
|
|
20504
|
+
const payload = { id: shape.id, type: shape.type, index: idx };
|
|
20505
|
+
if (onShapeHover) onShapeHover(payload);
|
|
20506
|
+
else if (eventBus) eventBus.emit(`UI:SHAPE_HOVER`, payload);
|
|
20507
|
+
}
|
|
20508
|
+
}
|
|
20509
|
+
},
|
|
20510
|
+
[interactive, onShapeHover, eventBus, findShapeAt, shapes]
|
|
20511
|
+
);
|
|
20512
|
+
const handleClick = React105.useCallback(
|
|
20513
|
+
(e) => {
|
|
20514
|
+
if (!interactive) return;
|
|
20515
|
+
const idx = findShapeAt(e.clientX, e.clientY);
|
|
20516
|
+
if (idx >= 0) {
|
|
20517
|
+
const shape = shapes[idx];
|
|
20518
|
+
const payload = { id: shape.id, type: shape.type, index: idx };
|
|
20519
|
+
if (onShapeClick) onShapeClick(payload);
|
|
20520
|
+
else if (eventBus) eventBus.emit(`UI:SHAPE_CLICK`, payload);
|
|
20521
|
+
}
|
|
20522
|
+
},
|
|
20523
|
+
[interactive, onShapeClick, eventBus, findShapeAt, shapes]
|
|
20524
|
+
);
|
|
20525
|
+
if (isLoading || error) {
|
|
20526
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
20527
|
+
"div",
|
|
20528
|
+
{
|
|
20529
|
+
className: cn(
|
|
20530
|
+
"flex items-center justify-center rounded border border-border bg-surface",
|
|
20531
|
+
className
|
|
20532
|
+
),
|
|
20533
|
+
style: { width, height },
|
|
20534
|
+
children: error ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-destructive", children: error.message }) : /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-muted-foreground", children: "Loading canvas\u2026" })
|
|
20535
|
+
}
|
|
20536
|
+
);
|
|
20537
|
+
}
|
|
20538
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
20539
|
+
"canvas",
|
|
20540
|
+
{
|
|
20541
|
+
ref: canvasRef,
|
|
20542
|
+
className: cn("block touch-none rounded border border-border", className),
|
|
20543
|
+
style: { width, height },
|
|
20544
|
+
onClick: handleClick,
|
|
20545
|
+
onPointerMove: handlePointerMove
|
|
20546
|
+
}
|
|
20547
|
+
);
|
|
20548
|
+
};
|
|
20549
|
+
}
|
|
20550
|
+
});
|
|
20459
20551
|
|
|
20460
20552
|
// components/core/atoms/index.ts
|
|
20461
20553
|
var init_atoms = __esm({
|
|
@@ -21941,91 +22033,6 @@ var init_ComponentPatterns = __esm({
|
|
|
21941
22033
|
AlertPattern.displayName = "AlertPattern";
|
|
21942
22034
|
}
|
|
21943
22035
|
});
|
|
21944
|
-
function parseValue(value) {
|
|
21945
|
-
if (value === "" || value == null) return { num: 0, prefix: "", suffix: "", decimals: 0 };
|
|
21946
|
-
const match = String(value).match(/^([^0-9]*)([0-9]+(?:\.[0-9]+)?)(.*)$/);
|
|
21947
|
-
if (!match) {
|
|
21948
|
-
return { num: 0, prefix: "", suffix: String(value), decimals: 0 };
|
|
21949
|
-
}
|
|
21950
|
-
const numStr = match[2];
|
|
21951
|
-
const decimalIdx = numStr.indexOf(".");
|
|
21952
|
-
const decimals = decimalIdx >= 0 ? numStr.length - decimalIdx - 1 : 0;
|
|
21953
|
-
return {
|
|
21954
|
-
prefix: match[1],
|
|
21955
|
-
num: parseFloat(numStr),
|
|
21956
|
-
suffix: match[3],
|
|
21957
|
-
decimals
|
|
21958
|
-
};
|
|
21959
|
-
}
|
|
21960
|
-
var AnimatedCounter;
|
|
21961
|
-
var init_AnimatedCounter = __esm({
|
|
21962
|
-
"components/core/molecules/AnimatedCounter.tsx"() {
|
|
21963
|
-
"use client";
|
|
21964
|
-
init_cn();
|
|
21965
|
-
init_Box();
|
|
21966
|
-
init_Typography();
|
|
21967
|
-
AnimatedCounter = ({
|
|
21968
|
-
value,
|
|
21969
|
-
label,
|
|
21970
|
-
duration = 1500,
|
|
21971
|
-
className
|
|
21972
|
-
}) => {
|
|
21973
|
-
const ref = React105.useRef(null);
|
|
21974
|
-
const [displayValue, setDisplayValue] = React105.useState("0");
|
|
21975
|
-
const [hasAnimated, setHasAnimated] = React105.useState(false);
|
|
21976
|
-
const animate = React105.useCallback(() => {
|
|
21977
|
-
const { num: num2, prefix, suffix, decimals } = parseValue(value);
|
|
21978
|
-
if (num2 === 0) {
|
|
21979
|
-
setDisplayValue(String(value));
|
|
21980
|
-
return;
|
|
21981
|
-
}
|
|
21982
|
-
const startTime = performance.now();
|
|
21983
|
-
const tick = (now2) => {
|
|
21984
|
-
const elapsed = now2 - startTime;
|
|
21985
|
-
const progress = Math.min(elapsed / duration, 1);
|
|
21986
|
-
const eased = 1 - Math.pow(1 - progress, 3);
|
|
21987
|
-
const current = eased * num2;
|
|
21988
|
-
setDisplayValue(`${prefix}${current.toFixed(decimals)}${suffix}`);
|
|
21989
|
-
if (progress < 1) {
|
|
21990
|
-
requestAnimationFrame(tick);
|
|
21991
|
-
} else {
|
|
21992
|
-
setDisplayValue(String(value));
|
|
21993
|
-
}
|
|
21994
|
-
};
|
|
21995
|
-
requestAnimationFrame(tick);
|
|
21996
|
-
}, [value, duration]);
|
|
21997
|
-
React105.useEffect(() => {
|
|
21998
|
-
if (hasAnimated) return;
|
|
21999
|
-
const el = ref.current;
|
|
22000
|
-
if (!el) return;
|
|
22001
|
-
const observer2 = new IntersectionObserver(
|
|
22002
|
-
(entries) => {
|
|
22003
|
-
if (entries[0].isIntersecting) {
|
|
22004
|
-
setHasAnimated(true);
|
|
22005
|
-
animate();
|
|
22006
|
-
observer2.disconnect();
|
|
22007
|
-
}
|
|
22008
|
-
},
|
|
22009
|
-
{ threshold: 0.3 }
|
|
22010
|
-
);
|
|
22011
|
-
observer2.observe(el);
|
|
22012
|
-
return () => observer2.disconnect();
|
|
22013
|
-
}, [hasAnimated, animate]);
|
|
22014
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(Box, { ref, className: cn("flex flex-col items-center gap-1 p-4", className), children: [
|
|
22015
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
22016
|
-
Typography,
|
|
22017
|
-
{
|
|
22018
|
-
variant: "h2",
|
|
22019
|
-
className: "text-primary font-bold tabular-nums",
|
|
22020
|
-
children: hasAnimated ? displayValue : "0"
|
|
22021
|
-
}
|
|
22022
|
-
),
|
|
22023
|
-
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", color: "muted", className: "text-center", children: label })
|
|
22024
|
-
] });
|
|
22025
|
-
};
|
|
22026
|
-
AnimatedCounter.displayName = "AnimatedCounter";
|
|
22027
|
-
}
|
|
22028
|
-
});
|
|
22029
22036
|
var AuthLayout;
|
|
22030
22037
|
var init_AuthLayout = __esm({
|
|
22031
22038
|
"components/marketing/templates/AuthLayout.tsx"() {
|
|
@@ -22994,10 +23001,107 @@ var init_BehaviorView = __esm({
|
|
|
22994
23001
|
BehaviorView.displayName = "BehaviorView";
|
|
22995
23002
|
}
|
|
22996
23003
|
});
|
|
23004
|
+
var BiologyCanvas;
|
|
23005
|
+
var init_BiologyCanvas = __esm({
|
|
23006
|
+
"components/learning/molecules/BiologyCanvas.tsx"() {
|
|
23007
|
+
"use client";
|
|
23008
|
+
init_atoms();
|
|
23009
|
+
init_Stack();
|
|
23010
|
+
init_LearningCanvas();
|
|
23011
|
+
BiologyCanvas = ({
|
|
23012
|
+
className,
|
|
23013
|
+
width = 600,
|
|
23014
|
+
height = 400,
|
|
23015
|
+
title,
|
|
23016
|
+
backgroundColor,
|
|
23017
|
+
nodes = [],
|
|
23018
|
+
edges = [],
|
|
23019
|
+
shapes = [],
|
|
23020
|
+
interactive = false,
|
|
23021
|
+
animate = false,
|
|
23022
|
+
onShapeClick,
|
|
23023
|
+
isLoading,
|
|
23024
|
+
error
|
|
23025
|
+
}) => {
|
|
23026
|
+
const derivedShapes = React105.useMemo(() => {
|
|
23027
|
+
const out = [];
|
|
23028
|
+
const nodeById = /* @__PURE__ */ new Map();
|
|
23029
|
+
for (const n of nodes) {
|
|
23030
|
+
if (n.id) nodeById.set(n.id, n);
|
|
23031
|
+
}
|
|
23032
|
+
for (const e of edges) {
|
|
23033
|
+
const a = nodeById.get(e.from);
|
|
23034
|
+
const b = nodeById.get(e.to);
|
|
23035
|
+
if (!a || !b) continue;
|
|
23036
|
+
out.push({
|
|
23037
|
+
type: "line",
|
|
23038
|
+
x1: a.x,
|
|
23039
|
+
y1: a.y,
|
|
23040
|
+
x2: b.x,
|
|
23041
|
+
y2: b.y,
|
|
23042
|
+
color: e.color ?? "#9ca3af",
|
|
23043
|
+
lineWidth: 2
|
|
23044
|
+
});
|
|
23045
|
+
if (e.label) {
|
|
23046
|
+
out.push({
|
|
23047
|
+
type: "text",
|
|
23048
|
+
x: (a.x + b.x) / 2 + 4,
|
|
23049
|
+
y: (a.y + b.y) / 2 - 4,
|
|
23050
|
+
text: e.label,
|
|
23051
|
+
color: "#374151",
|
|
23052
|
+
fontSize: 11
|
|
23053
|
+
});
|
|
23054
|
+
}
|
|
23055
|
+
}
|
|
23056
|
+
for (const n of nodes) {
|
|
23057
|
+
out.push({
|
|
23058
|
+
type: "circle",
|
|
23059
|
+
x: n.x,
|
|
23060
|
+
y: n.y,
|
|
23061
|
+
radius: n.radius ?? 16,
|
|
23062
|
+
color: n.color ?? "#16a34a",
|
|
23063
|
+
fill: `${n.color ?? "#16a34a"}33`,
|
|
23064
|
+
id: n.id
|
|
23065
|
+
});
|
|
23066
|
+
if (n.label) {
|
|
23067
|
+
out.push({
|
|
23068
|
+
type: "text",
|
|
23069
|
+
x: n.x,
|
|
23070
|
+
y: n.y + (n.radius ?? 16) + 14,
|
|
23071
|
+
text: n.label,
|
|
23072
|
+
color: "#111827",
|
|
23073
|
+
fontSize: 12,
|
|
23074
|
+
align: "center"
|
|
23075
|
+
});
|
|
23076
|
+
}
|
|
23077
|
+
}
|
|
23078
|
+
out.push(...shapes);
|
|
23079
|
+
return out;
|
|
23080
|
+
}, [nodes, edges, shapes]);
|
|
23081
|
+
return /* @__PURE__ */ jsxRuntime.jsx(Card, { className, children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "sm", children: [
|
|
23082
|
+
title ? /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h4", children: title }) : null,
|
|
23083
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
23084
|
+
LearningCanvas,
|
|
23085
|
+
{
|
|
23086
|
+
width,
|
|
23087
|
+
height,
|
|
23088
|
+
backgroundColor,
|
|
23089
|
+
shapes: derivedShapes,
|
|
23090
|
+
interactive,
|
|
23091
|
+
animate,
|
|
23092
|
+
onShapeClick,
|
|
23093
|
+
isLoading,
|
|
23094
|
+
error
|
|
23095
|
+
}
|
|
23096
|
+
)
|
|
23097
|
+
] }) });
|
|
23098
|
+
};
|
|
23099
|
+
}
|
|
23100
|
+
});
|
|
22997
23101
|
|
|
22998
|
-
// node_modules
|
|
23102
|
+
// node_modules/katex/dist/katex.min.css
|
|
22999
23103
|
var init_katex_min = __esm({
|
|
23000
|
-
"node_modules
|
|
23104
|
+
"node_modules/katex/dist/katex.min.css"() {
|
|
23001
23105
|
}
|
|
23002
23106
|
});
|
|
23003
23107
|
var Tabs;
|
|
@@ -27340,129 +27444,6 @@ var init_Breadcrumb = __esm({
|
|
|
27340
27444
|
Breadcrumb.displayName = "Breadcrumb";
|
|
27341
27445
|
}
|
|
27342
27446
|
});
|
|
27343
|
-
function useSafeEventBus2() {
|
|
27344
|
-
try {
|
|
27345
|
-
return useEventBus();
|
|
27346
|
-
} catch {
|
|
27347
|
-
return { emit: () => {
|
|
27348
|
-
}, on: () => () => {
|
|
27349
|
-
}, once: () => {
|
|
27350
|
-
} };
|
|
27351
|
-
}
|
|
27352
|
-
}
|
|
27353
|
-
var log6, lookStyles4, ButtonGroup;
|
|
27354
|
-
var init_ButtonGroup = __esm({
|
|
27355
|
-
"components/core/molecules/ButtonGroup.tsx"() {
|
|
27356
|
-
"use client";
|
|
27357
|
-
init_cn();
|
|
27358
|
-
init_atoms();
|
|
27359
|
-
init_useEventBus();
|
|
27360
|
-
log6 = logger.createLogger("almadar:ui:button-group");
|
|
27361
|
-
lookStyles4 = {
|
|
27362
|
-
"right-aligned-buttons": "",
|
|
27363
|
-
"floating-bar": "fixed bottom-section left-1/2 -translate-x-1/2 shadow-elevation-toast bg-card p-card-sm rounded-container",
|
|
27364
|
-
"inline-row": "gap-2 inline-flex",
|
|
27365
|
-
"dropdown-menu": "[&>button:not(:first-child)]:hidden",
|
|
27366
|
-
"command-palette-trigger": "[&>button:not(:first-child)]:hidden"
|
|
27367
|
-
};
|
|
27368
|
-
ButtonGroup = ({
|
|
27369
|
-
children,
|
|
27370
|
-
primary,
|
|
27371
|
-
secondary,
|
|
27372
|
-
variant = "default",
|
|
27373
|
-
orientation = "horizontal",
|
|
27374
|
-
className,
|
|
27375
|
-
// Filter-group pattern props (entity and filters are used for schema-driven filtering)
|
|
27376
|
-
entity: _entity,
|
|
27377
|
-
filters,
|
|
27378
|
-
look = "right-aligned-buttons"
|
|
27379
|
-
}) => {
|
|
27380
|
-
const eventBus = useSafeEventBus2();
|
|
27381
|
-
const variantClasses2 = {
|
|
27382
|
-
default: "gap-0",
|
|
27383
|
-
segmented: "gap-0 [&>button]:rounded-none [&>button:first-child]:rounded-l-lg [&>button:last-child]:rounded-r-lg [&>button:not(:first-child)]:border-l-0",
|
|
27384
|
-
toggle: "gap-0 [&>button]:rounded-none [&>button:first-child]:rounded-l-lg [&>button:last-child]:rounded-r-lg [&>button:not(:first-child)]:border-l-0"
|
|
27385
|
-
};
|
|
27386
|
-
const orientationClasses = {
|
|
27387
|
-
horizontal: "flex-row",
|
|
27388
|
-
vertical: "flex-col [&>button:first-child]:rounded-t-lg [&>button:last-child]:rounded-b-lg [&>button:not(:first-child)]:border-t-0 [&>button:not(:first-child)]:border-l"
|
|
27389
|
-
};
|
|
27390
|
-
const handleActionClick = (action) => {
|
|
27391
|
-
if (action.event) {
|
|
27392
|
-
eventBus.emit("UI:DISPATCH", { event: action.event });
|
|
27393
|
-
}
|
|
27394
|
-
if (action.navigatesTo) {
|
|
27395
|
-
eventBus.emit("UI:NAVIGATE", { url: action.navigatesTo });
|
|
27396
|
-
}
|
|
27397
|
-
};
|
|
27398
|
-
const renderFormActions = () => {
|
|
27399
|
-
const buttons = [];
|
|
27400
|
-
if (secondary) {
|
|
27401
|
-
secondary.forEach((action, index) => {
|
|
27402
|
-
buttons.push(
|
|
27403
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
27404
|
-
Button,
|
|
27405
|
-
{
|
|
27406
|
-
type: action.actionType === "submit" ? "submit" : "button",
|
|
27407
|
-
variant: action.variant || "ghost",
|
|
27408
|
-
onClick: () => handleActionClick(action),
|
|
27409
|
-
children: action.label
|
|
27410
|
-
},
|
|
27411
|
-
`secondary-${index}`
|
|
27412
|
-
)
|
|
27413
|
-
);
|
|
27414
|
-
});
|
|
27415
|
-
}
|
|
27416
|
-
if (primary) {
|
|
27417
|
-
const isSubmit = primary.actionType === "submit";
|
|
27418
|
-
buttons.push(
|
|
27419
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
27420
|
-
Button,
|
|
27421
|
-
{
|
|
27422
|
-
type: isSubmit ? "submit" : "button",
|
|
27423
|
-
variant: primary.variant || "primary",
|
|
27424
|
-
onClick: () => handleActionClick(primary),
|
|
27425
|
-
"data-testid": isSubmit ? "form-submit" : void 0,
|
|
27426
|
-
children: primary.label
|
|
27427
|
-
},
|
|
27428
|
-
"primary"
|
|
27429
|
-
)
|
|
27430
|
-
);
|
|
27431
|
-
}
|
|
27432
|
-
return buttons;
|
|
27433
|
-
};
|
|
27434
|
-
const renderFilters = () => {
|
|
27435
|
-
if (!filters || filters.length === 0) return null;
|
|
27436
|
-
return filters.map((filter, index) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
27437
|
-
Button,
|
|
27438
|
-
{
|
|
27439
|
-
variant: "ghost",
|
|
27440
|
-
onClick: () => {
|
|
27441
|
-
log6.debug("Filter clicked", { field: filter.field });
|
|
27442
|
-
},
|
|
27443
|
-
children: filter.label
|
|
27444
|
-
},
|
|
27445
|
-
`filter-${filter.field}-${index}`
|
|
27446
|
-
));
|
|
27447
|
-
};
|
|
27448
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
27449
|
-
"div",
|
|
27450
|
-
{
|
|
27451
|
-
className: cn(
|
|
27452
|
-
"inline-flex gap-2",
|
|
27453
|
-
variantClasses2[variant],
|
|
27454
|
-
orientationClasses[orientation],
|
|
27455
|
-
lookStyles4[look],
|
|
27456
|
-
className
|
|
27457
|
-
),
|
|
27458
|
-
role: "group",
|
|
27459
|
-
children: children || renderFilters() || renderFormActions()
|
|
27460
|
-
}
|
|
27461
|
-
);
|
|
27462
|
-
};
|
|
27463
|
-
ButtonGroup.displayName = "ButtonGroup";
|
|
27464
|
-
}
|
|
27465
|
-
});
|
|
27466
27447
|
function dayWindowForViewport(width) {
|
|
27467
27448
|
if (width <= 640) return 1;
|
|
27468
27449
|
if (width <= 1024) return 3;
|
|
@@ -28205,7 +28186,7 @@ var init_CardGrid = __esm({
|
|
|
28205
28186
|
CardGrid.displayName = "CardGrid";
|
|
28206
28187
|
}
|
|
28207
28188
|
});
|
|
28208
|
-
function
|
|
28189
|
+
function useSafeEventBus2() {
|
|
28209
28190
|
try {
|
|
28210
28191
|
return useEventBus();
|
|
28211
28192
|
} catch {
|
|
@@ -28241,7 +28222,7 @@ var init_Carousel = __esm({
|
|
|
28241
28222
|
const [activeIndex, setActiveIndex] = React105.useState(0);
|
|
28242
28223
|
const scrollRef = React105.useRef(null);
|
|
28243
28224
|
const autoPlayRef = React105.useRef(null);
|
|
28244
|
-
const eventBus =
|
|
28225
|
+
const eventBus = useSafeEventBus2();
|
|
28245
28226
|
const { t } = hooks.useTranslate();
|
|
28246
28227
|
const safeItems = items ?? [];
|
|
28247
28228
|
const totalSlides = safeItems.length;
|
|
@@ -29489,6 +29470,122 @@ var init_ChatBar = __esm({
|
|
|
29489
29470
|
ChatBar.displayName = "ChatBar";
|
|
29490
29471
|
}
|
|
29491
29472
|
});
|
|
29473
|
+
var ChemistryCanvas;
|
|
29474
|
+
var init_ChemistryCanvas = __esm({
|
|
29475
|
+
"components/learning/molecules/ChemistryCanvas.tsx"() {
|
|
29476
|
+
"use client";
|
|
29477
|
+
init_atoms();
|
|
29478
|
+
init_Stack();
|
|
29479
|
+
init_LearningCanvas();
|
|
29480
|
+
ChemistryCanvas = ({
|
|
29481
|
+
className,
|
|
29482
|
+
width = 600,
|
|
29483
|
+
height = 400,
|
|
29484
|
+
title,
|
|
29485
|
+
backgroundColor,
|
|
29486
|
+
atoms = [],
|
|
29487
|
+
bonds = [],
|
|
29488
|
+
arrows = [],
|
|
29489
|
+
shapes = [],
|
|
29490
|
+
interactive = false,
|
|
29491
|
+
animate = false,
|
|
29492
|
+
onShapeClick,
|
|
29493
|
+
isLoading,
|
|
29494
|
+
error
|
|
29495
|
+
}) => {
|
|
29496
|
+
const derivedShapes = React105.useMemo(() => {
|
|
29497
|
+
const out = [];
|
|
29498
|
+
const atomById = /* @__PURE__ */ new Map();
|
|
29499
|
+
for (const a of atoms) {
|
|
29500
|
+
if (a.id) atomById.set(a.id, a);
|
|
29501
|
+
}
|
|
29502
|
+
for (const b of bonds) {
|
|
29503
|
+
const a = atomById.get(b.from);
|
|
29504
|
+
const c = atomById.get(b.to);
|
|
29505
|
+
if (!a || !c) continue;
|
|
29506
|
+
const color = b.color ?? "#6b7280";
|
|
29507
|
+
const strokeWidth = b.type === "double" ? 4 : b.type === "triple" ? 6 : 2;
|
|
29508
|
+
out.push({
|
|
29509
|
+
type: "line",
|
|
29510
|
+
x1: a.x,
|
|
29511
|
+
y1: a.y,
|
|
29512
|
+
x2: c.x,
|
|
29513
|
+
y2: c.y,
|
|
29514
|
+
color,
|
|
29515
|
+
lineWidth: strokeWidth
|
|
29516
|
+
});
|
|
29517
|
+
}
|
|
29518
|
+
for (const a of arrows) {
|
|
29519
|
+
const angle = (a.angle ?? 0) * (Math.PI / 180);
|
|
29520
|
+
const len = a.length ?? 60;
|
|
29521
|
+
const x2 = a.x + Math.cos(angle) * len;
|
|
29522
|
+
const y2 = a.y + Math.sin(angle) * len;
|
|
29523
|
+
out.push({
|
|
29524
|
+
type: "arrow",
|
|
29525
|
+
x1: a.x,
|
|
29526
|
+
y1: a.y,
|
|
29527
|
+
x2,
|
|
29528
|
+
y2,
|
|
29529
|
+
color: a.color ?? "#dc2626",
|
|
29530
|
+
lineWidth: 2
|
|
29531
|
+
});
|
|
29532
|
+
if (a.label) {
|
|
29533
|
+
out.push({
|
|
29534
|
+
type: "text",
|
|
29535
|
+
x: (a.x + x2) / 2,
|
|
29536
|
+
y: (a.y + y2) / 2 - 10,
|
|
29537
|
+
text: a.label,
|
|
29538
|
+
color: "#111827",
|
|
29539
|
+
fontSize: 12,
|
|
29540
|
+
align: "center"
|
|
29541
|
+
});
|
|
29542
|
+
}
|
|
29543
|
+
}
|
|
29544
|
+
for (const a of atoms) {
|
|
29545
|
+
out.push({
|
|
29546
|
+
type: "circle",
|
|
29547
|
+
x: a.x,
|
|
29548
|
+
y: a.y,
|
|
29549
|
+
radius: a.radius ?? 14,
|
|
29550
|
+
color: a.color ?? "#2563eb",
|
|
29551
|
+
fill: a.color ?? "#2563eb",
|
|
29552
|
+
id: a.id
|
|
29553
|
+
});
|
|
29554
|
+
if (a.element) {
|
|
29555
|
+
out.push({
|
|
29556
|
+
type: "text",
|
|
29557
|
+
x: a.x,
|
|
29558
|
+
y: a.y,
|
|
29559
|
+
text: a.element,
|
|
29560
|
+
color: "#ffffff",
|
|
29561
|
+
fontSize: 12,
|
|
29562
|
+
align: "center"
|
|
29563
|
+
});
|
|
29564
|
+
}
|
|
29565
|
+
}
|
|
29566
|
+
out.push(...shapes);
|
|
29567
|
+
return out;
|
|
29568
|
+
}, [atoms, bonds, arrows, shapes]);
|
|
29569
|
+
return /* @__PURE__ */ jsxRuntime.jsx(Card, { className, children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "sm", children: [
|
|
29570
|
+
title ? /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h4", children: title }) : null,
|
|
29571
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
29572
|
+
LearningCanvas,
|
|
29573
|
+
{
|
|
29574
|
+
width,
|
|
29575
|
+
height,
|
|
29576
|
+
backgroundColor,
|
|
29577
|
+
shapes: derivedShapes,
|
|
29578
|
+
interactive,
|
|
29579
|
+
animate,
|
|
29580
|
+
onShapeClick,
|
|
29581
|
+
isLoading,
|
|
29582
|
+
error
|
|
29583
|
+
}
|
|
29584
|
+
)
|
|
29585
|
+
] }) });
|
|
29586
|
+
};
|
|
29587
|
+
}
|
|
29588
|
+
});
|
|
29492
29589
|
var CodeRunnerPanel;
|
|
29493
29590
|
var init_CodeRunnerPanel = __esm({
|
|
29494
29591
|
"components/core/organisms/CodeRunnerPanel.tsx"() {
|
|
@@ -31563,7 +31660,7 @@ function DataGrid({
|
|
|
31563
31660
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
31564
31661
|
Box,
|
|
31565
31662
|
{
|
|
31566
|
-
className: cn("grid", gapStyles5[gap], scrollX ? "grid-flow-col overflow-x-auto" : colsClass,
|
|
31663
|
+
className: cn("grid", gapStyles5[gap], scrollX ? "grid-flow-col overflow-x-auto" : colsClass, lookStyles4[look], className),
|
|
31567
31664
|
style: scrollX ? { gridAutoFlow: "column", gridAutoColumns: `minmax(${minCardWidth}px, 1fr)` } : gridTemplateColumns ? { gridTemplateColumns } : void 0,
|
|
31568
31665
|
children: data.map((item, index) => {
|
|
31569
31666
|
const itemData = item;
|
|
@@ -31758,7 +31855,7 @@ function DataGrid({
|
|
|
31758
31855
|
] })
|
|
31759
31856
|
);
|
|
31760
31857
|
}
|
|
31761
|
-
var dataGridLog, BADGE_VARIANTS, gapStyles5,
|
|
31858
|
+
var dataGridLog, BADGE_VARIANTS, gapStyles5, lookStyles4;
|
|
31762
31859
|
var init_DataGrid = __esm({
|
|
31763
31860
|
"components/core/molecules/DataGrid.tsx"() {
|
|
31764
31861
|
"use client";
|
|
@@ -31793,7 +31890,7 @@ var init_DataGrid = __esm({
|
|
|
31793
31890
|
lg: "gap-6",
|
|
31794
31891
|
xl: "gap-8"
|
|
31795
31892
|
};
|
|
31796
|
-
|
|
31893
|
+
lookStyles4 = {
|
|
31797
31894
|
dense: "gap-2 [&>*]:p-card-sm",
|
|
31798
31895
|
spacious: "gap-8 [&>*]:p-card-lg",
|
|
31799
31896
|
striped: "[&>*:nth-child(even)]:bg-muted/30",
|
|
@@ -32220,6 +32317,133 @@ var init_DataList = __esm({
|
|
|
32220
32317
|
DataList.displayName = "DataList";
|
|
32221
32318
|
}
|
|
32222
32319
|
});
|
|
32320
|
+
var FormSection, FormLayout, FormActions;
|
|
32321
|
+
var init_FormSection = __esm({
|
|
32322
|
+
"components/core/molecules/FormSection.tsx"() {
|
|
32323
|
+
"use client";
|
|
32324
|
+
init_cn();
|
|
32325
|
+
init_atoms();
|
|
32326
|
+
init_Box();
|
|
32327
|
+
init_Typography();
|
|
32328
|
+
init_Button();
|
|
32329
|
+
init_Stack();
|
|
32330
|
+
init_Icon();
|
|
32331
|
+
init_useEventBus();
|
|
32332
|
+
FormSection = ({
|
|
32333
|
+
title,
|
|
32334
|
+
description,
|
|
32335
|
+
children,
|
|
32336
|
+
collapsible = false,
|
|
32337
|
+
defaultCollapsed = false,
|
|
32338
|
+
card = false,
|
|
32339
|
+
columns = 1,
|
|
32340
|
+
className
|
|
32341
|
+
}) => {
|
|
32342
|
+
const [collapsed, setCollapsed] = React105__namespace.default.useState(defaultCollapsed);
|
|
32343
|
+
const { t } = hooks.useTranslate();
|
|
32344
|
+
const eventBus = useEventBus();
|
|
32345
|
+
const gridClass = {
|
|
32346
|
+
1: "grid-cols-1",
|
|
32347
|
+
2: "grid-cols-1 md:grid-cols-2",
|
|
32348
|
+
3: "grid-cols-1 md:grid-cols-2 lg:grid-cols-3"
|
|
32349
|
+
}[columns];
|
|
32350
|
+
React105__namespace.default.useCallback(() => {
|
|
32351
|
+
if (collapsible) {
|
|
32352
|
+
setCollapsed((prev) => !prev);
|
|
32353
|
+
eventBus.emit("UI:TOGGLE_COLLAPSE", { collapsed: !collapsed });
|
|
32354
|
+
}
|
|
32355
|
+
}, [collapsible, collapsed, eventBus]);
|
|
32356
|
+
const content = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
32357
|
+
(title || description) && /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "xs", className: "mb-4", children: [
|
|
32358
|
+
title && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
32359
|
+
HStack,
|
|
32360
|
+
{
|
|
32361
|
+
justify: "between",
|
|
32362
|
+
align: "center",
|
|
32363
|
+
className: cn(collapsible && "cursor-pointer"),
|
|
32364
|
+
action: collapsible ? "TOGGLE_COLLAPSE" : void 0,
|
|
32365
|
+
children: [
|
|
32366
|
+
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h3", weight: "semibold", children: title }),
|
|
32367
|
+
collapsible && /* @__PURE__ */ jsxRuntime.jsx(
|
|
32368
|
+
Button,
|
|
32369
|
+
{
|
|
32370
|
+
variant: "ghost",
|
|
32371
|
+
size: "sm",
|
|
32372
|
+
action: "TOGGLE_COLLAPSE",
|
|
32373
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
32374
|
+
Icon,
|
|
32375
|
+
{
|
|
32376
|
+
icon: LucideIcons2.ChevronDown,
|
|
32377
|
+
size: "sm",
|
|
32378
|
+
className: cn(
|
|
32379
|
+
"text-muted-foreground transition-transform",
|
|
32380
|
+
collapsed && "rotate-180"
|
|
32381
|
+
)
|
|
32382
|
+
}
|
|
32383
|
+
)
|
|
32384
|
+
}
|
|
32385
|
+
)
|
|
32386
|
+
]
|
|
32387
|
+
}
|
|
32388
|
+
),
|
|
32389
|
+
description && /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", color: "secondary", children: description })
|
|
32390
|
+
] }),
|
|
32391
|
+
(!collapsible || !collapsed) && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: cn("grid gap-4", gridClass), children })
|
|
32392
|
+
] });
|
|
32393
|
+
if (card) {
|
|
32394
|
+
return /* @__PURE__ */ jsxRuntime.jsx(Card, { className: cn("p-6", className), children: content });
|
|
32395
|
+
}
|
|
32396
|
+
return /* @__PURE__ */ jsxRuntime.jsx(Box, { className, children: content });
|
|
32397
|
+
};
|
|
32398
|
+
FormSection.displayName = "FormSection";
|
|
32399
|
+
FormLayout = ({
|
|
32400
|
+
children,
|
|
32401
|
+
dividers = true,
|
|
32402
|
+
className
|
|
32403
|
+
}) => {
|
|
32404
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
32405
|
+
VStack,
|
|
32406
|
+
{
|
|
32407
|
+
gap: "lg",
|
|
32408
|
+
className: cn(
|
|
32409
|
+
dividers && "[&>*+*]:pt-8 [&>*+*]:border-t [&>*+*]:border-border",
|
|
32410
|
+
className
|
|
32411
|
+
),
|
|
32412
|
+
children
|
|
32413
|
+
}
|
|
32414
|
+
);
|
|
32415
|
+
};
|
|
32416
|
+
FormLayout.displayName = "FormLayout";
|
|
32417
|
+
FormActions = ({
|
|
32418
|
+
children,
|
|
32419
|
+
sticky = false,
|
|
32420
|
+
align = "right",
|
|
32421
|
+
className
|
|
32422
|
+
}) => {
|
|
32423
|
+
const alignClass2 = {
|
|
32424
|
+
left: "justify-start",
|
|
32425
|
+
right: "justify-end",
|
|
32426
|
+
between: "justify-between",
|
|
32427
|
+
center: "justify-center"
|
|
32428
|
+
}[align];
|
|
32429
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
32430
|
+
HStack,
|
|
32431
|
+
{
|
|
32432
|
+
gap: "sm",
|
|
32433
|
+
align: "center",
|
|
32434
|
+
className: cn(
|
|
32435
|
+
"pt-6 border-t border-border",
|
|
32436
|
+
alignClass2,
|
|
32437
|
+
sticky && "sticky bottom-0 bg-card py-4 -mx-6 px-6 shadow-[0_-4px_6px_-1px_rgb(0,0,0,0.05)]",
|
|
32438
|
+
className
|
|
32439
|
+
),
|
|
32440
|
+
children
|
|
32441
|
+
}
|
|
32442
|
+
);
|
|
32443
|
+
};
|
|
32444
|
+
FormActions.displayName = "FormActions";
|
|
32445
|
+
}
|
|
32446
|
+
});
|
|
32223
32447
|
function fileIcon(name) {
|
|
32224
32448
|
const ext = name.split(".").pop()?.toLowerCase() ?? "";
|
|
32225
32449
|
switch (ext) {
|
|
@@ -32381,6 +32605,129 @@ var init_FormField = __esm({
|
|
|
32381
32605
|
FormField.displayName = "FormField";
|
|
32382
32606
|
}
|
|
32383
32607
|
});
|
|
32608
|
+
function useSafeEventBus3() {
|
|
32609
|
+
try {
|
|
32610
|
+
return useEventBus();
|
|
32611
|
+
} catch {
|
|
32612
|
+
return { emit: () => {
|
|
32613
|
+
}, on: () => () => {
|
|
32614
|
+
}, once: () => {
|
|
32615
|
+
} };
|
|
32616
|
+
}
|
|
32617
|
+
}
|
|
32618
|
+
var log6, lookStyles5, ButtonGroup;
|
|
32619
|
+
var init_ButtonGroup = __esm({
|
|
32620
|
+
"components/core/molecules/ButtonGroup.tsx"() {
|
|
32621
|
+
"use client";
|
|
32622
|
+
init_cn();
|
|
32623
|
+
init_atoms();
|
|
32624
|
+
init_useEventBus();
|
|
32625
|
+
log6 = logger.createLogger("almadar:ui:button-group");
|
|
32626
|
+
lookStyles5 = {
|
|
32627
|
+
"right-aligned-buttons": "",
|
|
32628
|
+
"floating-bar": "fixed bottom-section left-1/2 -translate-x-1/2 shadow-elevation-toast bg-card p-card-sm rounded-container",
|
|
32629
|
+
"inline-row": "gap-2 inline-flex",
|
|
32630
|
+
"dropdown-menu": "[&>button:not(:first-child)]:hidden",
|
|
32631
|
+
"command-palette-trigger": "[&>button:not(:first-child)]:hidden"
|
|
32632
|
+
};
|
|
32633
|
+
ButtonGroup = ({
|
|
32634
|
+
children,
|
|
32635
|
+
primary,
|
|
32636
|
+
secondary,
|
|
32637
|
+
variant = "default",
|
|
32638
|
+
orientation = "horizontal",
|
|
32639
|
+
className,
|
|
32640
|
+
// Filter-group pattern props (entity and filters are used for schema-driven filtering)
|
|
32641
|
+
entity: _entity,
|
|
32642
|
+
filters,
|
|
32643
|
+
look = "right-aligned-buttons"
|
|
32644
|
+
}) => {
|
|
32645
|
+
const eventBus = useSafeEventBus3();
|
|
32646
|
+
const variantClasses2 = {
|
|
32647
|
+
default: "gap-0",
|
|
32648
|
+
segmented: "gap-0 [&>button]:rounded-none [&>button:first-child]:rounded-l-lg [&>button:last-child]:rounded-r-lg [&>button:not(:first-child)]:border-l-0",
|
|
32649
|
+
toggle: "gap-0 [&>button]:rounded-none [&>button:first-child]:rounded-l-lg [&>button:last-child]:rounded-r-lg [&>button:not(:first-child)]:border-l-0"
|
|
32650
|
+
};
|
|
32651
|
+
const orientationClasses = {
|
|
32652
|
+
horizontal: "flex-row",
|
|
32653
|
+
vertical: "flex-col [&>button:first-child]:rounded-t-lg [&>button:last-child]:rounded-b-lg [&>button:not(:first-child)]:border-t-0 [&>button:not(:first-child)]:border-l"
|
|
32654
|
+
};
|
|
32655
|
+
const handleActionClick = (action) => {
|
|
32656
|
+
if (action.event) {
|
|
32657
|
+
eventBus.emit("UI:DISPATCH", { event: action.event });
|
|
32658
|
+
}
|
|
32659
|
+
if (action.navigatesTo) {
|
|
32660
|
+
eventBus.emit("UI:NAVIGATE", { url: action.navigatesTo });
|
|
32661
|
+
}
|
|
32662
|
+
};
|
|
32663
|
+
const renderFormActions = () => {
|
|
32664
|
+
const buttons = [];
|
|
32665
|
+
if (secondary) {
|
|
32666
|
+
secondary.forEach((action, index) => {
|
|
32667
|
+
buttons.push(
|
|
32668
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
32669
|
+
Button,
|
|
32670
|
+
{
|
|
32671
|
+
type: action.actionType === "submit" ? "submit" : "button",
|
|
32672
|
+
variant: action.variant || "ghost",
|
|
32673
|
+
onClick: () => handleActionClick(action),
|
|
32674
|
+
children: action.label
|
|
32675
|
+
},
|
|
32676
|
+
`secondary-${index}`
|
|
32677
|
+
)
|
|
32678
|
+
);
|
|
32679
|
+
});
|
|
32680
|
+
}
|
|
32681
|
+
if (primary) {
|
|
32682
|
+
const isSubmit = primary.actionType === "submit";
|
|
32683
|
+
buttons.push(
|
|
32684
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
32685
|
+
Button,
|
|
32686
|
+
{
|
|
32687
|
+
type: isSubmit ? "submit" : "button",
|
|
32688
|
+
variant: primary.variant || "primary",
|
|
32689
|
+
onClick: () => handleActionClick(primary),
|
|
32690
|
+
"data-testid": isSubmit ? "form-submit" : void 0,
|
|
32691
|
+
children: primary.label
|
|
32692
|
+
},
|
|
32693
|
+
"primary"
|
|
32694
|
+
)
|
|
32695
|
+
);
|
|
32696
|
+
}
|
|
32697
|
+
return buttons;
|
|
32698
|
+
};
|
|
32699
|
+
const renderFilters = () => {
|
|
32700
|
+
if (!filters || filters.length === 0) return null;
|
|
32701
|
+
return filters.map((filter, index) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
32702
|
+
Button,
|
|
32703
|
+
{
|
|
32704
|
+
variant: "ghost",
|
|
32705
|
+
onClick: () => {
|
|
32706
|
+
log6.debug("Filter clicked", { field: filter.field });
|
|
32707
|
+
},
|
|
32708
|
+
children: filter.label
|
|
32709
|
+
},
|
|
32710
|
+
`filter-${filter.field}-${index}`
|
|
32711
|
+
));
|
|
32712
|
+
};
|
|
32713
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
32714
|
+
"div",
|
|
32715
|
+
{
|
|
32716
|
+
className: cn(
|
|
32717
|
+
"inline-flex gap-2",
|
|
32718
|
+
variantClasses2[variant],
|
|
32719
|
+
orientationClasses[orientation],
|
|
32720
|
+
lookStyles5[look],
|
|
32721
|
+
className
|
|
32722
|
+
),
|
|
32723
|
+
role: "group",
|
|
32724
|
+
children: children || renderFilters() || renderFormActions()
|
|
32725
|
+
}
|
|
32726
|
+
);
|
|
32727
|
+
};
|
|
32728
|
+
ButtonGroup.displayName = "ButtonGroup";
|
|
32729
|
+
}
|
|
32730
|
+
});
|
|
32384
32731
|
var resolveFilterType, lookStyles6, FilterGroup;
|
|
32385
32732
|
var init_FilterGroup = __esm({
|
|
32386
32733
|
"components/core/molecules/FilterGroup.tsx"() {
|
|
@@ -34858,6 +35205,234 @@ var init_ProgressDots = __esm({
|
|
|
34858
35205
|
ProgressDots.displayName = "ProgressDots";
|
|
34859
35206
|
}
|
|
34860
35207
|
});
|
|
35208
|
+
var MathCanvas;
|
|
35209
|
+
var init_MathCanvas = __esm({
|
|
35210
|
+
"components/learning/molecules/MathCanvas.tsx"() {
|
|
35211
|
+
"use client";
|
|
35212
|
+
init_atoms();
|
|
35213
|
+
init_Stack();
|
|
35214
|
+
init_LearningCanvas();
|
|
35215
|
+
MathCanvas = ({
|
|
35216
|
+
className,
|
|
35217
|
+
width = 600,
|
|
35218
|
+
height = 400,
|
|
35219
|
+
title,
|
|
35220
|
+
xMin = -10,
|
|
35221
|
+
xMax = 10,
|
|
35222
|
+
yMin = -10,
|
|
35223
|
+
yMax = 10,
|
|
35224
|
+
showAxes = true,
|
|
35225
|
+
showGrid = true,
|
|
35226
|
+
gridStep = 1,
|
|
35227
|
+
curves = [],
|
|
35228
|
+
points = [],
|
|
35229
|
+
vectors = [],
|
|
35230
|
+
shapes = [],
|
|
35231
|
+
interactive = false,
|
|
35232
|
+
animate = false,
|
|
35233
|
+
onShapeClick,
|
|
35234
|
+
isLoading,
|
|
35235
|
+
error
|
|
35236
|
+
}) => {
|
|
35237
|
+
const derivedShapes = React105.useMemo(() => {
|
|
35238
|
+
const out = [];
|
|
35239
|
+
const margin = 24;
|
|
35240
|
+
const plotW = width - margin * 2;
|
|
35241
|
+
const plotH = height - margin * 2;
|
|
35242
|
+
const mapX = (x) => margin + (x - xMin) / (xMax - xMin) * plotW;
|
|
35243
|
+
const mapY = (y) => height - (margin + (y - yMin) / (yMax - yMin) * plotH);
|
|
35244
|
+
if (showGrid) {
|
|
35245
|
+
for (let x = Math.ceil(xMin / gridStep) * gridStep; x <= xMax; x += gridStep) {
|
|
35246
|
+
const px = mapX(x);
|
|
35247
|
+
out.push({ type: "line", x1: px, y1: margin, x2: px, y2: height - margin, color: "#e5e7eb", lineWidth: 1 });
|
|
35248
|
+
}
|
|
35249
|
+
for (let y = Math.ceil(yMin / gridStep) * gridStep; y <= yMax; y += gridStep) {
|
|
35250
|
+
const py = mapY(y);
|
|
35251
|
+
out.push({ type: "line", x1: margin, y1: py, x2: width - margin, y2: py, color: "#e5e7eb", lineWidth: 1 });
|
|
35252
|
+
}
|
|
35253
|
+
}
|
|
35254
|
+
if (showAxes) {
|
|
35255
|
+
const xAxisY = Math.max(margin, Math.min(height - margin, mapY(0)));
|
|
35256
|
+
const yAxisX = Math.max(margin, Math.min(width - margin, mapX(0)));
|
|
35257
|
+
out.push({ type: "line", x1: margin, y1: xAxisY, x2: width - margin, y2: xAxisY, color: "#374151", lineWidth: 2 });
|
|
35258
|
+
out.push({ type: "line", x1: yAxisX, y1: margin, x2: yAxisX, y2: height - margin, color: "#374151", lineWidth: 2 });
|
|
35259
|
+
}
|
|
35260
|
+
for (const curve of curves) {
|
|
35261
|
+
if (!curve.samples || curve.samples.length < 2) continue;
|
|
35262
|
+
for (let i = 1; i < curve.samples.length; i++) {
|
|
35263
|
+
const a = curve.samples[i - 1];
|
|
35264
|
+
const b = curve.samples[i];
|
|
35265
|
+
if (a.x < xMin || a.x > xMax || b.x < xMin || b.x > xMax) continue;
|
|
35266
|
+
out.push({
|
|
35267
|
+
type: "line",
|
|
35268
|
+
x1: mapX(a.x),
|
|
35269
|
+
y1: mapY(a.y),
|
|
35270
|
+
x2: mapX(b.x),
|
|
35271
|
+
y2: mapY(b.y),
|
|
35272
|
+
color: curve.color ?? "#2563eb",
|
|
35273
|
+
lineWidth: 2
|
|
35274
|
+
});
|
|
35275
|
+
}
|
|
35276
|
+
}
|
|
35277
|
+
for (const p2 of points) {
|
|
35278
|
+
if (p2.x < xMin || p2.x > xMax || p2.y < yMin || p2.y > yMax) continue;
|
|
35279
|
+
out.push({
|
|
35280
|
+
type: "circle",
|
|
35281
|
+
x: mapX(p2.x),
|
|
35282
|
+
y: mapY(p2.y),
|
|
35283
|
+
radius: p2.radius ?? 4,
|
|
35284
|
+
color: p2.color ?? "#dc2626",
|
|
35285
|
+
fill: p2.color ?? "#dc2626"
|
|
35286
|
+
});
|
|
35287
|
+
if (p2.label) {
|
|
35288
|
+
out.push({ type: "text", x: mapX(p2.x) + 8, y: mapY(p2.y) - 8, text: p2.label, color: "#111827", fontSize: 12 });
|
|
35289
|
+
}
|
|
35290
|
+
}
|
|
35291
|
+
for (const v of vectors) {
|
|
35292
|
+
if (v.x < xMin || v.x > xMax || v.y < yMin || v.y > yMax) continue;
|
|
35293
|
+
const x1 = mapX(v.x);
|
|
35294
|
+
const y1 = mapY(v.y);
|
|
35295
|
+
const x2 = mapX(v.x + v.vx);
|
|
35296
|
+
const y2 = mapY(v.y + v.vy);
|
|
35297
|
+
out.push({ type: "arrow", x1, y1, x2, y2, color: v.color ?? "#7c3aed", lineWidth: 2 });
|
|
35298
|
+
if (v.label) {
|
|
35299
|
+
out.push({ type: "text", x: x2 + 6, y: y2 - 6, text: v.label, color: "#111827", fontSize: 12 });
|
|
35300
|
+
}
|
|
35301
|
+
}
|
|
35302
|
+
out.push(...shapes);
|
|
35303
|
+
return out;
|
|
35304
|
+
}, [width, height, xMin, xMax, yMin, yMax, showAxes, showGrid, gridStep, curves, points, vectors, shapes]);
|
|
35305
|
+
return /* @__PURE__ */ jsxRuntime.jsx(Card, { className, children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "sm", children: [
|
|
35306
|
+
title ? /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h4", children: title }) : null,
|
|
35307
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
35308
|
+
LearningCanvas,
|
|
35309
|
+
{
|
|
35310
|
+
width,
|
|
35311
|
+
height,
|
|
35312
|
+
shapes: derivedShapes,
|
|
35313
|
+
interactive,
|
|
35314
|
+
animate,
|
|
35315
|
+
onShapeClick,
|
|
35316
|
+
isLoading,
|
|
35317
|
+
error
|
|
35318
|
+
}
|
|
35319
|
+
)
|
|
35320
|
+
] }) });
|
|
35321
|
+
};
|
|
35322
|
+
}
|
|
35323
|
+
});
|
|
35324
|
+
var PhysicsCanvas;
|
|
35325
|
+
var init_PhysicsCanvas = __esm({
|
|
35326
|
+
"components/learning/molecules/PhysicsCanvas.tsx"() {
|
|
35327
|
+
"use client";
|
|
35328
|
+
init_atoms();
|
|
35329
|
+
init_Stack();
|
|
35330
|
+
init_LearningCanvas();
|
|
35331
|
+
PhysicsCanvas = ({
|
|
35332
|
+
className,
|
|
35333
|
+
width = 600,
|
|
35334
|
+
height = 400,
|
|
35335
|
+
title,
|
|
35336
|
+
backgroundColor,
|
|
35337
|
+
bodies = [],
|
|
35338
|
+
constraints = [],
|
|
35339
|
+
showVelocity = true,
|
|
35340
|
+
showForces = false,
|
|
35341
|
+
velocityScale = 20,
|
|
35342
|
+
forceScale = 20,
|
|
35343
|
+
shapes = [],
|
|
35344
|
+
interactive = false,
|
|
35345
|
+
animate = false,
|
|
35346
|
+
onShapeClick,
|
|
35347
|
+
isLoading,
|
|
35348
|
+
error
|
|
35349
|
+
}) => {
|
|
35350
|
+
const derivedShapes = React105.useMemo(() => {
|
|
35351
|
+
const out = [];
|
|
35352
|
+
const bodyById = /* @__PURE__ */ new Map();
|
|
35353
|
+
for (const b of bodies) {
|
|
35354
|
+
if (b.id) bodyById.set(b.id, b);
|
|
35355
|
+
}
|
|
35356
|
+
for (const c of constraints) {
|
|
35357
|
+
const a = bodyById.get(c.from);
|
|
35358
|
+
const b = bodyById.get(c.to);
|
|
35359
|
+
if (!a || !b) continue;
|
|
35360
|
+
out.push({
|
|
35361
|
+
type: "line",
|
|
35362
|
+
x1: a.x,
|
|
35363
|
+
y1: a.y,
|
|
35364
|
+
x2: b.x,
|
|
35365
|
+
y2: b.y,
|
|
35366
|
+
color: c.color ?? "#9ca3af",
|
|
35367
|
+
lineWidth: 2
|
|
35368
|
+
});
|
|
35369
|
+
}
|
|
35370
|
+
for (const b of bodies) {
|
|
35371
|
+
out.push({
|
|
35372
|
+
type: "circle",
|
|
35373
|
+
x: b.x,
|
|
35374
|
+
y: b.y,
|
|
35375
|
+
radius: b.radius ?? 12,
|
|
35376
|
+
color: b.color ?? "#2563eb",
|
|
35377
|
+
fill: b.color ?? "#2563eb",
|
|
35378
|
+
id: b.id
|
|
35379
|
+
});
|
|
35380
|
+
if (b.label) {
|
|
35381
|
+
out.push({
|
|
35382
|
+
type: "text",
|
|
35383
|
+
x: b.x + (b.radius ?? 12) + 6,
|
|
35384
|
+
y: b.y - (b.radius ?? 12) - 6,
|
|
35385
|
+
text: b.label,
|
|
35386
|
+
color: "#111827",
|
|
35387
|
+
fontSize: 12
|
|
35388
|
+
});
|
|
35389
|
+
}
|
|
35390
|
+
if (showVelocity && b.vx != null && b.vy != null && (b.vx !== 0 || b.vy !== 0)) {
|
|
35391
|
+
out.push({
|
|
35392
|
+
type: "arrow",
|
|
35393
|
+
x1: b.x,
|
|
35394
|
+
y1: b.y,
|
|
35395
|
+
x2: b.x + b.vx * velocityScale,
|
|
35396
|
+
y2: b.y + b.vy * velocityScale,
|
|
35397
|
+
color: "#16a34a",
|
|
35398
|
+
lineWidth: 2
|
|
35399
|
+
});
|
|
35400
|
+
}
|
|
35401
|
+
if (showForces && b.fx != null && b.fy != null && (b.fx !== 0 || b.fy !== 0)) {
|
|
35402
|
+
out.push({
|
|
35403
|
+
type: "arrow",
|
|
35404
|
+
x1: b.x,
|
|
35405
|
+
y1: b.y,
|
|
35406
|
+
x2: b.x + b.fx * forceScale,
|
|
35407
|
+
y2: b.y + b.fy * forceScale,
|
|
35408
|
+
color: "#dc2626",
|
|
35409
|
+
lineWidth: 2
|
|
35410
|
+
});
|
|
35411
|
+
}
|
|
35412
|
+
}
|
|
35413
|
+
out.push(...shapes);
|
|
35414
|
+
return out;
|
|
35415
|
+
}, [bodies, constraints, showVelocity, showForces, velocityScale, forceScale, shapes]);
|
|
35416
|
+
return /* @__PURE__ */ jsxRuntime.jsx(Card, { className, children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "sm", children: [
|
|
35417
|
+
title ? /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h4", children: title }) : null,
|
|
35418
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
35419
|
+
LearningCanvas,
|
|
35420
|
+
{
|
|
35421
|
+
width,
|
|
35422
|
+
height,
|
|
35423
|
+
backgroundColor,
|
|
35424
|
+
shapes: derivedShapes,
|
|
35425
|
+
interactive,
|
|
35426
|
+
animate,
|
|
35427
|
+
onShapeClick,
|
|
35428
|
+
isLoading,
|
|
35429
|
+
error
|
|
35430
|
+
}
|
|
35431
|
+
)
|
|
35432
|
+
] }) });
|
|
35433
|
+
};
|
|
35434
|
+
}
|
|
35435
|
+
});
|
|
34861
35436
|
function resolveNodeColor(node, groups) {
|
|
34862
35437
|
if (node.color) return node.color;
|
|
34863
35438
|
if (node.group) {
|
|
@@ -35229,13 +35804,13 @@ var init_MapView = __esm({
|
|
|
35229
35804
|
shadowSize: [41, 41]
|
|
35230
35805
|
});
|
|
35231
35806
|
L.Marker.prototype.options.icon = defaultIcon;
|
|
35232
|
-
const { useEffect:
|
|
35807
|
+
const { useEffect: useEffect75, useRef: useRef77, useCallback: useCallback112, useState: useState107 } = React105__namespace.default;
|
|
35233
35808
|
const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
|
|
35234
35809
|
const { useEventBus: useEventBus2 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
|
|
35235
35810
|
function MapUpdater({ centerLat, centerLng, zoom }) {
|
|
35236
35811
|
const map = useMap();
|
|
35237
|
-
const prevRef =
|
|
35238
|
-
|
|
35812
|
+
const prevRef = useRef77({ centerLat, centerLng, zoom });
|
|
35813
|
+
useEffect75(() => {
|
|
35239
35814
|
const prev = prevRef.current;
|
|
35240
35815
|
if (prev.centerLat !== centerLat || prev.centerLng !== centerLng || prev.zoom !== zoom) {
|
|
35241
35816
|
map.setView([centerLat, centerLng], zoom);
|
|
@@ -35246,7 +35821,7 @@ var init_MapView = __esm({
|
|
|
35246
35821
|
}
|
|
35247
35822
|
function MapClickHandler({ onMapClick }) {
|
|
35248
35823
|
const map = useMap();
|
|
35249
|
-
|
|
35824
|
+
useEffect75(() => {
|
|
35250
35825
|
if (!onMapClick) return;
|
|
35251
35826
|
const handler = (e) => {
|
|
35252
35827
|
onMapClick(e.latlng.lat, e.latlng.lng);
|
|
@@ -43549,7 +44124,7 @@ function getGroupColor(group, groups) {
|
|
|
43549
44124
|
const idx = groups.indexOf(group);
|
|
43550
44125
|
return GROUP_COLORS2[idx % GROUP_COLORS2.length];
|
|
43551
44126
|
}
|
|
43552
|
-
function
|
|
44127
|
+
function resolveColor3(color, el) {
|
|
43553
44128
|
const m = /^var\((--[^,)]+)(?:,\s*([^)]+))?\)$/.exec(color.trim());
|
|
43554
44129
|
if (!m) return color;
|
|
43555
44130
|
const resolved = getComputedStyle(el).getPropertyValue(m[1]).trim();
|
|
@@ -43810,7 +44385,7 @@ var init_GraphCanvas = __esm({
|
|
|
43810
44385
|
const w = logicalW;
|
|
43811
44386
|
const h = height;
|
|
43812
44387
|
const nodes = nodesRef.current;
|
|
43813
|
-
const accentColor =
|
|
44388
|
+
const accentColor = resolveColor3("var(--color-accent)", canvas);
|
|
43814
44389
|
const dpr = typeof window !== "undefined" && window.devicePixelRatio || 1;
|
|
43815
44390
|
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
|
|
43816
44391
|
ctx.clearRect(0, 0, w, h);
|
|
@@ -43849,7 +44424,7 @@ var init_GraphCanvas = __esm({
|
|
|
43849
44424
|
ctx.globalAlpha = 1;
|
|
43850
44425
|
for (const node of nodes) {
|
|
43851
44426
|
const size = node.size || 8;
|
|
43852
|
-
const color =
|
|
44427
|
+
const color = resolveColor3(node.color || getGroupColor(node.group, groups), canvas);
|
|
43853
44428
|
const isHovered = hoveredNode === node.id;
|
|
43854
44429
|
const isSelected = selectedNodeId !== void 0 && node.id === selectedNodeId;
|
|
43855
44430
|
ctx.globalAlpha = hoveredNode && !neighbors.has(node.id) ? 0.2 : 1;
|
|
@@ -50747,6 +51322,7 @@ var init_component_registry_generated = __esm({
|
|
|
50747
51322
|
init_BattleBoard();
|
|
50748
51323
|
init_BattleTemplate();
|
|
50749
51324
|
init_BehaviorView();
|
|
51325
|
+
init_BiologyCanvas();
|
|
50750
51326
|
init_BloomQuizBlock();
|
|
50751
51327
|
init_BoardgameBoard();
|
|
50752
51328
|
init_BookChapterView();
|
|
@@ -50758,7 +51334,6 @@ var init_component_registry_generated = __esm({
|
|
|
50758
51334
|
init_BranchingLogicBuilder();
|
|
50759
51335
|
init_Breadcrumb();
|
|
50760
51336
|
init_BuilderBoard();
|
|
50761
|
-
init_ButtonGroup();
|
|
50762
51337
|
init_CTABanner();
|
|
50763
51338
|
init_CalendarGrid();
|
|
50764
51339
|
init_Canvas2D();
|
|
@@ -50777,6 +51352,7 @@ var init_component_registry_generated = __esm({
|
|
|
50777
51352
|
init_ChartLegend();
|
|
50778
51353
|
init_ChatBar();
|
|
50779
51354
|
init_Checkbox();
|
|
51355
|
+
init_ChemistryCanvas();
|
|
50780
51356
|
init_ChoiceButton();
|
|
50781
51357
|
init_CityBuilderBoard();
|
|
50782
51358
|
init_CityBuilderTemplate();
|
|
@@ -50835,9 +51411,9 @@ var init_component_registry_generated = __esm({
|
|
|
50835
51411
|
init_FlipContainer();
|
|
50836
51412
|
init_FloatingActionButton();
|
|
50837
51413
|
init_Form();
|
|
51414
|
+
init_FormSection();
|
|
50838
51415
|
init_FormField();
|
|
50839
51416
|
init_FormSectionHeader();
|
|
50840
|
-
init_GameAudioProvider();
|
|
50841
51417
|
init_GameAudioToggle();
|
|
50842
51418
|
init_GameCard();
|
|
50843
51419
|
init_GameHud();
|
|
@@ -50868,6 +51444,7 @@ var init_component_registry_generated = __esm({
|
|
|
50868
51444
|
init_JazariStateMachine();
|
|
50869
51445
|
init_LandingPageTemplate();
|
|
50870
51446
|
init_LawReferenceTooltip();
|
|
51447
|
+
init_LearningCanvas();
|
|
50871
51448
|
init_Lightbox();
|
|
50872
51449
|
init_LikertScale();
|
|
50873
51450
|
init_LineChart();
|
|
@@ -50876,9 +51453,11 @@ var init_component_registry_generated = __esm({
|
|
|
50876
51453
|
init_MapView();
|
|
50877
51454
|
init_MarkdownContent();
|
|
50878
51455
|
init_MarketingFooter();
|
|
51456
|
+
init_MarketingStatCard();
|
|
50879
51457
|
init_MasterDetail();
|
|
50880
51458
|
init_MasterDetailLayout();
|
|
50881
51459
|
init_MatchPuzzleBoard();
|
|
51460
|
+
init_MathCanvas();
|
|
50882
51461
|
init_MatrixQuestion();
|
|
50883
51462
|
init_MediaGallery();
|
|
50884
51463
|
init_Menu();
|
|
@@ -50897,6 +51476,7 @@ var init_component_registry_generated = __esm({
|
|
|
50897
51476
|
init_PageHeader();
|
|
50898
51477
|
init_Pagination();
|
|
50899
51478
|
init_PatternTile();
|
|
51479
|
+
init_PhysicsCanvas();
|
|
50900
51480
|
init_PinballBoard();
|
|
50901
51481
|
init_PirateBoard();
|
|
50902
51482
|
init_PlatformerBoard();
|
|
@@ -51073,6 +51653,7 @@ var init_component_registry_generated = __esm({
|
|
|
51073
51653
|
"BattleBoard": BattleBoard,
|
|
51074
51654
|
"BattleTemplate": BattleTemplate,
|
|
51075
51655
|
"BehaviorView": BehaviorView,
|
|
51656
|
+
"BiologyCanvas": BiologyCanvas,
|
|
51076
51657
|
"BloomQuizBlock": BloomQuizBlock,
|
|
51077
51658
|
"BoardgameBoard": BoardgameBoard,
|
|
51078
51659
|
"BookChapterView": BookChapterView,
|
|
@@ -51086,7 +51667,6 @@ var init_component_registry_generated = __esm({
|
|
|
51086
51667
|
"Breadcrumb": Breadcrumb,
|
|
51087
51668
|
"BuilderBoard": BuilderBoard,
|
|
51088
51669
|
"Button": ButtonPattern,
|
|
51089
|
-
"ButtonGroup": ButtonGroup,
|
|
51090
51670
|
"ButtonPattern": ButtonPattern,
|
|
51091
51671
|
"CTABanner": CTABanner,
|
|
51092
51672
|
"CalendarGrid": CalendarGrid,
|
|
@@ -51106,6 +51686,7 @@ var init_component_registry_generated = __esm({
|
|
|
51106
51686
|
"ChartLegend": ChartLegend,
|
|
51107
51687
|
"ChatBar": ChatBar,
|
|
51108
51688
|
"Checkbox": Checkbox,
|
|
51689
|
+
"ChemistryCanvas": ChemistryCanvas,
|
|
51109
51690
|
"ChoiceButton": ChoiceButton,
|
|
51110
51691
|
"CityBuilderBoard": CityBuilderBoard,
|
|
51111
51692
|
"CityBuilderTemplate": CityBuilderTemplate,
|
|
@@ -51166,9 +51747,10 @@ var init_component_registry_generated = __esm({
|
|
|
51166
51747
|
"FlipContainer": FlipContainer,
|
|
51167
51748
|
"FloatingActionButton": FloatingActionButton,
|
|
51168
51749
|
"Form": Form,
|
|
51750
|
+
"FormActions": FormActions,
|
|
51169
51751
|
"FormField": FormField,
|
|
51752
|
+
"FormLayout": FormLayout,
|
|
51170
51753
|
"FormSectionHeader": FormSectionHeader,
|
|
51171
|
-
"GameAudioProvider": GameAudioProvider,
|
|
51172
51754
|
"GameAudioToggle": GameAudioToggle,
|
|
51173
51755
|
"GameBoard3D": GameBoard3D,
|
|
51174
51756
|
"GameCanvas3D": GameCanvas3D,
|
|
@@ -51206,6 +51788,7 @@ var init_component_registry_generated = __esm({
|
|
|
51206
51788
|
"LabelPattern": LabelPattern,
|
|
51207
51789
|
"LandingPageTemplate": LandingPageTemplate,
|
|
51208
51790
|
"LawReferenceTooltip": LawReferenceTooltip,
|
|
51791
|
+
"LearningCanvas": LearningCanvas,
|
|
51209
51792
|
"Lightbox": Lightbox,
|
|
51210
51793
|
"LikertScale": LikertScale,
|
|
51211
51794
|
"LineChart": LineChart2,
|
|
@@ -51214,9 +51797,11 @@ var init_component_registry_generated = __esm({
|
|
|
51214
51797
|
"MapView": MapView,
|
|
51215
51798
|
"MarkdownContent": MarkdownContent,
|
|
51216
51799
|
"MarketingFooter": MarketingFooter,
|
|
51800
|
+
"MarketingStatCard": MarketingStatCard,
|
|
51217
51801
|
"MasterDetail": MasterDetail,
|
|
51218
51802
|
"MasterDetailLayout": MasterDetailLayout,
|
|
51219
51803
|
"MatchPuzzleBoard": MatchPuzzleBoard,
|
|
51804
|
+
"MathCanvas": MathCanvas,
|
|
51220
51805
|
"MatrixQuestion": MatrixQuestion,
|
|
51221
51806
|
"MediaGallery": MediaGallery,
|
|
51222
51807
|
"Menu": Menu,
|
|
@@ -51235,6 +51820,7 @@ var init_component_registry_generated = __esm({
|
|
|
51235
51820
|
"PageHeader": PageHeader,
|
|
51236
51821
|
"Pagination": Pagination,
|
|
51237
51822
|
"PatternTile": PatternTile,
|
|
51823
|
+
"PhysicsCanvas": PhysicsCanvas,
|
|
51238
51824
|
"PinballBoard": PinballBoard,
|
|
51239
51825
|
"PirateBoard": PirateBoard,
|
|
51240
51826
|
"PlatformerBoard": PlatformerBoard,
|