@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.js
CHANGED
|
@@ -6,7 +6,7 @@ import { createLogger, isLogLevelEnabled } from '@almadar/logger';
|
|
|
6
6
|
import { clsx } from 'clsx';
|
|
7
7
|
import { twMerge } from 'tailwind-merge';
|
|
8
8
|
import * as LucideIcons2 from 'lucide-react';
|
|
9
|
-
import { Loader2, X, Lightbulb, CheckCircle, List, Printer, ChevronRight, ChevronLeft, GitBranch, Pencil, Eye, Plus, ArrowRight, Trash, Code, FileText, WrapText, Check, Copy, RotateCcw, Play, Terminal, XCircle, AlertTriangle, Trash2, Link2, ZoomOut, ZoomIn, Download, Menu as Menu$1, Package, Calendar, MoreHorizontal, Image as Image$1, Upload, ArrowLeft, HelpCircle, PauseCircle, Search, Type, Heading1, Heading2, Heading3, ListOrdered, Quote, Minus, Eraser, TrendingUp, TrendingDown, AlertCircle, Circle, Clock, CheckCircle2, Bug, Send, ChevronUp,
|
|
9
|
+
import { Loader2, X, Lightbulb, CheckCircle, List, Printer, ChevronRight, ChevronLeft, GitBranch, Pencil, Eye, Plus, ArrowRight, Trash, Code, FileText, WrapText, Check, Copy, RotateCcw, Play, Terminal, XCircle, AlertTriangle, Trash2, Link2, ZoomOut, ZoomIn, Download, ChevronDown, Menu as Menu$1, Package, Calendar, MoreHorizontal, Image as Image$1, Upload, ArrowLeft, HelpCircle, PauseCircle, Search, Type, Heading1, Heading2, Heading3, ListOrdered, Quote, Minus, Eraser, TrendingUp, TrendingDown, AlertCircle, Circle, Clock, CheckCircle2, Bug, Send, ChevronUp, Wrench, Tag, User, DollarSign } from 'lucide-react';
|
|
10
10
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
11
11
|
import { createPortal } from 'react-dom';
|
|
12
12
|
import { useTranslate } from '@almadar/ui/hooks';
|
|
@@ -6180,6 +6180,66 @@ var init_RangeSlider = __esm({
|
|
|
6180
6180
|
RangeSlider.displayName = "RangeSlider";
|
|
6181
6181
|
}
|
|
6182
6182
|
});
|
|
6183
|
+
function easeOut(t) {
|
|
6184
|
+
return t * (2 - t);
|
|
6185
|
+
}
|
|
6186
|
+
var AnimatedCounter;
|
|
6187
|
+
var init_AnimatedCounter = __esm({
|
|
6188
|
+
"components/marketing/atoms/AnimatedCounter.tsx"() {
|
|
6189
|
+
"use client";
|
|
6190
|
+
init_cn();
|
|
6191
|
+
init_Typography();
|
|
6192
|
+
AnimatedCounter = ({
|
|
6193
|
+
value: rawValue,
|
|
6194
|
+
duration = 600,
|
|
6195
|
+
prefix,
|
|
6196
|
+
suffix,
|
|
6197
|
+
className
|
|
6198
|
+
}) => {
|
|
6199
|
+
const numericRaw = typeof rawValue === "number" ? rawValue : Number.parseFloat(String(rawValue ?? ""));
|
|
6200
|
+
const value = !Number.isNaN(numericRaw) ? numericRaw : 0;
|
|
6201
|
+
const [displayValue, setDisplayValue] = useState(value);
|
|
6202
|
+
const previousValueRef = useRef(value);
|
|
6203
|
+
const animationFrameRef = useRef(null);
|
|
6204
|
+
useEffect(() => {
|
|
6205
|
+
const from = previousValueRef.current;
|
|
6206
|
+
const to = value;
|
|
6207
|
+
previousValueRef.current = value;
|
|
6208
|
+
if (from === to) {
|
|
6209
|
+
setDisplayValue(to);
|
|
6210
|
+
return;
|
|
6211
|
+
}
|
|
6212
|
+
const startTime = performance.now();
|
|
6213
|
+
const diff = to - from;
|
|
6214
|
+
function animate(currentTime) {
|
|
6215
|
+
const elapsed = currentTime - startTime;
|
|
6216
|
+
const progress = Math.min(elapsed / duration, 1);
|
|
6217
|
+
const easedProgress = easeOut(progress);
|
|
6218
|
+
setDisplayValue(from + diff * easedProgress);
|
|
6219
|
+
if (progress < 1) {
|
|
6220
|
+
animationFrameRef.current = requestAnimationFrame(animate);
|
|
6221
|
+
} else {
|
|
6222
|
+
setDisplayValue(to);
|
|
6223
|
+
}
|
|
6224
|
+
}
|
|
6225
|
+
animationFrameRef.current = requestAnimationFrame(animate);
|
|
6226
|
+
return () => {
|
|
6227
|
+
if (animationFrameRef.current !== null) {
|
|
6228
|
+
cancelAnimationFrame(animationFrameRef.current);
|
|
6229
|
+
}
|
|
6230
|
+
};
|
|
6231
|
+
}, [value, duration]);
|
|
6232
|
+
const decimalPlaces = Number.isInteger(value) ? 0 : String(value).split(".")[1]?.length ?? 0;
|
|
6233
|
+
const formattedValue = displayValue.toFixed(decimalPlaces);
|
|
6234
|
+
return /* @__PURE__ */ jsxs(Typography, { variant: "h3", className: cn("tabular-nums", className), children: [
|
|
6235
|
+
prefix,
|
|
6236
|
+
formattedValue,
|
|
6237
|
+
suffix
|
|
6238
|
+
] });
|
|
6239
|
+
};
|
|
6240
|
+
AnimatedCounter.displayName = "AnimatedCounter";
|
|
6241
|
+
}
|
|
6242
|
+
});
|
|
6183
6243
|
var InfiniteScrollSentinel;
|
|
6184
6244
|
var init_InfiniteScrollSentinel = __esm({
|
|
6185
6245
|
"components/core/atoms/InfiniteScrollSentinel.tsx"() {
|
|
@@ -6441,6 +6501,34 @@ var init_SectionHeader = __esm({
|
|
|
6441
6501
|
SectionHeader.displayName = "SectionHeader";
|
|
6442
6502
|
}
|
|
6443
6503
|
});
|
|
6504
|
+
var sizeClasses6, MarketingStatCard;
|
|
6505
|
+
var init_MarketingStatCard = __esm({
|
|
6506
|
+
"components/marketing/atoms/MarketingStatCard.tsx"() {
|
|
6507
|
+
init_cn();
|
|
6508
|
+
init_Stack();
|
|
6509
|
+
init_Typography();
|
|
6510
|
+
sizeClasses6 = {
|
|
6511
|
+
sm: "text-2xl",
|
|
6512
|
+
md: "text-4xl",
|
|
6513
|
+
lg: "text-5xl"
|
|
6514
|
+
};
|
|
6515
|
+
MarketingStatCard = ({ value, label, size = "md", className }) => {
|
|
6516
|
+
return /* @__PURE__ */ jsxs(VStack, { gap: "xs", align: "center", className: cn(className), children: [
|
|
6517
|
+
/* @__PURE__ */ jsx(
|
|
6518
|
+
Typography,
|
|
6519
|
+
{
|
|
6520
|
+
weight: "bold",
|
|
6521
|
+
color: "primary",
|
|
6522
|
+
className: cn(sizeClasses6[size]),
|
|
6523
|
+
children: value
|
|
6524
|
+
}
|
|
6525
|
+
),
|
|
6526
|
+
/* @__PURE__ */ jsx(Typography, { variant: "caption", color: "muted", children: label })
|
|
6527
|
+
] });
|
|
6528
|
+
};
|
|
6529
|
+
MarketingStatCard.displayName = "MarketingStatCard";
|
|
6530
|
+
}
|
|
6531
|
+
});
|
|
6444
6532
|
var backgroundClasses, paddingClasses, ContentSection;
|
|
6445
6533
|
var init_ContentSection = __esm({
|
|
6446
6534
|
"components/marketing/atoms/ContentSection.tsx"() {
|
|
@@ -12455,318 +12543,14 @@ var init_useCanvasEffects = __esm({
|
|
|
12455
12543
|
"use client";
|
|
12456
12544
|
}
|
|
12457
12545
|
});
|
|
12458
|
-
function pickPath(entry) {
|
|
12459
|
-
if (Array.isArray(entry.path)) {
|
|
12460
|
-
return entry.path[Math.floor(Math.random() * entry.path.length)];
|
|
12461
|
-
}
|
|
12462
|
-
return entry.path;
|
|
12463
|
-
}
|
|
12464
|
-
function useGameAudio({
|
|
12465
|
-
manifest,
|
|
12466
|
-
baseUrl = "",
|
|
12467
|
-
initialMuted = false,
|
|
12468
|
-
initialVolume = 1
|
|
12469
|
-
}) {
|
|
12470
|
-
const [muted, setMutedState] = useState(initialMuted);
|
|
12471
|
-
const [masterVolume, setMasterVolumeState] = useState(initialVolume);
|
|
12472
|
-
const mutedRef = useRef(muted);
|
|
12473
|
-
const volumeRef = useRef(masterVolume);
|
|
12474
|
-
const manifestRef = useRef(manifest);
|
|
12475
|
-
mutedRef.current = muted;
|
|
12476
|
-
volumeRef.current = masterVolume;
|
|
12477
|
-
manifestRef.current = manifest;
|
|
12478
|
-
const poolsRef = useRef(/* @__PURE__ */ new Map());
|
|
12479
|
-
const getOrCreateElement = useCallback((key) => {
|
|
12480
|
-
const entry = manifestRef.current[key];
|
|
12481
|
-
if (!entry) return null;
|
|
12482
|
-
let pool = poolsRef.current.get(key);
|
|
12483
|
-
if (!pool) {
|
|
12484
|
-
pool = [];
|
|
12485
|
-
poolsRef.current.set(key, pool);
|
|
12486
|
-
}
|
|
12487
|
-
const maxSize = entry.poolSize ?? 1;
|
|
12488
|
-
for (const audio of pool) {
|
|
12489
|
-
if (audio.paused && (audio.ended || audio.currentTime === 0)) {
|
|
12490
|
-
return audio;
|
|
12491
|
-
}
|
|
12492
|
-
}
|
|
12493
|
-
if (pool.length < maxSize) {
|
|
12494
|
-
const src = baseUrl + pickPath(entry);
|
|
12495
|
-
const audio = new Audio(src);
|
|
12496
|
-
audio.loop = entry.loop ?? false;
|
|
12497
|
-
pool.push(audio);
|
|
12498
|
-
return audio;
|
|
12499
|
-
}
|
|
12500
|
-
if (!entry.loop) {
|
|
12501
|
-
let oldest = pool[0];
|
|
12502
|
-
for (const audio of pool) {
|
|
12503
|
-
if (audio.currentTime > oldest.currentTime) {
|
|
12504
|
-
oldest = audio;
|
|
12505
|
-
}
|
|
12506
|
-
}
|
|
12507
|
-
oldest.pause();
|
|
12508
|
-
oldest.currentTime = 0;
|
|
12509
|
-
return oldest;
|
|
12510
|
-
}
|
|
12511
|
-
return null;
|
|
12512
|
-
}, [baseUrl]);
|
|
12513
|
-
const play = useCallback((key) => {
|
|
12514
|
-
if (mutedRef.current) return;
|
|
12515
|
-
const entry = manifestRef.current[key];
|
|
12516
|
-
if (!entry) return;
|
|
12517
|
-
const audio = getOrCreateElement(key);
|
|
12518
|
-
if (!audio) return;
|
|
12519
|
-
audio.volume = Math.min(1, (entry.volume ?? 1) * volumeRef.current);
|
|
12520
|
-
if (!entry.loop) {
|
|
12521
|
-
audio.currentTime = 0;
|
|
12522
|
-
}
|
|
12523
|
-
const promise = audio.play();
|
|
12524
|
-
if (promise) {
|
|
12525
|
-
promise.catch(() => {
|
|
12526
|
-
});
|
|
12527
|
-
}
|
|
12528
|
-
}, [getOrCreateElement]);
|
|
12529
|
-
const stop = useCallback((key) => {
|
|
12530
|
-
const pool = poolsRef.current.get(key);
|
|
12531
|
-
if (!pool) return;
|
|
12532
|
-
for (const audio of pool) {
|
|
12533
|
-
audio.pause();
|
|
12534
|
-
audio.currentTime = 0;
|
|
12535
|
-
}
|
|
12536
|
-
}, []);
|
|
12537
|
-
const currentMusicKeyRef = useRef(null);
|
|
12538
|
-
const currentMusicElRef = useRef(null);
|
|
12539
|
-
const musicFadeRef = useRef(null);
|
|
12540
|
-
const pendingMusicKeyRef = useRef(null);
|
|
12541
|
-
const clearMusicFade = useCallback(() => {
|
|
12542
|
-
if (musicFadeRef.current) {
|
|
12543
|
-
clearInterval(musicFadeRef.current);
|
|
12544
|
-
musicFadeRef.current = null;
|
|
12545
|
-
}
|
|
12546
|
-
}, []);
|
|
12547
|
-
const playMusic = useCallback((key) => {
|
|
12548
|
-
if (key === currentMusicKeyRef.current) return;
|
|
12549
|
-
pendingMusicKeyRef.current = key;
|
|
12550
|
-
const entry = manifestRef.current[key];
|
|
12551
|
-
if (!entry) return;
|
|
12552
|
-
const fadeDurationMs = entry.crossfadeDurationMs ?? 1500;
|
|
12553
|
-
const stepMs = 50;
|
|
12554
|
-
const totalSteps = Math.max(1, fadeDurationMs / stepMs);
|
|
12555
|
-
const targetVolume = Math.min(1, (entry.volume ?? 1) * volumeRef.current);
|
|
12556
|
-
clearMusicFade();
|
|
12557
|
-
const src = baseUrl + (Array.isArray(entry.path) ? entry.path[0] : entry.path);
|
|
12558
|
-
const incoming = new Audio(src);
|
|
12559
|
-
incoming.loop = true;
|
|
12560
|
-
incoming.volume = 0;
|
|
12561
|
-
const outgoing = currentMusicElRef.current;
|
|
12562
|
-
const outgoingStartVol = outgoing?.volume ?? 0;
|
|
12563
|
-
currentMusicKeyRef.current = key;
|
|
12564
|
-
currentMusicElRef.current = incoming;
|
|
12565
|
-
if (!mutedRef.current) {
|
|
12566
|
-
incoming.play().catch(() => {
|
|
12567
|
-
currentMusicKeyRef.current = null;
|
|
12568
|
-
currentMusicElRef.current = outgoing;
|
|
12569
|
-
});
|
|
12570
|
-
}
|
|
12571
|
-
let step = 0;
|
|
12572
|
-
musicFadeRef.current = setInterval(() => {
|
|
12573
|
-
step++;
|
|
12574
|
-
const progress = Math.min(step / totalSteps, 1);
|
|
12575
|
-
incoming.volume = Math.min(1, targetVolume * progress);
|
|
12576
|
-
if (outgoing) {
|
|
12577
|
-
outgoing.volume = Math.max(0, outgoingStartVol * (1 - progress));
|
|
12578
|
-
}
|
|
12579
|
-
if (progress >= 1) {
|
|
12580
|
-
clearMusicFade();
|
|
12581
|
-
if (outgoing) {
|
|
12582
|
-
outgoing.pause();
|
|
12583
|
-
outgoing.src = "";
|
|
12584
|
-
}
|
|
12585
|
-
}
|
|
12586
|
-
}, stepMs);
|
|
12587
|
-
}, [baseUrl, clearMusicFade]);
|
|
12588
|
-
const stopMusic = useCallback((fadeDurationMs = 1e3) => {
|
|
12589
|
-
const outgoing = currentMusicElRef.current;
|
|
12590
|
-
if (!outgoing) return;
|
|
12591
|
-
currentMusicKeyRef.current = null;
|
|
12592
|
-
currentMusicElRef.current = null;
|
|
12593
|
-
pendingMusicKeyRef.current = null;
|
|
12594
|
-
clearMusicFade();
|
|
12595
|
-
const startVolume = outgoing.volume;
|
|
12596
|
-
const stepMs = 50;
|
|
12597
|
-
const totalSteps = Math.max(1, fadeDurationMs / stepMs);
|
|
12598
|
-
let step = 0;
|
|
12599
|
-
musicFadeRef.current = setInterval(() => {
|
|
12600
|
-
step++;
|
|
12601
|
-
const progress = step / totalSteps;
|
|
12602
|
-
outgoing.volume = Math.max(0, startVolume * (1 - progress));
|
|
12603
|
-
if (progress >= 1) {
|
|
12604
|
-
clearMusicFade();
|
|
12605
|
-
outgoing.pause();
|
|
12606
|
-
outgoing.src = "";
|
|
12607
|
-
}
|
|
12608
|
-
}, stepMs);
|
|
12609
|
-
}, [clearMusicFade]);
|
|
12610
|
-
const stopAll = useCallback(() => {
|
|
12611
|
-
for (const pool of poolsRef.current.values()) {
|
|
12612
|
-
for (const audio of pool) {
|
|
12613
|
-
audio.pause();
|
|
12614
|
-
audio.currentTime = 0;
|
|
12615
|
-
}
|
|
12616
|
-
}
|
|
12617
|
-
stopMusic(0);
|
|
12618
|
-
}, [stopMusic]);
|
|
12619
|
-
const setMuted = useCallback((value) => {
|
|
12620
|
-
setMutedState(value);
|
|
12621
|
-
if (value) {
|
|
12622
|
-
for (const [key, pool] of poolsRef.current.entries()) {
|
|
12623
|
-
if (manifestRef.current[key]?.loop) {
|
|
12624
|
-
for (const audio of pool) {
|
|
12625
|
-
if (!audio.paused) audio.pause();
|
|
12626
|
-
}
|
|
12627
|
-
}
|
|
12628
|
-
}
|
|
12629
|
-
currentMusicElRef.current?.pause();
|
|
12630
|
-
} else {
|
|
12631
|
-
for (const [key, pool] of poolsRef.current.entries()) {
|
|
12632
|
-
const entry = manifestRef.current[key];
|
|
12633
|
-
if (entry?.loop && entry?.autostart) {
|
|
12634
|
-
for (const audio of pool) {
|
|
12635
|
-
if (audio.paused) audio.play().catch(() => {
|
|
12636
|
-
});
|
|
12637
|
-
}
|
|
12638
|
-
}
|
|
12639
|
-
}
|
|
12640
|
-
const musicEl = currentMusicElRef.current;
|
|
12641
|
-
if (musicEl) {
|
|
12642
|
-
musicEl.play().catch(() => {
|
|
12643
|
-
});
|
|
12644
|
-
}
|
|
12645
|
-
}
|
|
12646
|
-
}, []);
|
|
12647
|
-
const setMasterVolume = useCallback((volume) => {
|
|
12648
|
-
const clamped = Math.max(0, Math.min(1, volume));
|
|
12649
|
-
setMasterVolumeState(clamped);
|
|
12650
|
-
for (const [key, pool] of poolsRef.current.entries()) {
|
|
12651
|
-
const entryVol = manifestRef.current[key]?.volume ?? 1;
|
|
12652
|
-
for (const audio of pool) {
|
|
12653
|
-
audio.volume = Math.min(1, entryVol * clamped);
|
|
12654
|
-
}
|
|
12655
|
-
}
|
|
12656
|
-
if (!musicFadeRef.current && currentMusicElRef.current) {
|
|
12657
|
-
const key = currentMusicKeyRef.current;
|
|
12658
|
-
const entryVol = key ? manifestRef.current[key]?.volume ?? 1 : 1;
|
|
12659
|
-
currentMusicElRef.current.volume = Math.min(1, entryVol * clamped);
|
|
12660
|
-
}
|
|
12661
|
-
}, []);
|
|
12662
|
-
const unlockedRef = useRef(false);
|
|
12663
|
-
useEffect(() => {
|
|
12664
|
-
const autoKeys = Object.keys(manifest).filter((k) => manifest[k].autostart);
|
|
12665
|
-
const hasPendingMusic = () => pendingMusicKeyRef.current !== null;
|
|
12666
|
-
const hasAutoStart = autoKeys.length > 0;
|
|
12667
|
-
if (!hasAutoStart && !hasPendingMusic()) return;
|
|
12668
|
-
const unlock = () => {
|
|
12669
|
-
if (unlockedRef.current) return;
|
|
12670
|
-
unlockedRef.current = true;
|
|
12671
|
-
if (!mutedRef.current) {
|
|
12672
|
-
for (const key of autoKeys) {
|
|
12673
|
-
play(key);
|
|
12674
|
-
}
|
|
12675
|
-
const pending = pendingMusicKeyRef.current;
|
|
12676
|
-
if (pending && pending !== currentMusicKeyRef.current) {
|
|
12677
|
-
playMusic(pending);
|
|
12678
|
-
}
|
|
12679
|
-
}
|
|
12680
|
-
};
|
|
12681
|
-
document.addEventListener("click", unlock, { once: true });
|
|
12682
|
-
document.addEventListener("keydown", unlock, { once: true });
|
|
12683
|
-
document.addEventListener("touchstart", unlock, { once: true });
|
|
12684
|
-
return () => {
|
|
12685
|
-
document.removeEventListener("click", unlock);
|
|
12686
|
-
document.removeEventListener("keydown", unlock);
|
|
12687
|
-
document.removeEventListener("touchstart", unlock);
|
|
12688
|
-
};
|
|
12689
|
-
}, [manifest, play, playMusic]);
|
|
12690
|
-
useEffect(() => {
|
|
12691
|
-
return () => {
|
|
12692
|
-
clearMusicFade();
|
|
12693
|
-
for (const pool of poolsRef.current.values()) {
|
|
12694
|
-
for (const audio of pool) {
|
|
12695
|
-
audio.pause();
|
|
12696
|
-
audio.src = "";
|
|
12697
|
-
}
|
|
12698
|
-
}
|
|
12699
|
-
poolsRef.current.clear();
|
|
12700
|
-
if (currentMusicElRef.current) {
|
|
12701
|
-
currentMusicElRef.current.pause();
|
|
12702
|
-
currentMusicElRef.current.src = "";
|
|
12703
|
-
currentMusicElRef.current = null;
|
|
12704
|
-
}
|
|
12705
|
-
};
|
|
12706
|
-
}, [clearMusicFade]);
|
|
12707
|
-
return {
|
|
12708
|
-
play,
|
|
12709
|
-
stop,
|
|
12710
|
-
stopAll,
|
|
12711
|
-
playMusic,
|
|
12712
|
-
stopMusic,
|
|
12713
|
-
muted,
|
|
12714
|
-
setMuted,
|
|
12715
|
-
masterVolume,
|
|
12716
|
-
setMasterVolume
|
|
12717
|
-
};
|
|
12718
|
-
}
|
|
12719
12546
|
var init_useGameAudio = __esm({
|
|
12720
12547
|
"components/game/shared/hooks/useGameAudio.ts"() {
|
|
12721
12548
|
"use client";
|
|
12722
|
-
useGameAudio.displayName = "useGameAudio";
|
|
12723
12549
|
}
|
|
12724
12550
|
});
|
|
12725
12551
|
function useGameAudioContextOptional() {
|
|
12726
12552
|
return useContext(GameAudioContext);
|
|
12727
12553
|
}
|
|
12728
|
-
function GameAudioProvider({
|
|
12729
|
-
manifest,
|
|
12730
|
-
baseUrl = "",
|
|
12731
|
-
children,
|
|
12732
|
-
initialMuted = false
|
|
12733
|
-
}) {
|
|
12734
|
-
const eventBus = useEventBus();
|
|
12735
|
-
const { play, stop, stopAll, playMusic, stopMusic, muted, setMuted, masterVolume, setMasterVolume } = useGameAudio({ manifest, baseUrl, initialMuted });
|
|
12736
|
-
useEffect(() => {
|
|
12737
|
-
const unsubPlay = eventBus.on("UI:PLAY_SOUND", (event) => {
|
|
12738
|
-
const key = event.payload?.key;
|
|
12739
|
-
if (key) play(key);
|
|
12740
|
-
});
|
|
12741
|
-
const unsubStop = eventBus.on("UI:STOP_SOUND", (event) => {
|
|
12742
|
-
const key = event.payload?.key;
|
|
12743
|
-
if (key) {
|
|
12744
|
-
stop(key);
|
|
12745
|
-
} else {
|
|
12746
|
-
stopAll();
|
|
12747
|
-
}
|
|
12748
|
-
});
|
|
12749
|
-
const unsubChangeMusic = eventBus.on("UI:CHANGE_MUSIC", (event) => {
|
|
12750
|
-
const key = event.payload?.key;
|
|
12751
|
-
if (key) {
|
|
12752
|
-
playMusic(key);
|
|
12753
|
-
} else {
|
|
12754
|
-
stopMusic();
|
|
12755
|
-
}
|
|
12756
|
-
});
|
|
12757
|
-
const unsubStopMusic = eventBus.on("UI:STOP_MUSIC", () => {
|
|
12758
|
-
stopMusic();
|
|
12759
|
-
});
|
|
12760
|
-
return () => {
|
|
12761
|
-
unsubPlay();
|
|
12762
|
-
unsubStop();
|
|
12763
|
-
unsubChangeMusic();
|
|
12764
|
-
unsubStopMusic();
|
|
12765
|
-
};
|
|
12766
|
-
}, [eventBus, play, stop, stopAll, playMusic, stopMusic]);
|
|
12767
|
-
const value = { muted, setMuted, masterVolume, setMasterVolume, play, playMusic, stopMusic };
|
|
12768
|
-
return /* @__PURE__ */ jsx(GameAudioContext.Provider, { value, children });
|
|
12769
|
-
}
|
|
12770
12554
|
var GameAudioContext;
|
|
12771
12555
|
var init_GameAudioProvider = __esm({
|
|
12772
12556
|
"components/game/shared/providers/GameAudioProvider.tsx"() {
|
|
@@ -12775,7 +12559,6 @@ var init_GameAudioProvider = __esm({
|
|
|
12775
12559
|
init_useGameAudio();
|
|
12776
12560
|
GameAudioContext = createContext(null);
|
|
12777
12561
|
GameAudioContext.displayName = "GameAudioContext";
|
|
12778
|
-
GameAudioProvider.displayName = "GameAudioProvider";
|
|
12779
12562
|
}
|
|
12780
12563
|
});
|
|
12781
12564
|
function GameAudioToggle({
|
|
@@ -20411,6 +20194,315 @@ var init_molecules = __esm({
|
|
|
20411
20194
|
init_puzzleObject();
|
|
20412
20195
|
}
|
|
20413
20196
|
});
|
|
20197
|
+
function resolveColor2(color, ctx, fallback) {
|
|
20198
|
+
if (!color) return fallback;
|
|
20199
|
+
if (color.startsWith("var(")) {
|
|
20200
|
+
ctx.canvas.style;
|
|
20201
|
+
const m = /^var\((--[^,)]+)(?:,\s*([^)]+))?\)$/.exec(color);
|
|
20202
|
+
if (m) {
|
|
20203
|
+
const computed = getComputedStyle(ctx.canvas).getPropertyValue(m[1]).trim();
|
|
20204
|
+
return computed || m[2]?.trim() || fallback;
|
|
20205
|
+
}
|
|
20206
|
+
}
|
|
20207
|
+
return color;
|
|
20208
|
+
}
|
|
20209
|
+
function shapeBounds(shape) {
|
|
20210
|
+
switch (shape.type) {
|
|
20211
|
+
case "line":
|
|
20212
|
+
case "arrow":
|
|
20213
|
+
if (shape.x1 == null || shape.y1 == null || shape.x2 == null || shape.y2 == null) return null;
|
|
20214
|
+
return {
|
|
20215
|
+
x: Math.min(shape.x1, shape.x2) - 6,
|
|
20216
|
+
y: Math.min(shape.y1, shape.y2) - 6,
|
|
20217
|
+
w: Math.abs(shape.x2 - shape.x1) + 12,
|
|
20218
|
+
h: Math.abs(shape.y2 - shape.y1) + 12
|
|
20219
|
+
};
|
|
20220
|
+
case "circle":
|
|
20221
|
+
if (shape.x == null || shape.y == null || shape.radius == null) return null;
|
|
20222
|
+
return {
|
|
20223
|
+
x: shape.x - shape.radius - 4,
|
|
20224
|
+
y: shape.y - shape.radius - 4,
|
|
20225
|
+
w: shape.radius * 2 + 8,
|
|
20226
|
+
h: shape.radius * 2 + 8
|
|
20227
|
+
};
|
|
20228
|
+
case "rect":
|
|
20229
|
+
if (shape.x == null || shape.y == null || shape.width == null || shape.height == null) return null;
|
|
20230
|
+
return { x: shape.x - 4, y: shape.y - 4, w: shape.width + 8, h: shape.height + 8 };
|
|
20231
|
+
case "polygon":
|
|
20232
|
+
if (!shape.points || shape.points.length === 0) return null;
|
|
20233
|
+
{
|
|
20234
|
+
const xs = shape.points.map((p2) => p2.x);
|
|
20235
|
+
const ys = shape.points.map((p2) => p2.y);
|
|
20236
|
+
const minX = Math.min(...xs);
|
|
20237
|
+
const minY = Math.min(...ys);
|
|
20238
|
+
return {
|
|
20239
|
+
x: minX - 4,
|
|
20240
|
+
y: minY - 4,
|
|
20241
|
+
w: Math.max(...xs) - minX + 8,
|
|
20242
|
+
h: Math.max(...ys) - minY + 8
|
|
20243
|
+
};
|
|
20244
|
+
}
|
|
20245
|
+
case "text":
|
|
20246
|
+
if (shape.x == null || shape.y == null) return null;
|
|
20247
|
+
return { x: shape.x - 4, y: shape.y - (shape.fontSize ?? 14) - 4, w: 120, h: (shape.fontSize ?? 14) + 8 };
|
|
20248
|
+
default:
|
|
20249
|
+
return null;
|
|
20250
|
+
}
|
|
20251
|
+
}
|
|
20252
|
+
function drawArrowHead(ctx, x1, y1, x2, y2, size) {
|
|
20253
|
+
const angle = Math.atan2(y2 - y1, x2 - x1);
|
|
20254
|
+
ctx.beginPath();
|
|
20255
|
+
ctx.moveTo(x2, y2);
|
|
20256
|
+
ctx.lineTo(x2 - size * Math.cos(angle - Math.PI / 6), y2 - size * Math.sin(angle - Math.PI / 6));
|
|
20257
|
+
ctx.lineTo(x2 - size * Math.cos(angle + Math.PI / 6), y2 - size * Math.sin(angle + Math.PI / 6));
|
|
20258
|
+
ctx.closePath();
|
|
20259
|
+
ctx.fill();
|
|
20260
|
+
}
|
|
20261
|
+
function drawShape(ctx, shape, width, height) {
|
|
20262
|
+
ctx.save();
|
|
20263
|
+
const opacity = shape.opacity ?? 1;
|
|
20264
|
+
ctx.globalAlpha = opacity;
|
|
20265
|
+
const stroke = resolveColor2(shape.color, ctx, "#333333");
|
|
20266
|
+
const fill = shape.fill ? resolveColor2(shape.fill, ctx, "#cccccc") : void 0;
|
|
20267
|
+
ctx.lineWidth = shape.lineWidth ?? 2;
|
|
20268
|
+
switch (shape.type) {
|
|
20269
|
+
case "grid": {
|
|
20270
|
+
const step = shape.step ?? 40;
|
|
20271
|
+
ctx.strokeStyle = stroke;
|
|
20272
|
+
ctx.globalAlpha = opacity * 0.25;
|
|
20273
|
+
ctx.lineWidth = 1;
|
|
20274
|
+
ctx.beginPath();
|
|
20275
|
+
for (let x = 0; x <= width; x += step) {
|
|
20276
|
+
ctx.moveTo(x, 0);
|
|
20277
|
+
ctx.lineTo(x, height);
|
|
20278
|
+
}
|
|
20279
|
+
for (let y = 0; y <= height; y += step) {
|
|
20280
|
+
ctx.moveTo(0, y);
|
|
20281
|
+
ctx.lineTo(width, y);
|
|
20282
|
+
}
|
|
20283
|
+
ctx.stroke();
|
|
20284
|
+
break;
|
|
20285
|
+
}
|
|
20286
|
+
case "axis": {
|
|
20287
|
+
const axis = shape.axis ?? "x";
|
|
20288
|
+
ctx.strokeStyle = stroke;
|
|
20289
|
+
ctx.lineWidth = shape.lineWidth ?? 2;
|
|
20290
|
+
ctx.beginPath();
|
|
20291
|
+
if (axis === "x") {
|
|
20292
|
+
ctx.moveTo(0, height / 2);
|
|
20293
|
+
ctx.lineTo(width, height / 2);
|
|
20294
|
+
} else {
|
|
20295
|
+
ctx.moveTo(width / 2, 0);
|
|
20296
|
+
ctx.lineTo(width / 2, height);
|
|
20297
|
+
}
|
|
20298
|
+
ctx.stroke();
|
|
20299
|
+
break;
|
|
20300
|
+
}
|
|
20301
|
+
case "line": {
|
|
20302
|
+
if (shape.x1 == null || shape.y1 == null || shape.x2 == null || shape.y2 == null) break;
|
|
20303
|
+
ctx.strokeStyle = stroke;
|
|
20304
|
+
ctx.beginPath();
|
|
20305
|
+
ctx.moveTo(shape.x1, shape.y1);
|
|
20306
|
+
ctx.lineTo(shape.x2, shape.y2);
|
|
20307
|
+
ctx.stroke();
|
|
20308
|
+
break;
|
|
20309
|
+
}
|
|
20310
|
+
case "arrow": {
|
|
20311
|
+
if (shape.x1 == null || shape.y1 == null || shape.x2 == null || shape.y2 == null) break;
|
|
20312
|
+
ctx.strokeStyle = stroke;
|
|
20313
|
+
ctx.fillStyle = stroke;
|
|
20314
|
+
ctx.beginPath();
|
|
20315
|
+
ctx.moveTo(shape.x1, shape.y1);
|
|
20316
|
+
ctx.lineTo(shape.x2, shape.y2);
|
|
20317
|
+
ctx.stroke();
|
|
20318
|
+
drawArrowHead(ctx, shape.x1, shape.y1, shape.x2, shape.y2, 10);
|
|
20319
|
+
break;
|
|
20320
|
+
}
|
|
20321
|
+
case "circle": {
|
|
20322
|
+
if (shape.x == null || shape.y == null || shape.radius == null) break;
|
|
20323
|
+
ctx.beginPath();
|
|
20324
|
+
ctx.arc(shape.x, shape.y, shape.radius, 0, Math.PI * 2);
|
|
20325
|
+
if (fill) {
|
|
20326
|
+
ctx.fillStyle = fill;
|
|
20327
|
+
ctx.fill();
|
|
20328
|
+
}
|
|
20329
|
+
ctx.strokeStyle = stroke;
|
|
20330
|
+
ctx.stroke();
|
|
20331
|
+
break;
|
|
20332
|
+
}
|
|
20333
|
+
case "rect": {
|
|
20334
|
+
if (shape.x == null || shape.y == null || shape.width == null || shape.height == null) break;
|
|
20335
|
+
if (fill) {
|
|
20336
|
+
ctx.fillStyle = fill;
|
|
20337
|
+
ctx.fillRect(shape.x, shape.y, shape.width, shape.height);
|
|
20338
|
+
}
|
|
20339
|
+
ctx.strokeStyle = stroke;
|
|
20340
|
+
ctx.strokeRect(shape.x, shape.y, shape.width, shape.height);
|
|
20341
|
+
break;
|
|
20342
|
+
}
|
|
20343
|
+
case "polygon": {
|
|
20344
|
+
if (!shape.points || shape.points.length < 2) break;
|
|
20345
|
+
ctx.beginPath();
|
|
20346
|
+
ctx.moveTo(shape.points[0].x, shape.points[0].y);
|
|
20347
|
+
for (let i = 1; i < shape.points.length; i++) {
|
|
20348
|
+
ctx.lineTo(shape.points[i].x, shape.points[i].y);
|
|
20349
|
+
}
|
|
20350
|
+
ctx.closePath();
|
|
20351
|
+
if (fill) {
|
|
20352
|
+
ctx.fillStyle = fill;
|
|
20353
|
+
ctx.fill();
|
|
20354
|
+
}
|
|
20355
|
+
ctx.strokeStyle = stroke;
|
|
20356
|
+
ctx.stroke();
|
|
20357
|
+
break;
|
|
20358
|
+
}
|
|
20359
|
+
case "path": {
|
|
20360
|
+
if (!shape.path) break;
|
|
20361
|
+
const p2 = new Path2D(shape.path);
|
|
20362
|
+
if (fill) {
|
|
20363
|
+
ctx.fillStyle = fill;
|
|
20364
|
+
ctx.fill(p2);
|
|
20365
|
+
}
|
|
20366
|
+
ctx.strokeStyle = stroke;
|
|
20367
|
+
ctx.stroke(p2);
|
|
20368
|
+
break;
|
|
20369
|
+
}
|
|
20370
|
+
case "text": {
|
|
20371
|
+
if (shape.x == null || shape.y == null || !shape.text) break;
|
|
20372
|
+
ctx.fillStyle = stroke;
|
|
20373
|
+
ctx.font = `${shape.fontSize ?? 14}px system-ui, sans-serif`;
|
|
20374
|
+
ctx.textAlign = shape.align ?? "left";
|
|
20375
|
+
ctx.textBaseline = "middle";
|
|
20376
|
+
ctx.fillText(shape.text, shape.x, shape.y);
|
|
20377
|
+
break;
|
|
20378
|
+
}
|
|
20379
|
+
}
|
|
20380
|
+
ctx.restore();
|
|
20381
|
+
}
|
|
20382
|
+
var LearningCanvas;
|
|
20383
|
+
var init_LearningCanvas = __esm({
|
|
20384
|
+
"components/learning/atoms/LearningCanvas.tsx"() {
|
|
20385
|
+
"use client";
|
|
20386
|
+
init_cn();
|
|
20387
|
+
init_useEventBus();
|
|
20388
|
+
LearningCanvas = ({
|
|
20389
|
+
className,
|
|
20390
|
+
width = 600,
|
|
20391
|
+
height = 400,
|
|
20392
|
+
backgroundColor,
|
|
20393
|
+
shapes = [],
|
|
20394
|
+
interactive = false,
|
|
20395
|
+
animate = false,
|
|
20396
|
+
onShapeClick,
|
|
20397
|
+
onShapeHover,
|
|
20398
|
+
isLoading,
|
|
20399
|
+
error
|
|
20400
|
+
}) => {
|
|
20401
|
+
const canvasRef = useRef(null);
|
|
20402
|
+
const eventBus = useEventBus();
|
|
20403
|
+
const animRef = useRef(0);
|
|
20404
|
+
const hoverIndexRef = useRef(-1);
|
|
20405
|
+
const findShapeAt = useCallback((clientX, clientY) => {
|
|
20406
|
+
const canvas = canvasRef.current;
|
|
20407
|
+
if (!canvas) return -1;
|
|
20408
|
+
const rect = canvas.getBoundingClientRect();
|
|
20409
|
+
const x = clientX - rect.left;
|
|
20410
|
+
const y = clientY - rect.top;
|
|
20411
|
+
for (let i = shapes.length - 1; i >= 0; i--) {
|
|
20412
|
+
const b = shapeBounds(shapes[i]);
|
|
20413
|
+
if (b && x >= b.x && x <= b.x + b.w && y >= b.y && y <= b.y + b.h) {
|
|
20414
|
+
return i;
|
|
20415
|
+
}
|
|
20416
|
+
}
|
|
20417
|
+
return -1;
|
|
20418
|
+
}, [shapes]);
|
|
20419
|
+
const draw = useCallback(() => {
|
|
20420
|
+
const canvas = canvasRef.current;
|
|
20421
|
+
if (!canvas) return;
|
|
20422
|
+
const ctx = canvas.getContext("2d");
|
|
20423
|
+
if (!ctx) return;
|
|
20424
|
+
const dpr = typeof window !== "undefined" ? window.devicePixelRatio || 1 : 1;
|
|
20425
|
+
canvas.width = Math.max(1, Math.floor(width * dpr));
|
|
20426
|
+
canvas.height = Math.max(1, Math.floor(height * dpr));
|
|
20427
|
+
canvas.style.width = `${width}px`;
|
|
20428
|
+
canvas.style.height = `${height}px`;
|
|
20429
|
+
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
|
|
20430
|
+
ctx.clearRect(0, 0, width, height);
|
|
20431
|
+
if (backgroundColor) {
|
|
20432
|
+
ctx.fillStyle = backgroundColor;
|
|
20433
|
+
ctx.fillRect(0, 0, width, height);
|
|
20434
|
+
}
|
|
20435
|
+
for (const shape of shapes) {
|
|
20436
|
+
drawShape(ctx, shape, width, height);
|
|
20437
|
+
}
|
|
20438
|
+
}, [width, height, backgroundColor, shapes]);
|
|
20439
|
+
useEffect(() => {
|
|
20440
|
+
draw();
|
|
20441
|
+
}, [draw]);
|
|
20442
|
+
useEffect(() => {
|
|
20443
|
+
if (!animate) return;
|
|
20444
|
+
const loop = () => {
|
|
20445
|
+
draw();
|
|
20446
|
+
animRef.current = requestAnimationFrame(loop);
|
|
20447
|
+
};
|
|
20448
|
+
animRef.current = requestAnimationFrame(loop);
|
|
20449
|
+
return () => cancelAnimationFrame(animRef.current);
|
|
20450
|
+
}, [animate, draw]);
|
|
20451
|
+
const handlePointerMove = useCallback(
|
|
20452
|
+
(e) => {
|
|
20453
|
+
if (!interactive) return;
|
|
20454
|
+
const idx = findShapeAt(e.clientX, e.clientY);
|
|
20455
|
+
if (idx !== hoverIndexRef.current) {
|
|
20456
|
+
hoverIndexRef.current = idx;
|
|
20457
|
+
if (idx >= 0) {
|
|
20458
|
+
const shape = shapes[idx];
|
|
20459
|
+
const payload = { id: shape.id, type: shape.type, index: idx };
|
|
20460
|
+
if (onShapeHover) onShapeHover(payload);
|
|
20461
|
+
else if (eventBus) eventBus.emit(`UI:SHAPE_HOVER`, payload);
|
|
20462
|
+
}
|
|
20463
|
+
}
|
|
20464
|
+
},
|
|
20465
|
+
[interactive, onShapeHover, eventBus, findShapeAt, shapes]
|
|
20466
|
+
);
|
|
20467
|
+
const handleClick = useCallback(
|
|
20468
|
+
(e) => {
|
|
20469
|
+
if (!interactive) return;
|
|
20470
|
+
const idx = findShapeAt(e.clientX, e.clientY);
|
|
20471
|
+
if (idx >= 0) {
|
|
20472
|
+
const shape = shapes[idx];
|
|
20473
|
+
const payload = { id: shape.id, type: shape.type, index: idx };
|
|
20474
|
+
if (onShapeClick) onShapeClick(payload);
|
|
20475
|
+
else if (eventBus) eventBus.emit(`UI:SHAPE_CLICK`, payload);
|
|
20476
|
+
}
|
|
20477
|
+
},
|
|
20478
|
+
[interactive, onShapeClick, eventBus, findShapeAt, shapes]
|
|
20479
|
+
);
|
|
20480
|
+
if (isLoading || error) {
|
|
20481
|
+
return /* @__PURE__ */ jsx(
|
|
20482
|
+
"div",
|
|
20483
|
+
{
|
|
20484
|
+
className: cn(
|
|
20485
|
+
"flex items-center justify-center rounded border border-border bg-surface",
|
|
20486
|
+
className
|
|
20487
|
+
),
|
|
20488
|
+
style: { width, height },
|
|
20489
|
+
children: error ? /* @__PURE__ */ jsx("span", { className: "text-sm text-destructive", children: error.message }) : /* @__PURE__ */ jsx("span", { className: "text-sm text-muted-foreground", children: "Loading canvas\u2026" })
|
|
20490
|
+
}
|
|
20491
|
+
);
|
|
20492
|
+
}
|
|
20493
|
+
return /* @__PURE__ */ jsx(
|
|
20494
|
+
"canvas",
|
|
20495
|
+
{
|
|
20496
|
+
ref: canvasRef,
|
|
20497
|
+
className: cn("block touch-none rounded border border-border", className),
|
|
20498
|
+
style: { width, height },
|
|
20499
|
+
onClick: handleClick,
|
|
20500
|
+
onPointerMove: handlePointerMove
|
|
20501
|
+
}
|
|
20502
|
+
);
|
|
20503
|
+
};
|
|
20504
|
+
}
|
|
20505
|
+
});
|
|
20414
20506
|
|
|
20415
20507
|
// components/core/atoms/index.ts
|
|
20416
20508
|
var init_atoms = __esm({
|
|
@@ -21896,91 +21988,6 @@ var init_ComponentPatterns = __esm({
|
|
|
21896
21988
|
AlertPattern.displayName = "AlertPattern";
|
|
21897
21989
|
}
|
|
21898
21990
|
});
|
|
21899
|
-
function parseValue(value) {
|
|
21900
|
-
if (value === "" || value == null) return { num: 0, prefix: "", suffix: "", decimals: 0 };
|
|
21901
|
-
const match = String(value).match(/^([^0-9]*)([0-9]+(?:\.[0-9]+)?)(.*)$/);
|
|
21902
|
-
if (!match) {
|
|
21903
|
-
return { num: 0, prefix: "", suffix: String(value), decimals: 0 };
|
|
21904
|
-
}
|
|
21905
|
-
const numStr = match[2];
|
|
21906
|
-
const decimalIdx = numStr.indexOf(".");
|
|
21907
|
-
const decimals = decimalIdx >= 0 ? numStr.length - decimalIdx - 1 : 0;
|
|
21908
|
-
return {
|
|
21909
|
-
prefix: match[1],
|
|
21910
|
-
num: parseFloat(numStr),
|
|
21911
|
-
suffix: match[3],
|
|
21912
|
-
decimals
|
|
21913
|
-
};
|
|
21914
|
-
}
|
|
21915
|
-
var AnimatedCounter;
|
|
21916
|
-
var init_AnimatedCounter = __esm({
|
|
21917
|
-
"components/core/molecules/AnimatedCounter.tsx"() {
|
|
21918
|
-
"use client";
|
|
21919
|
-
init_cn();
|
|
21920
|
-
init_Box();
|
|
21921
|
-
init_Typography();
|
|
21922
|
-
AnimatedCounter = ({
|
|
21923
|
-
value,
|
|
21924
|
-
label,
|
|
21925
|
-
duration = 1500,
|
|
21926
|
-
className
|
|
21927
|
-
}) => {
|
|
21928
|
-
const ref = useRef(null);
|
|
21929
|
-
const [displayValue, setDisplayValue] = useState("0");
|
|
21930
|
-
const [hasAnimated, setHasAnimated] = useState(false);
|
|
21931
|
-
const animate = useCallback(() => {
|
|
21932
|
-
const { num: num2, prefix, suffix, decimals } = parseValue(value);
|
|
21933
|
-
if (num2 === 0) {
|
|
21934
|
-
setDisplayValue(String(value));
|
|
21935
|
-
return;
|
|
21936
|
-
}
|
|
21937
|
-
const startTime = performance.now();
|
|
21938
|
-
const tick = (now2) => {
|
|
21939
|
-
const elapsed = now2 - startTime;
|
|
21940
|
-
const progress = Math.min(elapsed / duration, 1);
|
|
21941
|
-
const eased = 1 - Math.pow(1 - progress, 3);
|
|
21942
|
-
const current = eased * num2;
|
|
21943
|
-
setDisplayValue(`${prefix}${current.toFixed(decimals)}${suffix}`);
|
|
21944
|
-
if (progress < 1) {
|
|
21945
|
-
requestAnimationFrame(tick);
|
|
21946
|
-
} else {
|
|
21947
|
-
setDisplayValue(String(value));
|
|
21948
|
-
}
|
|
21949
|
-
};
|
|
21950
|
-
requestAnimationFrame(tick);
|
|
21951
|
-
}, [value, duration]);
|
|
21952
|
-
useEffect(() => {
|
|
21953
|
-
if (hasAnimated) return;
|
|
21954
|
-
const el = ref.current;
|
|
21955
|
-
if (!el) return;
|
|
21956
|
-
const observer2 = new IntersectionObserver(
|
|
21957
|
-
(entries) => {
|
|
21958
|
-
if (entries[0].isIntersecting) {
|
|
21959
|
-
setHasAnimated(true);
|
|
21960
|
-
animate();
|
|
21961
|
-
observer2.disconnect();
|
|
21962
|
-
}
|
|
21963
|
-
},
|
|
21964
|
-
{ threshold: 0.3 }
|
|
21965
|
-
);
|
|
21966
|
-
observer2.observe(el);
|
|
21967
|
-
return () => observer2.disconnect();
|
|
21968
|
-
}, [hasAnimated, animate]);
|
|
21969
|
-
return /* @__PURE__ */ jsxs(Box, { ref, className: cn("flex flex-col items-center gap-1 p-4", className), children: [
|
|
21970
|
-
/* @__PURE__ */ jsx(
|
|
21971
|
-
Typography,
|
|
21972
|
-
{
|
|
21973
|
-
variant: "h2",
|
|
21974
|
-
className: "text-primary font-bold tabular-nums",
|
|
21975
|
-
children: hasAnimated ? displayValue : "0"
|
|
21976
|
-
}
|
|
21977
|
-
),
|
|
21978
|
-
/* @__PURE__ */ jsx(Typography, { variant: "body2", color: "muted", className: "text-center", children: label })
|
|
21979
|
-
] });
|
|
21980
|
-
};
|
|
21981
|
-
AnimatedCounter.displayName = "AnimatedCounter";
|
|
21982
|
-
}
|
|
21983
|
-
});
|
|
21984
21991
|
var AuthLayout;
|
|
21985
21992
|
var init_AuthLayout = __esm({
|
|
21986
21993
|
"components/marketing/templates/AuthLayout.tsx"() {
|
|
@@ -22949,10 +22956,107 @@ var init_BehaviorView = __esm({
|
|
|
22949
22956
|
BehaviorView.displayName = "BehaviorView";
|
|
22950
22957
|
}
|
|
22951
22958
|
});
|
|
22959
|
+
var BiologyCanvas;
|
|
22960
|
+
var init_BiologyCanvas = __esm({
|
|
22961
|
+
"components/learning/molecules/BiologyCanvas.tsx"() {
|
|
22962
|
+
"use client";
|
|
22963
|
+
init_atoms();
|
|
22964
|
+
init_Stack();
|
|
22965
|
+
init_LearningCanvas();
|
|
22966
|
+
BiologyCanvas = ({
|
|
22967
|
+
className,
|
|
22968
|
+
width = 600,
|
|
22969
|
+
height = 400,
|
|
22970
|
+
title,
|
|
22971
|
+
backgroundColor,
|
|
22972
|
+
nodes = [],
|
|
22973
|
+
edges = [],
|
|
22974
|
+
shapes = [],
|
|
22975
|
+
interactive = false,
|
|
22976
|
+
animate = false,
|
|
22977
|
+
onShapeClick,
|
|
22978
|
+
isLoading,
|
|
22979
|
+
error
|
|
22980
|
+
}) => {
|
|
22981
|
+
const derivedShapes = useMemo(() => {
|
|
22982
|
+
const out = [];
|
|
22983
|
+
const nodeById = /* @__PURE__ */ new Map();
|
|
22984
|
+
for (const n of nodes) {
|
|
22985
|
+
if (n.id) nodeById.set(n.id, n);
|
|
22986
|
+
}
|
|
22987
|
+
for (const e of edges) {
|
|
22988
|
+
const a = nodeById.get(e.from);
|
|
22989
|
+
const b = nodeById.get(e.to);
|
|
22990
|
+
if (!a || !b) continue;
|
|
22991
|
+
out.push({
|
|
22992
|
+
type: "line",
|
|
22993
|
+
x1: a.x,
|
|
22994
|
+
y1: a.y,
|
|
22995
|
+
x2: b.x,
|
|
22996
|
+
y2: b.y,
|
|
22997
|
+
color: e.color ?? "#9ca3af",
|
|
22998
|
+
lineWidth: 2
|
|
22999
|
+
});
|
|
23000
|
+
if (e.label) {
|
|
23001
|
+
out.push({
|
|
23002
|
+
type: "text",
|
|
23003
|
+
x: (a.x + b.x) / 2 + 4,
|
|
23004
|
+
y: (a.y + b.y) / 2 - 4,
|
|
23005
|
+
text: e.label,
|
|
23006
|
+
color: "#374151",
|
|
23007
|
+
fontSize: 11
|
|
23008
|
+
});
|
|
23009
|
+
}
|
|
23010
|
+
}
|
|
23011
|
+
for (const n of nodes) {
|
|
23012
|
+
out.push({
|
|
23013
|
+
type: "circle",
|
|
23014
|
+
x: n.x,
|
|
23015
|
+
y: n.y,
|
|
23016
|
+
radius: n.radius ?? 16,
|
|
23017
|
+
color: n.color ?? "#16a34a",
|
|
23018
|
+
fill: `${n.color ?? "#16a34a"}33`,
|
|
23019
|
+
id: n.id
|
|
23020
|
+
});
|
|
23021
|
+
if (n.label) {
|
|
23022
|
+
out.push({
|
|
23023
|
+
type: "text",
|
|
23024
|
+
x: n.x,
|
|
23025
|
+
y: n.y + (n.radius ?? 16) + 14,
|
|
23026
|
+
text: n.label,
|
|
23027
|
+
color: "#111827",
|
|
23028
|
+
fontSize: 12,
|
|
23029
|
+
align: "center"
|
|
23030
|
+
});
|
|
23031
|
+
}
|
|
23032
|
+
}
|
|
23033
|
+
out.push(...shapes);
|
|
23034
|
+
return out;
|
|
23035
|
+
}, [nodes, edges, shapes]);
|
|
23036
|
+
return /* @__PURE__ */ jsx(Card, { className, children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
|
|
23037
|
+
title ? /* @__PURE__ */ jsx(Typography, { variant: "h4", children: title }) : null,
|
|
23038
|
+
/* @__PURE__ */ jsx(
|
|
23039
|
+
LearningCanvas,
|
|
23040
|
+
{
|
|
23041
|
+
width,
|
|
23042
|
+
height,
|
|
23043
|
+
backgroundColor,
|
|
23044
|
+
shapes: derivedShapes,
|
|
23045
|
+
interactive,
|
|
23046
|
+
animate,
|
|
23047
|
+
onShapeClick,
|
|
23048
|
+
isLoading,
|
|
23049
|
+
error
|
|
23050
|
+
}
|
|
23051
|
+
)
|
|
23052
|
+
] }) });
|
|
23053
|
+
};
|
|
23054
|
+
}
|
|
23055
|
+
});
|
|
22952
23056
|
|
|
22953
|
-
// node_modules
|
|
23057
|
+
// node_modules/katex/dist/katex.min.css
|
|
22954
23058
|
var init_katex_min = __esm({
|
|
22955
|
-
"node_modules
|
|
23059
|
+
"node_modules/katex/dist/katex.min.css"() {
|
|
22956
23060
|
}
|
|
22957
23061
|
});
|
|
22958
23062
|
var Tabs;
|
|
@@ -27295,129 +27399,6 @@ var init_Breadcrumb = __esm({
|
|
|
27295
27399
|
Breadcrumb.displayName = "Breadcrumb";
|
|
27296
27400
|
}
|
|
27297
27401
|
});
|
|
27298
|
-
function useSafeEventBus2() {
|
|
27299
|
-
try {
|
|
27300
|
-
return useEventBus();
|
|
27301
|
-
} catch {
|
|
27302
|
-
return { emit: () => {
|
|
27303
|
-
}, on: () => () => {
|
|
27304
|
-
}, once: () => {
|
|
27305
|
-
} };
|
|
27306
|
-
}
|
|
27307
|
-
}
|
|
27308
|
-
var log6, lookStyles4, ButtonGroup;
|
|
27309
|
-
var init_ButtonGroup = __esm({
|
|
27310
|
-
"components/core/molecules/ButtonGroup.tsx"() {
|
|
27311
|
-
"use client";
|
|
27312
|
-
init_cn();
|
|
27313
|
-
init_atoms();
|
|
27314
|
-
init_useEventBus();
|
|
27315
|
-
log6 = createLogger("almadar:ui:button-group");
|
|
27316
|
-
lookStyles4 = {
|
|
27317
|
-
"right-aligned-buttons": "",
|
|
27318
|
-
"floating-bar": "fixed bottom-section left-1/2 -translate-x-1/2 shadow-elevation-toast bg-card p-card-sm rounded-container",
|
|
27319
|
-
"inline-row": "gap-2 inline-flex",
|
|
27320
|
-
"dropdown-menu": "[&>button:not(:first-child)]:hidden",
|
|
27321
|
-
"command-palette-trigger": "[&>button:not(:first-child)]:hidden"
|
|
27322
|
-
};
|
|
27323
|
-
ButtonGroup = ({
|
|
27324
|
-
children,
|
|
27325
|
-
primary,
|
|
27326
|
-
secondary,
|
|
27327
|
-
variant = "default",
|
|
27328
|
-
orientation = "horizontal",
|
|
27329
|
-
className,
|
|
27330
|
-
// Filter-group pattern props (entity and filters are used for schema-driven filtering)
|
|
27331
|
-
entity: _entity,
|
|
27332
|
-
filters,
|
|
27333
|
-
look = "right-aligned-buttons"
|
|
27334
|
-
}) => {
|
|
27335
|
-
const eventBus = useSafeEventBus2();
|
|
27336
|
-
const variantClasses2 = {
|
|
27337
|
-
default: "gap-0",
|
|
27338
|
-
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",
|
|
27339
|
-
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"
|
|
27340
|
-
};
|
|
27341
|
-
const orientationClasses = {
|
|
27342
|
-
horizontal: "flex-row",
|
|
27343
|
-
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"
|
|
27344
|
-
};
|
|
27345
|
-
const handleActionClick = (action) => {
|
|
27346
|
-
if (action.event) {
|
|
27347
|
-
eventBus.emit("UI:DISPATCH", { event: action.event });
|
|
27348
|
-
}
|
|
27349
|
-
if (action.navigatesTo) {
|
|
27350
|
-
eventBus.emit("UI:NAVIGATE", { url: action.navigatesTo });
|
|
27351
|
-
}
|
|
27352
|
-
};
|
|
27353
|
-
const renderFormActions = () => {
|
|
27354
|
-
const buttons = [];
|
|
27355
|
-
if (secondary) {
|
|
27356
|
-
secondary.forEach((action, index) => {
|
|
27357
|
-
buttons.push(
|
|
27358
|
-
/* @__PURE__ */ jsx(
|
|
27359
|
-
Button,
|
|
27360
|
-
{
|
|
27361
|
-
type: action.actionType === "submit" ? "submit" : "button",
|
|
27362
|
-
variant: action.variant || "ghost",
|
|
27363
|
-
onClick: () => handleActionClick(action),
|
|
27364
|
-
children: action.label
|
|
27365
|
-
},
|
|
27366
|
-
`secondary-${index}`
|
|
27367
|
-
)
|
|
27368
|
-
);
|
|
27369
|
-
});
|
|
27370
|
-
}
|
|
27371
|
-
if (primary) {
|
|
27372
|
-
const isSubmit = primary.actionType === "submit";
|
|
27373
|
-
buttons.push(
|
|
27374
|
-
/* @__PURE__ */ jsx(
|
|
27375
|
-
Button,
|
|
27376
|
-
{
|
|
27377
|
-
type: isSubmit ? "submit" : "button",
|
|
27378
|
-
variant: primary.variant || "primary",
|
|
27379
|
-
onClick: () => handleActionClick(primary),
|
|
27380
|
-
"data-testid": isSubmit ? "form-submit" : void 0,
|
|
27381
|
-
children: primary.label
|
|
27382
|
-
},
|
|
27383
|
-
"primary"
|
|
27384
|
-
)
|
|
27385
|
-
);
|
|
27386
|
-
}
|
|
27387
|
-
return buttons;
|
|
27388
|
-
};
|
|
27389
|
-
const renderFilters = () => {
|
|
27390
|
-
if (!filters || filters.length === 0) return null;
|
|
27391
|
-
return filters.map((filter, index) => /* @__PURE__ */ jsx(
|
|
27392
|
-
Button,
|
|
27393
|
-
{
|
|
27394
|
-
variant: "ghost",
|
|
27395
|
-
onClick: () => {
|
|
27396
|
-
log6.debug("Filter clicked", { field: filter.field });
|
|
27397
|
-
},
|
|
27398
|
-
children: filter.label
|
|
27399
|
-
},
|
|
27400
|
-
`filter-${filter.field}-${index}`
|
|
27401
|
-
));
|
|
27402
|
-
};
|
|
27403
|
-
return /* @__PURE__ */ jsx(
|
|
27404
|
-
"div",
|
|
27405
|
-
{
|
|
27406
|
-
className: cn(
|
|
27407
|
-
"inline-flex gap-2",
|
|
27408
|
-
variantClasses2[variant],
|
|
27409
|
-
orientationClasses[orientation],
|
|
27410
|
-
lookStyles4[look],
|
|
27411
|
-
className
|
|
27412
|
-
),
|
|
27413
|
-
role: "group",
|
|
27414
|
-
children: children || renderFilters() || renderFormActions()
|
|
27415
|
-
}
|
|
27416
|
-
);
|
|
27417
|
-
};
|
|
27418
|
-
ButtonGroup.displayName = "ButtonGroup";
|
|
27419
|
-
}
|
|
27420
|
-
});
|
|
27421
27402
|
function dayWindowForViewport(width) {
|
|
27422
27403
|
if (width <= 640) return 1;
|
|
27423
27404
|
if (width <= 1024) return 3;
|
|
@@ -28160,7 +28141,7 @@ var init_CardGrid = __esm({
|
|
|
28160
28141
|
CardGrid.displayName = "CardGrid";
|
|
28161
28142
|
}
|
|
28162
28143
|
});
|
|
28163
|
-
function
|
|
28144
|
+
function useSafeEventBus2() {
|
|
28164
28145
|
try {
|
|
28165
28146
|
return useEventBus();
|
|
28166
28147
|
} catch {
|
|
@@ -28196,7 +28177,7 @@ var init_Carousel = __esm({
|
|
|
28196
28177
|
const [activeIndex, setActiveIndex] = useState(0);
|
|
28197
28178
|
const scrollRef = useRef(null);
|
|
28198
28179
|
const autoPlayRef = useRef(null);
|
|
28199
|
-
const eventBus =
|
|
28180
|
+
const eventBus = useSafeEventBus2();
|
|
28200
28181
|
const { t } = useTranslate();
|
|
28201
28182
|
const safeItems = items ?? [];
|
|
28202
28183
|
const totalSlides = safeItems.length;
|
|
@@ -29444,6 +29425,122 @@ var init_ChatBar = __esm({
|
|
|
29444
29425
|
ChatBar.displayName = "ChatBar";
|
|
29445
29426
|
}
|
|
29446
29427
|
});
|
|
29428
|
+
var ChemistryCanvas;
|
|
29429
|
+
var init_ChemistryCanvas = __esm({
|
|
29430
|
+
"components/learning/molecules/ChemistryCanvas.tsx"() {
|
|
29431
|
+
"use client";
|
|
29432
|
+
init_atoms();
|
|
29433
|
+
init_Stack();
|
|
29434
|
+
init_LearningCanvas();
|
|
29435
|
+
ChemistryCanvas = ({
|
|
29436
|
+
className,
|
|
29437
|
+
width = 600,
|
|
29438
|
+
height = 400,
|
|
29439
|
+
title,
|
|
29440
|
+
backgroundColor,
|
|
29441
|
+
atoms = [],
|
|
29442
|
+
bonds = [],
|
|
29443
|
+
arrows = [],
|
|
29444
|
+
shapes = [],
|
|
29445
|
+
interactive = false,
|
|
29446
|
+
animate = false,
|
|
29447
|
+
onShapeClick,
|
|
29448
|
+
isLoading,
|
|
29449
|
+
error
|
|
29450
|
+
}) => {
|
|
29451
|
+
const derivedShapes = useMemo(() => {
|
|
29452
|
+
const out = [];
|
|
29453
|
+
const atomById = /* @__PURE__ */ new Map();
|
|
29454
|
+
for (const a of atoms) {
|
|
29455
|
+
if (a.id) atomById.set(a.id, a);
|
|
29456
|
+
}
|
|
29457
|
+
for (const b of bonds) {
|
|
29458
|
+
const a = atomById.get(b.from);
|
|
29459
|
+
const c = atomById.get(b.to);
|
|
29460
|
+
if (!a || !c) continue;
|
|
29461
|
+
const color = b.color ?? "#6b7280";
|
|
29462
|
+
const strokeWidth = b.type === "double" ? 4 : b.type === "triple" ? 6 : 2;
|
|
29463
|
+
out.push({
|
|
29464
|
+
type: "line",
|
|
29465
|
+
x1: a.x,
|
|
29466
|
+
y1: a.y,
|
|
29467
|
+
x2: c.x,
|
|
29468
|
+
y2: c.y,
|
|
29469
|
+
color,
|
|
29470
|
+
lineWidth: strokeWidth
|
|
29471
|
+
});
|
|
29472
|
+
}
|
|
29473
|
+
for (const a of arrows) {
|
|
29474
|
+
const angle = (a.angle ?? 0) * (Math.PI / 180);
|
|
29475
|
+
const len = a.length ?? 60;
|
|
29476
|
+
const x2 = a.x + Math.cos(angle) * len;
|
|
29477
|
+
const y2 = a.y + Math.sin(angle) * len;
|
|
29478
|
+
out.push({
|
|
29479
|
+
type: "arrow",
|
|
29480
|
+
x1: a.x,
|
|
29481
|
+
y1: a.y,
|
|
29482
|
+
x2,
|
|
29483
|
+
y2,
|
|
29484
|
+
color: a.color ?? "#dc2626",
|
|
29485
|
+
lineWidth: 2
|
|
29486
|
+
});
|
|
29487
|
+
if (a.label) {
|
|
29488
|
+
out.push({
|
|
29489
|
+
type: "text",
|
|
29490
|
+
x: (a.x + x2) / 2,
|
|
29491
|
+
y: (a.y + y2) / 2 - 10,
|
|
29492
|
+
text: a.label,
|
|
29493
|
+
color: "#111827",
|
|
29494
|
+
fontSize: 12,
|
|
29495
|
+
align: "center"
|
|
29496
|
+
});
|
|
29497
|
+
}
|
|
29498
|
+
}
|
|
29499
|
+
for (const a of atoms) {
|
|
29500
|
+
out.push({
|
|
29501
|
+
type: "circle",
|
|
29502
|
+
x: a.x,
|
|
29503
|
+
y: a.y,
|
|
29504
|
+
radius: a.radius ?? 14,
|
|
29505
|
+
color: a.color ?? "#2563eb",
|
|
29506
|
+
fill: a.color ?? "#2563eb",
|
|
29507
|
+
id: a.id
|
|
29508
|
+
});
|
|
29509
|
+
if (a.element) {
|
|
29510
|
+
out.push({
|
|
29511
|
+
type: "text",
|
|
29512
|
+
x: a.x,
|
|
29513
|
+
y: a.y,
|
|
29514
|
+
text: a.element,
|
|
29515
|
+
color: "#ffffff",
|
|
29516
|
+
fontSize: 12,
|
|
29517
|
+
align: "center"
|
|
29518
|
+
});
|
|
29519
|
+
}
|
|
29520
|
+
}
|
|
29521
|
+
out.push(...shapes);
|
|
29522
|
+
return out;
|
|
29523
|
+
}, [atoms, bonds, arrows, shapes]);
|
|
29524
|
+
return /* @__PURE__ */ jsx(Card, { className, children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
|
|
29525
|
+
title ? /* @__PURE__ */ jsx(Typography, { variant: "h4", children: title }) : null,
|
|
29526
|
+
/* @__PURE__ */ jsx(
|
|
29527
|
+
LearningCanvas,
|
|
29528
|
+
{
|
|
29529
|
+
width,
|
|
29530
|
+
height,
|
|
29531
|
+
backgroundColor,
|
|
29532
|
+
shapes: derivedShapes,
|
|
29533
|
+
interactive,
|
|
29534
|
+
animate,
|
|
29535
|
+
onShapeClick,
|
|
29536
|
+
isLoading,
|
|
29537
|
+
error
|
|
29538
|
+
}
|
|
29539
|
+
)
|
|
29540
|
+
] }) });
|
|
29541
|
+
};
|
|
29542
|
+
}
|
|
29543
|
+
});
|
|
29447
29544
|
var CodeRunnerPanel;
|
|
29448
29545
|
var init_CodeRunnerPanel = __esm({
|
|
29449
29546
|
"components/core/organisms/CodeRunnerPanel.tsx"() {
|
|
@@ -31518,7 +31615,7 @@ function DataGrid({
|
|
|
31518
31615
|
/* @__PURE__ */ jsx(
|
|
31519
31616
|
Box,
|
|
31520
31617
|
{
|
|
31521
|
-
className: cn("grid", gapStyles5[gap], scrollX ? "grid-flow-col overflow-x-auto" : colsClass,
|
|
31618
|
+
className: cn("grid", gapStyles5[gap], scrollX ? "grid-flow-col overflow-x-auto" : colsClass, lookStyles4[look], className),
|
|
31522
31619
|
style: scrollX ? { gridAutoFlow: "column", gridAutoColumns: `minmax(${minCardWidth}px, 1fr)` } : gridTemplateColumns ? { gridTemplateColumns } : void 0,
|
|
31523
31620
|
children: data.map((item, index) => {
|
|
31524
31621
|
const itemData = item;
|
|
@@ -31713,7 +31810,7 @@ function DataGrid({
|
|
|
31713
31810
|
] })
|
|
31714
31811
|
);
|
|
31715
31812
|
}
|
|
31716
|
-
var dataGridLog, BADGE_VARIANTS, gapStyles5,
|
|
31813
|
+
var dataGridLog, BADGE_VARIANTS, gapStyles5, lookStyles4;
|
|
31717
31814
|
var init_DataGrid = __esm({
|
|
31718
31815
|
"components/core/molecules/DataGrid.tsx"() {
|
|
31719
31816
|
"use client";
|
|
@@ -31748,7 +31845,7 @@ var init_DataGrid = __esm({
|
|
|
31748
31845
|
lg: "gap-6",
|
|
31749
31846
|
xl: "gap-8"
|
|
31750
31847
|
};
|
|
31751
|
-
|
|
31848
|
+
lookStyles4 = {
|
|
31752
31849
|
dense: "gap-2 [&>*]:p-card-sm",
|
|
31753
31850
|
spacious: "gap-8 [&>*]:p-card-lg",
|
|
31754
31851
|
striped: "[&>*:nth-child(even)]:bg-muted/30",
|
|
@@ -32175,6 +32272,133 @@ var init_DataList = __esm({
|
|
|
32175
32272
|
DataList.displayName = "DataList";
|
|
32176
32273
|
}
|
|
32177
32274
|
});
|
|
32275
|
+
var FormSection, FormLayout, FormActions;
|
|
32276
|
+
var init_FormSection = __esm({
|
|
32277
|
+
"components/core/molecules/FormSection.tsx"() {
|
|
32278
|
+
"use client";
|
|
32279
|
+
init_cn();
|
|
32280
|
+
init_atoms();
|
|
32281
|
+
init_Box();
|
|
32282
|
+
init_Typography();
|
|
32283
|
+
init_Button();
|
|
32284
|
+
init_Stack();
|
|
32285
|
+
init_Icon();
|
|
32286
|
+
init_useEventBus();
|
|
32287
|
+
FormSection = ({
|
|
32288
|
+
title,
|
|
32289
|
+
description,
|
|
32290
|
+
children,
|
|
32291
|
+
collapsible = false,
|
|
32292
|
+
defaultCollapsed = false,
|
|
32293
|
+
card = false,
|
|
32294
|
+
columns = 1,
|
|
32295
|
+
className
|
|
32296
|
+
}) => {
|
|
32297
|
+
const [collapsed, setCollapsed] = React105__default.useState(defaultCollapsed);
|
|
32298
|
+
const { t } = useTranslate();
|
|
32299
|
+
const eventBus = useEventBus();
|
|
32300
|
+
const gridClass = {
|
|
32301
|
+
1: "grid-cols-1",
|
|
32302
|
+
2: "grid-cols-1 md:grid-cols-2",
|
|
32303
|
+
3: "grid-cols-1 md:grid-cols-2 lg:grid-cols-3"
|
|
32304
|
+
}[columns];
|
|
32305
|
+
React105__default.useCallback(() => {
|
|
32306
|
+
if (collapsible) {
|
|
32307
|
+
setCollapsed((prev) => !prev);
|
|
32308
|
+
eventBus.emit("UI:TOGGLE_COLLAPSE", { collapsed: !collapsed });
|
|
32309
|
+
}
|
|
32310
|
+
}, [collapsible, collapsed, eventBus]);
|
|
32311
|
+
const content = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
32312
|
+
(title || description) && /* @__PURE__ */ jsxs(VStack, { gap: "xs", className: "mb-4", children: [
|
|
32313
|
+
title && /* @__PURE__ */ jsxs(
|
|
32314
|
+
HStack,
|
|
32315
|
+
{
|
|
32316
|
+
justify: "between",
|
|
32317
|
+
align: "center",
|
|
32318
|
+
className: cn(collapsible && "cursor-pointer"),
|
|
32319
|
+
action: collapsible ? "TOGGLE_COLLAPSE" : void 0,
|
|
32320
|
+
children: [
|
|
32321
|
+
/* @__PURE__ */ jsx(Typography, { variant: "h3", weight: "semibold", children: title }),
|
|
32322
|
+
collapsible && /* @__PURE__ */ jsx(
|
|
32323
|
+
Button,
|
|
32324
|
+
{
|
|
32325
|
+
variant: "ghost",
|
|
32326
|
+
size: "sm",
|
|
32327
|
+
action: "TOGGLE_COLLAPSE",
|
|
32328
|
+
children: /* @__PURE__ */ jsx(
|
|
32329
|
+
Icon,
|
|
32330
|
+
{
|
|
32331
|
+
icon: ChevronDown,
|
|
32332
|
+
size: "sm",
|
|
32333
|
+
className: cn(
|
|
32334
|
+
"text-muted-foreground transition-transform",
|
|
32335
|
+
collapsed && "rotate-180"
|
|
32336
|
+
)
|
|
32337
|
+
}
|
|
32338
|
+
)
|
|
32339
|
+
}
|
|
32340
|
+
)
|
|
32341
|
+
]
|
|
32342
|
+
}
|
|
32343
|
+
),
|
|
32344
|
+
description && /* @__PURE__ */ jsx(Typography, { variant: "small", color: "secondary", children: description })
|
|
32345
|
+
] }),
|
|
32346
|
+
(!collapsible || !collapsed) && /* @__PURE__ */ jsx(Box, { className: cn("grid gap-4", gridClass), children })
|
|
32347
|
+
] });
|
|
32348
|
+
if (card) {
|
|
32349
|
+
return /* @__PURE__ */ jsx(Card, { className: cn("p-6", className), children: content });
|
|
32350
|
+
}
|
|
32351
|
+
return /* @__PURE__ */ jsx(Box, { className, children: content });
|
|
32352
|
+
};
|
|
32353
|
+
FormSection.displayName = "FormSection";
|
|
32354
|
+
FormLayout = ({
|
|
32355
|
+
children,
|
|
32356
|
+
dividers = true,
|
|
32357
|
+
className
|
|
32358
|
+
}) => {
|
|
32359
|
+
return /* @__PURE__ */ jsx(
|
|
32360
|
+
VStack,
|
|
32361
|
+
{
|
|
32362
|
+
gap: "lg",
|
|
32363
|
+
className: cn(
|
|
32364
|
+
dividers && "[&>*+*]:pt-8 [&>*+*]:border-t [&>*+*]:border-border",
|
|
32365
|
+
className
|
|
32366
|
+
),
|
|
32367
|
+
children
|
|
32368
|
+
}
|
|
32369
|
+
);
|
|
32370
|
+
};
|
|
32371
|
+
FormLayout.displayName = "FormLayout";
|
|
32372
|
+
FormActions = ({
|
|
32373
|
+
children,
|
|
32374
|
+
sticky = false,
|
|
32375
|
+
align = "right",
|
|
32376
|
+
className
|
|
32377
|
+
}) => {
|
|
32378
|
+
const alignClass2 = {
|
|
32379
|
+
left: "justify-start",
|
|
32380
|
+
right: "justify-end",
|
|
32381
|
+
between: "justify-between",
|
|
32382
|
+
center: "justify-center"
|
|
32383
|
+
}[align];
|
|
32384
|
+
return /* @__PURE__ */ jsx(
|
|
32385
|
+
HStack,
|
|
32386
|
+
{
|
|
32387
|
+
gap: "sm",
|
|
32388
|
+
align: "center",
|
|
32389
|
+
className: cn(
|
|
32390
|
+
"pt-6 border-t border-border",
|
|
32391
|
+
alignClass2,
|
|
32392
|
+
sticky && "sticky bottom-0 bg-card py-4 -mx-6 px-6 shadow-[0_-4px_6px_-1px_rgb(0,0,0,0.05)]",
|
|
32393
|
+
className
|
|
32394
|
+
),
|
|
32395
|
+
children
|
|
32396
|
+
}
|
|
32397
|
+
);
|
|
32398
|
+
};
|
|
32399
|
+
FormActions.displayName = "FormActions";
|
|
32400
|
+
}
|
|
32401
|
+
});
|
|
32178
32402
|
function fileIcon(name) {
|
|
32179
32403
|
const ext = name.split(".").pop()?.toLowerCase() ?? "";
|
|
32180
32404
|
switch (ext) {
|
|
@@ -32336,6 +32560,129 @@ var init_FormField = __esm({
|
|
|
32336
32560
|
FormField.displayName = "FormField";
|
|
32337
32561
|
}
|
|
32338
32562
|
});
|
|
32563
|
+
function useSafeEventBus3() {
|
|
32564
|
+
try {
|
|
32565
|
+
return useEventBus();
|
|
32566
|
+
} catch {
|
|
32567
|
+
return { emit: () => {
|
|
32568
|
+
}, on: () => () => {
|
|
32569
|
+
}, once: () => {
|
|
32570
|
+
} };
|
|
32571
|
+
}
|
|
32572
|
+
}
|
|
32573
|
+
var log6, lookStyles5, ButtonGroup;
|
|
32574
|
+
var init_ButtonGroup = __esm({
|
|
32575
|
+
"components/core/molecules/ButtonGroup.tsx"() {
|
|
32576
|
+
"use client";
|
|
32577
|
+
init_cn();
|
|
32578
|
+
init_atoms();
|
|
32579
|
+
init_useEventBus();
|
|
32580
|
+
log6 = createLogger("almadar:ui:button-group");
|
|
32581
|
+
lookStyles5 = {
|
|
32582
|
+
"right-aligned-buttons": "",
|
|
32583
|
+
"floating-bar": "fixed bottom-section left-1/2 -translate-x-1/2 shadow-elevation-toast bg-card p-card-sm rounded-container",
|
|
32584
|
+
"inline-row": "gap-2 inline-flex",
|
|
32585
|
+
"dropdown-menu": "[&>button:not(:first-child)]:hidden",
|
|
32586
|
+
"command-palette-trigger": "[&>button:not(:first-child)]:hidden"
|
|
32587
|
+
};
|
|
32588
|
+
ButtonGroup = ({
|
|
32589
|
+
children,
|
|
32590
|
+
primary,
|
|
32591
|
+
secondary,
|
|
32592
|
+
variant = "default",
|
|
32593
|
+
orientation = "horizontal",
|
|
32594
|
+
className,
|
|
32595
|
+
// Filter-group pattern props (entity and filters are used for schema-driven filtering)
|
|
32596
|
+
entity: _entity,
|
|
32597
|
+
filters,
|
|
32598
|
+
look = "right-aligned-buttons"
|
|
32599
|
+
}) => {
|
|
32600
|
+
const eventBus = useSafeEventBus3();
|
|
32601
|
+
const variantClasses2 = {
|
|
32602
|
+
default: "gap-0",
|
|
32603
|
+
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",
|
|
32604
|
+
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"
|
|
32605
|
+
};
|
|
32606
|
+
const orientationClasses = {
|
|
32607
|
+
horizontal: "flex-row",
|
|
32608
|
+
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"
|
|
32609
|
+
};
|
|
32610
|
+
const handleActionClick = (action) => {
|
|
32611
|
+
if (action.event) {
|
|
32612
|
+
eventBus.emit("UI:DISPATCH", { event: action.event });
|
|
32613
|
+
}
|
|
32614
|
+
if (action.navigatesTo) {
|
|
32615
|
+
eventBus.emit("UI:NAVIGATE", { url: action.navigatesTo });
|
|
32616
|
+
}
|
|
32617
|
+
};
|
|
32618
|
+
const renderFormActions = () => {
|
|
32619
|
+
const buttons = [];
|
|
32620
|
+
if (secondary) {
|
|
32621
|
+
secondary.forEach((action, index) => {
|
|
32622
|
+
buttons.push(
|
|
32623
|
+
/* @__PURE__ */ jsx(
|
|
32624
|
+
Button,
|
|
32625
|
+
{
|
|
32626
|
+
type: action.actionType === "submit" ? "submit" : "button",
|
|
32627
|
+
variant: action.variant || "ghost",
|
|
32628
|
+
onClick: () => handleActionClick(action),
|
|
32629
|
+
children: action.label
|
|
32630
|
+
},
|
|
32631
|
+
`secondary-${index}`
|
|
32632
|
+
)
|
|
32633
|
+
);
|
|
32634
|
+
});
|
|
32635
|
+
}
|
|
32636
|
+
if (primary) {
|
|
32637
|
+
const isSubmit = primary.actionType === "submit";
|
|
32638
|
+
buttons.push(
|
|
32639
|
+
/* @__PURE__ */ jsx(
|
|
32640
|
+
Button,
|
|
32641
|
+
{
|
|
32642
|
+
type: isSubmit ? "submit" : "button",
|
|
32643
|
+
variant: primary.variant || "primary",
|
|
32644
|
+
onClick: () => handleActionClick(primary),
|
|
32645
|
+
"data-testid": isSubmit ? "form-submit" : void 0,
|
|
32646
|
+
children: primary.label
|
|
32647
|
+
},
|
|
32648
|
+
"primary"
|
|
32649
|
+
)
|
|
32650
|
+
);
|
|
32651
|
+
}
|
|
32652
|
+
return buttons;
|
|
32653
|
+
};
|
|
32654
|
+
const renderFilters = () => {
|
|
32655
|
+
if (!filters || filters.length === 0) return null;
|
|
32656
|
+
return filters.map((filter, index) => /* @__PURE__ */ jsx(
|
|
32657
|
+
Button,
|
|
32658
|
+
{
|
|
32659
|
+
variant: "ghost",
|
|
32660
|
+
onClick: () => {
|
|
32661
|
+
log6.debug("Filter clicked", { field: filter.field });
|
|
32662
|
+
},
|
|
32663
|
+
children: filter.label
|
|
32664
|
+
},
|
|
32665
|
+
`filter-${filter.field}-${index}`
|
|
32666
|
+
));
|
|
32667
|
+
};
|
|
32668
|
+
return /* @__PURE__ */ jsx(
|
|
32669
|
+
"div",
|
|
32670
|
+
{
|
|
32671
|
+
className: cn(
|
|
32672
|
+
"inline-flex gap-2",
|
|
32673
|
+
variantClasses2[variant],
|
|
32674
|
+
orientationClasses[orientation],
|
|
32675
|
+
lookStyles5[look],
|
|
32676
|
+
className
|
|
32677
|
+
),
|
|
32678
|
+
role: "group",
|
|
32679
|
+
children: children || renderFilters() || renderFormActions()
|
|
32680
|
+
}
|
|
32681
|
+
);
|
|
32682
|
+
};
|
|
32683
|
+
ButtonGroup.displayName = "ButtonGroup";
|
|
32684
|
+
}
|
|
32685
|
+
});
|
|
32339
32686
|
var resolveFilterType, lookStyles6, FilterGroup;
|
|
32340
32687
|
var init_FilterGroup = __esm({
|
|
32341
32688
|
"components/core/molecules/FilterGroup.tsx"() {
|
|
@@ -34813,6 +35160,234 @@ var init_ProgressDots = __esm({
|
|
|
34813
35160
|
ProgressDots.displayName = "ProgressDots";
|
|
34814
35161
|
}
|
|
34815
35162
|
});
|
|
35163
|
+
var MathCanvas;
|
|
35164
|
+
var init_MathCanvas = __esm({
|
|
35165
|
+
"components/learning/molecules/MathCanvas.tsx"() {
|
|
35166
|
+
"use client";
|
|
35167
|
+
init_atoms();
|
|
35168
|
+
init_Stack();
|
|
35169
|
+
init_LearningCanvas();
|
|
35170
|
+
MathCanvas = ({
|
|
35171
|
+
className,
|
|
35172
|
+
width = 600,
|
|
35173
|
+
height = 400,
|
|
35174
|
+
title,
|
|
35175
|
+
xMin = -10,
|
|
35176
|
+
xMax = 10,
|
|
35177
|
+
yMin = -10,
|
|
35178
|
+
yMax = 10,
|
|
35179
|
+
showAxes = true,
|
|
35180
|
+
showGrid = true,
|
|
35181
|
+
gridStep = 1,
|
|
35182
|
+
curves = [],
|
|
35183
|
+
points = [],
|
|
35184
|
+
vectors = [],
|
|
35185
|
+
shapes = [],
|
|
35186
|
+
interactive = false,
|
|
35187
|
+
animate = false,
|
|
35188
|
+
onShapeClick,
|
|
35189
|
+
isLoading,
|
|
35190
|
+
error
|
|
35191
|
+
}) => {
|
|
35192
|
+
const derivedShapes = useMemo(() => {
|
|
35193
|
+
const out = [];
|
|
35194
|
+
const margin = 24;
|
|
35195
|
+
const plotW = width - margin * 2;
|
|
35196
|
+
const plotH = height - margin * 2;
|
|
35197
|
+
const mapX = (x) => margin + (x - xMin) / (xMax - xMin) * plotW;
|
|
35198
|
+
const mapY = (y) => height - (margin + (y - yMin) / (yMax - yMin) * plotH);
|
|
35199
|
+
if (showGrid) {
|
|
35200
|
+
for (let x = Math.ceil(xMin / gridStep) * gridStep; x <= xMax; x += gridStep) {
|
|
35201
|
+
const px = mapX(x);
|
|
35202
|
+
out.push({ type: "line", x1: px, y1: margin, x2: px, y2: height - margin, color: "#e5e7eb", lineWidth: 1 });
|
|
35203
|
+
}
|
|
35204
|
+
for (let y = Math.ceil(yMin / gridStep) * gridStep; y <= yMax; y += gridStep) {
|
|
35205
|
+
const py = mapY(y);
|
|
35206
|
+
out.push({ type: "line", x1: margin, y1: py, x2: width - margin, y2: py, color: "#e5e7eb", lineWidth: 1 });
|
|
35207
|
+
}
|
|
35208
|
+
}
|
|
35209
|
+
if (showAxes) {
|
|
35210
|
+
const xAxisY = Math.max(margin, Math.min(height - margin, mapY(0)));
|
|
35211
|
+
const yAxisX = Math.max(margin, Math.min(width - margin, mapX(0)));
|
|
35212
|
+
out.push({ type: "line", x1: margin, y1: xAxisY, x2: width - margin, y2: xAxisY, color: "#374151", lineWidth: 2 });
|
|
35213
|
+
out.push({ type: "line", x1: yAxisX, y1: margin, x2: yAxisX, y2: height - margin, color: "#374151", lineWidth: 2 });
|
|
35214
|
+
}
|
|
35215
|
+
for (const curve of curves) {
|
|
35216
|
+
if (!curve.samples || curve.samples.length < 2) continue;
|
|
35217
|
+
for (let i = 1; i < curve.samples.length; i++) {
|
|
35218
|
+
const a = curve.samples[i - 1];
|
|
35219
|
+
const b = curve.samples[i];
|
|
35220
|
+
if (a.x < xMin || a.x > xMax || b.x < xMin || b.x > xMax) continue;
|
|
35221
|
+
out.push({
|
|
35222
|
+
type: "line",
|
|
35223
|
+
x1: mapX(a.x),
|
|
35224
|
+
y1: mapY(a.y),
|
|
35225
|
+
x2: mapX(b.x),
|
|
35226
|
+
y2: mapY(b.y),
|
|
35227
|
+
color: curve.color ?? "#2563eb",
|
|
35228
|
+
lineWidth: 2
|
|
35229
|
+
});
|
|
35230
|
+
}
|
|
35231
|
+
}
|
|
35232
|
+
for (const p2 of points) {
|
|
35233
|
+
if (p2.x < xMin || p2.x > xMax || p2.y < yMin || p2.y > yMax) continue;
|
|
35234
|
+
out.push({
|
|
35235
|
+
type: "circle",
|
|
35236
|
+
x: mapX(p2.x),
|
|
35237
|
+
y: mapY(p2.y),
|
|
35238
|
+
radius: p2.radius ?? 4,
|
|
35239
|
+
color: p2.color ?? "#dc2626",
|
|
35240
|
+
fill: p2.color ?? "#dc2626"
|
|
35241
|
+
});
|
|
35242
|
+
if (p2.label) {
|
|
35243
|
+
out.push({ type: "text", x: mapX(p2.x) + 8, y: mapY(p2.y) - 8, text: p2.label, color: "#111827", fontSize: 12 });
|
|
35244
|
+
}
|
|
35245
|
+
}
|
|
35246
|
+
for (const v of vectors) {
|
|
35247
|
+
if (v.x < xMin || v.x > xMax || v.y < yMin || v.y > yMax) continue;
|
|
35248
|
+
const x1 = mapX(v.x);
|
|
35249
|
+
const y1 = mapY(v.y);
|
|
35250
|
+
const x2 = mapX(v.x + v.vx);
|
|
35251
|
+
const y2 = mapY(v.y + v.vy);
|
|
35252
|
+
out.push({ type: "arrow", x1, y1, x2, y2, color: v.color ?? "#7c3aed", lineWidth: 2 });
|
|
35253
|
+
if (v.label) {
|
|
35254
|
+
out.push({ type: "text", x: x2 + 6, y: y2 - 6, text: v.label, color: "#111827", fontSize: 12 });
|
|
35255
|
+
}
|
|
35256
|
+
}
|
|
35257
|
+
out.push(...shapes);
|
|
35258
|
+
return out;
|
|
35259
|
+
}, [width, height, xMin, xMax, yMin, yMax, showAxes, showGrid, gridStep, curves, points, vectors, shapes]);
|
|
35260
|
+
return /* @__PURE__ */ jsx(Card, { className, children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
|
|
35261
|
+
title ? /* @__PURE__ */ jsx(Typography, { variant: "h4", children: title }) : null,
|
|
35262
|
+
/* @__PURE__ */ jsx(
|
|
35263
|
+
LearningCanvas,
|
|
35264
|
+
{
|
|
35265
|
+
width,
|
|
35266
|
+
height,
|
|
35267
|
+
shapes: derivedShapes,
|
|
35268
|
+
interactive,
|
|
35269
|
+
animate,
|
|
35270
|
+
onShapeClick,
|
|
35271
|
+
isLoading,
|
|
35272
|
+
error
|
|
35273
|
+
}
|
|
35274
|
+
)
|
|
35275
|
+
] }) });
|
|
35276
|
+
};
|
|
35277
|
+
}
|
|
35278
|
+
});
|
|
35279
|
+
var PhysicsCanvas;
|
|
35280
|
+
var init_PhysicsCanvas = __esm({
|
|
35281
|
+
"components/learning/molecules/PhysicsCanvas.tsx"() {
|
|
35282
|
+
"use client";
|
|
35283
|
+
init_atoms();
|
|
35284
|
+
init_Stack();
|
|
35285
|
+
init_LearningCanvas();
|
|
35286
|
+
PhysicsCanvas = ({
|
|
35287
|
+
className,
|
|
35288
|
+
width = 600,
|
|
35289
|
+
height = 400,
|
|
35290
|
+
title,
|
|
35291
|
+
backgroundColor,
|
|
35292
|
+
bodies = [],
|
|
35293
|
+
constraints = [],
|
|
35294
|
+
showVelocity = true,
|
|
35295
|
+
showForces = false,
|
|
35296
|
+
velocityScale = 20,
|
|
35297
|
+
forceScale = 20,
|
|
35298
|
+
shapes = [],
|
|
35299
|
+
interactive = false,
|
|
35300
|
+
animate = false,
|
|
35301
|
+
onShapeClick,
|
|
35302
|
+
isLoading,
|
|
35303
|
+
error
|
|
35304
|
+
}) => {
|
|
35305
|
+
const derivedShapes = useMemo(() => {
|
|
35306
|
+
const out = [];
|
|
35307
|
+
const bodyById = /* @__PURE__ */ new Map();
|
|
35308
|
+
for (const b of bodies) {
|
|
35309
|
+
if (b.id) bodyById.set(b.id, b);
|
|
35310
|
+
}
|
|
35311
|
+
for (const c of constraints) {
|
|
35312
|
+
const a = bodyById.get(c.from);
|
|
35313
|
+
const b = bodyById.get(c.to);
|
|
35314
|
+
if (!a || !b) continue;
|
|
35315
|
+
out.push({
|
|
35316
|
+
type: "line",
|
|
35317
|
+
x1: a.x,
|
|
35318
|
+
y1: a.y,
|
|
35319
|
+
x2: b.x,
|
|
35320
|
+
y2: b.y,
|
|
35321
|
+
color: c.color ?? "#9ca3af",
|
|
35322
|
+
lineWidth: 2
|
|
35323
|
+
});
|
|
35324
|
+
}
|
|
35325
|
+
for (const b of bodies) {
|
|
35326
|
+
out.push({
|
|
35327
|
+
type: "circle",
|
|
35328
|
+
x: b.x,
|
|
35329
|
+
y: b.y,
|
|
35330
|
+
radius: b.radius ?? 12,
|
|
35331
|
+
color: b.color ?? "#2563eb",
|
|
35332
|
+
fill: b.color ?? "#2563eb",
|
|
35333
|
+
id: b.id
|
|
35334
|
+
});
|
|
35335
|
+
if (b.label) {
|
|
35336
|
+
out.push({
|
|
35337
|
+
type: "text",
|
|
35338
|
+
x: b.x + (b.radius ?? 12) + 6,
|
|
35339
|
+
y: b.y - (b.radius ?? 12) - 6,
|
|
35340
|
+
text: b.label,
|
|
35341
|
+
color: "#111827",
|
|
35342
|
+
fontSize: 12
|
|
35343
|
+
});
|
|
35344
|
+
}
|
|
35345
|
+
if (showVelocity && b.vx != null && b.vy != null && (b.vx !== 0 || b.vy !== 0)) {
|
|
35346
|
+
out.push({
|
|
35347
|
+
type: "arrow",
|
|
35348
|
+
x1: b.x,
|
|
35349
|
+
y1: b.y,
|
|
35350
|
+
x2: b.x + b.vx * velocityScale,
|
|
35351
|
+
y2: b.y + b.vy * velocityScale,
|
|
35352
|
+
color: "#16a34a",
|
|
35353
|
+
lineWidth: 2
|
|
35354
|
+
});
|
|
35355
|
+
}
|
|
35356
|
+
if (showForces && b.fx != null && b.fy != null && (b.fx !== 0 || b.fy !== 0)) {
|
|
35357
|
+
out.push({
|
|
35358
|
+
type: "arrow",
|
|
35359
|
+
x1: b.x,
|
|
35360
|
+
y1: b.y,
|
|
35361
|
+
x2: b.x + b.fx * forceScale,
|
|
35362
|
+
y2: b.y + b.fy * forceScale,
|
|
35363
|
+
color: "#dc2626",
|
|
35364
|
+
lineWidth: 2
|
|
35365
|
+
});
|
|
35366
|
+
}
|
|
35367
|
+
}
|
|
35368
|
+
out.push(...shapes);
|
|
35369
|
+
return out;
|
|
35370
|
+
}, [bodies, constraints, showVelocity, showForces, velocityScale, forceScale, shapes]);
|
|
35371
|
+
return /* @__PURE__ */ jsx(Card, { className, children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
|
|
35372
|
+
title ? /* @__PURE__ */ jsx(Typography, { variant: "h4", children: title }) : null,
|
|
35373
|
+
/* @__PURE__ */ jsx(
|
|
35374
|
+
LearningCanvas,
|
|
35375
|
+
{
|
|
35376
|
+
width,
|
|
35377
|
+
height,
|
|
35378
|
+
backgroundColor,
|
|
35379
|
+
shapes: derivedShapes,
|
|
35380
|
+
interactive,
|
|
35381
|
+
animate,
|
|
35382
|
+
onShapeClick,
|
|
35383
|
+
isLoading,
|
|
35384
|
+
error
|
|
35385
|
+
}
|
|
35386
|
+
)
|
|
35387
|
+
] }) });
|
|
35388
|
+
};
|
|
35389
|
+
}
|
|
35390
|
+
});
|
|
34816
35391
|
function resolveNodeColor(node, groups) {
|
|
34817
35392
|
if (node.color) return node.color;
|
|
34818
35393
|
if (node.group) {
|
|
@@ -35184,13 +35759,13 @@ var init_MapView = __esm({
|
|
|
35184
35759
|
shadowSize: [41, 41]
|
|
35185
35760
|
});
|
|
35186
35761
|
L.Marker.prototype.options.icon = defaultIcon;
|
|
35187
|
-
const { useEffect:
|
|
35762
|
+
const { useEffect: useEffect75, useRef: useRef77, useCallback: useCallback112, useState: useState107 } = React105__default;
|
|
35188
35763
|
const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
|
|
35189
35764
|
const { useEventBus: useEventBus2 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
|
|
35190
35765
|
function MapUpdater({ centerLat, centerLng, zoom }) {
|
|
35191
35766
|
const map = useMap();
|
|
35192
|
-
const prevRef =
|
|
35193
|
-
|
|
35767
|
+
const prevRef = useRef77({ centerLat, centerLng, zoom });
|
|
35768
|
+
useEffect75(() => {
|
|
35194
35769
|
const prev = prevRef.current;
|
|
35195
35770
|
if (prev.centerLat !== centerLat || prev.centerLng !== centerLng || prev.zoom !== zoom) {
|
|
35196
35771
|
map.setView([centerLat, centerLng], zoom);
|
|
@@ -35201,7 +35776,7 @@ var init_MapView = __esm({
|
|
|
35201
35776
|
}
|
|
35202
35777
|
function MapClickHandler({ onMapClick }) {
|
|
35203
35778
|
const map = useMap();
|
|
35204
|
-
|
|
35779
|
+
useEffect75(() => {
|
|
35205
35780
|
if (!onMapClick) return;
|
|
35206
35781
|
const handler = (e) => {
|
|
35207
35782
|
onMapClick(e.latlng.lat, e.latlng.lng);
|
|
@@ -43504,7 +44079,7 @@ function getGroupColor(group, groups) {
|
|
|
43504
44079
|
const idx = groups.indexOf(group);
|
|
43505
44080
|
return GROUP_COLORS2[idx % GROUP_COLORS2.length];
|
|
43506
44081
|
}
|
|
43507
|
-
function
|
|
44082
|
+
function resolveColor3(color, el) {
|
|
43508
44083
|
const m = /^var\((--[^,)]+)(?:,\s*([^)]+))?\)$/.exec(color.trim());
|
|
43509
44084
|
if (!m) return color;
|
|
43510
44085
|
const resolved = getComputedStyle(el).getPropertyValue(m[1]).trim();
|
|
@@ -43765,7 +44340,7 @@ var init_GraphCanvas = __esm({
|
|
|
43765
44340
|
const w = logicalW;
|
|
43766
44341
|
const h = height;
|
|
43767
44342
|
const nodes = nodesRef.current;
|
|
43768
|
-
const accentColor =
|
|
44343
|
+
const accentColor = resolveColor3("var(--color-accent)", canvas);
|
|
43769
44344
|
const dpr = typeof window !== "undefined" && window.devicePixelRatio || 1;
|
|
43770
44345
|
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
|
|
43771
44346
|
ctx.clearRect(0, 0, w, h);
|
|
@@ -43804,7 +44379,7 @@ var init_GraphCanvas = __esm({
|
|
|
43804
44379
|
ctx.globalAlpha = 1;
|
|
43805
44380
|
for (const node of nodes) {
|
|
43806
44381
|
const size = node.size || 8;
|
|
43807
|
-
const color =
|
|
44382
|
+
const color = resolveColor3(node.color || getGroupColor(node.group, groups), canvas);
|
|
43808
44383
|
const isHovered = hoveredNode === node.id;
|
|
43809
44384
|
const isSelected = selectedNodeId !== void 0 && node.id === selectedNodeId;
|
|
43810
44385
|
ctx.globalAlpha = hoveredNode && !neighbors.has(node.id) ? 0.2 : 1;
|
|
@@ -50702,6 +51277,7 @@ var init_component_registry_generated = __esm({
|
|
|
50702
51277
|
init_BattleBoard();
|
|
50703
51278
|
init_BattleTemplate();
|
|
50704
51279
|
init_BehaviorView();
|
|
51280
|
+
init_BiologyCanvas();
|
|
50705
51281
|
init_BloomQuizBlock();
|
|
50706
51282
|
init_BoardgameBoard();
|
|
50707
51283
|
init_BookChapterView();
|
|
@@ -50713,7 +51289,6 @@ var init_component_registry_generated = __esm({
|
|
|
50713
51289
|
init_BranchingLogicBuilder();
|
|
50714
51290
|
init_Breadcrumb();
|
|
50715
51291
|
init_BuilderBoard();
|
|
50716
|
-
init_ButtonGroup();
|
|
50717
51292
|
init_CTABanner();
|
|
50718
51293
|
init_CalendarGrid();
|
|
50719
51294
|
init_Canvas2D();
|
|
@@ -50732,6 +51307,7 @@ var init_component_registry_generated = __esm({
|
|
|
50732
51307
|
init_ChartLegend();
|
|
50733
51308
|
init_ChatBar();
|
|
50734
51309
|
init_Checkbox();
|
|
51310
|
+
init_ChemistryCanvas();
|
|
50735
51311
|
init_ChoiceButton();
|
|
50736
51312
|
init_CityBuilderBoard();
|
|
50737
51313
|
init_CityBuilderTemplate();
|
|
@@ -50790,9 +51366,9 @@ var init_component_registry_generated = __esm({
|
|
|
50790
51366
|
init_FlipContainer();
|
|
50791
51367
|
init_FloatingActionButton();
|
|
50792
51368
|
init_Form();
|
|
51369
|
+
init_FormSection();
|
|
50793
51370
|
init_FormField();
|
|
50794
51371
|
init_FormSectionHeader();
|
|
50795
|
-
init_GameAudioProvider();
|
|
50796
51372
|
init_GameAudioToggle();
|
|
50797
51373
|
init_GameCard();
|
|
50798
51374
|
init_GameHud();
|
|
@@ -50823,6 +51399,7 @@ var init_component_registry_generated = __esm({
|
|
|
50823
51399
|
init_JazariStateMachine();
|
|
50824
51400
|
init_LandingPageTemplate();
|
|
50825
51401
|
init_LawReferenceTooltip();
|
|
51402
|
+
init_LearningCanvas();
|
|
50826
51403
|
init_Lightbox();
|
|
50827
51404
|
init_LikertScale();
|
|
50828
51405
|
init_LineChart();
|
|
@@ -50831,9 +51408,11 @@ var init_component_registry_generated = __esm({
|
|
|
50831
51408
|
init_MapView();
|
|
50832
51409
|
init_MarkdownContent();
|
|
50833
51410
|
init_MarketingFooter();
|
|
51411
|
+
init_MarketingStatCard();
|
|
50834
51412
|
init_MasterDetail();
|
|
50835
51413
|
init_MasterDetailLayout();
|
|
50836
51414
|
init_MatchPuzzleBoard();
|
|
51415
|
+
init_MathCanvas();
|
|
50837
51416
|
init_MatrixQuestion();
|
|
50838
51417
|
init_MediaGallery();
|
|
50839
51418
|
init_Menu();
|
|
@@ -50852,6 +51431,7 @@ var init_component_registry_generated = __esm({
|
|
|
50852
51431
|
init_PageHeader();
|
|
50853
51432
|
init_Pagination();
|
|
50854
51433
|
init_PatternTile();
|
|
51434
|
+
init_PhysicsCanvas();
|
|
50855
51435
|
init_PinballBoard();
|
|
50856
51436
|
init_PirateBoard();
|
|
50857
51437
|
init_PlatformerBoard();
|
|
@@ -51028,6 +51608,7 @@ var init_component_registry_generated = __esm({
|
|
|
51028
51608
|
"BattleBoard": BattleBoard,
|
|
51029
51609
|
"BattleTemplate": BattleTemplate,
|
|
51030
51610
|
"BehaviorView": BehaviorView,
|
|
51611
|
+
"BiologyCanvas": BiologyCanvas,
|
|
51031
51612
|
"BloomQuizBlock": BloomQuizBlock,
|
|
51032
51613
|
"BoardgameBoard": BoardgameBoard,
|
|
51033
51614
|
"BookChapterView": BookChapterView,
|
|
@@ -51041,7 +51622,6 @@ var init_component_registry_generated = __esm({
|
|
|
51041
51622
|
"Breadcrumb": Breadcrumb,
|
|
51042
51623
|
"BuilderBoard": BuilderBoard,
|
|
51043
51624
|
"Button": ButtonPattern,
|
|
51044
|
-
"ButtonGroup": ButtonGroup,
|
|
51045
51625
|
"ButtonPattern": ButtonPattern,
|
|
51046
51626
|
"CTABanner": CTABanner,
|
|
51047
51627
|
"CalendarGrid": CalendarGrid,
|
|
@@ -51061,6 +51641,7 @@ var init_component_registry_generated = __esm({
|
|
|
51061
51641
|
"ChartLegend": ChartLegend,
|
|
51062
51642
|
"ChatBar": ChatBar,
|
|
51063
51643
|
"Checkbox": Checkbox,
|
|
51644
|
+
"ChemistryCanvas": ChemistryCanvas,
|
|
51064
51645
|
"ChoiceButton": ChoiceButton,
|
|
51065
51646
|
"CityBuilderBoard": CityBuilderBoard,
|
|
51066
51647
|
"CityBuilderTemplate": CityBuilderTemplate,
|
|
@@ -51121,9 +51702,10 @@ var init_component_registry_generated = __esm({
|
|
|
51121
51702
|
"FlipContainer": FlipContainer,
|
|
51122
51703
|
"FloatingActionButton": FloatingActionButton,
|
|
51123
51704
|
"Form": Form,
|
|
51705
|
+
"FormActions": FormActions,
|
|
51124
51706
|
"FormField": FormField,
|
|
51707
|
+
"FormLayout": FormLayout,
|
|
51125
51708
|
"FormSectionHeader": FormSectionHeader,
|
|
51126
|
-
"GameAudioProvider": GameAudioProvider,
|
|
51127
51709
|
"GameAudioToggle": GameAudioToggle,
|
|
51128
51710
|
"GameBoard3D": GameBoard3D,
|
|
51129
51711
|
"GameCanvas3D": GameCanvas3D,
|
|
@@ -51161,6 +51743,7 @@ var init_component_registry_generated = __esm({
|
|
|
51161
51743
|
"LabelPattern": LabelPattern,
|
|
51162
51744
|
"LandingPageTemplate": LandingPageTemplate,
|
|
51163
51745
|
"LawReferenceTooltip": LawReferenceTooltip,
|
|
51746
|
+
"LearningCanvas": LearningCanvas,
|
|
51164
51747
|
"Lightbox": Lightbox,
|
|
51165
51748
|
"LikertScale": LikertScale,
|
|
51166
51749
|
"LineChart": LineChart2,
|
|
@@ -51169,9 +51752,11 @@ var init_component_registry_generated = __esm({
|
|
|
51169
51752
|
"MapView": MapView,
|
|
51170
51753
|
"MarkdownContent": MarkdownContent,
|
|
51171
51754
|
"MarketingFooter": MarketingFooter,
|
|
51755
|
+
"MarketingStatCard": MarketingStatCard,
|
|
51172
51756
|
"MasterDetail": MasterDetail,
|
|
51173
51757
|
"MasterDetailLayout": MasterDetailLayout,
|
|
51174
51758
|
"MatchPuzzleBoard": MatchPuzzleBoard,
|
|
51759
|
+
"MathCanvas": MathCanvas,
|
|
51175
51760
|
"MatrixQuestion": MatrixQuestion,
|
|
51176
51761
|
"MediaGallery": MediaGallery,
|
|
51177
51762
|
"Menu": Menu,
|
|
@@ -51190,6 +51775,7 @@ var init_component_registry_generated = __esm({
|
|
|
51190
51775
|
"PageHeader": PageHeader,
|
|
51191
51776
|
"Pagination": Pagination,
|
|
51192
51777
|
"PatternTile": PatternTile,
|
|
51778
|
+
"PhysicsCanvas": PhysicsCanvas,
|
|
51193
51779
|
"PinballBoard": PinballBoard,
|
|
51194
51780
|
"PirateBoard": PirateBoard,
|
|
51195
51781
|
"PlatformerBoard": PlatformerBoard,
|