@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/avl/index.js
CHANGED
|
@@ -7,7 +7,7 @@ import ELK from 'elkjs/lib/elk.bundled.js';
|
|
|
7
7
|
import { MarkerType, useReactFlow, Handle, Position, getBezierPath, EdgeLabelRenderer, useNodeId, ReactFlowProvider, BaseEdge, useNodesState, useEdgesState, ReactFlow, Controls, Background, BackgroundVariant } from '@xyflow/react';
|
|
8
8
|
import { useTranslate } from '@almadar/ui/hooks';
|
|
9
9
|
import * as LucideIcons2 from 'lucide-react';
|
|
10
|
-
import { Loader2, X, Code, FileText, WrapText, Check, Copy, Lightbulb, CheckCircle, List, Printer, ChevronRight, ChevronLeft, GitBranch, Pencil, Eye, Plus, ArrowRight, Trash, 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,
|
|
10
|
+
import { Loader2, X, Code, FileText, WrapText, Check, Copy, Lightbulb, CheckCircle, List, Printer, ChevronRight, ChevronLeft, GitBranch, Pencil, Eye, Plus, ArrowRight, Trash, 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';
|
|
11
11
|
import { createPortal } from 'react-dom';
|
|
12
12
|
import { UISlotProvider, useUISlots, useTheme } from '@almadar/ui/context';
|
|
13
13
|
import { evaluateGuard, evaluate, createMinimalContext } from '@almadar/evaluator';
|
|
@@ -584,7 +584,7 @@ var init_AvlFieldType = __esm({
|
|
|
584
584
|
}
|
|
585
585
|
});
|
|
586
586
|
|
|
587
|
-
// node_modules
|
|
587
|
+
// node_modules/clsx/dist/clsx.mjs
|
|
588
588
|
function r(e) {
|
|
589
589
|
var t, f3, n = "";
|
|
590
590
|
if ("string" == typeof e || "number" == typeof e) n += e;
|
|
@@ -599,11 +599,11 @@ function clsx() {
|
|
|
599
599
|
return n;
|
|
600
600
|
}
|
|
601
601
|
var init_clsx = __esm({
|
|
602
|
-
"node_modules
|
|
602
|
+
"node_modules/clsx/dist/clsx.mjs"() {
|
|
603
603
|
}
|
|
604
604
|
});
|
|
605
605
|
|
|
606
|
-
// node_modules
|
|
606
|
+
// node_modules/tailwind-merge/dist/bundle-mjs.mjs
|
|
607
607
|
function twJoin() {
|
|
608
608
|
let index = 0;
|
|
609
609
|
let argument;
|
|
@@ -647,7 +647,7 @@ function createTailwindMerge(createConfigFirst, ...createConfigRest) {
|
|
|
647
647
|
}
|
|
648
648
|
var CLASS_PART_SEPARATOR, createClassGroupUtils, getGroupRecursive, arbitraryPropertyRegex, getGroupIdForArbitraryProperty, createClassMap, processClassesRecursively, getPart, isThemeGetter, getPrefixedClassGroupEntries, createLruCache, IMPORTANT_MODIFIER, createParseClassName, sortModifiers, createConfigUtils, SPLIT_CLASSES_REGEX, mergeClassList, toValue, fromTheme, arbitraryValueRegex, fractionRegex, stringLengths, tshirtUnitRegex, lengthUnitRegex, colorFunctionRegex, shadowRegex, imageRegex, isLength, isArbitraryLength, isNumber, isArbitraryNumber, isInteger, isPercent, isArbitraryValue, isTshirtSize, sizeLabels, isArbitrarySize, isArbitraryPosition, imageLabels, isArbitraryImage, isArbitraryShadow, isAny, getIsArbitraryValue, isLengthOnly, isNever, isShadow, isImage, getDefaultConfig, twMerge;
|
|
649
649
|
var init_bundle_mjs = __esm({
|
|
650
|
-
"node_modules
|
|
650
|
+
"node_modules/tailwind-merge/dist/bundle-mjs.mjs"() {
|
|
651
651
|
CLASS_PART_SEPARATOR = "-";
|
|
652
652
|
createClassGroupUtils = (config) => {
|
|
653
653
|
const classMap = createClassMap(config);
|
|
@@ -9373,6 +9373,66 @@ var init_RangeSlider = __esm({
|
|
|
9373
9373
|
RangeSlider.displayName = "RangeSlider";
|
|
9374
9374
|
}
|
|
9375
9375
|
});
|
|
9376
|
+
function easeOut(t) {
|
|
9377
|
+
return t * (2 - t);
|
|
9378
|
+
}
|
|
9379
|
+
var AnimatedCounter;
|
|
9380
|
+
var init_AnimatedCounter = __esm({
|
|
9381
|
+
"components/marketing/atoms/AnimatedCounter.tsx"() {
|
|
9382
|
+
"use client";
|
|
9383
|
+
init_cn();
|
|
9384
|
+
init_Typography();
|
|
9385
|
+
AnimatedCounter = ({
|
|
9386
|
+
value: rawValue,
|
|
9387
|
+
duration = 600,
|
|
9388
|
+
prefix,
|
|
9389
|
+
suffix,
|
|
9390
|
+
className
|
|
9391
|
+
}) => {
|
|
9392
|
+
const numericRaw = typeof rawValue === "number" ? rawValue : Number.parseFloat(String(rawValue ?? ""));
|
|
9393
|
+
const value = !Number.isNaN(numericRaw) ? numericRaw : 0;
|
|
9394
|
+
const [displayValue, setDisplayValue] = useState(value);
|
|
9395
|
+
const previousValueRef = useRef(value);
|
|
9396
|
+
const animationFrameRef = useRef(null);
|
|
9397
|
+
useEffect(() => {
|
|
9398
|
+
const from = previousValueRef.current;
|
|
9399
|
+
const to = value;
|
|
9400
|
+
previousValueRef.current = value;
|
|
9401
|
+
if (from === to) {
|
|
9402
|
+
setDisplayValue(to);
|
|
9403
|
+
return;
|
|
9404
|
+
}
|
|
9405
|
+
const startTime = performance.now();
|
|
9406
|
+
const diff = to - from;
|
|
9407
|
+
function animate(currentTime) {
|
|
9408
|
+
const elapsed = currentTime - startTime;
|
|
9409
|
+
const progress = Math.min(elapsed / duration, 1);
|
|
9410
|
+
const easedProgress = easeOut(progress);
|
|
9411
|
+
setDisplayValue(from + diff * easedProgress);
|
|
9412
|
+
if (progress < 1) {
|
|
9413
|
+
animationFrameRef.current = requestAnimationFrame(animate);
|
|
9414
|
+
} else {
|
|
9415
|
+
setDisplayValue(to);
|
|
9416
|
+
}
|
|
9417
|
+
}
|
|
9418
|
+
animationFrameRef.current = requestAnimationFrame(animate);
|
|
9419
|
+
return () => {
|
|
9420
|
+
if (animationFrameRef.current !== null) {
|
|
9421
|
+
cancelAnimationFrame(animationFrameRef.current);
|
|
9422
|
+
}
|
|
9423
|
+
};
|
|
9424
|
+
}, [value, duration]);
|
|
9425
|
+
const decimalPlaces = Number.isInteger(value) ? 0 : String(value).split(".")[1]?.length ?? 0;
|
|
9426
|
+
const formattedValue = displayValue.toFixed(decimalPlaces);
|
|
9427
|
+
return /* @__PURE__ */ jsxs(Typography, { variant: "h3", className: cn("tabular-nums", className), children: [
|
|
9428
|
+
prefix,
|
|
9429
|
+
formattedValue,
|
|
9430
|
+
suffix
|
|
9431
|
+
] });
|
|
9432
|
+
};
|
|
9433
|
+
AnimatedCounter.displayName = "AnimatedCounter";
|
|
9434
|
+
}
|
|
9435
|
+
});
|
|
9376
9436
|
function useInfiniteScroll(onLoadMore, options = {}) {
|
|
9377
9437
|
const { rootMargin = "200px", hasMore = true, isLoading = false } = options;
|
|
9378
9438
|
const observerRef = useRef(null);
|
|
@@ -9669,6 +9729,34 @@ var init_SectionHeader = __esm({
|
|
|
9669
9729
|
SectionHeader.displayName = "SectionHeader";
|
|
9670
9730
|
}
|
|
9671
9731
|
});
|
|
9732
|
+
var sizeClasses6, MarketingStatCard;
|
|
9733
|
+
var init_MarketingStatCard = __esm({
|
|
9734
|
+
"components/marketing/atoms/MarketingStatCard.tsx"() {
|
|
9735
|
+
init_cn();
|
|
9736
|
+
init_Stack();
|
|
9737
|
+
init_Typography();
|
|
9738
|
+
sizeClasses6 = {
|
|
9739
|
+
sm: "text-2xl",
|
|
9740
|
+
md: "text-4xl",
|
|
9741
|
+
lg: "text-5xl"
|
|
9742
|
+
};
|
|
9743
|
+
MarketingStatCard = ({ value, label, size = "md", className }) => {
|
|
9744
|
+
return /* @__PURE__ */ jsxs(VStack, { gap: "xs", align: "center", className: cn(className), children: [
|
|
9745
|
+
/* @__PURE__ */ jsx(
|
|
9746
|
+
Typography,
|
|
9747
|
+
{
|
|
9748
|
+
weight: "bold",
|
|
9749
|
+
color: "primary",
|
|
9750
|
+
className: cn(sizeClasses6[size]),
|
|
9751
|
+
children: value
|
|
9752
|
+
}
|
|
9753
|
+
),
|
|
9754
|
+
/* @__PURE__ */ jsx(Typography, { variant: "caption", color: "muted", children: label })
|
|
9755
|
+
] });
|
|
9756
|
+
};
|
|
9757
|
+
MarketingStatCard.displayName = "MarketingStatCard";
|
|
9758
|
+
}
|
|
9759
|
+
});
|
|
9672
9760
|
var backgroundClasses, paddingClasses, ContentSection;
|
|
9673
9761
|
var init_ContentSection = __esm({
|
|
9674
9762
|
"components/marketing/atoms/ContentSection.tsx"() {
|
|
@@ -15862,318 +15950,14 @@ var init_useCanvasEffects = __esm({
|
|
|
15862
15950
|
"use client";
|
|
15863
15951
|
}
|
|
15864
15952
|
});
|
|
15865
|
-
function pickPath(entry) {
|
|
15866
|
-
if (Array.isArray(entry.path)) {
|
|
15867
|
-
return entry.path[Math.floor(Math.random() * entry.path.length)];
|
|
15868
|
-
}
|
|
15869
|
-
return entry.path;
|
|
15870
|
-
}
|
|
15871
|
-
function useGameAudio({
|
|
15872
|
-
manifest,
|
|
15873
|
-
baseUrl = "",
|
|
15874
|
-
initialMuted = false,
|
|
15875
|
-
initialVolume = 1
|
|
15876
|
-
}) {
|
|
15877
|
-
const [muted, setMutedState] = useState(initialMuted);
|
|
15878
|
-
const [masterVolume, setMasterVolumeState] = useState(initialVolume);
|
|
15879
|
-
const mutedRef = useRef(muted);
|
|
15880
|
-
const volumeRef = useRef(masterVolume);
|
|
15881
|
-
const manifestRef = useRef(manifest);
|
|
15882
|
-
mutedRef.current = muted;
|
|
15883
|
-
volumeRef.current = masterVolume;
|
|
15884
|
-
manifestRef.current = manifest;
|
|
15885
|
-
const poolsRef = useRef(/* @__PURE__ */ new Map());
|
|
15886
|
-
const getOrCreateElement = useCallback((key) => {
|
|
15887
|
-
const entry = manifestRef.current[key];
|
|
15888
|
-
if (!entry) return null;
|
|
15889
|
-
let pool = poolsRef.current.get(key);
|
|
15890
|
-
if (!pool) {
|
|
15891
|
-
pool = [];
|
|
15892
|
-
poolsRef.current.set(key, pool);
|
|
15893
|
-
}
|
|
15894
|
-
const maxSize = entry.poolSize ?? 1;
|
|
15895
|
-
for (const audio of pool) {
|
|
15896
|
-
if (audio.paused && (audio.ended || audio.currentTime === 0)) {
|
|
15897
|
-
return audio;
|
|
15898
|
-
}
|
|
15899
|
-
}
|
|
15900
|
-
if (pool.length < maxSize) {
|
|
15901
|
-
const src = baseUrl + pickPath(entry);
|
|
15902
|
-
const audio = new Audio(src);
|
|
15903
|
-
audio.loop = entry.loop ?? false;
|
|
15904
|
-
pool.push(audio);
|
|
15905
|
-
return audio;
|
|
15906
|
-
}
|
|
15907
|
-
if (!entry.loop) {
|
|
15908
|
-
let oldest = pool[0];
|
|
15909
|
-
for (const audio of pool) {
|
|
15910
|
-
if (audio.currentTime > oldest.currentTime) {
|
|
15911
|
-
oldest = audio;
|
|
15912
|
-
}
|
|
15913
|
-
}
|
|
15914
|
-
oldest.pause();
|
|
15915
|
-
oldest.currentTime = 0;
|
|
15916
|
-
return oldest;
|
|
15917
|
-
}
|
|
15918
|
-
return null;
|
|
15919
|
-
}, [baseUrl]);
|
|
15920
|
-
const play = useCallback((key) => {
|
|
15921
|
-
if (mutedRef.current) return;
|
|
15922
|
-
const entry = manifestRef.current[key];
|
|
15923
|
-
if (!entry) return;
|
|
15924
|
-
const audio = getOrCreateElement(key);
|
|
15925
|
-
if (!audio) return;
|
|
15926
|
-
audio.volume = Math.min(1, (entry.volume ?? 1) * volumeRef.current);
|
|
15927
|
-
if (!entry.loop) {
|
|
15928
|
-
audio.currentTime = 0;
|
|
15929
|
-
}
|
|
15930
|
-
const promise = audio.play();
|
|
15931
|
-
if (promise) {
|
|
15932
|
-
promise.catch(() => {
|
|
15933
|
-
});
|
|
15934
|
-
}
|
|
15935
|
-
}, [getOrCreateElement]);
|
|
15936
|
-
const stop = useCallback((key) => {
|
|
15937
|
-
const pool = poolsRef.current.get(key);
|
|
15938
|
-
if (!pool) return;
|
|
15939
|
-
for (const audio of pool) {
|
|
15940
|
-
audio.pause();
|
|
15941
|
-
audio.currentTime = 0;
|
|
15942
|
-
}
|
|
15943
|
-
}, []);
|
|
15944
|
-
const currentMusicKeyRef = useRef(null);
|
|
15945
|
-
const currentMusicElRef = useRef(null);
|
|
15946
|
-
const musicFadeRef = useRef(null);
|
|
15947
|
-
const pendingMusicKeyRef = useRef(null);
|
|
15948
|
-
const clearMusicFade = useCallback(() => {
|
|
15949
|
-
if (musicFadeRef.current) {
|
|
15950
|
-
clearInterval(musicFadeRef.current);
|
|
15951
|
-
musicFadeRef.current = null;
|
|
15952
|
-
}
|
|
15953
|
-
}, []);
|
|
15954
|
-
const playMusic = useCallback((key) => {
|
|
15955
|
-
if (key === currentMusicKeyRef.current) return;
|
|
15956
|
-
pendingMusicKeyRef.current = key;
|
|
15957
|
-
const entry = manifestRef.current[key];
|
|
15958
|
-
if (!entry) return;
|
|
15959
|
-
const fadeDurationMs = entry.crossfadeDurationMs ?? 1500;
|
|
15960
|
-
const stepMs = 50;
|
|
15961
|
-
const totalSteps = Math.max(1, fadeDurationMs / stepMs);
|
|
15962
|
-
const targetVolume = Math.min(1, (entry.volume ?? 1) * volumeRef.current);
|
|
15963
|
-
clearMusicFade();
|
|
15964
|
-
const src = baseUrl + (Array.isArray(entry.path) ? entry.path[0] : entry.path);
|
|
15965
|
-
const incoming = new Audio(src);
|
|
15966
|
-
incoming.loop = true;
|
|
15967
|
-
incoming.volume = 0;
|
|
15968
|
-
const outgoing = currentMusicElRef.current;
|
|
15969
|
-
const outgoingStartVol = outgoing?.volume ?? 0;
|
|
15970
|
-
currentMusicKeyRef.current = key;
|
|
15971
|
-
currentMusicElRef.current = incoming;
|
|
15972
|
-
if (!mutedRef.current) {
|
|
15973
|
-
incoming.play().catch(() => {
|
|
15974
|
-
currentMusicKeyRef.current = null;
|
|
15975
|
-
currentMusicElRef.current = outgoing;
|
|
15976
|
-
});
|
|
15977
|
-
}
|
|
15978
|
-
let step = 0;
|
|
15979
|
-
musicFadeRef.current = setInterval(() => {
|
|
15980
|
-
step++;
|
|
15981
|
-
const progress = Math.min(step / totalSteps, 1);
|
|
15982
|
-
incoming.volume = Math.min(1, targetVolume * progress);
|
|
15983
|
-
if (outgoing) {
|
|
15984
|
-
outgoing.volume = Math.max(0, outgoingStartVol * (1 - progress));
|
|
15985
|
-
}
|
|
15986
|
-
if (progress >= 1) {
|
|
15987
|
-
clearMusicFade();
|
|
15988
|
-
if (outgoing) {
|
|
15989
|
-
outgoing.pause();
|
|
15990
|
-
outgoing.src = "";
|
|
15991
|
-
}
|
|
15992
|
-
}
|
|
15993
|
-
}, stepMs);
|
|
15994
|
-
}, [baseUrl, clearMusicFade]);
|
|
15995
|
-
const stopMusic = useCallback((fadeDurationMs = 1e3) => {
|
|
15996
|
-
const outgoing = currentMusicElRef.current;
|
|
15997
|
-
if (!outgoing) return;
|
|
15998
|
-
currentMusicKeyRef.current = null;
|
|
15999
|
-
currentMusicElRef.current = null;
|
|
16000
|
-
pendingMusicKeyRef.current = null;
|
|
16001
|
-
clearMusicFade();
|
|
16002
|
-
const startVolume = outgoing.volume;
|
|
16003
|
-
const stepMs = 50;
|
|
16004
|
-
const totalSteps = Math.max(1, fadeDurationMs / stepMs);
|
|
16005
|
-
let step = 0;
|
|
16006
|
-
musicFadeRef.current = setInterval(() => {
|
|
16007
|
-
step++;
|
|
16008
|
-
const progress = step / totalSteps;
|
|
16009
|
-
outgoing.volume = Math.max(0, startVolume * (1 - progress));
|
|
16010
|
-
if (progress >= 1) {
|
|
16011
|
-
clearMusicFade();
|
|
16012
|
-
outgoing.pause();
|
|
16013
|
-
outgoing.src = "";
|
|
16014
|
-
}
|
|
16015
|
-
}, stepMs);
|
|
16016
|
-
}, [clearMusicFade]);
|
|
16017
|
-
const stopAll = useCallback(() => {
|
|
16018
|
-
for (const pool of poolsRef.current.values()) {
|
|
16019
|
-
for (const audio of pool) {
|
|
16020
|
-
audio.pause();
|
|
16021
|
-
audio.currentTime = 0;
|
|
16022
|
-
}
|
|
16023
|
-
}
|
|
16024
|
-
stopMusic(0);
|
|
16025
|
-
}, [stopMusic]);
|
|
16026
|
-
const setMuted = useCallback((value) => {
|
|
16027
|
-
setMutedState(value);
|
|
16028
|
-
if (value) {
|
|
16029
|
-
for (const [key, pool] of poolsRef.current.entries()) {
|
|
16030
|
-
if (manifestRef.current[key]?.loop) {
|
|
16031
|
-
for (const audio of pool) {
|
|
16032
|
-
if (!audio.paused) audio.pause();
|
|
16033
|
-
}
|
|
16034
|
-
}
|
|
16035
|
-
}
|
|
16036
|
-
currentMusicElRef.current?.pause();
|
|
16037
|
-
} else {
|
|
16038
|
-
for (const [key, pool] of poolsRef.current.entries()) {
|
|
16039
|
-
const entry = manifestRef.current[key];
|
|
16040
|
-
if (entry?.loop && entry?.autostart) {
|
|
16041
|
-
for (const audio of pool) {
|
|
16042
|
-
if (audio.paused) audio.play().catch(() => {
|
|
16043
|
-
});
|
|
16044
|
-
}
|
|
16045
|
-
}
|
|
16046
|
-
}
|
|
16047
|
-
const musicEl = currentMusicElRef.current;
|
|
16048
|
-
if (musicEl) {
|
|
16049
|
-
musicEl.play().catch(() => {
|
|
16050
|
-
});
|
|
16051
|
-
}
|
|
16052
|
-
}
|
|
16053
|
-
}, []);
|
|
16054
|
-
const setMasterVolume = useCallback((volume) => {
|
|
16055
|
-
const clamped = Math.max(0, Math.min(1, volume));
|
|
16056
|
-
setMasterVolumeState(clamped);
|
|
16057
|
-
for (const [key, pool] of poolsRef.current.entries()) {
|
|
16058
|
-
const entryVol = manifestRef.current[key]?.volume ?? 1;
|
|
16059
|
-
for (const audio of pool) {
|
|
16060
|
-
audio.volume = Math.min(1, entryVol * clamped);
|
|
16061
|
-
}
|
|
16062
|
-
}
|
|
16063
|
-
if (!musicFadeRef.current && currentMusicElRef.current) {
|
|
16064
|
-
const key = currentMusicKeyRef.current;
|
|
16065
|
-
const entryVol = key ? manifestRef.current[key]?.volume ?? 1 : 1;
|
|
16066
|
-
currentMusicElRef.current.volume = Math.min(1, entryVol * clamped);
|
|
16067
|
-
}
|
|
16068
|
-
}, []);
|
|
16069
|
-
const unlockedRef = useRef(false);
|
|
16070
|
-
useEffect(() => {
|
|
16071
|
-
const autoKeys = Object.keys(manifest).filter((k) => manifest[k].autostart);
|
|
16072
|
-
const hasPendingMusic = () => pendingMusicKeyRef.current !== null;
|
|
16073
|
-
const hasAutoStart = autoKeys.length > 0;
|
|
16074
|
-
if (!hasAutoStart && !hasPendingMusic()) return;
|
|
16075
|
-
const unlock = () => {
|
|
16076
|
-
if (unlockedRef.current) return;
|
|
16077
|
-
unlockedRef.current = true;
|
|
16078
|
-
if (!mutedRef.current) {
|
|
16079
|
-
for (const key of autoKeys) {
|
|
16080
|
-
play(key);
|
|
16081
|
-
}
|
|
16082
|
-
const pending = pendingMusicKeyRef.current;
|
|
16083
|
-
if (pending && pending !== currentMusicKeyRef.current) {
|
|
16084
|
-
playMusic(pending);
|
|
16085
|
-
}
|
|
16086
|
-
}
|
|
16087
|
-
};
|
|
16088
|
-
document.addEventListener("click", unlock, { once: true });
|
|
16089
|
-
document.addEventListener("keydown", unlock, { once: true });
|
|
16090
|
-
document.addEventListener("touchstart", unlock, { once: true });
|
|
16091
|
-
return () => {
|
|
16092
|
-
document.removeEventListener("click", unlock);
|
|
16093
|
-
document.removeEventListener("keydown", unlock);
|
|
16094
|
-
document.removeEventListener("touchstart", unlock);
|
|
16095
|
-
};
|
|
16096
|
-
}, [manifest, play, playMusic]);
|
|
16097
|
-
useEffect(() => {
|
|
16098
|
-
return () => {
|
|
16099
|
-
clearMusicFade();
|
|
16100
|
-
for (const pool of poolsRef.current.values()) {
|
|
16101
|
-
for (const audio of pool) {
|
|
16102
|
-
audio.pause();
|
|
16103
|
-
audio.src = "";
|
|
16104
|
-
}
|
|
16105
|
-
}
|
|
16106
|
-
poolsRef.current.clear();
|
|
16107
|
-
if (currentMusicElRef.current) {
|
|
16108
|
-
currentMusicElRef.current.pause();
|
|
16109
|
-
currentMusicElRef.current.src = "";
|
|
16110
|
-
currentMusicElRef.current = null;
|
|
16111
|
-
}
|
|
16112
|
-
};
|
|
16113
|
-
}, [clearMusicFade]);
|
|
16114
|
-
return {
|
|
16115
|
-
play,
|
|
16116
|
-
stop,
|
|
16117
|
-
stopAll,
|
|
16118
|
-
playMusic,
|
|
16119
|
-
stopMusic,
|
|
16120
|
-
muted,
|
|
16121
|
-
setMuted,
|
|
16122
|
-
masterVolume,
|
|
16123
|
-
setMasterVolume
|
|
16124
|
-
};
|
|
16125
|
-
}
|
|
16126
15953
|
var init_useGameAudio = __esm({
|
|
16127
15954
|
"components/game/shared/hooks/useGameAudio.ts"() {
|
|
16128
15955
|
"use client";
|
|
16129
|
-
useGameAudio.displayName = "useGameAudio";
|
|
16130
15956
|
}
|
|
16131
15957
|
});
|
|
16132
15958
|
function useGameAudioContextOptional() {
|
|
16133
15959
|
return useContext(GameAudioContext);
|
|
16134
15960
|
}
|
|
16135
|
-
function GameAudioProvider({
|
|
16136
|
-
manifest,
|
|
16137
|
-
baseUrl = "",
|
|
16138
|
-
children,
|
|
16139
|
-
initialMuted = false
|
|
16140
|
-
}) {
|
|
16141
|
-
const eventBus = useEventBus();
|
|
16142
|
-
const { play, stop, stopAll, playMusic, stopMusic, muted, setMuted, masterVolume, setMasterVolume } = useGameAudio({ manifest, baseUrl, initialMuted });
|
|
16143
|
-
useEffect(() => {
|
|
16144
|
-
const unsubPlay = eventBus.on("UI:PLAY_SOUND", (event) => {
|
|
16145
|
-
const key = event.payload?.key;
|
|
16146
|
-
if (key) play(key);
|
|
16147
|
-
});
|
|
16148
|
-
const unsubStop = eventBus.on("UI:STOP_SOUND", (event) => {
|
|
16149
|
-
const key = event.payload?.key;
|
|
16150
|
-
if (key) {
|
|
16151
|
-
stop(key);
|
|
16152
|
-
} else {
|
|
16153
|
-
stopAll();
|
|
16154
|
-
}
|
|
16155
|
-
});
|
|
16156
|
-
const unsubChangeMusic = eventBus.on("UI:CHANGE_MUSIC", (event) => {
|
|
16157
|
-
const key = event.payload?.key;
|
|
16158
|
-
if (key) {
|
|
16159
|
-
playMusic(key);
|
|
16160
|
-
} else {
|
|
16161
|
-
stopMusic();
|
|
16162
|
-
}
|
|
16163
|
-
});
|
|
16164
|
-
const unsubStopMusic = eventBus.on("UI:STOP_MUSIC", () => {
|
|
16165
|
-
stopMusic();
|
|
16166
|
-
});
|
|
16167
|
-
return () => {
|
|
16168
|
-
unsubPlay();
|
|
16169
|
-
unsubStop();
|
|
16170
|
-
unsubChangeMusic();
|
|
16171
|
-
unsubStopMusic();
|
|
16172
|
-
};
|
|
16173
|
-
}, [eventBus, play, stop, stopAll, playMusic, stopMusic]);
|
|
16174
|
-
const value = { muted, setMuted, masterVolume, setMasterVolume, play, playMusic, stopMusic };
|
|
16175
|
-
return /* @__PURE__ */ jsx(GameAudioContext.Provider, { value, children });
|
|
16176
|
-
}
|
|
16177
15961
|
var GameAudioContext;
|
|
16178
15962
|
var init_GameAudioProvider = __esm({
|
|
16179
15963
|
"components/game/shared/providers/GameAudioProvider.tsx"() {
|
|
@@ -16182,7 +15966,6 @@ var init_GameAudioProvider = __esm({
|
|
|
16182
15966
|
init_useGameAudio();
|
|
16183
15967
|
GameAudioContext = createContext(null);
|
|
16184
15968
|
GameAudioContext.displayName = "GameAudioContext";
|
|
16185
|
-
GameAudioProvider.displayName = "GameAudioProvider";
|
|
16186
15969
|
}
|
|
16187
15970
|
});
|
|
16188
15971
|
function GameAudioToggle({
|
|
@@ -23818,6 +23601,315 @@ var init_molecules = __esm({
|
|
|
23818
23601
|
init_puzzleObject();
|
|
23819
23602
|
}
|
|
23820
23603
|
});
|
|
23604
|
+
function resolveColor2(color, ctx, fallback) {
|
|
23605
|
+
if (!color) return fallback;
|
|
23606
|
+
if (color.startsWith("var(")) {
|
|
23607
|
+
ctx.canvas.style;
|
|
23608
|
+
const m = /^var\((--[^,)]+)(?:,\s*([^)]+))?\)$/.exec(color);
|
|
23609
|
+
if (m) {
|
|
23610
|
+
const computed = getComputedStyle(ctx.canvas).getPropertyValue(m[1]).trim();
|
|
23611
|
+
return computed || m[2]?.trim() || fallback;
|
|
23612
|
+
}
|
|
23613
|
+
}
|
|
23614
|
+
return color;
|
|
23615
|
+
}
|
|
23616
|
+
function shapeBounds(shape) {
|
|
23617
|
+
switch (shape.type) {
|
|
23618
|
+
case "line":
|
|
23619
|
+
case "arrow":
|
|
23620
|
+
if (shape.x1 == null || shape.y1 == null || shape.x2 == null || shape.y2 == null) return null;
|
|
23621
|
+
return {
|
|
23622
|
+
x: Math.min(shape.x1, shape.x2) - 6,
|
|
23623
|
+
y: Math.min(shape.y1, shape.y2) - 6,
|
|
23624
|
+
w: Math.abs(shape.x2 - shape.x1) + 12,
|
|
23625
|
+
h: Math.abs(shape.y2 - shape.y1) + 12
|
|
23626
|
+
};
|
|
23627
|
+
case "circle":
|
|
23628
|
+
if (shape.x == null || shape.y == null || shape.radius == null) return null;
|
|
23629
|
+
return {
|
|
23630
|
+
x: shape.x - shape.radius - 4,
|
|
23631
|
+
y: shape.y - shape.radius - 4,
|
|
23632
|
+
w: shape.radius * 2 + 8,
|
|
23633
|
+
h: shape.radius * 2 + 8
|
|
23634
|
+
};
|
|
23635
|
+
case "rect":
|
|
23636
|
+
if (shape.x == null || shape.y == null || shape.width == null || shape.height == null) return null;
|
|
23637
|
+
return { x: shape.x - 4, y: shape.y - 4, w: shape.width + 8, h: shape.height + 8 };
|
|
23638
|
+
case "polygon":
|
|
23639
|
+
if (!shape.points || shape.points.length === 0) return null;
|
|
23640
|
+
{
|
|
23641
|
+
const xs = shape.points.map((p2) => p2.x);
|
|
23642
|
+
const ys = shape.points.map((p2) => p2.y);
|
|
23643
|
+
const minX = Math.min(...xs);
|
|
23644
|
+
const minY = Math.min(...ys);
|
|
23645
|
+
return {
|
|
23646
|
+
x: minX - 4,
|
|
23647
|
+
y: minY - 4,
|
|
23648
|
+
w: Math.max(...xs) - minX + 8,
|
|
23649
|
+
h: Math.max(...ys) - minY + 8
|
|
23650
|
+
};
|
|
23651
|
+
}
|
|
23652
|
+
case "text":
|
|
23653
|
+
if (shape.x == null || shape.y == null) return null;
|
|
23654
|
+
return { x: shape.x - 4, y: shape.y - (shape.fontSize ?? 14) - 4, w: 120, h: (shape.fontSize ?? 14) + 8 };
|
|
23655
|
+
default:
|
|
23656
|
+
return null;
|
|
23657
|
+
}
|
|
23658
|
+
}
|
|
23659
|
+
function drawArrowHead(ctx, x1, y1, x2, y2, size) {
|
|
23660
|
+
const angle = Math.atan2(y2 - y1, x2 - x1);
|
|
23661
|
+
ctx.beginPath();
|
|
23662
|
+
ctx.moveTo(x2, y2);
|
|
23663
|
+
ctx.lineTo(x2 - size * Math.cos(angle - Math.PI / 6), y2 - size * Math.sin(angle - Math.PI / 6));
|
|
23664
|
+
ctx.lineTo(x2 - size * Math.cos(angle + Math.PI / 6), y2 - size * Math.sin(angle + Math.PI / 6));
|
|
23665
|
+
ctx.closePath();
|
|
23666
|
+
ctx.fill();
|
|
23667
|
+
}
|
|
23668
|
+
function drawShape(ctx, shape, width, height) {
|
|
23669
|
+
ctx.save();
|
|
23670
|
+
const opacity = shape.opacity ?? 1;
|
|
23671
|
+
ctx.globalAlpha = opacity;
|
|
23672
|
+
const stroke = resolveColor2(shape.color, ctx, "#333333");
|
|
23673
|
+
const fill = shape.fill ? resolveColor2(shape.fill, ctx, "#cccccc") : void 0;
|
|
23674
|
+
ctx.lineWidth = shape.lineWidth ?? 2;
|
|
23675
|
+
switch (shape.type) {
|
|
23676
|
+
case "grid": {
|
|
23677
|
+
const step = shape.step ?? 40;
|
|
23678
|
+
ctx.strokeStyle = stroke;
|
|
23679
|
+
ctx.globalAlpha = opacity * 0.25;
|
|
23680
|
+
ctx.lineWidth = 1;
|
|
23681
|
+
ctx.beginPath();
|
|
23682
|
+
for (let x = 0; x <= width; x += step) {
|
|
23683
|
+
ctx.moveTo(x, 0);
|
|
23684
|
+
ctx.lineTo(x, height);
|
|
23685
|
+
}
|
|
23686
|
+
for (let y = 0; y <= height; y += step) {
|
|
23687
|
+
ctx.moveTo(0, y);
|
|
23688
|
+
ctx.lineTo(width, y);
|
|
23689
|
+
}
|
|
23690
|
+
ctx.stroke();
|
|
23691
|
+
break;
|
|
23692
|
+
}
|
|
23693
|
+
case "axis": {
|
|
23694
|
+
const axis = shape.axis ?? "x";
|
|
23695
|
+
ctx.strokeStyle = stroke;
|
|
23696
|
+
ctx.lineWidth = shape.lineWidth ?? 2;
|
|
23697
|
+
ctx.beginPath();
|
|
23698
|
+
if (axis === "x") {
|
|
23699
|
+
ctx.moveTo(0, height / 2);
|
|
23700
|
+
ctx.lineTo(width, height / 2);
|
|
23701
|
+
} else {
|
|
23702
|
+
ctx.moveTo(width / 2, 0);
|
|
23703
|
+
ctx.lineTo(width / 2, height);
|
|
23704
|
+
}
|
|
23705
|
+
ctx.stroke();
|
|
23706
|
+
break;
|
|
23707
|
+
}
|
|
23708
|
+
case "line": {
|
|
23709
|
+
if (shape.x1 == null || shape.y1 == null || shape.x2 == null || shape.y2 == null) break;
|
|
23710
|
+
ctx.strokeStyle = stroke;
|
|
23711
|
+
ctx.beginPath();
|
|
23712
|
+
ctx.moveTo(shape.x1, shape.y1);
|
|
23713
|
+
ctx.lineTo(shape.x2, shape.y2);
|
|
23714
|
+
ctx.stroke();
|
|
23715
|
+
break;
|
|
23716
|
+
}
|
|
23717
|
+
case "arrow": {
|
|
23718
|
+
if (shape.x1 == null || shape.y1 == null || shape.x2 == null || shape.y2 == null) break;
|
|
23719
|
+
ctx.strokeStyle = stroke;
|
|
23720
|
+
ctx.fillStyle = stroke;
|
|
23721
|
+
ctx.beginPath();
|
|
23722
|
+
ctx.moveTo(shape.x1, shape.y1);
|
|
23723
|
+
ctx.lineTo(shape.x2, shape.y2);
|
|
23724
|
+
ctx.stroke();
|
|
23725
|
+
drawArrowHead(ctx, shape.x1, shape.y1, shape.x2, shape.y2, 10);
|
|
23726
|
+
break;
|
|
23727
|
+
}
|
|
23728
|
+
case "circle": {
|
|
23729
|
+
if (shape.x == null || shape.y == null || shape.radius == null) break;
|
|
23730
|
+
ctx.beginPath();
|
|
23731
|
+
ctx.arc(shape.x, shape.y, shape.radius, 0, Math.PI * 2);
|
|
23732
|
+
if (fill) {
|
|
23733
|
+
ctx.fillStyle = fill;
|
|
23734
|
+
ctx.fill();
|
|
23735
|
+
}
|
|
23736
|
+
ctx.strokeStyle = stroke;
|
|
23737
|
+
ctx.stroke();
|
|
23738
|
+
break;
|
|
23739
|
+
}
|
|
23740
|
+
case "rect": {
|
|
23741
|
+
if (shape.x == null || shape.y == null || shape.width == null || shape.height == null) break;
|
|
23742
|
+
if (fill) {
|
|
23743
|
+
ctx.fillStyle = fill;
|
|
23744
|
+
ctx.fillRect(shape.x, shape.y, shape.width, shape.height);
|
|
23745
|
+
}
|
|
23746
|
+
ctx.strokeStyle = stroke;
|
|
23747
|
+
ctx.strokeRect(shape.x, shape.y, shape.width, shape.height);
|
|
23748
|
+
break;
|
|
23749
|
+
}
|
|
23750
|
+
case "polygon": {
|
|
23751
|
+
if (!shape.points || shape.points.length < 2) break;
|
|
23752
|
+
ctx.beginPath();
|
|
23753
|
+
ctx.moveTo(shape.points[0].x, shape.points[0].y);
|
|
23754
|
+
for (let i = 1; i < shape.points.length; i++) {
|
|
23755
|
+
ctx.lineTo(shape.points[i].x, shape.points[i].y);
|
|
23756
|
+
}
|
|
23757
|
+
ctx.closePath();
|
|
23758
|
+
if (fill) {
|
|
23759
|
+
ctx.fillStyle = fill;
|
|
23760
|
+
ctx.fill();
|
|
23761
|
+
}
|
|
23762
|
+
ctx.strokeStyle = stroke;
|
|
23763
|
+
ctx.stroke();
|
|
23764
|
+
break;
|
|
23765
|
+
}
|
|
23766
|
+
case "path": {
|
|
23767
|
+
if (!shape.path) break;
|
|
23768
|
+
const p2 = new Path2D(shape.path);
|
|
23769
|
+
if (fill) {
|
|
23770
|
+
ctx.fillStyle = fill;
|
|
23771
|
+
ctx.fill(p2);
|
|
23772
|
+
}
|
|
23773
|
+
ctx.strokeStyle = stroke;
|
|
23774
|
+
ctx.stroke(p2);
|
|
23775
|
+
break;
|
|
23776
|
+
}
|
|
23777
|
+
case "text": {
|
|
23778
|
+
if (shape.x == null || shape.y == null || !shape.text) break;
|
|
23779
|
+
ctx.fillStyle = stroke;
|
|
23780
|
+
ctx.font = `${shape.fontSize ?? 14}px system-ui, sans-serif`;
|
|
23781
|
+
ctx.textAlign = shape.align ?? "left";
|
|
23782
|
+
ctx.textBaseline = "middle";
|
|
23783
|
+
ctx.fillText(shape.text, shape.x, shape.y);
|
|
23784
|
+
break;
|
|
23785
|
+
}
|
|
23786
|
+
}
|
|
23787
|
+
ctx.restore();
|
|
23788
|
+
}
|
|
23789
|
+
var LearningCanvas;
|
|
23790
|
+
var init_LearningCanvas = __esm({
|
|
23791
|
+
"components/learning/atoms/LearningCanvas.tsx"() {
|
|
23792
|
+
"use client";
|
|
23793
|
+
init_cn();
|
|
23794
|
+
init_useEventBus();
|
|
23795
|
+
LearningCanvas = ({
|
|
23796
|
+
className,
|
|
23797
|
+
width = 600,
|
|
23798
|
+
height = 400,
|
|
23799
|
+
backgroundColor,
|
|
23800
|
+
shapes = [],
|
|
23801
|
+
interactive = false,
|
|
23802
|
+
animate = false,
|
|
23803
|
+
onShapeClick,
|
|
23804
|
+
onShapeHover,
|
|
23805
|
+
isLoading,
|
|
23806
|
+
error
|
|
23807
|
+
}) => {
|
|
23808
|
+
const canvasRef = useRef(null);
|
|
23809
|
+
const eventBus = useEventBus();
|
|
23810
|
+
const animRef = useRef(0);
|
|
23811
|
+
const hoverIndexRef = useRef(-1);
|
|
23812
|
+
const findShapeAt = useCallback((clientX, clientY) => {
|
|
23813
|
+
const canvas = canvasRef.current;
|
|
23814
|
+
if (!canvas) return -1;
|
|
23815
|
+
const rect = canvas.getBoundingClientRect();
|
|
23816
|
+
const x = clientX - rect.left;
|
|
23817
|
+
const y = clientY - rect.top;
|
|
23818
|
+
for (let i = shapes.length - 1; i >= 0; i--) {
|
|
23819
|
+
const b = shapeBounds(shapes[i]);
|
|
23820
|
+
if (b && x >= b.x && x <= b.x + b.w && y >= b.y && y <= b.y + b.h) {
|
|
23821
|
+
return i;
|
|
23822
|
+
}
|
|
23823
|
+
}
|
|
23824
|
+
return -1;
|
|
23825
|
+
}, [shapes]);
|
|
23826
|
+
const draw = useCallback(() => {
|
|
23827
|
+
const canvas = canvasRef.current;
|
|
23828
|
+
if (!canvas) return;
|
|
23829
|
+
const ctx = canvas.getContext("2d");
|
|
23830
|
+
if (!ctx) return;
|
|
23831
|
+
const dpr = typeof window !== "undefined" ? window.devicePixelRatio || 1 : 1;
|
|
23832
|
+
canvas.width = Math.max(1, Math.floor(width * dpr));
|
|
23833
|
+
canvas.height = Math.max(1, Math.floor(height * dpr));
|
|
23834
|
+
canvas.style.width = `${width}px`;
|
|
23835
|
+
canvas.style.height = `${height}px`;
|
|
23836
|
+
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
|
|
23837
|
+
ctx.clearRect(0, 0, width, height);
|
|
23838
|
+
if (backgroundColor) {
|
|
23839
|
+
ctx.fillStyle = backgroundColor;
|
|
23840
|
+
ctx.fillRect(0, 0, width, height);
|
|
23841
|
+
}
|
|
23842
|
+
for (const shape of shapes) {
|
|
23843
|
+
drawShape(ctx, shape, width, height);
|
|
23844
|
+
}
|
|
23845
|
+
}, [width, height, backgroundColor, shapes]);
|
|
23846
|
+
useEffect(() => {
|
|
23847
|
+
draw();
|
|
23848
|
+
}, [draw]);
|
|
23849
|
+
useEffect(() => {
|
|
23850
|
+
if (!animate) return;
|
|
23851
|
+
const loop = () => {
|
|
23852
|
+
draw();
|
|
23853
|
+
animRef.current = requestAnimationFrame(loop);
|
|
23854
|
+
};
|
|
23855
|
+
animRef.current = requestAnimationFrame(loop);
|
|
23856
|
+
return () => cancelAnimationFrame(animRef.current);
|
|
23857
|
+
}, [animate, draw]);
|
|
23858
|
+
const handlePointerMove = useCallback(
|
|
23859
|
+
(e) => {
|
|
23860
|
+
if (!interactive) return;
|
|
23861
|
+
const idx = findShapeAt(e.clientX, e.clientY);
|
|
23862
|
+
if (idx !== hoverIndexRef.current) {
|
|
23863
|
+
hoverIndexRef.current = idx;
|
|
23864
|
+
if (idx >= 0) {
|
|
23865
|
+
const shape = shapes[idx];
|
|
23866
|
+
const payload = { id: shape.id, type: shape.type, index: idx };
|
|
23867
|
+
if (onShapeHover) onShapeHover(payload);
|
|
23868
|
+
else if (eventBus) eventBus.emit(`UI:SHAPE_HOVER`, payload);
|
|
23869
|
+
}
|
|
23870
|
+
}
|
|
23871
|
+
},
|
|
23872
|
+
[interactive, onShapeHover, eventBus, findShapeAt, shapes]
|
|
23873
|
+
);
|
|
23874
|
+
const handleClick = useCallback(
|
|
23875
|
+
(e) => {
|
|
23876
|
+
if (!interactive) return;
|
|
23877
|
+
const idx = findShapeAt(e.clientX, e.clientY);
|
|
23878
|
+
if (idx >= 0) {
|
|
23879
|
+
const shape = shapes[idx];
|
|
23880
|
+
const payload = { id: shape.id, type: shape.type, index: idx };
|
|
23881
|
+
if (onShapeClick) onShapeClick(payload);
|
|
23882
|
+
else if (eventBus) eventBus.emit(`UI:SHAPE_CLICK`, payload);
|
|
23883
|
+
}
|
|
23884
|
+
},
|
|
23885
|
+
[interactive, onShapeClick, eventBus, findShapeAt, shapes]
|
|
23886
|
+
);
|
|
23887
|
+
if (isLoading || error) {
|
|
23888
|
+
return /* @__PURE__ */ jsx(
|
|
23889
|
+
"div",
|
|
23890
|
+
{
|
|
23891
|
+
className: cn(
|
|
23892
|
+
"flex items-center justify-center rounded border border-border bg-surface",
|
|
23893
|
+
className
|
|
23894
|
+
),
|
|
23895
|
+
style: { width, height },
|
|
23896
|
+
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" })
|
|
23897
|
+
}
|
|
23898
|
+
);
|
|
23899
|
+
}
|
|
23900
|
+
return /* @__PURE__ */ jsx(
|
|
23901
|
+
"canvas",
|
|
23902
|
+
{
|
|
23903
|
+
ref: canvasRef,
|
|
23904
|
+
className: cn("block touch-none rounded border border-border", className),
|
|
23905
|
+
style: { width, height },
|
|
23906
|
+
onClick: handleClick,
|
|
23907
|
+
onPointerMove: handlePointerMove
|
|
23908
|
+
}
|
|
23909
|
+
);
|
|
23910
|
+
};
|
|
23911
|
+
}
|
|
23912
|
+
});
|
|
23821
23913
|
|
|
23822
23914
|
// components/core/atoms/index.ts
|
|
23823
23915
|
var init_atoms = __esm({
|
|
@@ -25303,91 +25395,6 @@ var init_ComponentPatterns = __esm({
|
|
|
25303
25395
|
AlertPattern.displayName = "AlertPattern";
|
|
25304
25396
|
}
|
|
25305
25397
|
});
|
|
25306
|
-
function parseValue(value) {
|
|
25307
|
-
if (value === "" || value == null) return { num: 0, prefix: "", suffix: "", decimals: 0 };
|
|
25308
|
-
const match = String(value).match(/^([^0-9]*)([0-9]+(?:\.[0-9]+)?)(.*)$/);
|
|
25309
|
-
if (!match) {
|
|
25310
|
-
return { num: 0, prefix: "", suffix: String(value), decimals: 0 };
|
|
25311
|
-
}
|
|
25312
|
-
const numStr = match[2];
|
|
25313
|
-
const decimalIdx = numStr.indexOf(".");
|
|
25314
|
-
const decimals = decimalIdx >= 0 ? numStr.length - decimalIdx - 1 : 0;
|
|
25315
|
-
return {
|
|
25316
|
-
prefix: match[1],
|
|
25317
|
-
num: parseFloat(numStr),
|
|
25318
|
-
suffix: match[3],
|
|
25319
|
-
decimals
|
|
25320
|
-
};
|
|
25321
|
-
}
|
|
25322
|
-
var AnimatedCounter;
|
|
25323
|
-
var init_AnimatedCounter = __esm({
|
|
25324
|
-
"components/core/molecules/AnimatedCounter.tsx"() {
|
|
25325
|
-
"use client";
|
|
25326
|
-
init_cn();
|
|
25327
|
-
init_Box();
|
|
25328
|
-
init_Typography();
|
|
25329
|
-
AnimatedCounter = ({
|
|
25330
|
-
value,
|
|
25331
|
-
label,
|
|
25332
|
-
duration = 1500,
|
|
25333
|
-
className
|
|
25334
|
-
}) => {
|
|
25335
|
-
const ref = useRef(null);
|
|
25336
|
-
const [displayValue, setDisplayValue] = useState("0");
|
|
25337
|
-
const [hasAnimated, setHasAnimated] = useState(false);
|
|
25338
|
-
const animate = useCallback(() => {
|
|
25339
|
-
const { num: num2, prefix, suffix, decimals } = parseValue(value);
|
|
25340
|
-
if (num2 === 0) {
|
|
25341
|
-
setDisplayValue(String(value));
|
|
25342
|
-
return;
|
|
25343
|
-
}
|
|
25344
|
-
const startTime = performance.now();
|
|
25345
|
-
const tick = (now2) => {
|
|
25346
|
-
const elapsed = now2 - startTime;
|
|
25347
|
-
const progress = Math.min(elapsed / duration, 1);
|
|
25348
|
-
const eased = 1 - Math.pow(1 - progress, 3);
|
|
25349
|
-
const current = eased * num2;
|
|
25350
|
-
setDisplayValue(`${prefix}${current.toFixed(decimals)}${suffix}`);
|
|
25351
|
-
if (progress < 1) {
|
|
25352
|
-
requestAnimationFrame(tick);
|
|
25353
|
-
} else {
|
|
25354
|
-
setDisplayValue(String(value));
|
|
25355
|
-
}
|
|
25356
|
-
};
|
|
25357
|
-
requestAnimationFrame(tick);
|
|
25358
|
-
}, [value, duration]);
|
|
25359
|
-
useEffect(() => {
|
|
25360
|
-
if (hasAnimated) return;
|
|
25361
|
-
const el = ref.current;
|
|
25362
|
-
if (!el) return;
|
|
25363
|
-
const observer2 = new IntersectionObserver(
|
|
25364
|
-
(entries) => {
|
|
25365
|
-
if (entries[0].isIntersecting) {
|
|
25366
|
-
setHasAnimated(true);
|
|
25367
|
-
animate();
|
|
25368
|
-
observer2.disconnect();
|
|
25369
|
-
}
|
|
25370
|
-
},
|
|
25371
|
-
{ threshold: 0.3 }
|
|
25372
|
-
);
|
|
25373
|
-
observer2.observe(el);
|
|
25374
|
-
return () => observer2.disconnect();
|
|
25375
|
-
}, [hasAnimated, animate]);
|
|
25376
|
-
return /* @__PURE__ */ jsxs(Box, { ref, className: cn("flex flex-col items-center gap-1 p-4", className), children: [
|
|
25377
|
-
/* @__PURE__ */ jsx(
|
|
25378
|
-
Typography,
|
|
25379
|
-
{
|
|
25380
|
-
variant: "h2",
|
|
25381
|
-
className: "text-primary font-bold tabular-nums",
|
|
25382
|
-
children: hasAnimated ? displayValue : "0"
|
|
25383
|
-
}
|
|
25384
|
-
),
|
|
25385
|
-
/* @__PURE__ */ jsx(Typography, { variant: "body2", color: "muted", className: "text-center", children: label })
|
|
25386
|
-
] });
|
|
25387
|
-
};
|
|
25388
|
-
AnimatedCounter.displayName = "AnimatedCounter";
|
|
25389
|
-
}
|
|
25390
|
-
});
|
|
25391
25398
|
var AuthLayout;
|
|
25392
25399
|
var init_AuthLayout = __esm({
|
|
25393
25400
|
"components/marketing/templates/AuthLayout.tsx"() {
|
|
@@ -25529,10 +25536,107 @@ var init_AuthLayout = __esm({
|
|
|
25529
25536
|
AuthLayout.displayName = "AuthLayout";
|
|
25530
25537
|
}
|
|
25531
25538
|
});
|
|
25539
|
+
var BiologyCanvas;
|
|
25540
|
+
var init_BiologyCanvas = __esm({
|
|
25541
|
+
"components/learning/molecules/BiologyCanvas.tsx"() {
|
|
25542
|
+
"use client";
|
|
25543
|
+
init_atoms();
|
|
25544
|
+
init_Stack();
|
|
25545
|
+
init_LearningCanvas();
|
|
25546
|
+
BiologyCanvas = ({
|
|
25547
|
+
className,
|
|
25548
|
+
width = 600,
|
|
25549
|
+
height = 400,
|
|
25550
|
+
title,
|
|
25551
|
+
backgroundColor,
|
|
25552
|
+
nodes = [],
|
|
25553
|
+
edges = [],
|
|
25554
|
+
shapes = [],
|
|
25555
|
+
interactive = false,
|
|
25556
|
+
animate = false,
|
|
25557
|
+
onShapeClick,
|
|
25558
|
+
isLoading,
|
|
25559
|
+
error
|
|
25560
|
+
}) => {
|
|
25561
|
+
const derivedShapes = useMemo(() => {
|
|
25562
|
+
const out = [];
|
|
25563
|
+
const nodeById = /* @__PURE__ */ new Map();
|
|
25564
|
+
for (const n of nodes) {
|
|
25565
|
+
if (n.id) nodeById.set(n.id, n);
|
|
25566
|
+
}
|
|
25567
|
+
for (const e of edges) {
|
|
25568
|
+
const a = nodeById.get(e.from);
|
|
25569
|
+
const b = nodeById.get(e.to);
|
|
25570
|
+
if (!a || !b) continue;
|
|
25571
|
+
out.push({
|
|
25572
|
+
type: "line",
|
|
25573
|
+
x1: a.x,
|
|
25574
|
+
y1: a.y,
|
|
25575
|
+
x2: b.x,
|
|
25576
|
+
y2: b.y,
|
|
25577
|
+
color: e.color ?? "#9ca3af",
|
|
25578
|
+
lineWidth: 2
|
|
25579
|
+
});
|
|
25580
|
+
if (e.label) {
|
|
25581
|
+
out.push({
|
|
25582
|
+
type: "text",
|
|
25583
|
+
x: (a.x + b.x) / 2 + 4,
|
|
25584
|
+
y: (a.y + b.y) / 2 - 4,
|
|
25585
|
+
text: e.label,
|
|
25586
|
+
color: "#374151",
|
|
25587
|
+
fontSize: 11
|
|
25588
|
+
});
|
|
25589
|
+
}
|
|
25590
|
+
}
|
|
25591
|
+
for (const n of nodes) {
|
|
25592
|
+
out.push({
|
|
25593
|
+
type: "circle",
|
|
25594
|
+
x: n.x,
|
|
25595
|
+
y: n.y,
|
|
25596
|
+
radius: n.radius ?? 16,
|
|
25597
|
+
color: n.color ?? "#16a34a",
|
|
25598
|
+
fill: `${n.color ?? "#16a34a"}33`,
|
|
25599
|
+
id: n.id
|
|
25600
|
+
});
|
|
25601
|
+
if (n.label) {
|
|
25602
|
+
out.push({
|
|
25603
|
+
type: "text",
|
|
25604
|
+
x: n.x,
|
|
25605
|
+
y: n.y + (n.radius ?? 16) + 14,
|
|
25606
|
+
text: n.label,
|
|
25607
|
+
color: "#111827",
|
|
25608
|
+
fontSize: 12,
|
|
25609
|
+
align: "center"
|
|
25610
|
+
});
|
|
25611
|
+
}
|
|
25612
|
+
}
|
|
25613
|
+
out.push(...shapes);
|
|
25614
|
+
return out;
|
|
25615
|
+
}, [nodes, edges, shapes]);
|
|
25616
|
+
return /* @__PURE__ */ jsx(Card, { className, children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
|
|
25617
|
+
title ? /* @__PURE__ */ jsx(Typography, { variant: "h4", children: title }) : null,
|
|
25618
|
+
/* @__PURE__ */ jsx(
|
|
25619
|
+
LearningCanvas,
|
|
25620
|
+
{
|
|
25621
|
+
width,
|
|
25622
|
+
height,
|
|
25623
|
+
backgroundColor,
|
|
25624
|
+
shapes: derivedShapes,
|
|
25625
|
+
interactive,
|
|
25626
|
+
animate,
|
|
25627
|
+
onShapeClick,
|
|
25628
|
+
isLoading,
|
|
25629
|
+
error
|
|
25630
|
+
}
|
|
25631
|
+
)
|
|
25632
|
+
] }) });
|
|
25633
|
+
};
|
|
25634
|
+
}
|
|
25635
|
+
});
|
|
25532
25636
|
|
|
25533
|
-
// node_modules
|
|
25637
|
+
// node_modules/katex/dist/katex.min.css
|
|
25534
25638
|
var init_katex_min = __esm({
|
|
25535
|
-
"node_modules
|
|
25639
|
+
"node_modules/katex/dist/katex.min.css"() {
|
|
25536
25640
|
}
|
|
25537
25641
|
});
|
|
25538
25642
|
var Tabs;
|
|
@@ -29925,129 +30029,6 @@ var init_Breadcrumb = __esm({
|
|
|
29925
30029
|
Breadcrumb.displayName = "Breadcrumb";
|
|
29926
30030
|
}
|
|
29927
30031
|
});
|
|
29928
|
-
function useSafeEventBus2() {
|
|
29929
|
-
try {
|
|
29930
|
-
return useEventBus();
|
|
29931
|
-
} catch {
|
|
29932
|
-
return { emit: () => {
|
|
29933
|
-
}, on: () => () => {
|
|
29934
|
-
}, once: () => {
|
|
29935
|
-
} };
|
|
29936
|
-
}
|
|
29937
|
-
}
|
|
29938
|
-
var log5, lookStyles4, ButtonGroup;
|
|
29939
|
-
var init_ButtonGroup = __esm({
|
|
29940
|
-
"components/core/molecules/ButtonGroup.tsx"() {
|
|
29941
|
-
"use client";
|
|
29942
|
-
init_cn();
|
|
29943
|
-
init_atoms();
|
|
29944
|
-
init_useEventBus();
|
|
29945
|
-
log5 = createLogger("almadar:ui:button-group");
|
|
29946
|
-
lookStyles4 = {
|
|
29947
|
-
"right-aligned-buttons": "",
|
|
29948
|
-
"floating-bar": "fixed bottom-section left-1/2 -translate-x-1/2 shadow-elevation-toast bg-card p-card-sm rounded-container",
|
|
29949
|
-
"inline-row": "gap-2 inline-flex",
|
|
29950
|
-
"dropdown-menu": "[&>button:not(:first-child)]:hidden",
|
|
29951
|
-
"command-palette-trigger": "[&>button:not(:first-child)]:hidden"
|
|
29952
|
-
};
|
|
29953
|
-
ButtonGroup = ({
|
|
29954
|
-
children,
|
|
29955
|
-
primary,
|
|
29956
|
-
secondary,
|
|
29957
|
-
variant = "default",
|
|
29958
|
-
orientation = "horizontal",
|
|
29959
|
-
className,
|
|
29960
|
-
// Filter-group pattern props (entity and filters are used for schema-driven filtering)
|
|
29961
|
-
entity: _entity,
|
|
29962
|
-
filters,
|
|
29963
|
-
look = "right-aligned-buttons"
|
|
29964
|
-
}) => {
|
|
29965
|
-
const eventBus = useSafeEventBus2();
|
|
29966
|
-
const variantClasses2 = {
|
|
29967
|
-
default: "gap-0",
|
|
29968
|
-
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",
|
|
29969
|
-
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"
|
|
29970
|
-
};
|
|
29971
|
-
const orientationClasses = {
|
|
29972
|
-
horizontal: "flex-row",
|
|
29973
|
-
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"
|
|
29974
|
-
};
|
|
29975
|
-
const handleActionClick = (action) => {
|
|
29976
|
-
if (action.event) {
|
|
29977
|
-
eventBus.emit("UI:DISPATCH", { event: action.event });
|
|
29978
|
-
}
|
|
29979
|
-
if (action.navigatesTo) {
|
|
29980
|
-
eventBus.emit("UI:NAVIGATE", { url: action.navigatesTo });
|
|
29981
|
-
}
|
|
29982
|
-
};
|
|
29983
|
-
const renderFormActions = () => {
|
|
29984
|
-
const buttons = [];
|
|
29985
|
-
if (secondary) {
|
|
29986
|
-
secondary.forEach((action, index) => {
|
|
29987
|
-
buttons.push(
|
|
29988
|
-
/* @__PURE__ */ jsx(
|
|
29989
|
-
Button,
|
|
29990
|
-
{
|
|
29991
|
-
type: action.actionType === "submit" ? "submit" : "button",
|
|
29992
|
-
variant: action.variant || "ghost",
|
|
29993
|
-
onClick: () => handleActionClick(action),
|
|
29994
|
-
children: action.label
|
|
29995
|
-
},
|
|
29996
|
-
`secondary-${index}`
|
|
29997
|
-
)
|
|
29998
|
-
);
|
|
29999
|
-
});
|
|
30000
|
-
}
|
|
30001
|
-
if (primary) {
|
|
30002
|
-
const isSubmit = primary.actionType === "submit";
|
|
30003
|
-
buttons.push(
|
|
30004
|
-
/* @__PURE__ */ jsx(
|
|
30005
|
-
Button,
|
|
30006
|
-
{
|
|
30007
|
-
type: isSubmit ? "submit" : "button",
|
|
30008
|
-
variant: primary.variant || "primary",
|
|
30009
|
-
onClick: () => handleActionClick(primary),
|
|
30010
|
-
"data-testid": isSubmit ? "form-submit" : void 0,
|
|
30011
|
-
children: primary.label
|
|
30012
|
-
},
|
|
30013
|
-
"primary"
|
|
30014
|
-
)
|
|
30015
|
-
);
|
|
30016
|
-
}
|
|
30017
|
-
return buttons;
|
|
30018
|
-
};
|
|
30019
|
-
const renderFilters = () => {
|
|
30020
|
-
if (!filters || filters.length === 0) return null;
|
|
30021
|
-
return filters.map((filter, index) => /* @__PURE__ */ jsx(
|
|
30022
|
-
Button,
|
|
30023
|
-
{
|
|
30024
|
-
variant: "ghost",
|
|
30025
|
-
onClick: () => {
|
|
30026
|
-
log5.debug("Filter clicked", { field: filter.field });
|
|
30027
|
-
},
|
|
30028
|
-
children: filter.label
|
|
30029
|
-
},
|
|
30030
|
-
`filter-${filter.field}-${index}`
|
|
30031
|
-
));
|
|
30032
|
-
};
|
|
30033
|
-
return /* @__PURE__ */ jsx(
|
|
30034
|
-
"div",
|
|
30035
|
-
{
|
|
30036
|
-
className: cn(
|
|
30037
|
-
"inline-flex gap-2",
|
|
30038
|
-
variantClasses2[variant],
|
|
30039
|
-
orientationClasses[orientation],
|
|
30040
|
-
lookStyles4[look],
|
|
30041
|
-
className
|
|
30042
|
-
),
|
|
30043
|
-
role: "group",
|
|
30044
|
-
children: children || renderFilters() || renderFormActions()
|
|
30045
|
-
}
|
|
30046
|
-
);
|
|
30047
|
-
};
|
|
30048
|
-
ButtonGroup.displayName = "ButtonGroup";
|
|
30049
|
-
}
|
|
30050
|
-
});
|
|
30051
30032
|
function useSwipeGesture(callbacks, options = {}) {
|
|
30052
30033
|
const { threshold = 50, velocityThreshold = 0.3, preventDefault = false } = options;
|
|
30053
30034
|
const startX = useRef(0);
|
|
@@ -30857,7 +30838,7 @@ var init_CardGrid = __esm({
|
|
|
30857
30838
|
CardGrid.displayName = "CardGrid";
|
|
30858
30839
|
}
|
|
30859
30840
|
});
|
|
30860
|
-
function
|
|
30841
|
+
function useSafeEventBus2() {
|
|
30861
30842
|
try {
|
|
30862
30843
|
return useEventBus();
|
|
30863
30844
|
} catch {
|
|
@@ -30893,7 +30874,7 @@ var init_Carousel = __esm({
|
|
|
30893
30874
|
const [activeIndex, setActiveIndex] = useState(0);
|
|
30894
30875
|
const scrollRef = useRef(null);
|
|
30895
30876
|
const autoPlayRef = useRef(null);
|
|
30896
|
-
const eventBus =
|
|
30877
|
+
const eventBus = useSafeEventBus2();
|
|
30897
30878
|
const { t } = useTranslate();
|
|
30898
30879
|
const safeItems = items ?? [];
|
|
30899
30880
|
const totalSlides = safeItems.length;
|
|
@@ -32141,6 +32122,122 @@ var init_ChatBar = __esm({
|
|
|
32141
32122
|
ChatBar.displayName = "ChatBar";
|
|
32142
32123
|
}
|
|
32143
32124
|
});
|
|
32125
|
+
var ChemistryCanvas;
|
|
32126
|
+
var init_ChemistryCanvas = __esm({
|
|
32127
|
+
"components/learning/molecules/ChemistryCanvas.tsx"() {
|
|
32128
|
+
"use client";
|
|
32129
|
+
init_atoms();
|
|
32130
|
+
init_Stack();
|
|
32131
|
+
init_LearningCanvas();
|
|
32132
|
+
ChemistryCanvas = ({
|
|
32133
|
+
className,
|
|
32134
|
+
width = 600,
|
|
32135
|
+
height = 400,
|
|
32136
|
+
title,
|
|
32137
|
+
backgroundColor,
|
|
32138
|
+
atoms = [],
|
|
32139
|
+
bonds = [],
|
|
32140
|
+
arrows = [],
|
|
32141
|
+
shapes = [],
|
|
32142
|
+
interactive = false,
|
|
32143
|
+
animate = false,
|
|
32144
|
+
onShapeClick,
|
|
32145
|
+
isLoading,
|
|
32146
|
+
error
|
|
32147
|
+
}) => {
|
|
32148
|
+
const derivedShapes = useMemo(() => {
|
|
32149
|
+
const out = [];
|
|
32150
|
+
const atomById = /* @__PURE__ */ new Map();
|
|
32151
|
+
for (const a of atoms) {
|
|
32152
|
+
if (a.id) atomById.set(a.id, a);
|
|
32153
|
+
}
|
|
32154
|
+
for (const b of bonds) {
|
|
32155
|
+
const a = atomById.get(b.from);
|
|
32156
|
+
const c = atomById.get(b.to);
|
|
32157
|
+
if (!a || !c) continue;
|
|
32158
|
+
const color = b.color ?? "#6b7280";
|
|
32159
|
+
const strokeWidth = b.type === "double" ? 4 : b.type === "triple" ? 6 : 2;
|
|
32160
|
+
out.push({
|
|
32161
|
+
type: "line",
|
|
32162
|
+
x1: a.x,
|
|
32163
|
+
y1: a.y,
|
|
32164
|
+
x2: c.x,
|
|
32165
|
+
y2: c.y,
|
|
32166
|
+
color,
|
|
32167
|
+
lineWidth: strokeWidth
|
|
32168
|
+
});
|
|
32169
|
+
}
|
|
32170
|
+
for (const a of arrows) {
|
|
32171
|
+
const angle = (a.angle ?? 0) * (Math.PI / 180);
|
|
32172
|
+
const len = a.length ?? 60;
|
|
32173
|
+
const x2 = a.x + Math.cos(angle) * len;
|
|
32174
|
+
const y2 = a.y + Math.sin(angle) * len;
|
|
32175
|
+
out.push({
|
|
32176
|
+
type: "arrow",
|
|
32177
|
+
x1: a.x,
|
|
32178
|
+
y1: a.y,
|
|
32179
|
+
x2,
|
|
32180
|
+
y2,
|
|
32181
|
+
color: a.color ?? "#dc2626",
|
|
32182
|
+
lineWidth: 2
|
|
32183
|
+
});
|
|
32184
|
+
if (a.label) {
|
|
32185
|
+
out.push({
|
|
32186
|
+
type: "text",
|
|
32187
|
+
x: (a.x + x2) / 2,
|
|
32188
|
+
y: (a.y + y2) / 2 - 10,
|
|
32189
|
+
text: a.label,
|
|
32190
|
+
color: "#111827",
|
|
32191
|
+
fontSize: 12,
|
|
32192
|
+
align: "center"
|
|
32193
|
+
});
|
|
32194
|
+
}
|
|
32195
|
+
}
|
|
32196
|
+
for (const a of atoms) {
|
|
32197
|
+
out.push({
|
|
32198
|
+
type: "circle",
|
|
32199
|
+
x: a.x,
|
|
32200
|
+
y: a.y,
|
|
32201
|
+
radius: a.radius ?? 14,
|
|
32202
|
+
color: a.color ?? "#2563eb",
|
|
32203
|
+
fill: a.color ?? "#2563eb",
|
|
32204
|
+
id: a.id
|
|
32205
|
+
});
|
|
32206
|
+
if (a.element) {
|
|
32207
|
+
out.push({
|
|
32208
|
+
type: "text",
|
|
32209
|
+
x: a.x,
|
|
32210
|
+
y: a.y,
|
|
32211
|
+
text: a.element,
|
|
32212
|
+
color: "#ffffff",
|
|
32213
|
+
fontSize: 12,
|
|
32214
|
+
align: "center"
|
|
32215
|
+
});
|
|
32216
|
+
}
|
|
32217
|
+
}
|
|
32218
|
+
out.push(...shapes);
|
|
32219
|
+
return out;
|
|
32220
|
+
}, [atoms, bonds, arrows, shapes]);
|
|
32221
|
+
return /* @__PURE__ */ jsx(Card, { className, children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
|
|
32222
|
+
title ? /* @__PURE__ */ jsx(Typography, { variant: "h4", children: title }) : null,
|
|
32223
|
+
/* @__PURE__ */ jsx(
|
|
32224
|
+
LearningCanvas,
|
|
32225
|
+
{
|
|
32226
|
+
width,
|
|
32227
|
+
height,
|
|
32228
|
+
backgroundColor,
|
|
32229
|
+
shapes: derivedShapes,
|
|
32230
|
+
interactive,
|
|
32231
|
+
animate,
|
|
32232
|
+
onShapeClick,
|
|
32233
|
+
isLoading,
|
|
32234
|
+
error
|
|
32235
|
+
}
|
|
32236
|
+
)
|
|
32237
|
+
] }) });
|
|
32238
|
+
};
|
|
32239
|
+
}
|
|
32240
|
+
});
|
|
32144
32241
|
var CodeRunnerPanel;
|
|
32145
32242
|
var init_CodeRunnerPanel = __esm({
|
|
32146
32243
|
"components/core/organisms/CodeRunnerPanel.tsx"() {
|
|
@@ -34229,7 +34326,7 @@ function DataGrid({
|
|
|
34229
34326
|
/* @__PURE__ */ jsx(
|
|
34230
34327
|
Box,
|
|
34231
34328
|
{
|
|
34232
|
-
className: cn("grid", gapStyles5[gap], scrollX ? "grid-flow-col overflow-x-auto" : colsClass,
|
|
34329
|
+
className: cn("grid", gapStyles5[gap], scrollX ? "grid-flow-col overflow-x-auto" : colsClass, lookStyles4[look], className),
|
|
34233
34330
|
style: scrollX ? { gridAutoFlow: "column", gridAutoColumns: `minmax(${minCardWidth}px, 1fr)` } : gridTemplateColumns ? { gridTemplateColumns } : void 0,
|
|
34234
34331
|
children: data.map((item, index) => {
|
|
34235
34332
|
const itemData = item;
|
|
@@ -34424,7 +34521,7 @@ function DataGrid({
|
|
|
34424
34521
|
] })
|
|
34425
34522
|
);
|
|
34426
34523
|
}
|
|
34427
|
-
var dataGridLog, BADGE_VARIANTS, gapStyles5,
|
|
34524
|
+
var dataGridLog, BADGE_VARIANTS, gapStyles5, lookStyles4;
|
|
34428
34525
|
var init_DataGrid = __esm({
|
|
34429
34526
|
"components/core/molecules/DataGrid.tsx"() {
|
|
34430
34527
|
"use client";
|
|
@@ -34459,7 +34556,7 @@ var init_DataGrid = __esm({
|
|
|
34459
34556
|
lg: "gap-6",
|
|
34460
34557
|
xl: "gap-8"
|
|
34461
34558
|
};
|
|
34462
|
-
|
|
34559
|
+
lookStyles4 = {
|
|
34463
34560
|
dense: "gap-2 [&>*]:p-card-sm",
|
|
34464
34561
|
spacious: "gap-8 [&>*]:p-card-lg",
|
|
34465
34562
|
striped: "[&>*:nth-child(even)]:bg-muted/30",
|
|
@@ -34886,6 +34983,133 @@ var init_DataList = __esm({
|
|
|
34886
34983
|
DataList.displayName = "DataList";
|
|
34887
34984
|
}
|
|
34888
34985
|
});
|
|
34986
|
+
var FormSection, FormLayout, FormActions;
|
|
34987
|
+
var init_FormSection = __esm({
|
|
34988
|
+
"components/core/molecules/FormSection.tsx"() {
|
|
34989
|
+
"use client";
|
|
34990
|
+
init_cn();
|
|
34991
|
+
init_atoms();
|
|
34992
|
+
init_Box();
|
|
34993
|
+
init_Typography();
|
|
34994
|
+
init_Button();
|
|
34995
|
+
init_Stack();
|
|
34996
|
+
init_Icon();
|
|
34997
|
+
init_useEventBus();
|
|
34998
|
+
FormSection = ({
|
|
34999
|
+
title,
|
|
35000
|
+
description,
|
|
35001
|
+
children,
|
|
35002
|
+
collapsible = false,
|
|
35003
|
+
defaultCollapsed = false,
|
|
35004
|
+
card = false,
|
|
35005
|
+
columns = 1,
|
|
35006
|
+
className
|
|
35007
|
+
}) => {
|
|
35008
|
+
const [collapsed, setCollapsed] = React114__default.useState(defaultCollapsed);
|
|
35009
|
+
const { t } = useTranslate();
|
|
35010
|
+
const eventBus = useEventBus();
|
|
35011
|
+
const gridClass = {
|
|
35012
|
+
1: "grid-cols-1",
|
|
35013
|
+
2: "grid-cols-1 md:grid-cols-2",
|
|
35014
|
+
3: "grid-cols-1 md:grid-cols-2 lg:grid-cols-3"
|
|
35015
|
+
}[columns];
|
|
35016
|
+
React114__default.useCallback(() => {
|
|
35017
|
+
if (collapsible) {
|
|
35018
|
+
setCollapsed((prev) => !prev);
|
|
35019
|
+
eventBus.emit("UI:TOGGLE_COLLAPSE", { collapsed: !collapsed });
|
|
35020
|
+
}
|
|
35021
|
+
}, [collapsible, collapsed, eventBus]);
|
|
35022
|
+
const content = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
35023
|
+
(title || description) && /* @__PURE__ */ jsxs(VStack, { gap: "xs", className: "mb-4", children: [
|
|
35024
|
+
title && /* @__PURE__ */ jsxs(
|
|
35025
|
+
HStack,
|
|
35026
|
+
{
|
|
35027
|
+
justify: "between",
|
|
35028
|
+
align: "center",
|
|
35029
|
+
className: cn(collapsible && "cursor-pointer"),
|
|
35030
|
+
action: collapsible ? "TOGGLE_COLLAPSE" : void 0,
|
|
35031
|
+
children: [
|
|
35032
|
+
/* @__PURE__ */ jsx(Typography, { variant: "h3", weight: "semibold", children: title }),
|
|
35033
|
+
collapsible && /* @__PURE__ */ jsx(
|
|
35034
|
+
Button,
|
|
35035
|
+
{
|
|
35036
|
+
variant: "ghost",
|
|
35037
|
+
size: "sm",
|
|
35038
|
+
action: "TOGGLE_COLLAPSE",
|
|
35039
|
+
children: /* @__PURE__ */ jsx(
|
|
35040
|
+
Icon,
|
|
35041
|
+
{
|
|
35042
|
+
icon: ChevronDown,
|
|
35043
|
+
size: "sm",
|
|
35044
|
+
className: cn(
|
|
35045
|
+
"text-muted-foreground transition-transform",
|
|
35046
|
+
collapsed && "rotate-180"
|
|
35047
|
+
)
|
|
35048
|
+
}
|
|
35049
|
+
)
|
|
35050
|
+
}
|
|
35051
|
+
)
|
|
35052
|
+
]
|
|
35053
|
+
}
|
|
35054
|
+
),
|
|
35055
|
+
description && /* @__PURE__ */ jsx(Typography, { variant: "small", color: "secondary", children: description })
|
|
35056
|
+
] }),
|
|
35057
|
+
(!collapsible || !collapsed) && /* @__PURE__ */ jsx(Box, { className: cn("grid gap-4", gridClass), children })
|
|
35058
|
+
] });
|
|
35059
|
+
if (card) {
|
|
35060
|
+
return /* @__PURE__ */ jsx(Card, { className: cn("p-6", className), children: content });
|
|
35061
|
+
}
|
|
35062
|
+
return /* @__PURE__ */ jsx(Box, { className, children: content });
|
|
35063
|
+
};
|
|
35064
|
+
FormSection.displayName = "FormSection";
|
|
35065
|
+
FormLayout = ({
|
|
35066
|
+
children,
|
|
35067
|
+
dividers = true,
|
|
35068
|
+
className
|
|
35069
|
+
}) => {
|
|
35070
|
+
return /* @__PURE__ */ jsx(
|
|
35071
|
+
VStack,
|
|
35072
|
+
{
|
|
35073
|
+
gap: "lg",
|
|
35074
|
+
className: cn(
|
|
35075
|
+
dividers && "[&>*+*]:pt-8 [&>*+*]:border-t [&>*+*]:border-border",
|
|
35076
|
+
className
|
|
35077
|
+
),
|
|
35078
|
+
children
|
|
35079
|
+
}
|
|
35080
|
+
);
|
|
35081
|
+
};
|
|
35082
|
+
FormLayout.displayName = "FormLayout";
|
|
35083
|
+
FormActions = ({
|
|
35084
|
+
children,
|
|
35085
|
+
sticky = false,
|
|
35086
|
+
align = "right",
|
|
35087
|
+
className
|
|
35088
|
+
}) => {
|
|
35089
|
+
const alignClass2 = {
|
|
35090
|
+
left: "justify-start",
|
|
35091
|
+
right: "justify-end",
|
|
35092
|
+
between: "justify-between",
|
|
35093
|
+
center: "justify-center"
|
|
35094
|
+
}[align];
|
|
35095
|
+
return /* @__PURE__ */ jsx(
|
|
35096
|
+
HStack,
|
|
35097
|
+
{
|
|
35098
|
+
gap: "sm",
|
|
35099
|
+
align: "center",
|
|
35100
|
+
className: cn(
|
|
35101
|
+
"pt-6 border-t border-border",
|
|
35102
|
+
alignClass2,
|
|
35103
|
+
sticky && "sticky bottom-0 bg-card py-4 -mx-6 px-6 shadow-[0_-4px_6px_-1px_rgb(0,0,0,0.05)]",
|
|
35104
|
+
className
|
|
35105
|
+
),
|
|
35106
|
+
children
|
|
35107
|
+
}
|
|
35108
|
+
);
|
|
35109
|
+
};
|
|
35110
|
+
FormActions.displayName = "FormActions";
|
|
35111
|
+
}
|
|
35112
|
+
});
|
|
34889
35113
|
function fileIcon(name) {
|
|
34890
35114
|
const ext = name.split(".").pop()?.toLowerCase() ?? "";
|
|
34891
35115
|
switch (ext) {
|
|
@@ -35047,6 +35271,129 @@ var init_FormField = __esm({
|
|
|
35047
35271
|
FormField.displayName = "FormField";
|
|
35048
35272
|
}
|
|
35049
35273
|
});
|
|
35274
|
+
function useSafeEventBus3() {
|
|
35275
|
+
try {
|
|
35276
|
+
return useEventBus();
|
|
35277
|
+
} catch {
|
|
35278
|
+
return { emit: () => {
|
|
35279
|
+
}, on: () => () => {
|
|
35280
|
+
}, once: () => {
|
|
35281
|
+
} };
|
|
35282
|
+
}
|
|
35283
|
+
}
|
|
35284
|
+
var log5, lookStyles5, ButtonGroup;
|
|
35285
|
+
var init_ButtonGroup = __esm({
|
|
35286
|
+
"components/core/molecules/ButtonGroup.tsx"() {
|
|
35287
|
+
"use client";
|
|
35288
|
+
init_cn();
|
|
35289
|
+
init_atoms();
|
|
35290
|
+
init_useEventBus();
|
|
35291
|
+
log5 = createLogger("almadar:ui:button-group");
|
|
35292
|
+
lookStyles5 = {
|
|
35293
|
+
"right-aligned-buttons": "",
|
|
35294
|
+
"floating-bar": "fixed bottom-section left-1/2 -translate-x-1/2 shadow-elevation-toast bg-card p-card-sm rounded-container",
|
|
35295
|
+
"inline-row": "gap-2 inline-flex",
|
|
35296
|
+
"dropdown-menu": "[&>button:not(:first-child)]:hidden",
|
|
35297
|
+
"command-palette-trigger": "[&>button:not(:first-child)]:hidden"
|
|
35298
|
+
};
|
|
35299
|
+
ButtonGroup = ({
|
|
35300
|
+
children,
|
|
35301
|
+
primary,
|
|
35302
|
+
secondary,
|
|
35303
|
+
variant = "default",
|
|
35304
|
+
orientation = "horizontal",
|
|
35305
|
+
className,
|
|
35306
|
+
// Filter-group pattern props (entity and filters are used for schema-driven filtering)
|
|
35307
|
+
entity: _entity,
|
|
35308
|
+
filters,
|
|
35309
|
+
look = "right-aligned-buttons"
|
|
35310
|
+
}) => {
|
|
35311
|
+
const eventBus = useSafeEventBus3();
|
|
35312
|
+
const variantClasses2 = {
|
|
35313
|
+
default: "gap-0",
|
|
35314
|
+
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",
|
|
35315
|
+
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"
|
|
35316
|
+
};
|
|
35317
|
+
const orientationClasses = {
|
|
35318
|
+
horizontal: "flex-row",
|
|
35319
|
+
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"
|
|
35320
|
+
};
|
|
35321
|
+
const handleActionClick = (action) => {
|
|
35322
|
+
if (action.event) {
|
|
35323
|
+
eventBus.emit("UI:DISPATCH", { event: action.event });
|
|
35324
|
+
}
|
|
35325
|
+
if (action.navigatesTo) {
|
|
35326
|
+
eventBus.emit("UI:NAVIGATE", { url: action.navigatesTo });
|
|
35327
|
+
}
|
|
35328
|
+
};
|
|
35329
|
+
const renderFormActions = () => {
|
|
35330
|
+
const buttons = [];
|
|
35331
|
+
if (secondary) {
|
|
35332
|
+
secondary.forEach((action, index) => {
|
|
35333
|
+
buttons.push(
|
|
35334
|
+
/* @__PURE__ */ jsx(
|
|
35335
|
+
Button,
|
|
35336
|
+
{
|
|
35337
|
+
type: action.actionType === "submit" ? "submit" : "button",
|
|
35338
|
+
variant: action.variant || "ghost",
|
|
35339
|
+
onClick: () => handleActionClick(action),
|
|
35340
|
+
children: action.label
|
|
35341
|
+
},
|
|
35342
|
+
`secondary-${index}`
|
|
35343
|
+
)
|
|
35344
|
+
);
|
|
35345
|
+
});
|
|
35346
|
+
}
|
|
35347
|
+
if (primary) {
|
|
35348
|
+
const isSubmit = primary.actionType === "submit";
|
|
35349
|
+
buttons.push(
|
|
35350
|
+
/* @__PURE__ */ jsx(
|
|
35351
|
+
Button,
|
|
35352
|
+
{
|
|
35353
|
+
type: isSubmit ? "submit" : "button",
|
|
35354
|
+
variant: primary.variant || "primary",
|
|
35355
|
+
onClick: () => handleActionClick(primary),
|
|
35356
|
+
"data-testid": isSubmit ? "form-submit" : void 0,
|
|
35357
|
+
children: primary.label
|
|
35358
|
+
},
|
|
35359
|
+
"primary"
|
|
35360
|
+
)
|
|
35361
|
+
);
|
|
35362
|
+
}
|
|
35363
|
+
return buttons;
|
|
35364
|
+
};
|
|
35365
|
+
const renderFilters = () => {
|
|
35366
|
+
if (!filters || filters.length === 0) return null;
|
|
35367
|
+
return filters.map((filter, index) => /* @__PURE__ */ jsx(
|
|
35368
|
+
Button,
|
|
35369
|
+
{
|
|
35370
|
+
variant: "ghost",
|
|
35371
|
+
onClick: () => {
|
|
35372
|
+
log5.debug("Filter clicked", { field: filter.field });
|
|
35373
|
+
},
|
|
35374
|
+
children: filter.label
|
|
35375
|
+
},
|
|
35376
|
+
`filter-${filter.field}-${index}`
|
|
35377
|
+
));
|
|
35378
|
+
};
|
|
35379
|
+
return /* @__PURE__ */ jsx(
|
|
35380
|
+
"div",
|
|
35381
|
+
{
|
|
35382
|
+
className: cn(
|
|
35383
|
+
"inline-flex gap-2",
|
|
35384
|
+
variantClasses2[variant],
|
|
35385
|
+
orientationClasses[orientation],
|
|
35386
|
+
lookStyles5[look],
|
|
35387
|
+
className
|
|
35388
|
+
),
|
|
35389
|
+
role: "group",
|
|
35390
|
+
children: children || renderFilters() || renderFormActions()
|
|
35391
|
+
}
|
|
35392
|
+
);
|
|
35393
|
+
};
|
|
35394
|
+
ButtonGroup.displayName = "ButtonGroup";
|
|
35395
|
+
}
|
|
35396
|
+
});
|
|
35050
35397
|
function getOrCreateStore(query) {
|
|
35051
35398
|
if (!queryStores.has(query)) {
|
|
35052
35399
|
queryStores.set(query, {
|
|
@@ -37588,6 +37935,234 @@ var init_ProgressDots = __esm({
|
|
|
37588
37935
|
ProgressDots.displayName = "ProgressDots";
|
|
37589
37936
|
}
|
|
37590
37937
|
});
|
|
37938
|
+
var MathCanvas;
|
|
37939
|
+
var init_MathCanvas = __esm({
|
|
37940
|
+
"components/learning/molecules/MathCanvas.tsx"() {
|
|
37941
|
+
"use client";
|
|
37942
|
+
init_atoms();
|
|
37943
|
+
init_Stack();
|
|
37944
|
+
init_LearningCanvas();
|
|
37945
|
+
MathCanvas = ({
|
|
37946
|
+
className,
|
|
37947
|
+
width = 600,
|
|
37948
|
+
height = 400,
|
|
37949
|
+
title,
|
|
37950
|
+
xMin = -10,
|
|
37951
|
+
xMax = 10,
|
|
37952
|
+
yMin = -10,
|
|
37953
|
+
yMax = 10,
|
|
37954
|
+
showAxes = true,
|
|
37955
|
+
showGrid = true,
|
|
37956
|
+
gridStep = 1,
|
|
37957
|
+
curves = [],
|
|
37958
|
+
points = [],
|
|
37959
|
+
vectors = [],
|
|
37960
|
+
shapes = [],
|
|
37961
|
+
interactive = false,
|
|
37962
|
+
animate = false,
|
|
37963
|
+
onShapeClick,
|
|
37964
|
+
isLoading,
|
|
37965
|
+
error
|
|
37966
|
+
}) => {
|
|
37967
|
+
const derivedShapes = useMemo(() => {
|
|
37968
|
+
const out = [];
|
|
37969
|
+
const margin = 24;
|
|
37970
|
+
const plotW = width - margin * 2;
|
|
37971
|
+
const plotH = height - margin * 2;
|
|
37972
|
+
const mapX = (x) => margin + (x - xMin) / (xMax - xMin) * plotW;
|
|
37973
|
+
const mapY = (y) => height - (margin + (y - yMin) / (yMax - yMin) * plotH);
|
|
37974
|
+
if (showGrid) {
|
|
37975
|
+
for (let x = Math.ceil(xMin / gridStep) * gridStep; x <= xMax; x += gridStep) {
|
|
37976
|
+
const px = mapX(x);
|
|
37977
|
+
out.push({ type: "line", x1: px, y1: margin, x2: px, y2: height - margin, color: "#e5e7eb", lineWidth: 1 });
|
|
37978
|
+
}
|
|
37979
|
+
for (let y = Math.ceil(yMin / gridStep) * gridStep; y <= yMax; y += gridStep) {
|
|
37980
|
+
const py = mapY(y);
|
|
37981
|
+
out.push({ type: "line", x1: margin, y1: py, x2: width - margin, y2: py, color: "#e5e7eb", lineWidth: 1 });
|
|
37982
|
+
}
|
|
37983
|
+
}
|
|
37984
|
+
if (showAxes) {
|
|
37985
|
+
const xAxisY = Math.max(margin, Math.min(height - margin, mapY(0)));
|
|
37986
|
+
const yAxisX = Math.max(margin, Math.min(width - margin, mapX(0)));
|
|
37987
|
+
out.push({ type: "line", x1: margin, y1: xAxisY, x2: width - margin, y2: xAxisY, color: "#374151", lineWidth: 2 });
|
|
37988
|
+
out.push({ type: "line", x1: yAxisX, y1: margin, x2: yAxisX, y2: height - margin, color: "#374151", lineWidth: 2 });
|
|
37989
|
+
}
|
|
37990
|
+
for (const curve of curves) {
|
|
37991
|
+
if (!curve.samples || curve.samples.length < 2) continue;
|
|
37992
|
+
for (let i = 1; i < curve.samples.length; i++) {
|
|
37993
|
+
const a = curve.samples[i - 1];
|
|
37994
|
+
const b = curve.samples[i];
|
|
37995
|
+
if (a.x < xMin || a.x > xMax || b.x < xMin || b.x > xMax) continue;
|
|
37996
|
+
out.push({
|
|
37997
|
+
type: "line",
|
|
37998
|
+
x1: mapX(a.x),
|
|
37999
|
+
y1: mapY(a.y),
|
|
38000
|
+
x2: mapX(b.x),
|
|
38001
|
+
y2: mapY(b.y),
|
|
38002
|
+
color: curve.color ?? "#2563eb",
|
|
38003
|
+
lineWidth: 2
|
|
38004
|
+
});
|
|
38005
|
+
}
|
|
38006
|
+
}
|
|
38007
|
+
for (const p2 of points) {
|
|
38008
|
+
if (p2.x < xMin || p2.x > xMax || p2.y < yMin || p2.y > yMax) continue;
|
|
38009
|
+
out.push({
|
|
38010
|
+
type: "circle",
|
|
38011
|
+
x: mapX(p2.x),
|
|
38012
|
+
y: mapY(p2.y),
|
|
38013
|
+
radius: p2.radius ?? 4,
|
|
38014
|
+
color: p2.color ?? "#dc2626",
|
|
38015
|
+
fill: p2.color ?? "#dc2626"
|
|
38016
|
+
});
|
|
38017
|
+
if (p2.label) {
|
|
38018
|
+
out.push({ type: "text", x: mapX(p2.x) + 8, y: mapY(p2.y) - 8, text: p2.label, color: "#111827", fontSize: 12 });
|
|
38019
|
+
}
|
|
38020
|
+
}
|
|
38021
|
+
for (const v of vectors) {
|
|
38022
|
+
if (v.x < xMin || v.x > xMax || v.y < yMin || v.y > yMax) continue;
|
|
38023
|
+
const x1 = mapX(v.x);
|
|
38024
|
+
const y1 = mapY(v.y);
|
|
38025
|
+
const x2 = mapX(v.x + v.vx);
|
|
38026
|
+
const y2 = mapY(v.y + v.vy);
|
|
38027
|
+
out.push({ type: "arrow", x1, y1, x2, y2, color: v.color ?? "#7c3aed", lineWidth: 2 });
|
|
38028
|
+
if (v.label) {
|
|
38029
|
+
out.push({ type: "text", x: x2 + 6, y: y2 - 6, text: v.label, color: "#111827", fontSize: 12 });
|
|
38030
|
+
}
|
|
38031
|
+
}
|
|
38032
|
+
out.push(...shapes);
|
|
38033
|
+
return out;
|
|
38034
|
+
}, [width, height, xMin, xMax, yMin, yMax, showAxes, showGrid, gridStep, curves, points, vectors, shapes]);
|
|
38035
|
+
return /* @__PURE__ */ jsx(Card, { className, children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
|
|
38036
|
+
title ? /* @__PURE__ */ jsx(Typography, { variant: "h4", children: title }) : null,
|
|
38037
|
+
/* @__PURE__ */ jsx(
|
|
38038
|
+
LearningCanvas,
|
|
38039
|
+
{
|
|
38040
|
+
width,
|
|
38041
|
+
height,
|
|
38042
|
+
shapes: derivedShapes,
|
|
38043
|
+
interactive,
|
|
38044
|
+
animate,
|
|
38045
|
+
onShapeClick,
|
|
38046
|
+
isLoading,
|
|
38047
|
+
error
|
|
38048
|
+
}
|
|
38049
|
+
)
|
|
38050
|
+
] }) });
|
|
38051
|
+
};
|
|
38052
|
+
}
|
|
38053
|
+
});
|
|
38054
|
+
var PhysicsCanvas;
|
|
38055
|
+
var init_PhysicsCanvas = __esm({
|
|
38056
|
+
"components/learning/molecules/PhysicsCanvas.tsx"() {
|
|
38057
|
+
"use client";
|
|
38058
|
+
init_atoms();
|
|
38059
|
+
init_Stack();
|
|
38060
|
+
init_LearningCanvas();
|
|
38061
|
+
PhysicsCanvas = ({
|
|
38062
|
+
className,
|
|
38063
|
+
width = 600,
|
|
38064
|
+
height = 400,
|
|
38065
|
+
title,
|
|
38066
|
+
backgroundColor,
|
|
38067
|
+
bodies = [],
|
|
38068
|
+
constraints = [],
|
|
38069
|
+
showVelocity = true,
|
|
38070
|
+
showForces = false,
|
|
38071
|
+
velocityScale = 20,
|
|
38072
|
+
forceScale = 20,
|
|
38073
|
+
shapes = [],
|
|
38074
|
+
interactive = false,
|
|
38075
|
+
animate = false,
|
|
38076
|
+
onShapeClick,
|
|
38077
|
+
isLoading,
|
|
38078
|
+
error
|
|
38079
|
+
}) => {
|
|
38080
|
+
const derivedShapes = useMemo(() => {
|
|
38081
|
+
const out = [];
|
|
38082
|
+
const bodyById = /* @__PURE__ */ new Map();
|
|
38083
|
+
for (const b of bodies) {
|
|
38084
|
+
if (b.id) bodyById.set(b.id, b);
|
|
38085
|
+
}
|
|
38086
|
+
for (const c of constraints) {
|
|
38087
|
+
const a = bodyById.get(c.from);
|
|
38088
|
+
const b = bodyById.get(c.to);
|
|
38089
|
+
if (!a || !b) continue;
|
|
38090
|
+
out.push({
|
|
38091
|
+
type: "line",
|
|
38092
|
+
x1: a.x,
|
|
38093
|
+
y1: a.y,
|
|
38094
|
+
x2: b.x,
|
|
38095
|
+
y2: b.y,
|
|
38096
|
+
color: c.color ?? "#9ca3af",
|
|
38097
|
+
lineWidth: 2
|
|
38098
|
+
});
|
|
38099
|
+
}
|
|
38100
|
+
for (const b of bodies) {
|
|
38101
|
+
out.push({
|
|
38102
|
+
type: "circle",
|
|
38103
|
+
x: b.x,
|
|
38104
|
+
y: b.y,
|
|
38105
|
+
radius: b.radius ?? 12,
|
|
38106
|
+
color: b.color ?? "#2563eb",
|
|
38107
|
+
fill: b.color ?? "#2563eb",
|
|
38108
|
+
id: b.id
|
|
38109
|
+
});
|
|
38110
|
+
if (b.label) {
|
|
38111
|
+
out.push({
|
|
38112
|
+
type: "text",
|
|
38113
|
+
x: b.x + (b.radius ?? 12) + 6,
|
|
38114
|
+
y: b.y - (b.radius ?? 12) - 6,
|
|
38115
|
+
text: b.label,
|
|
38116
|
+
color: "#111827",
|
|
38117
|
+
fontSize: 12
|
|
38118
|
+
});
|
|
38119
|
+
}
|
|
38120
|
+
if (showVelocity && b.vx != null && b.vy != null && (b.vx !== 0 || b.vy !== 0)) {
|
|
38121
|
+
out.push({
|
|
38122
|
+
type: "arrow",
|
|
38123
|
+
x1: b.x,
|
|
38124
|
+
y1: b.y,
|
|
38125
|
+
x2: b.x + b.vx * velocityScale,
|
|
38126
|
+
y2: b.y + b.vy * velocityScale,
|
|
38127
|
+
color: "#16a34a",
|
|
38128
|
+
lineWidth: 2
|
|
38129
|
+
});
|
|
38130
|
+
}
|
|
38131
|
+
if (showForces && b.fx != null && b.fy != null && (b.fx !== 0 || b.fy !== 0)) {
|
|
38132
|
+
out.push({
|
|
38133
|
+
type: "arrow",
|
|
38134
|
+
x1: b.x,
|
|
38135
|
+
y1: b.y,
|
|
38136
|
+
x2: b.x + b.fx * forceScale,
|
|
38137
|
+
y2: b.y + b.fy * forceScale,
|
|
38138
|
+
color: "#dc2626",
|
|
38139
|
+
lineWidth: 2
|
|
38140
|
+
});
|
|
38141
|
+
}
|
|
38142
|
+
}
|
|
38143
|
+
out.push(...shapes);
|
|
38144
|
+
return out;
|
|
38145
|
+
}, [bodies, constraints, showVelocity, showForces, velocityScale, forceScale, shapes]);
|
|
38146
|
+
return /* @__PURE__ */ jsx(Card, { className, children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
|
|
38147
|
+
title ? /* @__PURE__ */ jsx(Typography, { variant: "h4", children: title }) : null,
|
|
38148
|
+
/* @__PURE__ */ jsx(
|
|
38149
|
+
LearningCanvas,
|
|
38150
|
+
{
|
|
38151
|
+
width,
|
|
38152
|
+
height,
|
|
38153
|
+
backgroundColor,
|
|
38154
|
+
shapes: derivedShapes,
|
|
38155
|
+
interactive,
|
|
38156
|
+
animate,
|
|
38157
|
+
onShapeClick,
|
|
38158
|
+
isLoading,
|
|
38159
|
+
error
|
|
38160
|
+
}
|
|
38161
|
+
)
|
|
38162
|
+
] }) });
|
|
38163
|
+
};
|
|
38164
|
+
}
|
|
38165
|
+
});
|
|
37591
38166
|
function resolveNodeColor(node, groups) {
|
|
37592
38167
|
if (node.color) return node.color;
|
|
37593
38168
|
if (node.group) {
|
|
@@ -37959,13 +38534,13 @@ var init_MapView = __esm({
|
|
|
37959
38534
|
shadowSize: [41, 41]
|
|
37960
38535
|
});
|
|
37961
38536
|
L.Marker.prototype.options.icon = defaultIcon;
|
|
37962
|
-
const { useEffect:
|
|
38537
|
+
const { useEffect: useEffect79, useRef: useRef79, useCallback: useCallback118, useState: useState115 } = React114__default;
|
|
37963
38538
|
const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
|
|
37964
38539
|
const { useEventBus: useEventBus2 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
|
|
37965
38540
|
function MapUpdater({ centerLat, centerLng, zoom }) {
|
|
37966
38541
|
const map = useMap();
|
|
37967
|
-
const prevRef =
|
|
37968
|
-
|
|
38542
|
+
const prevRef = useRef79({ centerLat, centerLng, zoom });
|
|
38543
|
+
useEffect79(() => {
|
|
37969
38544
|
const prev = prevRef.current;
|
|
37970
38545
|
if (prev.centerLat !== centerLat || prev.centerLng !== centerLng || prev.zoom !== zoom) {
|
|
37971
38546
|
map.setView([centerLat, centerLng], zoom);
|
|
@@ -37976,7 +38551,7 @@ var init_MapView = __esm({
|
|
|
37976
38551
|
}
|
|
37977
38552
|
function MapClickHandler({ onMapClick }) {
|
|
37978
38553
|
const map = useMap();
|
|
37979
|
-
|
|
38554
|
+
useEffect79(() => {
|
|
37980
38555
|
if (!onMapClick) return;
|
|
37981
38556
|
const handler = (e) => {
|
|
37982
38557
|
onMapClick(e.latlng.lat, e.latlng.lng);
|
|
@@ -46008,7 +46583,7 @@ function getGroupColor(group, groups) {
|
|
|
46008
46583
|
const idx = groups.indexOf(group);
|
|
46009
46584
|
return GROUP_COLORS2[idx % GROUP_COLORS2.length];
|
|
46010
46585
|
}
|
|
46011
|
-
function
|
|
46586
|
+
function resolveColor3(color, el) {
|
|
46012
46587
|
const m = /^var\((--[^,)]+)(?:,\s*([^)]+))?\)$/.exec(color.trim());
|
|
46013
46588
|
if (!m) return color;
|
|
46014
46589
|
const resolved = getComputedStyle(el).getPropertyValue(m[1]).trim();
|
|
@@ -46269,7 +46844,7 @@ var init_GraphCanvas = __esm({
|
|
|
46269
46844
|
const w = logicalW;
|
|
46270
46845
|
const h = height;
|
|
46271
46846
|
const nodes = nodesRef.current;
|
|
46272
|
-
const accentColor =
|
|
46847
|
+
const accentColor = resolveColor3("var(--color-accent)", canvas);
|
|
46273
46848
|
const dpr = typeof window !== "undefined" && window.devicePixelRatio || 1;
|
|
46274
46849
|
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
|
|
46275
46850
|
ctx.clearRect(0, 0, w, h);
|
|
@@ -46308,7 +46883,7 @@ var init_GraphCanvas = __esm({
|
|
|
46308
46883
|
ctx.globalAlpha = 1;
|
|
46309
46884
|
for (const node of nodes) {
|
|
46310
46885
|
const size = node.size || 8;
|
|
46311
|
-
const color =
|
|
46886
|
+
const color = resolveColor3(node.color || getGroupColor(node.group, groups), canvas);
|
|
46312
46887
|
const isHovered = hoveredNode === node.id;
|
|
46313
46888
|
const isSelected = selectedNodeId !== void 0 && node.id === selectedNodeId;
|
|
46314
46889
|
ctx.globalAlpha = hoveredNode && !neighbors.has(node.id) ? 0.2 : 1;
|
|
@@ -53206,6 +53781,7 @@ var init_component_registry_generated = __esm({
|
|
|
53206
53781
|
init_BattleBoard();
|
|
53207
53782
|
init_BattleTemplate();
|
|
53208
53783
|
init_BehaviorView();
|
|
53784
|
+
init_BiologyCanvas();
|
|
53209
53785
|
init_BloomQuizBlock();
|
|
53210
53786
|
init_BoardgameBoard();
|
|
53211
53787
|
init_BookChapterView();
|
|
@@ -53217,7 +53793,6 @@ var init_component_registry_generated = __esm({
|
|
|
53217
53793
|
init_BranchingLogicBuilder();
|
|
53218
53794
|
init_Breadcrumb();
|
|
53219
53795
|
init_BuilderBoard();
|
|
53220
|
-
init_ButtonGroup();
|
|
53221
53796
|
init_CTABanner();
|
|
53222
53797
|
init_CalendarGrid();
|
|
53223
53798
|
init_Canvas2D();
|
|
@@ -53236,6 +53811,7 @@ var init_component_registry_generated = __esm({
|
|
|
53236
53811
|
init_ChartLegend();
|
|
53237
53812
|
init_ChatBar();
|
|
53238
53813
|
init_Checkbox();
|
|
53814
|
+
init_ChemistryCanvas();
|
|
53239
53815
|
init_ChoiceButton();
|
|
53240
53816
|
init_CityBuilderBoard();
|
|
53241
53817
|
init_CityBuilderTemplate();
|
|
@@ -53294,9 +53870,9 @@ var init_component_registry_generated = __esm({
|
|
|
53294
53870
|
init_FlipContainer();
|
|
53295
53871
|
init_FloatingActionButton();
|
|
53296
53872
|
init_Form();
|
|
53873
|
+
init_FormSection();
|
|
53297
53874
|
init_FormField();
|
|
53298
53875
|
init_FormSectionHeader();
|
|
53299
|
-
init_GameAudioProvider();
|
|
53300
53876
|
init_GameAudioToggle();
|
|
53301
53877
|
init_GameCard();
|
|
53302
53878
|
init_GameHud();
|
|
@@ -53327,6 +53903,7 @@ var init_component_registry_generated = __esm({
|
|
|
53327
53903
|
init_JazariStateMachine();
|
|
53328
53904
|
init_LandingPageTemplate();
|
|
53329
53905
|
init_LawReferenceTooltip();
|
|
53906
|
+
init_LearningCanvas();
|
|
53330
53907
|
init_Lightbox();
|
|
53331
53908
|
init_LikertScale();
|
|
53332
53909
|
init_LineChart();
|
|
@@ -53335,9 +53912,11 @@ var init_component_registry_generated = __esm({
|
|
|
53335
53912
|
init_MapView();
|
|
53336
53913
|
init_MarkdownContent();
|
|
53337
53914
|
init_MarketingFooter();
|
|
53915
|
+
init_MarketingStatCard();
|
|
53338
53916
|
init_MasterDetail();
|
|
53339
53917
|
init_MasterDetailLayout();
|
|
53340
53918
|
init_MatchPuzzleBoard();
|
|
53919
|
+
init_MathCanvas();
|
|
53341
53920
|
init_MatrixQuestion();
|
|
53342
53921
|
init_MediaGallery();
|
|
53343
53922
|
init_Menu();
|
|
@@ -53356,6 +53935,7 @@ var init_component_registry_generated = __esm({
|
|
|
53356
53935
|
init_PageHeader();
|
|
53357
53936
|
init_Pagination();
|
|
53358
53937
|
init_PatternTile();
|
|
53938
|
+
init_PhysicsCanvas();
|
|
53359
53939
|
init_PinballBoard();
|
|
53360
53940
|
init_PirateBoard();
|
|
53361
53941
|
init_PlatformerBoard();
|
|
@@ -53532,6 +54112,7 @@ var init_component_registry_generated = __esm({
|
|
|
53532
54112
|
"BattleBoard": BattleBoard,
|
|
53533
54113
|
"BattleTemplate": BattleTemplate,
|
|
53534
54114
|
"BehaviorView": BehaviorView,
|
|
54115
|
+
"BiologyCanvas": BiologyCanvas,
|
|
53535
54116
|
"BloomQuizBlock": BloomQuizBlock,
|
|
53536
54117
|
"BoardgameBoard": BoardgameBoard,
|
|
53537
54118
|
"BookChapterView": BookChapterView,
|
|
@@ -53545,7 +54126,6 @@ var init_component_registry_generated = __esm({
|
|
|
53545
54126
|
"Breadcrumb": Breadcrumb,
|
|
53546
54127
|
"BuilderBoard": BuilderBoard,
|
|
53547
54128
|
"Button": ButtonPattern,
|
|
53548
|
-
"ButtonGroup": ButtonGroup,
|
|
53549
54129
|
"ButtonPattern": ButtonPattern,
|
|
53550
54130
|
"CTABanner": CTABanner,
|
|
53551
54131
|
"CalendarGrid": CalendarGrid,
|
|
@@ -53565,6 +54145,7 @@ var init_component_registry_generated = __esm({
|
|
|
53565
54145
|
"ChartLegend": ChartLegend,
|
|
53566
54146
|
"ChatBar": ChatBar,
|
|
53567
54147
|
"Checkbox": Checkbox,
|
|
54148
|
+
"ChemistryCanvas": ChemistryCanvas,
|
|
53568
54149
|
"ChoiceButton": ChoiceButton,
|
|
53569
54150
|
"CityBuilderBoard": CityBuilderBoard,
|
|
53570
54151
|
"CityBuilderTemplate": CityBuilderTemplate,
|
|
@@ -53625,9 +54206,10 @@ var init_component_registry_generated = __esm({
|
|
|
53625
54206
|
"FlipContainer": FlipContainer,
|
|
53626
54207
|
"FloatingActionButton": FloatingActionButton,
|
|
53627
54208
|
"Form": Form,
|
|
54209
|
+
"FormActions": FormActions,
|
|
53628
54210
|
"FormField": FormField,
|
|
54211
|
+
"FormLayout": FormLayout,
|
|
53629
54212
|
"FormSectionHeader": FormSectionHeader,
|
|
53630
|
-
"GameAudioProvider": GameAudioProvider,
|
|
53631
54213
|
"GameAudioToggle": GameAudioToggle,
|
|
53632
54214
|
"GameBoard3D": GameBoard3D,
|
|
53633
54215
|
"GameCanvas3D": GameCanvas3D,
|
|
@@ -53665,6 +54247,7 @@ var init_component_registry_generated = __esm({
|
|
|
53665
54247
|
"LabelPattern": LabelPattern,
|
|
53666
54248
|
"LandingPageTemplate": LandingPageTemplate,
|
|
53667
54249
|
"LawReferenceTooltip": LawReferenceTooltip,
|
|
54250
|
+
"LearningCanvas": LearningCanvas,
|
|
53668
54251
|
"Lightbox": Lightbox,
|
|
53669
54252
|
"LikertScale": LikertScale,
|
|
53670
54253
|
"LineChart": LineChart2,
|
|
@@ -53673,9 +54256,11 @@ var init_component_registry_generated = __esm({
|
|
|
53673
54256
|
"MapView": MapView,
|
|
53674
54257
|
"MarkdownContent": MarkdownContent,
|
|
53675
54258
|
"MarketingFooter": MarketingFooter,
|
|
54259
|
+
"MarketingStatCard": MarketingStatCard,
|
|
53676
54260
|
"MasterDetail": MasterDetail,
|
|
53677
54261
|
"MasterDetailLayout": MasterDetailLayout,
|
|
53678
54262
|
"MatchPuzzleBoard": MatchPuzzleBoard,
|
|
54263
|
+
"MathCanvas": MathCanvas,
|
|
53679
54264
|
"MatrixQuestion": MatrixQuestion,
|
|
53680
54265
|
"MediaGallery": MediaGallery,
|
|
53681
54266
|
"Menu": Menu,
|
|
@@ -53694,6 +54279,7 @@ var init_component_registry_generated = __esm({
|
|
|
53694
54279
|
"PageHeader": PageHeader,
|
|
53695
54280
|
"Pagination": Pagination,
|
|
53696
54281
|
"PatternTile": PatternTile,
|
|
54282
|
+
"PhysicsCanvas": PhysicsCanvas,
|
|
53697
54283
|
"PinballBoard": PinballBoard,
|
|
53698
54284
|
"PirateBoard": PirateBoard,
|
|
53699
54285
|
"PlatformerBoard": PlatformerBoard,
|