@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/providers/index.js
CHANGED
|
@@ -5,7 +5,7 @@ import { createLogger, isLogLevelEnabled } from '@almadar/logger';
|
|
|
5
5
|
import { clsx } from 'clsx';
|
|
6
6
|
import { twMerge } from 'tailwind-merge';
|
|
7
7
|
import * as LucideIcons2 from 'lucide-react';
|
|
8
|
-
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,
|
|
8
|
+
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';
|
|
9
9
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
10
10
|
import { createPortal } from 'react-dom';
|
|
11
11
|
import { useTranslate } from '@almadar/ui/hooks';
|
|
@@ -5631,6 +5631,66 @@ var init_RangeSlider = __esm({
|
|
|
5631
5631
|
RangeSlider.displayName = "RangeSlider";
|
|
5632
5632
|
}
|
|
5633
5633
|
});
|
|
5634
|
+
function easeOut(t) {
|
|
5635
|
+
return t * (2 - t);
|
|
5636
|
+
}
|
|
5637
|
+
var AnimatedCounter;
|
|
5638
|
+
var init_AnimatedCounter = __esm({
|
|
5639
|
+
"components/marketing/atoms/AnimatedCounter.tsx"() {
|
|
5640
|
+
"use client";
|
|
5641
|
+
init_cn();
|
|
5642
|
+
init_Typography();
|
|
5643
|
+
AnimatedCounter = ({
|
|
5644
|
+
value: rawValue,
|
|
5645
|
+
duration = 600,
|
|
5646
|
+
prefix,
|
|
5647
|
+
suffix,
|
|
5648
|
+
className
|
|
5649
|
+
}) => {
|
|
5650
|
+
const numericRaw = typeof rawValue === "number" ? rawValue : Number.parseFloat(String(rawValue ?? ""));
|
|
5651
|
+
const value = !Number.isNaN(numericRaw) ? numericRaw : 0;
|
|
5652
|
+
const [displayValue, setDisplayValue] = useState(value);
|
|
5653
|
+
const previousValueRef = useRef(value);
|
|
5654
|
+
const animationFrameRef = useRef(null);
|
|
5655
|
+
useEffect(() => {
|
|
5656
|
+
const from = previousValueRef.current;
|
|
5657
|
+
const to = value;
|
|
5658
|
+
previousValueRef.current = value;
|
|
5659
|
+
if (from === to) {
|
|
5660
|
+
setDisplayValue(to);
|
|
5661
|
+
return;
|
|
5662
|
+
}
|
|
5663
|
+
const startTime = performance.now();
|
|
5664
|
+
const diff = to - from;
|
|
5665
|
+
function animate(currentTime) {
|
|
5666
|
+
const elapsed = currentTime - startTime;
|
|
5667
|
+
const progress = Math.min(elapsed / duration, 1);
|
|
5668
|
+
const easedProgress = easeOut(progress);
|
|
5669
|
+
setDisplayValue(from + diff * easedProgress);
|
|
5670
|
+
if (progress < 1) {
|
|
5671
|
+
animationFrameRef.current = requestAnimationFrame(animate);
|
|
5672
|
+
} else {
|
|
5673
|
+
setDisplayValue(to);
|
|
5674
|
+
}
|
|
5675
|
+
}
|
|
5676
|
+
animationFrameRef.current = requestAnimationFrame(animate);
|
|
5677
|
+
return () => {
|
|
5678
|
+
if (animationFrameRef.current !== null) {
|
|
5679
|
+
cancelAnimationFrame(animationFrameRef.current);
|
|
5680
|
+
}
|
|
5681
|
+
};
|
|
5682
|
+
}, [value, duration]);
|
|
5683
|
+
const decimalPlaces = Number.isInteger(value) ? 0 : String(value).split(".")[1]?.length ?? 0;
|
|
5684
|
+
const formattedValue = displayValue.toFixed(decimalPlaces);
|
|
5685
|
+
return /* @__PURE__ */ jsxs(Typography, { variant: "h3", className: cn("tabular-nums", className), children: [
|
|
5686
|
+
prefix,
|
|
5687
|
+
formattedValue,
|
|
5688
|
+
suffix
|
|
5689
|
+
] });
|
|
5690
|
+
};
|
|
5691
|
+
AnimatedCounter.displayName = "AnimatedCounter";
|
|
5692
|
+
}
|
|
5693
|
+
});
|
|
5634
5694
|
function useInfiniteScroll(onLoadMore, options = {}) {
|
|
5635
5695
|
const { rootMargin = "200px", hasMore = true, isLoading = false } = options;
|
|
5636
5696
|
const observerRef = useRef(null);
|
|
@@ -5927,6 +5987,34 @@ var init_SectionHeader = __esm({
|
|
|
5927
5987
|
SectionHeader.displayName = "SectionHeader";
|
|
5928
5988
|
}
|
|
5929
5989
|
});
|
|
5990
|
+
var sizeClasses6, MarketingStatCard;
|
|
5991
|
+
var init_MarketingStatCard = __esm({
|
|
5992
|
+
"components/marketing/atoms/MarketingStatCard.tsx"() {
|
|
5993
|
+
init_cn();
|
|
5994
|
+
init_Stack();
|
|
5995
|
+
init_Typography();
|
|
5996
|
+
sizeClasses6 = {
|
|
5997
|
+
sm: "text-2xl",
|
|
5998
|
+
md: "text-4xl",
|
|
5999
|
+
lg: "text-5xl"
|
|
6000
|
+
};
|
|
6001
|
+
MarketingStatCard = ({ value, label, size = "md", className }) => {
|
|
6002
|
+
return /* @__PURE__ */ jsxs(VStack, { gap: "xs", align: "center", className: cn(className), children: [
|
|
6003
|
+
/* @__PURE__ */ jsx(
|
|
6004
|
+
Typography,
|
|
6005
|
+
{
|
|
6006
|
+
weight: "bold",
|
|
6007
|
+
color: "primary",
|
|
6008
|
+
className: cn(sizeClasses6[size]),
|
|
6009
|
+
children: value
|
|
6010
|
+
}
|
|
6011
|
+
),
|
|
6012
|
+
/* @__PURE__ */ jsx(Typography, { variant: "caption", color: "muted", children: label })
|
|
6013
|
+
] });
|
|
6014
|
+
};
|
|
6015
|
+
MarketingStatCard.displayName = "MarketingStatCard";
|
|
6016
|
+
}
|
|
6017
|
+
});
|
|
5930
6018
|
var backgroundClasses, paddingClasses, ContentSection;
|
|
5931
6019
|
var init_ContentSection = __esm({
|
|
5932
6020
|
"components/marketing/atoms/ContentSection.tsx"() {
|
|
@@ -12108,318 +12196,14 @@ var init_useCanvasEffects = __esm({
|
|
|
12108
12196
|
"use client";
|
|
12109
12197
|
}
|
|
12110
12198
|
});
|
|
12111
|
-
function pickPath(entry) {
|
|
12112
|
-
if (Array.isArray(entry.path)) {
|
|
12113
|
-
return entry.path[Math.floor(Math.random() * entry.path.length)];
|
|
12114
|
-
}
|
|
12115
|
-
return entry.path;
|
|
12116
|
-
}
|
|
12117
|
-
function useGameAudio({
|
|
12118
|
-
manifest,
|
|
12119
|
-
baseUrl = "",
|
|
12120
|
-
initialMuted = false,
|
|
12121
|
-
initialVolume = 1
|
|
12122
|
-
}) {
|
|
12123
|
-
const [muted, setMutedState] = useState(initialMuted);
|
|
12124
|
-
const [masterVolume, setMasterVolumeState] = useState(initialVolume);
|
|
12125
|
-
const mutedRef = useRef(muted);
|
|
12126
|
-
const volumeRef = useRef(masterVolume);
|
|
12127
|
-
const manifestRef = useRef(manifest);
|
|
12128
|
-
mutedRef.current = muted;
|
|
12129
|
-
volumeRef.current = masterVolume;
|
|
12130
|
-
manifestRef.current = manifest;
|
|
12131
|
-
const poolsRef = useRef(/* @__PURE__ */ new Map());
|
|
12132
|
-
const getOrCreateElement = useCallback((key) => {
|
|
12133
|
-
const entry = manifestRef.current[key];
|
|
12134
|
-
if (!entry) return null;
|
|
12135
|
-
let pool = poolsRef.current.get(key);
|
|
12136
|
-
if (!pool) {
|
|
12137
|
-
pool = [];
|
|
12138
|
-
poolsRef.current.set(key, pool);
|
|
12139
|
-
}
|
|
12140
|
-
const maxSize = entry.poolSize ?? 1;
|
|
12141
|
-
for (const audio of pool) {
|
|
12142
|
-
if (audio.paused && (audio.ended || audio.currentTime === 0)) {
|
|
12143
|
-
return audio;
|
|
12144
|
-
}
|
|
12145
|
-
}
|
|
12146
|
-
if (pool.length < maxSize) {
|
|
12147
|
-
const src = baseUrl + pickPath(entry);
|
|
12148
|
-
const audio = new Audio(src);
|
|
12149
|
-
audio.loop = entry.loop ?? false;
|
|
12150
|
-
pool.push(audio);
|
|
12151
|
-
return audio;
|
|
12152
|
-
}
|
|
12153
|
-
if (!entry.loop) {
|
|
12154
|
-
let oldest = pool[0];
|
|
12155
|
-
for (const audio of pool) {
|
|
12156
|
-
if (audio.currentTime > oldest.currentTime) {
|
|
12157
|
-
oldest = audio;
|
|
12158
|
-
}
|
|
12159
|
-
}
|
|
12160
|
-
oldest.pause();
|
|
12161
|
-
oldest.currentTime = 0;
|
|
12162
|
-
return oldest;
|
|
12163
|
-
}
|
|
12164
|
-
return null;
|
|
12165
|
-
}, [baseUrl]);
|
|
12166
|
-
const play = useCallback((key) => {
|
|
12167
|
-
if (mutedRef.current) return;
|
|
12168
|
-
const entry = manifestRef.current[key];
|
|
12169
|
-
if (!entry) return;
|
|
12170
|
-
const audio = getOrCreateElement(key);
|
|
12171
|
-
if (!audio) return;
|
|
12172
|
-
audio.volume = Math.min(1, (entry.volume ?? 1) * volumeRef.current);
|
|
12173
|
-
if (!entry.loop) {
|
|
12174
|
-
audio.currentTime = 0;
|
|
12175
|
-
}
|
|
12176
|
-
const promise = audio.play();
|
|
12177
|
-
if (promise) {
|
|
12178
|
-
promise.catch(() => {
|
|
12179
|
-
});
|
|
12180
|
-
}
|
|
12181
|
-
}, [getOrCreateElement]);
|
|
12182
|
-
const stop = useCallback((key) => {
|
|
12183
|
-
const pool = poolsRef.current.get(key);
|
|
12184
|
-
if (!pool) return;
|
|
12185
|
-
for (const audio of pool) {
|
|
12186
|
-
audio.pause();
|
|
12187
|
-
audio.currentTime = 0;
|
|
12188
|
-
}
|
|
12189
|
-
}, []);
|
|
12190
|
-
const currentMusicKeyRef = useRef(null);
|
|
12191
|
-
const currentMusicElRef = useRef(null);
|
|
12192
|
-
const musicFadeRef = useRef(null);
|
|
12193
|
-
const pendingMusicKeyRef = useRef(null);
|
|
12194
|
-
const clearMusicFade = useCallback(() => {
|
|
12195
|
-
if (musicFadeRef.current) {
|
|
12196
|
-
clearInterval(musicFadeRef.current);
|
|
12197
|
-
musicFadeRef.current = null;
|
|
12198
|
-
}
|
|
12199
|
-
}, []);
|
|
12200
|
-
const playMusic = useCallback((key) => {
|
|
12201
|
-
if (key === currentMusicKeyRef.current) return;
|
|
12202
|
-
pendingMusicKeyRef.current = key;
|
|
12203
|
-
const entry = manifestRef.current[key];
|
|
12204
|
-
if (!entry) return;
|
|
12205
|
-
const fadeDurationMs = entry.crossfadeDurationMs ?? 1500;
|
|
12206
|
-
const stepMs = 50;
|
|
12207
|
-
const totalSteps = Math.max(1, fadeDurationMs / stepMs);
|
|
12208
|
-
const targetVolume = Math.min(1, (entry.volume ?? 1) * volumeRef.current);
|
|
12209
|
-
clearMusicFade();
|
|
12210
|
-
const src = baseUrl + (Array.isArray(entry.path) ? entry.path[0] : entry.path);
|
|
12211
|
-
const incoming = new Audio(src);
|
|
12212
|
-
incoming.loop = true;
|
|
12213
|
-
incoming.volume = 0;
|
|
12214
|
-
const outgoing = currentMusicElRef.current;
|
|
12215
|
-
const outgoingStartVol = outgoing?.volume ?? 0;
|
|
12216
|
-
currentMusicKeyRef.current = key;
|
|
12217
|
-
currentMusicElRef.current = incoming;
|
|
12218
|
-
if (!mutedRef.current) {
|
|
12219
|
-
incoming.play().catch(() => {
|
|
12220
|
-
currentMusicKeyRef.current = null;
|
|
12221
|
-
currentMusicElRef.current = outgoing;
|
|
12222
|
-
});
|
|
12223
|
-
}
|
|
12224
|
-
let step = 0;
|
|
12225
|
-
musicFadeRef.current = setInterval(() => {
|
|
12226
|
-
step++;
|
|
12227
|
-
const progress = Math.min(step / totalSteps, 1);
|
|
12228
|
-
incoming.volume = Math.min(1, targetVolume * progress);
|
|
12229
|
-
if (outgoing) {
|
|
12230
|
-
outgoing.volume = Math.max(0, outgoingStartVol * (1 - progress));
|
|
12231
|
-
}
|
|
12232
|
-
if (progress >= 1) {
|
|
12233
|
-
clearMusicFade();
|
|
12234
|
-
if (outgoing) {
|
|
12235
|
-
outgoing.pause();
|
|
12236
|
-
outgoing.src = "";
|
|
12237
|
-
}
|
|
12238
|
-
}
|
|
12239
|
-
}, stepMs);
|
|
12240
|
-
}, [baseUrl, clearMusicFade]);
|
|
12241
|
-
const stopMusic = useCallback((fadeDurationMs = 1e3) => {
|
|
12242
|
-
const outgoing = currentMusicElRef.current;
|
|
12243
|
-
if (!outgoing) return;
|
|
12244
|
-
currentMusicKeyRef.current = null;
|
|
12245
|
-
currentMusicElRef.current = null;
|
|
12246
|
-
pendingMusicKeyRef.current = null;
|
|
12247
|
-
clearMusicFade();
|
|
12248
|
-
const startVolume = outgoing.volume;
|
|
12249
|
-
const stepMs = 50;
|
|
12250
|
-
const totalSteps = Math.max(1, fadeDurationMs / stepMs);
|
|
12251
|
-
let step = 0;
|
|
12252
|
-
musicFadeRef.current = setInterval(() => {
|
|
12253
|
-
step++;
|
|
12254
|
-
const progress = step / totalSteps;
|
|
12255
|
-
outgoing.volume = Math.max(0, startVolume * (1 - progress));
|
|
12256
|
-
if (progress >= 1) {
|
|
12257
|
-
clearMusicFade();
|
|
12258
|
-
outgoing.pause();
|
|
12259
|
-
outgoing.src = "";
|
|
12260
|
-
}
|
|
12261
|
-
}, stepMs);
|
|
12262
|
-
}, [clearMusicFade]);
|
|
12263
|
-
const stopAll = useCallback(() => {
|
|
12264
|
-
for (const pool of poolsRef.current.values()) {
|
|
12265
|
-
for (const audio of pool) {
|
|
12266
|
-
audio.pause();
|
|
12267
|
-
audio.currentTime = 0;
|
|
12268
|
-
}
|
|
12269
|
-
}
|
|
12270
|
-
stopMusic(0);
|
|
12271
|
-
}, [stopMusic]);
|
|
12272
|
-
const setMuted = useCallback((value) => {
|
|
12273
|
-
setMutedState(value);
|
|
12274
|
-
if (value) {
|
|
12275
|
-
for (const [key, pool] of poolsRef.current.entries()) {
|
|
12276
|
-
if (manifestRef.current[key]?.loop) {
|
|
12277
|
-
for (const audio of pool) {
|
|
12278
|
-
if (!audio.paused) audio.pause();
|
|
12279
|
-
}
|
|
12280
|
-
}
|
|
12281
|
-
}
|
|
12282
|
-
currentMusicElRef.current?.pause();
|
|
12283
|
-
} else {
|
|
12284
|
-
for (const [key, pool] of poolsRef.current.entries()) {
|
|
12285
|
-
const entry = manifestRef.current[key];
|
|
12286
|
-
if (entry?.loop && entry?.autostart) {
|
|
12287
|
-
for (const audio of pool) {
|
|
12288
|
-
if (audio.paused) audio.play().catch(() => {
|
|
12289
|
-
});
|
|
12290
|
-
}
|
|
12291
|
-
}
|
|
12292
|
-
}
|
|
12293
|
-
const musicEl = currentMusicElRef.current;
|
|
12294
|
-
if (musicEl) {
|
|
12295
|
-
musicEl.play().catch(() => {
|
|
12296
|
-
});
|
|
12297
|
-
}
|
|
12298
|
-
}
|
|
12299
|
-
}, []);
|
|
12300
|
-
const setMasterVolume = useCallback((volume) => {
|
|
12301
|
-
const clamped = Math.max(0, Math.min(1, volume));
|
|
12302
|
-
setMasterVolumeState(clamped);
|
|
12303
|
-
for (const [key, pool] of poolsRef.current.entries()) {
|
|
12304
|
-
const entryVol = manifestRef.current[key]?.volume ?? 1;
|
|
12305
|
-
for (const audio of pool) {
|
|
12306
|
-
audio.volume = Math.min(1, entryVol * clamped);
|
|
12307
|
-
}
|
|
12308
|
-
}
|
|
12309
|
-
if (!musicFadeRef.current && currentMusicElRef.current) {
|
|
12310
|
-
const key = currentMusicKeyRef.current;
|
|
12311
|
-
const entryVol = key ? manifestRef.current[key]?.volume ?? 1 : 1;
|
|
12312
|
-
currentMusicElRef.current.volume = Math.min(1, entryVol * clamped);
|
|
12313
|
-
}
|
|
12314
|
-
}, []);
|
|
12315
|
-
const unlockedRef = useRef(false);
|
|
12316
|
-
useEffect(() => {
|
|
12317
|
-
const autoKeys = Object.keys(manifest).filter((k) => manifest[k].autostart);
|
|
12318
|
-
const hasPendingMusic = () => pendingMusicKeyRef.current !== null;
|
|
12319
|
-
const hasAutoStart = autoKeys.length > 0;
|
|
12320
|
-
if (!hasAutoStart && !hasPendingMusic()) return;
|
|
12321
|
-
const unlock = () => {
|
|
12322
|
-
if (unlockedRef.current) return;
|
|
12323
|
-
unlockedRef.current = true;
|
|
12324
|
-
if (!mutedRef.current) {
|
|
12325
|
-
for (const key of autoKeys) {
|
|
12326
|
-
play(key);
|
|
12327
|
-
}
|
|
12328
|
-
const pending = pendingMusicKeyRef.current;
|
|
12329
|
-
if (pending && pending !== currentMusicKeyRef.current) {
|
|
12330
|
-
playMusic(pending);
|
|
12331
|
-
}
|
|
12332
|
-
}
|
|
12333
|
-
};
|
|
12334
|
-
document.addEventListener("click", unlock, { once: true });
|
|
12335
|
-
document.addEventListener("keydown", unlock, { once: true });
|
|
12336
|
-
document.addEventListener("touchstart", unlock, { once: true });
|
|
12337
|
-
return () => {
|
|
12338
|
-
document.removeEventListener("click", unlock);
|
|
12339
|
-
document.removeEventListener("keydown", unlock);
|
|
12340
|
-
document.removeEventListener("touchstart", unlock);
|
|
12341
|
-
};
|
|
12342
|
-
}, [manifest, play, playMusic]);
|
|
12343
|
-
useEffect(() => {
|
|
12344
|
-
return () => {
|
|
12345
|
-
clearMusicFade();
|
|
12346
|
-
for (const pool of poolsRef.current.values()) {
|
|
12347
|
-
for (const audio of pool) {
|
|
12348
|
-
audio.pause();
|
|
12349
|
-
audio.src = "";
|
|
12350
|
-
}
|
|
12351
|
-
}
|
|
12352
|
-
poolsRef.current.clear();
|
|
12353
|
-
if (currentMusicElRef.current) {
|
|
12354
|
-
currentMusicElRef.current.pause();
|
|
12355
|
-
currentMusicElRef.current.src = "";
|
|
12356
|
-
currentMusicElRef.current = null;
|
|
12357
|
-
}
|
|
12358
|
-
};
|
|
12359
|
-
}, [clearMusicFade]);
|
|
12360
|
-
return {
|
|
12361
|
-
play,
|
|
12362
|
-
stop,
|
|
12363
|
-
stopAll,
|
|
12364
|
-
playMusic,
|
|
12365
|
-
stopMusic,
|
|
12366
|
-
muted,
|
|
12367
|
-
setMuted,
|
|
12368
|
-
masterVolume,
|
|
12369
|
-
setMasterVolume
|
|
12370
|
-
};
|
|
12371
|
-
}
|
|
12372
12199
|
var init_useGameAudio = __esm({
|
|
12373
12200
|
"components/game/shared/hooks/useGameAudio.ts"() {
|
|
12374
12201
|
"use client";
|
|
12375
|
-
useGameAudio.displayName = "useGameAudio";
|
|
12376
12202
|
}
|
|
12377
12203
|
});
|
|
12378
12204
|
function useGameAudioContextOptional() {
|
|
12379
12205
|
return useContext(GameAudioContext);
|
|
12380
12206
|
}
|
|
12381
|
-
function GameAudioProvider({
|
|
12382
|
-
manifest,
|
|
12383
|
-
baseUrl = "",
|
|
12384
|
-
children,
|
|
12385
|
-
initialMuted = false
|
|
12386
|
-
}) {
|
|
12387
|
-
const eventBus = useEventBus();
|
|
12388
|
-
const { play, stop, stopAll, playMusic, stopMusic, muted, setMuted, masterVolume, setMasterVolume } = useGameAudio({ manifest, baseUrl, initialMuted });
|
|
12389
|
-
useEffect(() => {
|
|
12390
|
-
const unsubPlay = eventBus.on("UI:PLAY_SOUND", (event) => {
|
|
12391
|
-
const key = event.payload?.key;
|
|
12392
|
-
if (key) play(key);
|
|
12393
|
-
});
|
|
12394
|
-
const unsubStop = eventBus.on("UI:STOP_SOUND", (event) => {
|
|
12395
|
-
const key = event.payload?.key;
|
|
12396
|
-
if (key) {
|
|
12397
|
-
stop(key);
|
|
12398
|
-
} else {
|
|
12399
|
-
stopAll();
|
|
12400
|
-
}
|
|
12401
|
-
});
|
|
12402
|
-
const unsubChangeMusic = eventBus.on("UI:CHANGE_MUSIC", (event) => {
|
|
12403
|
-
const key = event.payload?.key;
|
|
12404
|
-
if (key) {
|
|
12405
|
-
playMusic(key);
|
|
12406
|
-
} else {
|
|
12407
|
-
stopMusic();
|
|
12408
|
-
}
|
|
12409
|
-
});
|
|
12410
|
-
const unsubStopMusic = eventBus.on("UI:STOP_MUSIC", () => {
|
|
12411
|
-
stopMusic();
|
|
12412
|
-
});
|
|
12413
|
-
return () => {
|
|
12414
|
-
unsubPlay();
|
|
12415
|
-
unsubStop();
|
|
12416
|
-
unsubChangeMusic();
|
|
12417
|
-
unsubStopMusic();
|
|
12418
|
-
};
|
|
12419
|
-
}, [eventBus, play, stop, stopAll, playMusic, stopMusic]);
|
|
12420
|
-
const value = { muted, setMuted, masterVolume, setMasterVolume, play, playMusic, stopMusic };
|
|
12421
|
-
return /* @__PURE__ */ jsx(GameAudioContext.Provider, { value, children });
|
|
12422
|
-
}
|
|
12423
12207
|
var GameAudioContext;
|
|
12424
12208
|
var init_GameAudioProvider = __esm({
|
|
12425
12209
|
"components/game/shared/providers/GameAudioProvider.tsx"() {
|
|
@@ -12428,7 +12212,6 @@ var init_GameAudioProvider = __esm({
|
|
|
12428
12212
|
init_useGameAudio();
|
|
12429
12213
|
GameAudioContext = createContext(null);
|
|
12430
12214
|
GameAudioContext.displayName = "GameAudioContext";
|
|
12431
|
-
GameAudioProvider.displayName = "GameAudioProvider";
|
|
12432
12215
|
}
|
|
12433
12216
|
});
|
|
12434
12217
|
function GameAudioToggle({
|
|
@@ -20064,6 +19847,315 @@ var init_molecules = __esm({
|
|
|
20064
19847
|
init_puzzleObject();
|
|
20065
19848
|
}
|
|
20066
19849
|
});
|
|
19850
|
+
function resolveColor2(color, ctx, fallback) {
|
|
19851
|
+
if (!color) return fallback;
|
|
19852
|
+
if (color.startsWith("var(")) {
|
|
19853
|
+
ctx.canvas.style;
|
|
19854
|
+
const m = /^var\((--[^,)]+)(?:,\s*([^)]+))?\)$/.exec(color);
|
|
19855
|
+
if (m) {
|
|
19856
|
+
const computed = getComputedStyle(ctx.canvas).getPropertyValue(m[1]).trim();
|
|
19857
|
+
return computed || m[2]?.trim() || fallback;
|
|
19858
|
+
}
|
|
19859
|
+
}
|
|
19860
|
+
return color;
|
|
19861
|
+
}
|
|
19862
|
+
function shapeBounds(shape) {
|
|
19863
|
+
switch (shape.type) {
|
|
19864
|
+
case "line":
|
|
19865
|
+
case "arrow":
|
|
19866
|
+
if (shape.x1 == null || shape.y1 == null || shape.x2 == null || shape.y2 == null) return null;
|
|
19867
|
+
return {
|
|
19868
|
+
x: Math.min(shape.x1, shape.x2) - 6,
|
|
19869
|
+
y: Math.min(shape.y1, shape.y2) - 6,
|
|
19870
|
+
w: Math.abs(shape.x2 - shape.x1) + 12,
|
|
19871
|
+
h: Math.abs(shape.y2 - shape.y1) + 12
|
|
19872
|
+
};
|
|
19873
|
+
case "circle":
|
|
19874
|
+
if (shape.x == null || shape.y == null || shape.radius == null) return null;
|
|
19875
|
+
return {
|
|
19876
|
+
x: shape.x - shape.radius - 4,
|
|
19877
|
+
y: shape.y - shape.radius - 4,
|
|
19878
|
+
w: shape.radius * 2 + 8,
|
|
19879
|
+
h: shape.radius * 2 + 8
|
|
19880
|
+
};
|
|
19881
|
+
case "rect":
|
|
19882
|
+
if (shape.x == null || shape.y == null || shape.width == null || shape.height == null) return null;
|
|
19883
|
+
return { x: shape.x - 4, y: shape.y - 4, w: shape.width + 8, h: shape.height + 8 };
|
|
19884
|
+
case "polygon":
|
|
19885
|
+
if (!shape.points || shape.points.length === 0) return null;
|
|
19886
|
+
{
|
|
19887
|
+
const xs = shape.points.map((p2) => p2.x);
|
|
19888
|
+
const ys = shape.points.map((p2) => p2.y);
|
|
19889
|
+
const minX = Math.min(...xs);
|
|
19890
|
+
const minY = Math.min(...ys);
|
|
19891
|
+
return {
|
|
19892
|
+
x: minX - 4,
|
|
19893
|
+
y: minY - 4,
|
|
19894
|
+
w: Math.max(...xs) - minX + 8,
|
|
19895
|
+
h: Math.max(...ys) - minY + 8
|
|
19896
|
+
};
|
|
19897
|
+
}
|
|
19898
|
+
case "text":
|
|
19899
|
+
if (shape.x == null || shape.y == null) return null;
|
|
19900
|
+
return { x: shape.x - 4, y: shape.y - (shape.fontSize ?? 14) - 4, w: 120, h: (shape.fontSize ?? 14) + 8 };
|
|
19901
|
+
default:
|
|
19902
|
+
return null;
|
|
19903
|
+
}
|
|
19904
|
+
}
|
|
19905
|
+
function drawArrowHead(ctx, x1, y1, x2, y2, size) {
|
|
19906
|
+
const angle = Math.atan2(y2 - y1, x2 - x1);
|
|
19907
|
+
ctx.beginPath();
|
|
19908
|
+
ctx.moveTo(x2, y2);
|
|
19909
|
+
ctx.lineTo(x2 - size * Math.cos(angle - Math.PI / 6), y2 - size * Math.sin(angle - Math.PI / 6));
|
|
19910
|
+
ctx.lineTo(x2 - size * Math.cos(angle + Math.PI / 6), y2 - size * Math.sin(angle + Math.PI / 6));
|
|
19911
|
+
ctx.closePath();
|
|
19912
|
+
ctx.fill();
|
|
19913
|
+
}
|
|
19914
|
+
function drawShape(ctx, shape, width, height) {
|
|
19915
|
+
ctx.save();
|
|
19916
|
+
const opacity = shape.opacity ?? 1;
|
|
19917
|
+
ctx.globalAlpha = opacity;
|
|
19918
|
+
const stroke = resolveColor2(shape.color, ctx, "#333333");
|
|
19919
|
+
const fill = shape.fill ? resolveColor2(shape.fill, ctx, "#cccccc") : void 0;
|
|
19920
|
+
ctx.lineWidth = shape.lineWidth ?? 2;
|
|
19921
|
+
switch (shape.type) {
|
|
19922
|
+
case "grid": {
|
|
19923
|
+
const step = shape.step ?? 40;
|
|
19924
|
+
ctx.strokeStyle = stroke;
|
|
19925
|
+
ctx.globalAlpha = opacity * 0.25;
|
|
19926
|
+
ctx.lineWidth = 1;
|
|
19927
|
+
ctx.beginPath();
|
|
19928
|
+
for (let x = 0; x <= width; x += step) {
|
|
19929
|
+
ctx.moveTo(x, 0);
|
|
19930
|
+
ctx.lineTo(x, height);
|
|
19931
|
+
}
|
|
19932
|
+
for (let y = 0; y <= height; y += step) {
|
|
19933
|
+
ctx.moveTo(0, y);
|
|
19934
|
+
ctx.lineTo(width, y);
|
|
19935
|
+
}
|
|
19936
|
+
ctx.stroke();
|
|
19937
|
+
break;
|
|
19938
|
+
}
|
|
19939
|
+
case "axis": {
|
|
19940
|
+
const axis = shape.axis ?? "x";
|
|
19941
|
+
ctx.strokeStyle = stroke;
|
|
19942
|
+
ctx.lineWidth = shape.lineWidth ?? 2;
|
|
19943
|
+
ctx.beginPath();
|
|
19944
|
+
if (axis === "x") {
|
|
19945
|
+
ctx.moveTo(0, height / 2);
|
|
19946
|
+
ctx.lineTo(width, height / 2);
|
|
19947
|
+
} else {
|
|
19948
|
+
ctx.moveTo(width / 2, 0);
|
|
19949
|
+
ctx.lineTo(width / 2, height);
|
|
19950
|
+
}
|
|
19951
|
+
ctx.stroke();
|
|
19952
|
+
break;
|
|
19953
|
+
}
|
|
19954
|
+
case "line": {
|
|
19955
|
+
if (shape.x1 == null || shape.y1 == null || shape.x2 == null || shape.y2 == null) break;
|
|
19956
|
+
ctx.strokeStyle = stroke;
|
|
19957
|
+
ctx.beginPath();
|
|
19958
|
+
ctx.moveTo(shape.x1, shape.y1);
|
|
19959
|
+
ctx.lineTo(shape.x2, shape.y2);
|
|
19960
|
+
ctx.stroke();
|
|
19961
|
+
break;
|
|
19962
|
+
}
|
|
19963
|
+
case "arrow": {
|
|
19964
|
+
if (shape.x1 == null || shape.y1 == null || shape.x2 == null || shape.y2 == null) break;
|
|
19965
|
+
ctx.strokeStyle = stroke;
|
|
19966
|
+
ctx.fillStyle = stroke;
|
|
19967
|
+
ctx.beginPath();
|
|
19968
|
+
ctx.moveTo(shape.x1, shape.y1);
|
|
19969
|
+
ctx.lineTo(shape.x2, shape.y2);
|
|
19970
|
+
ctx.stroke();
|
|
19971
|
+
drawArrowHead(ctx, shape.x1, shape.y1, shape.x2, shape.y2, 10);
|
|
19972
|
+
break;
|
|
19973
|
+
}
|
|
19974
|
+
case "circle": {
|
|
19975
|
+
if (shape.x == null || shape.y == null || shape.radius == null) break;
|
|
19976
|
+
ctx.beginPath();
|
|
19977
|
+
ctx.arc(shape.x, shape.y, shape.radius, 0, Math.PI * 2);
|
|
19978
|
+
if (fill) {
|
|
19979
|
+
ctx.fillStyle = fill;
|
|
19980
|
+
ctx.fill();
|
|
19981
|
+
}
|
|
19982
|
+
ctx.strokeStyle = stroke;
|
|
19983
|
+
ctx.stroke();
|
|
19984
|
+
break;
|
|
19985
|
+
}
|
|
19986
|
+
case "rect": {
|
|
19987
|
+
if (shape.x == null || shape.y == null || shape.width == null || shape.height == null) break;
|
|
19988
|
+
if (fill) {
|
|
19989
|
+
ctx.fillStyle = fill;
|
|
19990
|
+
ctx.fillRect(shape.x, shape.y, shape.width, shape.height);
|
|
19991
|
+
}
|
|
19992
|
+
ctx.strokeStyle = stroke;
|
|
19993
|
+
ctx.strokeRect(shape.x, shape.y, shape.width, shape.height);
|
|
19994
|
+
break;
|
|
19995
|
+
}
|
|
19996
|
+
case "polygon": {
|
|
19997
|
+
if (!shape.points || shape.points.length < 2) break;
|
|
19998
|
+
ctx.beginPath();
|
|
19999
|
+
ctx.moveTo(shape.points[0].x, shape.points[0].y);
|
|
20000
|
+
for (let i = 1; i < shape.points.length; i++) {
|
|
20001
|
+
ctx.lineTo(shape.points[i].x, shape.points[i].y);
|
|
20002
|
+
}
|
|
20003
|
+
ctx.closePath();
|
|
20004
|
+
if (fill) {
|
|
20005
|
+
ctx.fillStyle = fill;
|
|
20006
|
+
ctx.fill();
|
|
20007
|
+
}
|
|
20008
|
+
ctx.strokeStyle = stroke;
|
|
20009
|
+
ctx.stroke();
|
|
20010
|
+
break;
|
|
20011
|
+
}
|
|
20012
|
+
case "path": {
|
|
20013
|
+
if (!shape.path) break;
|
|
20014
|
+
const p2 = new Path2D(shape.path);
|
|
20015
|
+
if (fill) {
|
|
20016
|
+
ctx.fillStyle = fill;
|
|
20017
|
+
ctx.fill(p2);
|
|
20018
|
+
}
|
|
20019
|
+
ctx.strokeStyle = stroke;
|
|
20020
|
+
ctx.stroke(p2);
|
|
20021
|
+
break;
|
|
20022
|
+
}
|
|
20023
|
+
case "text": {
|
|
20024
|
+
if (shape.x == null || shape.y == null || !shape.text) break;
|
|
20025
|
+
ctx.fillStyle = stroke;
|
|
20026
|
+
ctx.font = `${shape.fontSize ?? 14}px system-ui, sans-serif`;
|
|
20027
|
+
ctx.textAlign = shape.align ?? "left";
|
|
20028
|
+
ctx.textBaseline = "middle";
|
|
20029
|
+
ctx.fillText(shape.text, shape.x, shape.y);
|
|
20030
|
+
break;
|
|
20031
|
+
}
|
|
20032
|
+
}
|
|
20033
|
+
ctx.restore();
|
|
20034
|
+
}
|
|
20035
|
+
var LearningCanvas;
|
|
20036
|
+
var init_LearningCanvas = __esm({
|
|
20037
|
+
"components/learning/atoms/LearningCanvas.tsx"() {
|
|
20038
|
+
"use client";
|
|
20039
|
+
init_cn();
|
|
20040
|
+
init_useEventBus();
|
|
20041
|
+
LearningCanvas = ({
|
|
20042
|
+
className,
|
|
20043
|
+
width = 600,
|
|
20044
|
+
height = 400,
|
|
20045
|
+
backgroundColor,
|
|
20046
|
+
shapes = [],
|
|
20047
|
+
interactive = false,
|
|
20048
|
+
animate = false,
|
|
20049
|
+
onShapeClick,
|
|
20050
|
+
onShapeHover,
|
|
20051
|
+
isLoading,
|
|
20052
|
+
error
|
|
20053
|
+
}) => {
|
|
20054
|
+
const canvasRef = useRef(null);
|
|
20055
|
+
const eventBus = useEventBus();
|
|
20056
|
+
const animRef = useRef(0);
|
|
20057
|
+
const hoverIndexRef = useRef(-1);
|
|
20058
|
+
const findShapeAt = useCallback((clientX, clientY) => {
|
|
20059
|
+
const canvas = canvasRef.current;
|
|
20060
|
+
if (!canvas) return -1;
|
|
20061
|
+
const rect = canvas.getBoundingClientRect();
|
|
20062
|
+
const x = clientX - rect.left;
|
|
20063
|
+
const y = clientY - rect.top;
|
|
20064
|
+
for (let i = shapes.length - 1; i >= 0; i--) {
|
|
20065
|
+
const b = shapeBounds(shapes[i]);
|
|
20066
|
+
if (b && x >= b.x && x <= b.x + b.w && y >= b.y && y <= b.y + b.h) {
|
|
20067
|
+
return i;
|
|
20068
|
+
}
|
|
20069
|
+
}
|
|
20070
|
+
return -1;
|
|
20071
|
+
}, [shapes]);
|
|
20072
|
+
const draw = useCallback(() => {
|
|
20073
|
+
const canvas = canvasRef.current;
|
|
20074
|
+
if (!canvas) return;
|
|
20075
|
+
const ctx = canvas.getContext("2d");
|
|
20076
|
+
if (!ctx) return;
|
|
20077
|
+
const dpr = typeof window !== "undefined" ? window.devicePixelRatio || 1 : 1;
|
|
20078
|
+
canvas.width = Math.max(1, Math.floor(width * dpr));
|
|
20079
|
+
canvas.height = Math.max(1, Math.floor(height * dpr));
|
|
20080
|
+
canvas.style.width = `${width}px`;
|
|
20081
|
+
canvas.style.height = `${height}px`;
|
|
20082
|
+
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
|
|
20083
|
+
ctx.clearRect(0, 0, width, height);
|
|
20084
|
+
if (backgroundColor) {
|
|
20085
|
+
ctx.fillStyle = backgroundColor;
|
|
20086
|
+
ctx.fillRect(0, 0, width, height);
|
|
20087
|
+
}
|
|
20088
|
+
for (const shape of shapes) {
|
|
20089
|
+
drawShape(ctx, shape, width, height);
|
|
20090
|
+
}
|
|
20091
|
+
}, [width, height, backgroundColor, shapes]);
|
|
20092
|
+
useEffect(() => {
|
|
20093
|
+
draw();
|
|
20094
|
+
}, [draw]);
|
|
20095
|
+
useEffect(() => {
|
|
20096
|
+
if (!animate) return;
|
|
20097
|
+
const loop = () => {
|
|
20098
|
+
draw();
|
|
20099
|
+
animRef.current = requestAnimationFrame(loop);
|
|
20100
|
+
};
|
|
20101
|
+
animRef.current = requestAnimationFrame(loop);
|
|
20102
|
+
return () => cancelAnimationFrame(animRef.current);
|
|
20103
|
+
}, [animate, draw]);
|
|
20104
|
+
const handlePointerMove = useCallback(
|
|
20105
|
+
(e) => {
|
|
20106
|
+
if (!interactive) return;
|
|
20107
|
+
const idx = findShapeAt(e.clientX, e.clientY);
|
|
20108
|
+
if (idx !== hoverIndexRef.current) {
|
|
20109
|
+
hoverIndexRef.current = idx;
|
|
20110
|
+
if (idx >= 0) {
|
|
20111
|
+
const shape = shapes[idx];
|
|
20112
|
+
const payload = { id: shape.id, type: shape.type, index: idx };
|
|
20113
|
+
if (onShapeHover) onShapeHover(payload);
|
|
20114
|
+
else if (eventBus) eventBus.emit(`UI:SHAPE_HOVER`, payload);
|
|
20115
|
+
}
|
|
20116
|
+
}
|
|
20117
|
+
},
|
|
20118
|
+
[interactive, onShapeHover, eventBus, findShapeAt, shapes]
|
|
20119
|
+
);
|
|
20120
|
+
const handleClick = useCallback(
|
|
20121
|
+
(e) => {
|
|
20122
|
+
if (!interactive) return;
|
|
20123
|
+
const idx = findShapeAt(e.clientX, e.clientY);
|
|
20124
|
+
if (idx >= 0) {
|
|
20125
|
+
const shape = shapes[idx];
|
|
20126
|
+
const payload = { id: shape.id, type: shape.type, index: idx };
|
|
20127
|
+
if (onShapeClick) onShapeClick(payload);
|
|
20128
|
+
else if (eventBus) eventBus.emit(`UI:SHAPE_CLICK`, payload);
|
|
20129
|
+
}
|
|
20130
|
+
},
|
|
20131
|
+
[interactive, onShapeClick, eventBus, findShapeAt, shapes]
|
|
20132
|
+
);
|
|
20133
|
+
if (isLoading || error) {
|
|
20134
|
+
return /* @__PURE__ */ jsx(
|
|
20135
|
+
"div",
|
|
20136
|
+
{
|
|
20137
|
+
className: cn(
|
|
20138
|
+
"flex items-center justify-center rounded border border-border bg-surface",
|
|
20139
|
+
className
|
|
20140
|
+
),
|
|
20141
|
+
style: { width, height },
|
|
20142
|
+
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" })
|
|
20143
|
+
}
|
|
20144
|
+
);
|
|
20145
|
+
}
|
|
20146
|
+
return /* @__PURE__ */ jsx(
|
|
20147
|
+
"canvas",
|
|
20148
|
+
{
|
|
20149
|
+
ref: canvasRef,
|
|
20150
|
+
className: cn("block touch-none rounded border border-border", className),
|
|
20151
|
+
style: { width, height },
|
|
20152
|
+
onClick: handleClick,
|
|
20153
|
+
onPointerMove: handlePointerMove
|
|
20154
|
+
}
|
|
20155
|
+
);
|
|
20156
|
+
};
|
|
20157
|
+
}
|
|
20158
|
+
});
|
|
20067
20159
|
|
|
20068
20160
|
// components/core/atoms/index.ts
|
|
20069
20161
|
var init_atoms = __esm({
|
|
@@ -21932,91 +22024,6 @@ var init_ComponentPatterns = __esm({
|
|
|
21932
22024
|
AlertPattern.displayName = "AlertPattern";
|
|
21933
22025
|
}
|
|
21934
22026
|
});
|
|
21935
|
-
function parseValue(value) {
|
|
21936
|
-
if (value === "" || value == null) return { num: 0, prefix: "", suffix: "", decimals: 0 };
|
|
21937
|
-
const match = String(value).match(/^([^0-9]*)([0-9]+(?:\.[0-9]+)?)(.*)$/);
|
|
21938
|
-
if (!match) {
|
|
21939
|
-
return { num: 0, prefix: "", suffix: String(value), decimals: 0 };
|
|
21940
|
-
}
|
|
21941
|
-
const numStr = match[2];
|
|
21942
|
-
const decimalIdx = numStr.indexOf(".");
|
|
21943
|
-
const decimals = decimalIdx >= 0 ? numStr.length - decimalIdx - 1 : 0;
|
|
21944
|
-
return {
|
|
21945
|
-
prefix: match[1],
|
|
21946
|
-
num: parseFloat(numStr),
|
|
21947
|
-
suffix: match[3],
|
|
21948
|
-
decimals
|
|
21949
|
-
};
|
|
21950
|
-
}
|
|
21951
|
-
var AnimatedCounter;
|
|
21952
|
-
var init_AnimatedCounter = __esm({
|
|
21953
|
-
"components/core/molecules/AnimatedCounter.tsx"() {
|
|
21954
|
-
"use client";
|
|
21955
|
-
init_cn();
|
|
21956
|
-
init_Box();
|
|
21957
|
-
init_Typography();
|
|
21958
|
-
AnimatedCounter = ({
|
|
21959
|
-
value,
|
|
21960
|
-
label,
|
|
21961
|
-
duration = 1500,
|
|
21962
|
-
className
|
|
21963
|
-
}) => {
|
|
21964
|
-
const ref = useRef(null);
|
|
21965
|
-
const [displayValue, setDisplayValue] = useState("0");
|
|
21966
|
-
const [hasAnimated, setHasAnimated] = useState(false);
|
|
21967
|
-
const animate = useCallback(() => {
|
|
21968
|
-
const { num: num2, prefix, suffix, decimals } = parseValue(value);
|
|
21969
|
-
if (num2 === 0) {
|
|
21970
|
-
setDisplayValue(String(value));
|
|
21971
|
-
return;
|
|
21972
|
-
}
|
|
21973
|
-
const startTime = performance.now();
|
|
21974
|
-
const tick = (now) => {
|
|
21975
|
-
const elapsed = now - startTime;
|
|
21976
|
-
const progress = Math.min(elapsed / duration, 1);
|
|
21977
|
-
const eased = 1 - Math.pow(1 - progress, 3);
|
|
21978
|
-
const current = eased * num2;
|
|
21979
|
-
setDisplayValue(`${prefix}${current.toFixed(decimals)}${suffix}`);
|
|
21980
|
-
if (progress < 1) {
|
|
21981
|
-
requestAnimationFrame(tick);
|
|
21982
|
-
} else {
|
|
21983
|
-
setDisplayValue(String(value));
|
|
21984
|
-
}
|
|
21985
|
-
};
|
|
21986
|
-
requestAnimationFrame(tick);
|
|
21987
|
-
}, [value, duration]);
|
|
21988
|
-
useEffect(() => {
|
|
21989
|
-
if (hasAnimated) return;
|
|
21990
|
-
const el = ref.current;
|
|
21991
|
-
if (!el) return;
|
|
21992
|
-
const observer2 = new IntersectionObserver(
|
|
21993
|
-
(entries) => {
|
|
21994
|
-
if (entries[0].isIntersecting) {
|
|
21995
|
-
setHasAnimated(true);
|
|
21996
|
-
animate();
|
|
21997
|
-
observer2.disconnect();
|
|
21998
|
-
}
|
|
21999
|
-
},
|
|
22000
|
-
{ threshold: 0.3 }
|
|
22001
|
-
);
|
|
22002
|
-
observer2.observe(el);
|
|
22003
|
-
return () => observer2.disconnect();
|
|
22004
|
-
}, [hasAnimated, animate]);
|
|
22005
|
-
return /* @__PURE__ */ jsxs(Box, { ref, className: cn("flex flex-col items-center gap-1 p-4", className), children: [
|
|
22006
|
-
/* @__PURE__ */ jsx(
|
|
22007
|
-
Typography,
|
|
22008
|
-
{
|
|
22009
|
-
variant: "h2",
|
|
22010
|
-
className: "text-primary font-bold tabular-nums",
|
|
22011
|
-
children: hasAnimated ? displayValue : "0"
|
|
22012
|
-
}
|
|
22013
|
-
),
|
|
22014
|
-
/* @__PURE__ */ jsx(Typography, { variant: "body2", color: "muted", className: "text-center", children: label })
|
|
22015
|
-
] });
|
|
22016
|
-
};
|
|
22017
|
-
AnimatedCounter.displayName = "AnimatedCounter";
|
|
22018
|
-
}
|
|
22019
|
-
});
|
|
22020
22027
|
var AuthLayout;
|
|
22021
22028
|
var init_AuthLayout = __esm({
|
|
22022
22029
|
"components/marketing/templates/AuthLayout.tsx"() {
|
|
@@ -22985,10 +22992,107 @@ var init_BehaviorView = __esm({
|
|
|
22985
22992
|
BehaviorView.displayName = "BehaviorView";
|
|
22986
22993
|
}
|
|
22987
22994
|
});
|
|
22995
|
+
var BiologyCanvas;
|
|
22996
|
+
var init_BiologyCanvas = __esm({
|
|
22997
|
+
"components/learning/molecules/BiologyCanvas.tsx"() {
|
|
22998
|
+
"use client";
|
|
22999
|
+
init_atoms();
|
|
23000
|
+
init_Stack();
|
|
23001
|
+
init_LearningCanvas();
|
|
23002
|
+
BiologyCanvas = ({
|
|
23003
|
+
className,
|
|
23004
|
+
width = 600,
|
|
23005
|
+
height = 400,
|
|
23006
|
+
title,
|
|
23007
|
+
backgroundColor,
|
|
23008
|
+
nodes = [],
|
|
23009
|
+
edges = [],
|
|
23010
|
+
shapes = [],
|
|
23011
|
+
interactive = false,
|
|
23012
|
+
animate = false,
|
|
23013
|
+
onShapeClick,
|
|
23014
|
+
isLoading,
|
|
23015
|
+
error
|
|
23016
|
+
}) => {
|
|
23017
|
+
const derivedShapes = useMemo(() => {
|
|
23018
|
+
const out = [];
|
|
23019
|
+
const nodeById = /* @__PURE__ */ new Map();
|
|
23020
|
+
for (const n of nodes) {
|
|
23021
|
+
if (n.id) nodeById.set(n.id, n);
|
|
23022
|
+
}
|
|
23023
|
+
for (const e of edges) {
|
|
23024
|
+
const a = nodeById.get(e.from);
|
|
23025
|
+
const b = nodeById.get(e.to);
|
|
23026
|
+
if (!a || !b) continue;
|
|
23027
|
+
out.push({
|
|
23028
|
+
type: "line",
|
|
23029
|
+
x1: a.x,
|
|
23030
|
+
y1: a.y,
|
|
23031
|
+
x2: b.x,
|
|
23032
|
+
y2: b.y,
|
|
23033
|
+
color: e.color ?? "#9ca3af",
|
|
23034
|
+
lineWidth: 2
|
|
23035
|
+
});
|
|
23036
|
+
if (e.label) {
|
|
23037
|
+
out.push({
|
|
23038
|
+
type: "text",
|
|
23039
|
+
x: (a.x + b.x) / 2 + 4,
|
|
23040
|
+
y: (a.y + b.y) / 2 - 4,
|
|
23041
|
+
text: e.label,
|
|
23042
|
+
color: "#374151",
|
|
23043
|
+
fontSize: 11
|
|
23044
|
+
});
|
|
23045
|
+
}
|
|
23046
|
+
}
|
|
23047
|
+
for (const n of nodes) {
|
|
23048
|
+
out.push({
|
|
23049
|
+
type: "circle",
|
|
23050
|
+
x: n.x,
|
|
23051
|
+
y: n.y,
|
|
23052
|
+
radius: n.radius ?? 16,
|
|
23053
|
+
color: n.color ?? "#16a34a",
|
|
23054
|
+
fill: `${n.color ?? "#16a34a"}33`,
|
|
23055
|
+
id: n.id
|
|
23056
|
+
});
|
|
23057
|
+
if (n.label) {
|
|
23058
|
+
out.push({
|
|
23059
|
+
type: "text",
|
|
23060
|
+
x: n.x,
|
|
23061
|
+
y: n.y + (n.radius ?? 16) + 14,
|
|
23062
|
+
text: n.label,
|
|
23063
|
+
color: "#111827",
|
|
23064
|
+
fontSize: 12,
|
|
23065
|
+
align: "center"
|
|
23066
|
+
});
|
|
23067
|
+
}
|
|
23068
|
+
}
|
|
23069
|
+
out.push(...shapes);
|
|
23070
|
+
return out;
|
|
23071
|
+
}, [nodes, edges, shapes]);
|
|
23072
|
+
return /* @__PURE__ */ jsx(Card, { className, children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
|
|
23073
|
+
title ? /* @__PURE__ */ jsx(Typography, { variant: "h4", children: title }) : null,
|
|
23074
|
+
/* @__PURE__ */ jsx(
|
|
23075
|
+
LearningCanvas,
|
|
23076
|
+
{
|
|
23077
|
+
width,
|
|
23078
|
+
height,
|
|
23079
|
+
backgroundColor,
|
|
23080
|
+
shapes: derivedShapes,
|
|
23081
|
+
interactive,
|
|
23082
|
+
animate,
|
|
23083
|
+
onShapeClick,
|
|
23084
|
+
isLoading,
|
|
23085
|
+
error
|
|
23086
|
+
}
|
|
23087
|
+
)
|
|
23088
|
+
] }) });
|
|
23089
|
+
};
|
|
23090
|
+
}
|
|
23091
|
+
});
|
|
22988
23092
|
|
|
22989
|
-
// node_modules
|
|
23093
|
+
// node_modules/katex/dist/katex.min.css
|
|
22990
23094
|
var init_katex_min = __esm({
|
|
22991
|
-
"node_modules
|
|
23095
|
+
"node_modules/katex/dist/katex.min.css"() {
|
|
22992
23096
|
}
|
|
22993
23097
|
});
|
|
22994
23098
|
var Tabs;
|
|
@@ -27381,129 +27485,6 @@ var init_Breadcrumb = __esm({
|
|
|
27381
27485
|
Breadcrumb.displayName = "Breadcrumb";
|
|
27382
27486
|
}
|
|
27383
27487
|
});
|
|
27384
|
-
function useSafeEventBus2() {
|
|
27385
|
-
try {
|
|
27386
|
-
return useEventBus();
|
|
27387
|
-
} catch {
|
|
27388
|
-
return { emit: () => {
|
|
27389
|
-
}, on: () => () => {
|
|
27390
|
-
}, once: () => {
|
|
27391
|
-
} };
|
|
27392
|
-
}
|
|
27393
|
-
}
|
|
27394
|
-
var log8, lookStyles4, ButtonGroup;
|
|
27395
|
-
var init_ButtonGroup = __esm({
|
|
27396
|
-
"components/core/molecules/ButtonGroup.tsx"() {
|
|
27397
|
-
"use client";
|
|
27398
|
-
init_cn();
|
|
27399
|
-
init_atoms();
|
|
27400
|
-
init_useEventBus();
|
|
27401
|
-
log8 = createLogger("almadar:ui:button-group");
|
|
27402
|
-
lookStyles4 = {
|
|
27403
|
-
"right-aligned-buttons": "",
|
|
27404
|
-
"floating-bar": "fixed bottom-section left-1/2 -translate-x-1/2 shadow-elevation-toast bg-card p-card-sm rounded-container",
|
|
27405
|
-
"inline-row": "gap-2 inline-flex",
|
|
27406
|
-
"dropdown-menu": "[&>button:not(:first-child)]:hidden",
|
|
27407
|
-
"command-palette-trigger": "[&>button:not(:first-child)]:hidden"
|
|
27408
|
-
};
|
|
27409
|
-
ButtonGroup = ({
|
|
27410
|
-
children,
|
|
27411
|
-
primary,
|
|
27412
|
-
secondary,
|
|
27413
|
-
variant = "default",
|
|
27414
|
-
orientation = "horizontal",
|
|
27415
|
-
className,
|
|
27416
|
-
// Filter-group pattern props (entity and filters are used for schema-driven filtering)
|
|
27417
|
-
entity: _entity,
|
|
27418
|
-
filters,
|
|
27419
|
-
look = "right-aligned-buttons"
|
|
27420
|
-
}) => {
|
|
27421
|
-
const eventBus = useSafeEventBus2();
|
|
27422
|
-
const variantClasses2 = {
|
|
27423
|
-
default: "gap-0",
|
|
27424
|
-
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",
|
|
27425
|
-
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"
|
|
27426
|
-
};
|
|
27427
|
-
const orientationClasses = {
|
|
27428
|
-
horizontal: "flex-row",
|
|
27429
|
-
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"
|
|
27430
|
-
};
|
|
27431
|
-
const handleActionClick = (action) => {
|
|
27432
|
-
if (action.event) {
|
|
27433
|
-
eventBus.emit("UI:DISPATCH", { event: action.event });
|
|
27434
|
-
}
|
|
27435
|
-
if (action.navigatesTo) {
|
|
27436
|
-
eventBus.emit("UI:NAVIGATE", { url: action.navigatesTo });
|
|
27437
|
-
}
|
|
27438
|
-
};
|
|
27439
|
-
const renderFormActions = () => {
|
|
27440
|
-
const buttons = [];
|
|
27441
|
-
if (secondary) {
|
|
27442
|
-
secondary.forEach((action, index) => {
|
|
27443
|
-
buttons.push(
|
|
27444
|
-
/* @__PURE__ */ jsx(
|
|
27445
|
-
Button,
|
|
27446
|
-
{
|
|
27447
|
-
type: action.actionType === "submit" ? "submit" : "button",
|
|
27448
|
-
variant: action.variant || "ghost",
|
|
27449
|
-
onClick: () => handleActionClick(action),
|
|
27450
|
-
children: action.label
|
|
27451
|
-
},
|
|
27452
|
-
`secondary-${index}`
|
|
27453
|
-
)
|
|
27454
|
-
);
|
|
27455
|
-
});
|
|
27456
|
-
}
|
|
27457
|
-
if (primary) {
|
|
27458
|
-
const isSubmit = primary.actionType === "submit";
|
|
27459
|
-
buttons.push(
|
|
27460
|
-
/* @__PURE__ */ jsx(
|
|
27461
|
-
Button,
|
|
27462
|
-
{
|
|
27463
|
-
type: isSubmit ? "submit" : "button",
|
|
27464
|
-
variant: primary.variant || "primary",
|
|
27465
|
-
onClick: () => handleActionClick(primary),
|
|
27466
|
-
"data-testid": isSubmit ? "form-submit" : void 0,
|
|
27467
|
-
children: primary.label
|
|
27468
|
-
},
|
|
27469
|
-
"primary"
|
|
27470
|
-
)
|
|
27471
|
-
);
|
|
27472
|
-
}
|
|
27473
|
-
return buttons;
|
|
27474
|
-
};
|
|
27475
|
-
const renderFilters = () => {
|
|
27476
|
-
if (!filters || filters.length === 0) return null;
|
|
27477
|
-
return filters.map((filter, index) => /* @__PURE__ */ jsx(
|
|
27478
|
-
Button,
|
|
27479
|
-
{
|
|
27480
|
-
variant: "ghost",
|
|
27481
|
-
onClick: () => {
|
|
27482
|
-
log8.debug("Filter clicked", { field: filter.field });
|
|
27483
|
-
},
|
|
27484
|
-
children: filter.label
|
|
27485
|
-
},
|
|
27486
|
-
`filter-${filter.field}-${index}`
|
|
27487
|
-
));
|
|
27488
|
-
};
|
|
27489
|
-
return /* @__PURE__ */ jsx(
|
|
27490
|
-
"div",
|
|
27491
|
-
{
|
|
27492
|
-
className: cn(
|
|
27493
|
-
"inline-flex gap-2",
|
|
27494
|
-
variantClasses2[variant],
|
|
27495
|
-
orientationClasses[orientation],
|
|
27496
|
-
lookStyles4[look],
|
|
27497
|
-
className
|
|
27498
|
-
),
|
|
27499
|
-
role: "group",
|
|
27500
|
-
children: children || renderFilters() || renderFormActions()
|
|
27501
|
-
}
|
|
27502
|
-
);
|
|
27503
|
-
};
|
|
27504
|
-
ButtonGroup.displayName = "ButtonGroup";
|
|
27505
|
-
}
|
|
27506
|
-
});
|
|
27507
27488
|
function useSwipeGesture(callbacks, options = {}) {
|
|
27508
27489
|
const { threshold = 50, velocityThreshold = 0.3, preventDefault = false } = options;
|
|
27509
27490
|
const startX = useRef(0);
|
|
@@ -28313,7 +28294,7 @@ var init_CardGrid = __esm({
|
|
|
28313
28294
|
CardGrid.displayName = "CardGrid";
|
|
28314
28295
|
}
|
|
28315
28296
|
});
|
|
28316
|
-
function
|
|
28297
|
+
function useSafeEventBus2() {
|
|
28317
28298
|
try {
|
|
28318
28299
|
return useEventBus();
|
|
28319
28300
|
} catch {
|
|
@@ -28349,7 +28330,7 @@ var init_Carousel = __esm({
|
|
|
28349
28330
|
const [activeIndex, setActiveIndex] = useState(0);
|
|
28350
28331
|
const scrollRef = useRef(null);
|
|
28351
28332
|
const autoPlayRef = useRef(null);
|
|
28352
|
-
const eventBus =
|
|
28333
|
+
const eventBus = useSafeEventBus2();
|
|
28353
28334
|
const { t } = useTranslate();
|
|
28354
28335
|
const safeItems = items ?? [];
|
|
28355
28336
|
const totalSlides = safeItems.length;
|
|
@@ -29597,6 +29578,122 @@ var init_ChatBar = __esm({
|
|
|
29597
29578
|
ChatBar.displayName = "ChatBar";
|
|
29598
29579
|
}
|
|
29599
29580
|
});
|
|
29581
|
+
var ChemistryCanvas;
|
|
29582
|
+
var init_ChemistryCanvas = __esm({
|
|
29583
|
+
"components/learning/molecules/ChemistryCanvas.tsx"() {
|
|
29584
|
+
"use client";
|
|
29585
|
+
init_atoms();
|
|
29586
|
+
init_Stack();
|
|
29587
|
+
init_LearningCanvas();
|
|
29588
|
+
ChemistryCanvas = ({
|
|
29589
|
+
className,
|
|
29590
|
+
width = 600,
|
|
29591
|
+
height = 400,
|
|
29592
|
+
title,
|
|
29593
|
+
backgroundColor,
|
|
29594
|
+
atoms = [],
|
|
29595
|
+
bonds = [],
|
|
29596
|
+
arrows = [],
|
|
29597
|
+
shapes = [],
|
|
29598
|
+
interactive = false,
|
|
29599
|
+
animate = false,
|
|
29600
|
+
onShapeClick,
|
|
29601
|
+
isLoading,
|
|
29602
|
+
error
|
|
29603
|
+
}) => {
|
|
29604
|
+
const derivedShapes = useMemo(() => {
|
|
29605
|
+
const out = [];
|
|
29606
|
+
const atomById = /* @__PURE__ */ new Map();
|
|
29607
|
+
for (const a of atoms) {
|
|
29608
|
+
if (a.id) atomById.set(a.id, a);
|
|
29609
|
+
}
|
|
29610
|
+
for (const b of bonds) {
|
|
29611
|
+
const a = atomById.get(b.from);
|
|
29612
|
+
const c = atomById.get(b.to);
|
|
29613
|
+
if (!a || !c) continue;
|
|
29614
|
+
const color = b.color ?? "#6b7280";
|
|
29615
|
+
const strokeWidth = b.type === "double" ? 4 : b.type === "triple" ? 6 : 2;
|
|
29616
|
+
out.push({
|
|
29617
|
+
type: "line",
|
|
29618
|
+
x1: a.x,
|
|
29619
|
+
y1: a.y,
|
|
29620
|
+
x2: c.x,
|
|
29621
|
+
y2: c.y,
|
|
29622
|
+
color,
|
|
29623
|
+
lineWidth: strokeWidth
|
|
29624
|
+
});
|
|
29625
|
+
}
|
|
29626
|
+
for (const a of arrows) {
|
|
29627
|
+
const angle = (a.angle ?? 0) * (Math.PI / 180);
|
|
29628
|
+
const len = a.length ?? 60;
|
|
29629
|
+
const x2 = a.x + Math.cos(angle) * len;
|
|
29630
|
+
const y2 = a.y + Math.sin(angle) * len;
|
|
29631
|
+
out.push({
|
|
29632
|
+
type: "arrow",
|
|
29633
|
+
x1: a.x,
|
|
29634
|
+
y1: a.y,
|
|
29635
|
+
x2,
|
|
29636
|
+
y2,
|
|
29637
|
+
color: a.color ?? "#dc2626",
|
|
29638
|
+
lineWidth: 2
|
|
29639
|
+
});
|
|
29640
|
+
if (a.label) {
|
|
29641
|
+
out.push({
|
|
29642
|
+
type: "text",
|
|
29643
|
+
x: (a.x + x2) / 2,
|
|
29644
|
+
y: (a.y + y2) / 2 - 10,
|
|
29645
|
+
text: a.label,
|
|
29646
|
+
color: "#111827",
|
|
29647
|
+
fontSize: 12,
|
|
29648
|
+
align: "center"
|
|
29649
|
+
});
|
|
29650
|
+
}
|
|
29651
|
+
}
|
|
29652
|
+
for (const a of atoms) {
|
|
29653
|
+
out.push({
|
|
29654
|
+
type: "circle",
|
|
29655
|
+
x: a.x,
|
|
29656
|
+
y: a.y,
|
|
29657
|
+
radius: a.radius ?? 14,
|
|
29658
|
+
color: a.color ?? "#2563eb",
|
|
29659
|
+
fill: a.color ?? "#2563eb",
|
|
29660
|
+
id: a.id
|
|
29661
|
+
});
|
|
29662
|
+
if (a.element) {
|
|
29663
|
+
out.push({
|
|
29664
|
+
type: "text",
|
|
29665
|
+
x: a.x,
|
|
29666
|
+
y: a.y,
|
|
29667
|
+
text: a.element,
|
|
29668
|
+
color: "#ffffff",
|
|
29669
|
+
fontSize: 12,
|
|
29670
|
+
align: "center"
|
|
29671
|
+
});
|
|
29672
|
+
}
|
|
29673
|
+
}
|
|
29674
|
+
out.push(...shapes);
|
|
29675
|
+
return out;
|
|
29676
|
+
}, [atoms, bonds, arrows, shapes]);
|
|
29677
|
+
return /* @__PURE__ */ jsx(Card, { className, children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
|
|
29678
|
+
title ? /* @__PURE__ */ jsx(Typography, { variant: "h4", children: title }) : null,
|
|
29679
|
+
/* @__PURE__ */ jsx(
|
|
29680
|
+
LearningCanvas,
|
|
29681
|
+
{
|
|
29682
|
+
width,
|
|
29683
|
+
height,
|
|
29684
|
+
backgroundColor,
|
|
29685
|
+
shapes: derivedShapes,
|
|
29686
|
+
interactive,
|
|
29687
|
+
animate,
|
|
29688
|
+
onShapeClick,
|
|
29689
|
+
isLoading,
|
|
29690
|
+
error
|
|
29691
|
+
}
|
|
29692
|
+
)
|
|
29693
|
+
] }) });
|
|
29694
|
+
};
|
|
29695
|
+
}
|
|
29696
|
+
});
|
|
29600
29697
|
var CodeRunnerPanel;
|
|
29601
29698
|
var init_CodeRunnerPanel = __esm({
|
|
29602
29699
|
"components/core/organisms/CodeRunnerPanel.tsx"() {
|
|
@@ -31685,7 +31782,7 @@ function DataGrid({
|
|
|
31685
31782
|
/* @__PURE__ */ jsx(
|
|
31686
31783
|
Box,
|
|
31687
31784
|
{
|
|
31688
|
-
className: cn("grid", gapStyles5[gap], scrollX ? "grid-flow-col overflow-x-auto" : colsClass,
|
|
31785
|
+
className: cn("grid", gapStyles5[gap], scrollX ? "grid-flow-col overflow-x-auto" : colsClass, lookStyles4[look], className),
|
|
31689
31786
|
style: scrollX ? { gridAutoFlow: "column", gridAutoColumns: `minmax(${minCardWidth}px, 1fr)` } : gridTemplateColumns ? { gridTemplateColumns } : void 0,
|
|
31690
31787
|
children: data.map((item, index) => {
|
|
31691
31788
|
const itemData = item;
|
|
@@ -31880,7 +31977,7 @@ function DataGrid({
|
|
|
31880
31977
|
] })
|
|
31881
31978
|
);
|
|
31882
31979
|
}
|
|
31883
|
-
var dataGridLog, BADGE_VARIANTS, gapStyles5,
|
|
31980
|
+
var dataGridLog, BADGE_VARIANTS, gapStyles5, lookStyles4;
|
|
31884
31981
|
var init_DataGrid = __esm({
|
|
31885
31982
|
"components/core/molecules/DataGrid.tsx"() {
|
|
31886
31983
|
"use client";
|
|
@@ -31915,7 +32012,7 @@ var init_DataGrid = __esm({
|
|
|
31915
32012
|
lg: "gap-6",
|
|
31916
32013
|
xl: "gap-8"
|
|
31917
32014
|
};
|
|
31918
|
-
|
|
32015
|
+
lookStyles4 = {
|
|
31919
32016
|
dense: "gap-2 [&>*]:p-card-sm",
|
|
31920
32017
|
spacious: "gap-8 [&>*]:p-card-lg",
|
|
31921
32018
|
striped: "[&>*:nth-child(even)]:bg-muted/30",
|
|
@@ -32342,6 +32439,133 @@ var init_DataList = __esm({
|
|
|
32342
32439
|
DataList.displayName = "DataList";
|
|
32343
32440
|
}
|
|
32344
32441
|
});
|
|
32442
|
+
var FormSection, FormLayout, FormActions;
|
|
32443
|
+
var init_FormSection = __esm({
|
|
32444
|
+
"components/core/molecules/FormSection.tsx"() {
|
|
32445
|
+
"use client";
|
|
32446
|
+
init_cn();
|
|
32447
|
+
init_atoms();
|
|
32448
|
+
init_Box();
|
|
32449
|
+
init_Typography();
|
|
32450
|
+
init_Button();
|
|
32451
|
+
init_Stack();
|
|
32452
|
+
init_Icon();
|
|
32453
|
+
init_useEventBus();
|
|
32454
|
+
FormSection = ({
|
|
32455
|
+
title,
|
|
32456
|
+
description,
|
|
32457
|
+
children,
|
|
32458
|
+
collapsible = false,
|
|
32459
|
+
defaultCollapsed = false,
|
|
32460
|
+
card = false,
|
|
32461
|
+
columns = 1,
|
|
32462
|
+
className
|
|
32463
|
+
}) => {
|
|
32464
|
+
const [collapsed, setCollapsed] = React107__default.useState(defaultCollapsed);
|
|
32465
|
+
const { t } = useTranslate();
|
|
32466
|
+
const eventBus = useEventBus();
|
|
32467
|
+
const gridClass = {
|
|
32468
|
+
1: "grid-cols-1",
|
|
32469
|
+
2: "grid-cols-1 md:grid-cols-2",
|
|
32470
|
+
3: "grid-cols-1 md:grid-cols-2 lg:grid-cols-3"
|
|
32471
|
+
}[columns];
|
|
32472
|
+
React107__default.useCallback(() => {
|
|
32473
|
+
if (collapsible) {
|
|
32474
|
+
setCollapsed((prev) => !prev);
|
|
32475
|
+
eventBus.emit("UI:TOGGLE_COLLAPSE", { collapsed: !collapsed });
|
|
32476
|
+
}
|
|
32477
|
+
}, [collapsible, collapsed, eventBus]);
|
|
32478
|
+
const content = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
32479
|
+
(title || description) && /* @__PURE__ */ jsxs(VStack, { gap: "xs", className: "mb-4", children: [
|
|
32480
|
+
title && /* @__PURE__ */ jsxs(
|
|
32481
|
+
HStack,
|
|
32482
|
+
{
|
|
32483
|
+
justify: "between",
|
|
32484
|
+
align: "center",
|
|
32485
|
+
className: cn(collapsible && "cursor-pointer"),
|
|
32486
|
+
action: collapsible ? "TOGGLE_COLLAPSE" : void 0,
|
|
32487
|
+
children: [
|
|
32488
|
+
/* @__PURE__ */ jsx(Typography, { variant: "h3", weight: "semibold", children: title }),
|
|
32489
|
+
collapsible && /* @__PURE__ */ jsx(
|
|
32490
|
+
Button,
|
|
32491
|
+
{
|
|
32492
|
+
variant: "ghost",
|
|
32493
|
+
size: "sm",
|
|
32494
|
+
action: "TOGGLE_COLLAPSE",
|
|
32495
|
+
children: /* @__PURE__ */ jsx(
|
|
32496
|
+
Icon,
|
|
32497
|
+
{
|
|
32498
|
+
icon: ChevronDown,
|
|
32499
|
+
size: "sm",
|
|
32500
|
+
className: cn(
|
|
32501
|
+
"text-muted-foreground transition-transform",
|
|
32502
|
+
collapsed && "rotate-180"
|
|
32503
|
+
)
|
|
32504
|
+
}
|
|
32505
|
+
)
|
|
32506
|
+
}
|
|
32507
|
+
)
|
|
32508
|
+
]
|
|
32509
|
+
}
|
|
32510
|
+
),
|
|
32511
|
+
description && /* @__PURE__ */ jsx(Typography, { variant: "small", color: "secondary", children: description })
|
|
32512
|
+
] }),
|
|
32513
|
+
(!collapsible || !collapsed) && /* @__PURE__ */ jsx(Box, { className: cn("grid gap-4", gridClass), children })
|
|
32514
|
+
] });
|
|
32515
|
+
if (card) {
|
|
32516
|
+
return /* @__PURE__ */ jsx(Card, { className: cn("p-6", className), children: content });
|
|
32517
|
+
}
|
|
32518
|
+
return /* @__PURE__ */ jsx(Box, { className, children: content });
|
|
32519
|
+
};
|
|
32520
|
+
FormSection.displayName = "FormSection";
|
|
32521
|
+
FormLayout = ({
|
|
32522
|
+
children,
|
|
32523
|
+
dividers = true,
|
|
32524
|
+
className
|
|
32525
|
+
}) => {
|
|
32526
|
+
return /* @__PURE__ */ jsx(
|
|
32527
|
+
VStack,
|
|
32528
|
+
{
|
|
32529
|
+
gap: "lg",
|
|
32530
|
+
className: cn(
|
|
32531
|
+
dividers && "[&>*+*]:pt-8 [&>*+*]:border-t [&>*+*]:border-border",
|
|
32532
|
+
className
|
|
32533
|
+
),
|
|
32534
|
+
children
|
|
32535
|
+
}
|
|
32536
|
+
);
|
|
32537
|
+
};
|
|
32538
|
+
FormLayout.displayName = "FormLayout";
|
|
32539
|
+
FormActions = ({
|
|
32540
|
+
children,
|
|
32541
|
+
sticky = false,
|
|
32542
|
+
align = "right",
|
|
32543
|
+
className
|
|
32544
|
+
}) => {
|
|
32545
|
+
const alignClass2 = {
|
|
32546
|
+
left: "justify-start",
|
|
32547
|
+
right: "justify-end",
|
|
32548
|
+
between: "justify-between",
|
|
32549
|
+
center: "justify-center"
|
|
32550
|
+
}[align];
|
|
32551
|
+
return /* @__PURE__ */ jsx(
|
|
32552
|
+
HStack,
|
|
32553
|
+
{
|
|
32554
|
+
gap: "sm",
|
|
32555
|
+
align: "center",
|
|
32556
|
+
className: cn(
|
|
32557
|
+
"pt-6 border-t border-border",
|
|
32558
|
+
alignClass2,
|
|
32559
|
+
sticky && "sticky bottom-0 bg-card py-4 -mx-6 px-6 shadow-[0_-4px_6px_-1px_rgb(0,0,0,0.05)]",
|
|
32560
|
+
className
|
|
32561
|
+
),
|
|
32562
|
+
children
|
|
32563
|
+
}
|
|
32564
|
+
);
|
|
32565
|
+
};
|
|
32566
|
+
FormActions.displayName = "FormActions";
|
|
32567
|
+
}
|
|
32568
|
+
});
|
|
32345
32569
|
function fileIcon(name) {
|
|
32346
32570
|
const ext = name.split(".").pop()?.toLowerCase() ?? "";
|
|
32347
32571
|
switch (ext) {
|
|
@@ -32503,6 +32727,129 @@ var init_FormField = __esm({
|
|
|
32503
32727
|
FormField.displayName = "FormField";
|
|
32504
32728
|
}
|
|
32505
32729
|
});
|
|
32730
|
+
function useSafeEventBus3() {
|
|
32731
|
+
try {
|
|
32732
|
+
return useEventBus();
|
|
32733
|
+
} catch {
|
|
32734
|
+
return { emit: () => {
|
|
32735
|
+
}, on: () => () => {
|
|
32736
|
+
}, once: () => {
|
|
32737
|
+
} };
|
|
32738
|
+
}
|
|
32739
|
+
}
|
|
32740
|
+
var log8, lookStyles5, ButtonGroup;
|
|
32741
|
+
var init_ButtonGroup = __esm({
|
|
32742
|
+
"components/core/molecules/ButtonGroup.tsx"() {
|
|
32743
|
+
"use client";
|
|
32744
|
+
init_cn();
|
|
32745
|
+
init_atoms();
|
|
32746
|
+
init_useEventBus();
|
|
32747
|
+
log8 = createLogger("almadar:ui:button-group");
|
|
32748
|
+
lookStyles5 = {
|
|
32749
|
+
"right-aligned-buttons": "",
|
|
32750
|
+
"floating-bar": "fixed bottom-section left-1/2 -translate-x-1/2 shadow-elevation-toast bg-card p-card-sm rounded-container",
|
|
32751
|
+
"inline-row": "gap-2 inline-flex",
|
|
32752
|
+
"dropdown-menu": "[&>button:not(:first-child)]:hidden",
|
|
32753
|
+
"command-palette-trigger": "[&>button:not(:first-child)]:hidden"
|
|
32754
|
+
};
|
|
32755
|
+
ButtonGroup = ({
|
|
32756
|
+
children,
|
|
32757
|
+
primary,
|
|
32758
|
+
secondary,
|
|
32759
|
+
variant = "default",
|
|
32760
|
+
orientation = "horizontal",
|
|
32761
|
+
className,
|
|
32762
|
+
// Filter-group pattern props (entity and filters are used for schema-driven filtering)
|
|
32763
|
+
entity: _entity,
|
|
32764
|
+
filters,
|
|
32765
|
+
look = "right-aligned-buttons"
|
|
32766
|
+
}) => {
|
|
32767
|
+
const eventBus = useSafeEventBus3();
|
|
32768
|
+
const variantClasses2 = {
|
|
32769
|
+
default: "gap-0",
|
|
32770
|
+
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",
|
|
32771
|
+
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"
|
|
32772
|
+
};
|
|
32773
|
+
const orientationClasses = {
|
|
32774
|
+
horizontal: "flex-row",
|
|
32775
|
+
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"
|
|
32776
|
+
};
|
|
32777
|
+
const handleActionClick = (action) => {
|
|
32778
|
+
if (action.event) {
|
|
32779
|
+
eventBus.emit("UI:DISPATCH", { event: action.event });
|
|
32780
|
+
}
|
|
32781
|
+
if (action.navigatesTo) {
|
|
32782
|
+
eventBus.emit("UI:NAVIGATE", { url: action.navigatesTo });
|
|
32783
|
+
}
|
|
32784
|
+
};
|
|
32785
|
+
const renderFormActions = () => {
|
|
32786
|
+
const buttons = [];
|
|
32787
|
+
if (secondary) {
|
|
32788
|
+
secondary.forEach((action, index) => {
|
|
32789
|
+
buttons.push(
|
|
32790
|
+
/* @__PURE__ */ jsx(
|
|
32791
|
+
Button,
|
|
32792
|
+
{
|
|
32793
|
+
type: action.actionType === "submit" ? "submit" : "button",
|
|
32794
|
+
variant: action.variant || "ghost",
|
|
32795
|
+
onClick: () => handleActionClick(action),
|
|
32796
|
+
children: action.label
|
|
32797
|
+
},
|
|
32798
|
+
`secondary-${index}`
|
|
32799
|
+
)
|
|
32800
|
+
);
|
|
32801
|
+
});
|
|
32802
|
+
}
|
|
32803
|
+
if (primary) {
|
|
32804
|
+
const isSubmit = primary.actionType === "submit";
|
|
32805
|
+
buttons.push(
|
|
32806
|
+
/* @__PURE__ */ jsx(
|
|
32807
|
+
Button,
|
|
32808
|
+
{
|
|
32809
|
+
type: isSubmit ? "submit" : "button",
|
|
32810
|
+
variant: primary.variant || "primary",
|
|
32811
|
+
onClick: () => handleActionClick(primary),
|
|
32812
|
+
"data-testid": isSubmit ? "form-submit" : void 0,
|
|
32813
|
+
children: primary.label
|
|
32814
|
+
},
|
|
32815
|
+
"primary"
|
|
32816
|
+
)
|
|
32817
|
+
);
|
|
32818
|
+
}
|
|
32819
|
+
return buttons;
|
|
32820
|
+
};
|
|
32821
|
+
const renderFilters = () => {
|
|
32822
|
+
if (!filters || filters.length === 0) return null;
|
|
32823
|
+
return filters.map((filter, index) => /* @__PURE__ */ jsx(
|
|
32824
|
+
Button,
|
|
32825
|
+
{
|
|
32826
|
+
variant: "ghost",
|
|
32827
|
+
onClick: () => {
|
|
32828
|
+
log8.debug("Filter clicked", { field: filter.field });
|
|
32829
|
+
},
|
|
32830
|
+
children: filter.label
|
|
32831
|
+
},
|
|
32832
|
+
`filter-${filter.field}-${index}`
|
|
32833
|
+
));
|
|
32834
|
+
};
|
|
32835
|
+
return /* @__PURE__ */ jsx(
|
|
32836
|
+
"div",
|
|
32837
|
+
{
|
|
32838
|
+
className: cn(
|
|
32839
|
+
"inline-flex gap-2",
|
|
32840
|
+
variantClasses2[variant],
|
|
32841
|
+
orientationClasses[orientation],
|
|
32842
|
+
lookStyles5[look],
|
|
32843
|
+
className
|
|
32844
|
+
),
|
|
32845
|
+
role: "group",
|
|
32846
|
+
children: children || renderFilters() || renderFormActions()
|
|
32847
|
+
}
|
|
32848
|
+
);
|
|
32849
|
+
};
|
|
32850
|
+
ButtonGroup.displayName = "ButtonGroup";
|
|
32851
|
+
}
|
|
32852
|
+
});
|
|
32506
32853
|
function getOrCreateStore(query) {
|
|
32507
32854
|
if (!queryStores.has(query)) {
|
|
32508
32855
|
queryStores.set(query, {
|
|
@@ -35044,6 +35391,234 @@ var init_ProgressDots = __esm({
|
|
|
35044
35391
|
ProgressDots.displayName = "ProgressDots";
|
|
35045
35392
|
}
|
|
35046
35393
|
});
|
|
35394
|
+
var MathCanvas;
|
|
35395
|
+
var init_MathCanvas = __esm({
|
|
35396
|
+
"components/learning/molecules/MathCanvas.tsx"() {
|
|
35397
|
+
"use client";
|
|
35398
|
+
init_atoms();
|
|
35399
|
+
init_Stack();
|
|
35400
|
+
init_LearningCanvas();
|
|
35401
|
+
MathCanvas = ({
|
|
35402
|
+
className,
|
|
35403
|
+
width = 600,
|
|
35404
|
+
height = 400,
|
|
35405
|
+
title,
|
|
35406
|
+
xMin = -10,
|
|
35407
|
+
xMax = 10,
|
|
35408
|
+
yMin = -10,
|
|
35409
|
+
yMax = 10,
|
|
35410
|
+
showAxes = true,
|
|
35411
|
+
showGrid = true,
|
|
35412
|
+
gridStep = 1,
|
|
35413
|
+
curves = [],
|
|
35414
|
+
points = [],
|
|
35415
|
+
vectors = [],
|
|
35416
|
+
shapes = [],
|
|
35417
|
+
interactive = false,
|
|
35418
|
+
animate = false,
|
|
35419
|
+
onShapeClick,
|
|
35420
|
+
isLoading,
|
|
35421
|
+
error
|
|
35422
|
+
}) => {
|
|
35423
|
+
const derivedShapes = useMemo(() => {
|
|
35424
|
+
const out = [];
|
|
35425
|
+
const margin = 24;
|
|
35426
|
+
const plotW = width - margin * 2;
|
|
35427
|
+
const plotH = height - margin * 2;
|
|
35428
|
+
const mapX = (x) => margin + (x - xMin) / (xMax - xMin) * plotW;
|
|
35429
|
+
const mapY = (y) => height - (margin + (y - yMin) / (yMax - yMin) * plotH);
|
|
35430
|
+
if (showGrid) {
|
|
35431
|
+
for (let x = Math.ceil(xMin / gridStep) * gridStep; x <= xMax; x += gridStep) {
|
|
35432
|
+
const px = mapX(x);
|
|
35433
|
+
out.push({ type: "line", x1: px, y1: margin, x2: px, y2: height - margin, color: "#e5e7eb", lineWidth: 1 });
|
|
35434
|
+
}
|
|
35435
|
+
for (let y = Math.ceil(yMin / gridStep) * gridStep; y <= yMax; y += gridStep) {
|
|
35436
|
+
const py = mapY(y);
|
|
35437
|
+
out.push({ type: "line", x1: margin, y1: py, x2: width - margin, y2: py, color: "#e5e7eb", lineWidth: 1 });
|
|
35438
|
+
}
|
|
35439
|
+
}
|
|
35440
|
+
if (showAxes) {
|
|
35441
|
+
const xAxisY = Math.max(margin, Math.min(height - margin, mapY(0)));
|
|
35442
|
+
const yAxisX = Math.max(margin, Math.min(width - margin, mapX(0)));
|
|
35443
|
+
out.push({ type: "line", x1: margin, y1: xAxisY, x2: width - margin, y2: xAxisY, color: "#374151", lineWidth: 2 });
|
|
35444
|
+
out.push({ type: "line", x1: yAxisX, y1: margin, x2: yAxisX, y2: height - margin, color: "#374151", lineWidth: 2 });
|
|
35445
|
+
}
|
|
35446
|
+
for (const curve of curves) {
|
|
35447
|
+
if (!curve.samples || curve.samples.length < 2) continue;
|
|
35448
|
+
for (let i = 1; i < curve.samples.length; i++) {
|
|
35449
|
+
const a = curve.samples[i - 1];
|
|
35450
|
+
const b = curve.samples[i];
|
|
35451
|
+
if (a.x < xMin || a.x > xMax || b.x < xMin || b.x > xMax) continue;
|
|
35452
|
+
out.push({
|
|
35453
|
+
type: "line",
|
|
35454
|
+
x1: mapX(a.x),
|
|
35455
|
+
y1: mapY(a.y),
|
|
35456
|
+
x2: mapX(b.x),
|
|
35457
|
+
y2: mapY(b.y),
|
|
35458
|
+
color: curve.color ?? "#2563eb",
|
|
35459
|
+
lineWidth: 2
|
|
35460
|
+
});
|
|
35461
|
+
}
|
|
35462
|
+
}
|
|
35463
|
+
for (const p2 of points) {
|
|
35464
|
+
if (p2.x < xMin || p2.x > xMax || p2.y < yMin || p2.y > yMax) continue;
|
|
35465
|
+
out.push({
|
|
35466
|
+
type: "circle",
|
|
35467
|
+
x: mapX(p2.x),
|
|
35468
|
+
y: mapY(p2.y),
|
|
35469
|
+
radius: p2.radius ?? 4,
|
|
35470
|
+
color: p2.color ?? "#dc2626",
|
|
35471
|
+
fill: p2.color ?? "#dc2626"
|
|
35472
|
+
});
|
|
35473
|
+
if (p2.label) {
|
|
35474
|
+
out.push({ type: "text", x: mapX(p2.x) + 8, y: mapY(p2.y) - 8, text: p2.label, color: "#111827", fontSize: 12 });
|
|
35475
|
+
}
|
|
35476
|
+
}
|
|
35477
|
+
for (const v of vectors) {
|
|
35478
|
+
if (v.x < xMin || v.x > xMax || v.y < yMin || v.y > yMax) continue;
|
|
35479
|
+
const x1 = mapX(v.x);
|
|
35480
|
+
const y1 = mapY(v.y);
|
|
35481
|
+
const x2 = mapX(v.x + v.vx);
|
|
35482
|
+
const y2 = mapY(v.y + v.vy);
|
|
35483
|
+
out.push({ type: "arrow", x1, y1, x2, y2, color: v.color ?? "#7c3aed", lineWidth: 2 });
|
|
35484
|
+
if (v.label) {
|
|
35485
|
+
out.push({ type: "text", x: x2 + 6, y: y2 - 6, text: v.label, color: "#111827", fontSize: 12 });
|
|
35486
|
+
}
|
|
35487
|
+
}
|
|
35488
|
+
out.push(...shapes);
|
|
35489
|
+
return out;
|
|
35490
|
+
}, [width, height, xMin, xMax, yMin, yMax, showAxes, showGrid, gridStep, curves, points, vectors, shapes]);
|
|
35491
|
+
return /* @__PURE__ */ jsx(Card, { className, children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
|
|
35492
|
+
title ? /* @__PURE__ */ jsx(Typography, { variant: "h4", children: title }) : null,
|
|
35493
|
+
/* @__PURE__ */ jsx(
|
|
35494
|
+
LearningCanvas,
|
|
35495
|
+
{
|
|
35496
|
+
width,
|
|
35497
|
+
height,
|
|
35498
|
+
shapes: derivedShapes,
|
|
35499
|
+
interactive,
|
|
35500
|
+
animate,
|
|
35501
|
+
onShapeClick,
|
|
35502
|
+
isLoading,
|
|
35503
|
+
error
|
|
35504
|
+
}
|
|
35505
|
+
)
|
|
35506
|
+
] }) });
|
|
35507
|
+
};
|
|
35508
|
+
}
|
|
35509
|
+
});
|
|
35510
|
+
var PhysicsCanvas;
|
|
35511
|
+
var init_PhysicsCanvas = __esm({
|
|
35512
|
+
"components/learning/molecules/PhysicsCanvas.tsx"() {
|
|
35513
|
+
"use client";
|
|
35514
|
+
init_atoms();
|
|
35515
|
+
init_Stack();
|
|
35516
|
+
init_LearningCanvas();
|
|
35517
|
+
PhysicsCanvas = ({
|
|
35518
|
+
className,
|
|
35519
|
+
width = 600,
|
|
35520
|
+
height = 400,
|
|
35521
|
+
title,
|
|
35522
|
+
backgroundColor,
|
|
35523
|
+
bodies = [],
|
|
35524
|
+
constraints = [],
|
|
35525
|
+
showVelocity = true,
|
|
35526
|
+
showForces = false,
|
|
35527
|
+
velocityScale = 20,
|
|
35528
|
+
forceScale = 20,
|
|
35529
|
+
shapes = [],
|
|
35530
|
+
interactive = false,
|
|
35531
|
+
animate = false,
|
|
35532
|
+
onShapeClick,
|
|
35533
|
+
isLoading,
|
|
35534
|
+
error
|
|
35535
|
+
}) => {
|
|
35536
|
+
const derivedShapes = useMemo(() => {
|
|
35537
|
+
const out = [];
|
|
35538
|
+
const bodyById = /* @__PURE__ */ new Map();
|
|
35539
|
+
for (const b of bodies) {
|
|
35540
|
+
if (b.id) bodyById.set(b.id, b);
|
|
35541
|
+
}
|
|
35542
|
+
for (const c of constraints) {
|
|
35543
|
+
const a = bodyById.get(c.from);
|
|
35544
|
+
const b = bodyById.get(c.to);
|
|
35545
|
+
if (!a || !b) continue;
|
|
35546
|
+
out.push({
|
|
35547
|
+
type: "line",
|
|
35548
|
+
x1: a.x,
|
|
35549
|
+
y1: a.y,
|
|
35550
|
+
x2: b.x,
|
|
35551
|
+
y2: b.y,
|
|
35552
|
+
color: c.color ?? "#9ca3af",
|
|
35553
|
+
lineWidth: 2
|
|
35554
|
+
});
|
|
35555
|
+
}
|
|
35556
|
+
for (const b of bodies) {
|
|
35557
|
+
out.push({
|
|
35558
|
+
type: "circle",
|
|
35559
|
+
x: b.x,
|
|
35560
|
+
y: b.y,
|
|
35561
|
+
radius: b.radius ?? 12,
|
|
35562
|
+
color: b.color ?? "#2563eb",
|
|
35563
|
+
fill: b.color ?? "#2563eb",
|
|
35564
|
+
id: b.id
|
|
35565
|
+
});
|
|
35566
|
+
if (b.label) {
|
|
35567
|
+
out.push({
|
|
35568
|
+
type: "text",
|
|
35569
|
+
x: b.x + (b.radius ?? 12) + 6,
|
|
35570
|
+
y: b.y - (b.radius ?? 12) - 6,
|
|
35571
|
+
text: b.label,
|
|
35572
|
+
color: "#111827",
|
|
35573
|
+
fontSize: 12
|
|
35574
|
+
});
|
|
35575
|
+
}
|
|
35576
|
+
if (showVelocity && b.vx != null && b.vy != null && (b.vx !== 0 || b.vy !== 0)) {
|
|
35577
|
+
out.push({
|
|
35578
|
+
type: "arrow",
|
|
35579
|
+
x1: b.x,
|
|
35580
|
+
y1: b.y,
|
|
35581
|
+
x2: b.x + b.vx * velocityScale,
|
|
35582
|
+
y2: b.y + b.vy * velocityScale,
|
|
35583
|
+
color: "#16a34a",
|
|
35584
|
+
lineWidth: 2
|
|
35585
|
+
});
|
|
35586
|
+
}
|
|
35587
|
+
if (showForces && b.fx != null && b.fy != null && (b.fx !== 0 || b.fy !== 0)) {
|
|
35588
|
+
out.push({
|
|
35589
|
+
type: "arrow",
|
|
35590
|
+
x1: b.x,
|
|
35591
|
+
y1: b.y,
|
|
35592
|
+
x2: b.x + b.fx * forceScale,
|
|
35593
|
+
y2: b.y + b.fy * forceScale,
|
|
35594
|
+
color: "#dc2626",
|
|
35595
|
+
lineWidth: 2
|
|
35596
|
+
});
|
|
35597
|
+
}
|
|
35598
|
+
}
|
|
35599
|
+
out.push(...shapes);
|
|
35600
|
+
return out;
|
|
35601
|
+
}, [bodies, constraints, showVelocity, showForces, velocityScale, forceScale, shapes]);
|
|
35602
|
+
return /* @__PURE__ */ jsx(Card, { className, children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
|
|
35603
|
+
title ? /* @__PURE__ */ jsx(Typography, { variant: "h4", children: title }) : null,
|
|
35604
|
+
/* @__PURE__ */ jsx(
|
|
35605
|
+
LearningCanvas,
|
|
35606
|
+
{
|
|
35607
|
+
width,
|
|
35608
|
+
height,
|
|
35609
|
+
backgroundColor,
|
|
35610
|
+
shapes: derivedShapes,
|
|
35611
|
+
interactive,
|
|
35612
|
+
animate,
|
|
35613
|
+
onShapeClick,
|
|
35614
|
+
isLoading,
|
|
35615
|
+
error
|
|
35616
|
+
}
|
|
35617
|
+
)
|
|
35618
|
+
] }) });
|
|
35619
|
+
};
|
|
35620
|
+
}
|
|
35621
|
+
});
|
|
35047
35622
|
function resolveNodeColor(node, groups) {
|
|
35048
35623
|
if (node.color) return node.color;
|
|
35049
35624
|
if (node.group) {
|
|
@@ -35415,13 +35990,13 @@ var init_MapView = __esm({
|
|
|
35415
35990
|
shadowSize: [41, 41]
|
|
35416
35991
|
});
|
|
35417
35992
|
L.Marker.prototype.options.icon = defaultIcon;
|
|
35418
|
-
const { useEffect:
|
|
35993
|
+
const { useEffect: useEffect76, useRef: useRef77, useCallback: useCallback117, useState: useState108 } = React107__default;
|
|
35419
35994
|
const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
|
|
35420
35995
|
const { useEventBus: useEventBus2 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
|
|
35421
35996
|
function MapUpdater({ centerLat, centerLng, zoom }) {
|
|
35422
35997
|
const map = useMap();
|
|
35423
|
-
const prevRef =
|
|
35424
|
-
|
|
35998
|
+
const prevRef = useRef77({ centerLat, centerLng, zoom });
|
|
35999
|
+
useEffect76(() => {
|
|
35425
36000
|
const prev = prevRef.current;
|
|
35426
36001
|
if (prev.centerLat !== centerLat || prev.centerLng !== centerLng || prev.zoom !== zoom) {
|
|
35427
36002
|
map.setView([centerLat, centerLng], zoom);
|
|
@@ -35432,7 +36007,7 @@ var init_MapView = __esm({
|
|
|
35432
36007
|
}
|
|
35433
36008
|
function MapClickHandler({ onMapClick }) {
|
|
35434
36009
|
const map = useMap();
|
|
35435
|
-
|
|
36010
|
+
useEffect76(() => {
|
|
35436
36011
|
if (!onMapClick) return;
|
|
35437
36012
|
const handler = (e) => {
|
|
35438
36013
|
onMapClick(e.latlng.lat, e.latlng.lng);
|
|
@@ -43873,7 +44448,7 @@ function getGroupColor(group, groups) {
|
|
|
43873
44448
|
const idx = groups.indexOf(group);
|
|
43874
44449
|
return GROUP_COLORS2[idx % GROUP_COLORS2.length];
|
|
43875
44450
|
}
|
|
43876
|
-
function
|
|
44451
|
+
function resolveColor3(color, el) {
|
|
43877
44452
|
const m = /^var\((--[^,)]+)(?:,\s*([^)]+))?\)$/.exec(color.trim());
|
|
43878
44453
|
if (!m) return color;
|
|
43879
44454
|
const resolved = getComputedStyle(el).getPropertyValue(m[1]).trim();
|
|
@@ -44134,7 +44709,7 @@ var init_GraphCanvas = __esm({
|
|
|
44134
44709
|
const w = logicalW;
|
|
44135
44710
|
const h = height;
|
|
44136
44711
|
const nodes = nodesRef.current;
|
|
44137
|
-
const accentColor =
|
|
44712
|
+
const accentColor = resolveColor3("var(--color-accent)", canvas);
|
|
44138
44713
|
const dpr = typeof window !== "undefined" && window.devicePixelRatio || 1;
|
|
44139
44714
|
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
|
|
44140
44715
|
ctx.clearRect(0, 0, w, h);
|
|
@@ -44173,7 +44748,7 @@ var init_GraphCanvas = __esm({
|
|
|
44173
44748
|
ctx.globalAlpha = 1;
|
|
44174
44749
|
for (const node of nodes) {
|
|
44175
44750
|
const size = node.size || 8;
|
|
44176
|
-
const color =
|
|
44751
|
+
const color = resolveColor3(node.color || getGroupColor(node.group, groups), canvas);
|
|
44177
44752
|
const isHovered = hoveredNode === node.id;
|
|
44178
44753
|
const isSelected = selectedNodeId !== void 0 && node.id === selectedNodeId;
|
|
44179
44754
|
ctx.globalAlpha = hoveredNode && !neighbors.has(node.id) ? 0.2 : 1;
|
|
@@ -51052,6 +51627,7 @@ var init_component_registry_generated = __esm({
|
|
|
51052
51627
|
init_BattleBoard();
|
|
51053
51628
|
init_BattleTemplate();
|
|
51054
51629
|
init_BehaviorView();
|
|
51630
|
+
init_BiologyCanvas();
|
|
51055
51631
|
init_BloomQuizBlock();
|
|
51056
51632
|
init_BoardgameBoard();
|
|
51057
51633
|
init_BookChapterView();
|
|
@@ -51063,7 +51639,6 @@ var init_component_registry_generated = __esm({
|
|
|
51063
51639
|
init_BranchingLogicBuilder();
|
|
51064
51640
|
init_Breadcrumb();
|
|
51065
51641
|
init_BuilderBoard();
|
|
51066
|
-
init_ButtonGroup();
|
|
51067
51642
|
init_CTABanner();
|
|
51068
51643
|
init_CalendarGrid();
|
|
51069
51644
|
init_Canvas2D();
|
|
@@ -51082,6 +51657,7 @@ var init_component_registry_generated = __esm({
|
|
|
51082
51657
|
init_ChartLegend();
|
|
51083
51658
|
init_ChatBar();
|
|
51084
51659
|
init_Checkbox();
|
|
51660
|
+
init_ChemistryCanvas();
|
|
51085
51661
|
init_ChoiceButton();
|
|
51086
51662
|
init_CityBuilderBoard();
|
|
51087
51663
|
init_CityBuilderTemplate();
|
|
@@ -51140,9 +51716,9 @@ var init_component_registry_generated = __esm({
|
|
|
51140
51716
|
init_FlipContainer();
|
|
51141
51717
|
init_FloatingActionButton();
|
|
51142
51718
|
init_Form();
|
|
51719
|
+
init_FormSection();
|
|
51143
51720
|
init_FormField();
|
|
51144
51721
|
init_FormSectionHeader();
|
|
51145
|
-
init_GameAudioProvider();
|
|
51146
51722
|
init_GameAudioToggle();
|
|
51147
51723
|
init_GameCard();
|
|
51148
51724
|
init_GameHud();
|
|
@@ -51173,6 +51749,7 @@ var init_component_registry_generated = __esm({
|
|
|
51173
51749
|
init_JazariStateMachine();
|
|
51174
51750
|
init_LandingPageTemplate();
|
|
51175
51751
|
init_LawReferenceTooltip();
|
|
51752
|
+
init_LearningCanvas();
|
|
51176
51753
|
init_Lightbox();
|
|
51177
51754
|
init_LikertScale();
|
|
51178
51755
|
init_LineChart();
|
|
@@ -51181,9 +51758,11 @@ var init_component_registry_generated = __esm({
|
|
|
51181
51758
|
init_MapView();
|
|
51182
51759
|
init_MarkdownContent();
|
|
51183
51760
|
init_MarketingFooter();
|
|
51761
|
+
init_MarketingStatCard();
|
|
51184
51762
|
init_MasterDetail();
|
|
51185
51763
|
init_MasterDetailLayout();
|
|
51186
51764
|
init_MatchPuzzleBoard();
|
|
51765
|
+
init_MathCanvas();
|
|
51187
51766
|
init_MatrixQuestion();
|
|
51188
51767
|
init_MediaGallery();
|
|
51189
51768
|
init_Menu();
|
|
@@ -51202,6 +51781,7 @@ var init_component_registry_generated = __esm({
|
|
|
51202
51781
|
init_PageHeader();
|
|
51203
51782
|
init_Pagination();
|
|
51204
51783
|
init_PatternTile();
|
|
51784
|
+
init_PhysicsCanvas();
|
|
51205
51785
|
init_PinballBoard();
|
|
51206
51786
|
init_PirateBoard();
|
|
51207
51787
|
init_PlatformerBoard();
|
|
@@ -51378,6 +51958,7 @@ var init_component_registry_generated = __esm({
|
|
|
51378
51958
|
"BattleBoard": BattleBoard,
|
|
51379
51959
|
"BattleTemplate": BattleTemplate,
|
|
51380
51960
|
"BehaviorView": BehaviorView,
|
|
51961
|
+
"BiologyCanvas": BiologyCanvas,
|
|
51381
51962
|
"BloomQuizBlock": BloomQuizBlock,
|
|
51382
51963
|
"BoardgameBoard": BoardgameBoard,
|
|
51383
51964
|
"BookChapterView": BookChapterView,
|
|
@@ -51391,7 +51972,6 @@ var init_component_registry_generated = __esm({
|
|
|
51391
51972
|
"Breadcrumb": Breadcrumb,
|
|
51392
51973
|
"BuilderBoard": BuilderBoard,
|
|
51393
51974
|
"Button": ButtonPattern,
|
|
51394
|
-
"ButtonGroup": ButtonGroup,
|
|
51395
51975
|
"ButtonPattern": ButtonPattern,
|
|
51396
51976
|
"CTABanner": CTABanner,
|
|
51397
51977
|
"CalendarGrid": CalendarGrid,
|
|
@@ -51411,6 +51991,7 @@ var init_component_registry_generated = __esm({
|
|
|
51411
51991
|
"ChartLegend": ChartLegend,
|
|
51412
51992
|
"ChatBar": ChatBar,
|
|
51413
51993
|
"Checkbox": Checkbox,
|
|
51994
|
+
"ChemistryCanvas": ChemistryCanvas,
|
|
51414
51995
|
"ChoiceButton": ChoiceButton,
|
|
51415
51996
|
"CityBuilderBoard": CityBuilderBoard,
|
|
51416
51997
|
"CityBuilderTemplate": CityBuilderTemplate,
|
|
@@ -51471,9 +52052,10 @@ var init_component_registry_generated = __esm({
|
|
|
51471
52052
|
"FlipContainer": FlipContainer,
|
|
51472
52053
|
"FloatingActionButton": FloatingActionButton,
|
|
51473
52054
|
"Form": Form,
|
|
52055
|
+
"FormActions": FormActions,
|
|
51474
52056
|
"FormField": FormField,
|
|
52057
|
+
"FormLayout": FormLayout,
|
|
51475
52058
|
"FormSectionHeader": FormSectionHeader,
|
|
51476
|
-
"GameAudioProvider": GameAudioProvider,
|
|
51477
52059
|
"GameAudioToggle": GameAudioToggle,
|
|
51478
52060
|
"GameBoard3D": GameBoard3D,
|
|
51479
52061
|
"GameCanvas3D": GameCanvas3D,
|
|
@@ -51511,6 +52093,7 @@ var init_component_registry_generated = __esm({
|
|
|
51511
52093
|
"LabelPattern": LabelPattern,
|
|
51512
52094
|
"LandingPageTemplate": LandingPageTemplate,
|
|
51513
52095
|
"LawReferenceTooltip": LawReferenceTooltip,
|
|
52096
|
+
"LearningCanvas": LearningCanvas,
|
|
51514
52097
|
"Lightbox": Lightbox,
|
|
51515
52098
|
"LikertScale": LikertScale,
|
|
51516
52099
|
"LineChart": LineChart2,
|
|
@@ -51519,9 +52102,11 @@ var init_component_registry_generated = __esm({
|
|
|
51519
52102
|
"MapView": MapView,
|
|
51520
52103
|
"MarkdownContent": MarkdownContent,
|
|
51521
52104
|
"MarketingFooter": MarketingFooter,
|
|
52105
|
+
"MarketingStatCard": MarketingStatCard,
|
|
51522
52106
|
"MasterDetail": MasterDetail,
|
|
51523
52107
|
"MasterDetailLayout": MasterDetailLayout,
|
|
51524
52108
|
"MatchPuzzleBoard": MatchPuzzleBoard,
|
|
52109
|
+
"MathCanvas": MathCanvas,
|
|
51525
52110
|
"MatrixQuestion": MatrixQuestion,
|
|
51526
52111
|
"MediaGallery": MediaGallery,
|
|
51527
52112
|
"Menu": Menu,
|
|
@@ -51540,6 +52125,7 @@ var init_component_registry_generated = __esm({
|
|
|
51540
52125
|
"PageHeader": PageHeader,
|
|
51541
52126
|
"Pagination": Pagination,
|
|
51542
52127
|
"PatternTile": PatternTile,
|
|
52128
|
+
"PhysicsCanvas": PhysicsCanvas,
|
|
51543
52129
|
"PinballBoard": PinballBoard,
|
|
51544
52130
|
"PirateBoard": PirateBoard,
|
|
51545
52131
|
"PlatformerBoard": PlatformerBoard,
|