@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.cjs
CHANGED
|
@@ -630,7 +630,7 @@ var init_AvlFieldType = __esm({
|
|
|
630
630
|
}
|
|
631
631
|
});
|
|
632
632
|
|
|
633
|
-
// node_modules
|
|
633
|
+
// node_modules/clsx/dist/clsx.mjs
|
|
634
634
|
function r(e) {
|
|
635
635
|
var t, f3, n = "";
|
|
636
636
|
if ("string" == typeof e || "number" == typeof e) n += e;
|
|
@@ -645,11 +645,11 @@ function clsx() {
|
|
|
645
645
|
return n;
|
|
646
646
|
}
|
|
647
647
|
var init_clsx = __esm({
|
|
648
|
-
"node_modules
|
|
648
|
+
"node_modules/clsx/dist/clsx.mjs"() {
|
|
649
649
|
}
|
|
650
650
|
});
|
|
651
651
|
|
|
652
|
-
// node_modules
|
|
652
|
+
// node_modules/tailwind-merge/dist/bundle-mjs.mjs
|
|
653
653
|
function twJoin() {
|
|
654
654
|
let index = 0;
|
|
655
655
|
let argument;
|
|
@@ -693,7 +693,7 @@ function createTailwindMerge(createConfigFirst, ...createConfigRest) {
|
|
|
693
693
|
}
|
|
694
694
|
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;
|
|
695
695
|
var init_bundle_mjs = __esm({
|
|
696
|
-
"node_modules
|
|
696
|
+
"node_modules/tailwind-merge/dist/bundle-mjs.mjs"() {
|
|
697
697
|
CLASS_PART_SEPARATOR = "-";
|
|
698
698
|
createClassGroupUtils = (config) => {
|
|
699
699
|
const classMap = createClassMap(config);
|
|
@@ -9419,6 +9419,66 @@ var init_RangeSlider = __esm({
|
|
|
9419
9419
|
RangeSlider.displayName = "RangeSlider";
|
|
9420
9420
|
}
|
|
9421
9421
|
});
|
|
9422
|
+
function easeOut(t) {
|
|
9423
|
+
return t * (2 - t);
|
|
9424
|
+
}
|
|
9425
|
+
var AnimatedCounter;
|
|
9426
|
+
var init_AnimatedCounter = __esm({
|
|
9427
|
+
"components/marketing/atoms/AnimatedCounter.tsx"() {
|
|
9428
|
+
"use client";
|
|
9429
|
+
init_cn();
|
|
9430
|
+
init_Typography();
|
|
9431
|
+
AnimatedCounter = ({
|
|
9432
|
+
value: rawValue,
|
|
9433
|
+
duration = 600,
|
|
9434
|
+
prefix,
|
|
9435
|
+
suffix,
|
|
9436
|
+
className
|
|
9437
|
+
}) => {
|
|
9438
|
+
const numericRaw = typeof rawValue === "number" ? rawValue : Number.parseFloat(String(rawValue ?? ""));
|
|
9439
|
+
const value = !Number.isNaN(numericRaw) ? numericRaw : 0;
|
|
9440
|
+
const [displayValue, setDisplayValue] = React114.useState(value);
|
|
9441
|
+
const previousValueRef = React114.useRef(value);
|
|
9442
|
+
const animationFrameRef = React114.useRef(null);
|
|
9443
|
+
React114.useEffect(() => {
|
|
9444
|
+
const from = previousValueRef.current;
|
|
9445
|
+
const to = value;
|
|
9446
|
+
previousValueRef.current = value;
|
|
9447
|
+
if (from === to) {
|
|
9448
|
+
setDisplayValue(to);
|
|
9449
|
+
return;
|
|
9450
|
+
}
|
|
9451
|
+
const startTime = performance.now();
|
|
9452
|
+
const diff = to - from;
|
|
9453
|
+
function animate(currentTime) {
|
|
9454
|
+
const elapsed = currentTime - startTime;
|
|
9455
|
+
const progress = Math.min(elapsed / duration, 1);
|
|
9456
|
+
const easedProgress = easeOut(progress);
|
|
9457
|
+
setDisplayValue(from + diff * easedProgress);
|
|
9458
|
+
if (progress < 1) {
|
|
9459
|
+
animationFrameRef.current = requestAnimationFrame(animate);
|
|
9460
|
+
} else {
|
|
9461
|
+
setDisplayValue(to);
|
|
9462
|
+
}
|
|
9463
|
+
}
|
|
9464
|
+
animationFrameRef.current = requestAnimationFrame(animate);
|
|
9465
|
+
return () => {
|
|
9466
|
+
if (animationFrameRef.current !== null) {
|
|
9467
|
+
cancelAnimationFrame(animationFrameRef.current);
|
|
9468
|
+
}
|
|
9469
|
+
};
|
|
9470
|
+
}, [value, duration]);
|
|
9471
|
+
const decimalPlaces = Number.isInteger(value) ? 0 : String(value).split(".")[1]?.length ?? 0;
|
|
9472
|
+
const formattedValue = displayValue.toFixed(decimalPlaces);
|
|
9473
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(Typography, { variant: "h3", className: cn("tabular-nums", className), children: [
|
|
9474
|
+
prefix,
|
|
9475
|
+
formattedValue,
|
|
9476
|
+
suffix
|
|
9477
|
+
] });
|
|
9478
|
+
};
|
|
9479
|
+
AnimatedCounter.displayName = "AnimatedCounter";
|
|
9480
|
+
}
|
|
9481
|
+
});
|
|
9422
9482
|
function useInfiniteScroll(onLoadMore, options = {}) {
|
|
9423
9483
|
const { rootMargin = "200px", hasMore = true, isLoading = false } = options;
|
|
9424
9484
|
const observerRef = React114.useRef(null);
|
|
@@ -9715,6 +9775,34 @@ var init_SectionHeader = __esm({
|
|
|
9715
9775
|
SectionHeader.displayName = "SectionHeader";
|
|
9716
9776
|
}
|
|
9717
9777
|
});
|
|
9778
|
+
var sizeClasses6, MarketingStatCard;
|
|
9779
|
+
var init_MarketingStatCard = __esm({
|
|
9780
|
+
"components/marketing/atoms/MarketingStatCard.tsx"() {
|
|
9781
|
+
init_cn();
|
|
9782
|
+
init_Stack();
|
|
9783
|
+
init_Typography();
|
|
9784
|
+
sizeClasses6 = {
|
|
9785
|
+
sm: "text-2xl",
|
|
9786
|
+
md: "text-4xl",
|
|
9787
|
+
lg: "text-5xl"
|
|
9788
|
+
};
|
|
9789
|
+
MarketingStatCard = ({ value, label, size = "md", className }) => {
|
|
9790
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "xs", align: "center", className: cn(className), children: [
|
|
9791
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
9792
|
+
Typography,
|
|
9793
|
+
{
|
|
9794
|
+
weight: "bold",
|
|
9795
|
+
color: "primary",
|
|
9796
|
+
className: cn(sizeClasses6[size]),
|
|
9797
|
+
children: value
|
|
9798
|
+
}
|
|
9799
|
+
),
|
|
9800
|
+
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", color: "muted", children: label })
|
|
9801
|
+
] });
|
|
9802
|
+
};
|
|
9803
|
+
MarketingStatCard.displayName = "MarketingStatCard";
|
|
9804
|
+
}
|
|
9805
|
+
});
|
|
9718
9806
|
var backgroundClasses, paddingClasses, ContentSection;
|
|
9719
9807
|
var init_ContentSection = __esm({
|
|
9720
9808
|
"components/marketing/atoms/ContentSection.tsx"() {
|
|
@@ -15908,318 +15996,14 @@ var init_useCanvasEffects = __esm({
|
|
|
15908
15996
|
"use client";
|
|
15909
15997
|
}
|
|
15910
15998
|
});
|
|
15911
|
-
function pickPath(entry) {
|
|
15912
|
-
if (Array.isArray(entry.path)) {
|
|
15913
|
-
return entry.path[Math.floor(Math.random() * entry.path.length)];
|
|
15914
|
-
}
|
|
15915
|
-
return entry.path;
|
|
15916
|
-
}
|
|
15917
|
-
function useGameAudio({
|
|
15918
|
-
manifest,
|
|
15919
|
-
baseUrl = "",
|
|
15920
|
-
initialMuted = false,
|
|
15921
|
-
initialVolume = 1
|
|
15922
|
-
}) {
|
|
15923
|
-
const [muted, setMutedState] = React114.useState(initialMuted);
|
|
15924
|
-
const [masterVolume, setMasterVolumeState] = React114.useState(initialVolume);
|
|
15925
|
-
const mutedRef = React114.useRef(muted);
|
|
15926
|
-
const volumeRef = React114.useRef(masterVolume);
|
|
15927
|
-
const manifestRef = React114.useRef(manifest);
|
|
15928
|
-
mutedRef.current = muted;
|
|
15929
|
-
volumeRef.current = masterVolume;
|
|
15930
|
-
manifestRef.current = manifest;
|
|
15931
|
-
const poolsRef = React114.useRef(/* @__PURE__ */ new Map());
|
|
15932
|
-
const getOrCreateElement = React114.useCallback((key) => {
|
|
15933
|
-
const entry = manifestRef.current[key];
|
|
15934
|
-
if (!entry) return null;
|
|
15935
|
-
let pool = poolsRef.current.get(key);
|
|
15936
|
-
if (!pool) {
|
|
15937
|
-
pool = [];
|
|
15938
|
-
poolsRef.current.set(key, pool);
|
|
15939
|
-
}
|
|
15940
|
-
const maxSize = entry.poolSize ?? 1;
|
|
15941
|
-
for (const audio of pool) {
|
|
15942
|
-
if (audio.paused && (audio.ended || audio.currentTime === 0)) {
|
|
15943
|
-
return audio;
|
|
15944
|
-
}
|
|
15945
|
-
}
|
|
15946
|
-
if (pool.length < maxSize) {
|
|
15947
|
-
const src = baseUrl + pickPath(entry);
|
|
15948
|
-
const audio = new Audio(src);
|
|
15949
|
-
audio.loop = entry.loop ?? false;
|
|
15950
|
-
pool.push(audio);
|
|
15951
|
-
return audio;
|
|
15952
|
-
}
|
|
15953
|
-
if (!entry.loop) {
|
|
15954
|
-
let oldest = pool[0];
|
|
15955
|
-
for (const audio of pool) {
|
|
15956
|
-
if (audio.currentTime > oldest.currentTime) {
|
|
15957
|
-
oldest = audio;
|
|
15958
|
-
}
|
|
15959
|
-
}
|
|
15960
|
-
oldest.pause();
|
|
15961
|
-
oldest.currentTime = 0;
|
|
15962
|
-
return oldest;
|
|
15963
|
-
}
|
|
15964
|
-
return null;
|
|
15965
|
-
}, [baseUrl]);
|
|
15966
|
-
const play = React114.useCallback((key) => {
|
|
15967
|
-
if (mutedRef.current) return;
|
|
15968
|
-
const entry = manifestRef.current[key];
|
|
15969
|
-
if (!entry) return;
|
|
15970
|
-
const audio = getOrCreateElement(key);
|
|
15971
|
-
if (!audio) return;
|
|
15972
|
-
audio.volume = Math.min(1, (entry.volume ?? 1) * volumeRef.current);
|
|
15973
|
-
if (!entry.loop) {
|
|
15974
|
-
audio.currentTime = 0;
|
|
15975
|
-
}
|
|
15976
|
-
const promise = audio.play();
|
|
15977
|
-
if (promise) {
|
|
15978
|
-
promise.catch(() => {
|
|
15979
|
-
});
|
|
15980
|
-
}
|
|
15981
|
-
}, [getOrCreateElement]);
|
|
15982
|
-
const stop = React114.useCallback((key) => {
|
|
15983
|
-
const pool = poolsRef.current.get(key);
|
|
15984
|
-
if (!pool) return;
|
|
15985
|
-
for (const audio of pool) {
|
|
15986
|
-
audio.pause();
|
|
15987
|
-
audio.currentTime = 0;
|
|
15988
|
-
}
|
|
15989
|
-
}, []);
|
|
15990
|
-
const currentMusicKeyRef = React114.useRef(null);
|
|
15991
|
-
const currentMusicElRef = React114.useRef(null);
|
|
15992
|
-
const musicFadeRef = React114.useRef(null);
|
|
15993
|
-
const pendingMusicKeyRef = React114.useRef(null);
|
|
15994
|
-
const clearMusicFade = React114.useCallback(() => {
|
|
15995
|
-
if (musicFadeRef.current) {
|
|
15996
|
-
clearInterval(musicFadeRef.current);
|
|
15997
|
-
musicFadeRef.current = null;
|
|
15998
|
-
}
|
|
15999
|
-
}, []);
|
|
16000
|
-
const playMusic = React114.useCallback((key) => {
|
|
16001
|
-
if (key === currentMusicKeyRef.current) return;
|
|
16002
|
-
pendingMusicKeyRef.current = key;
|
|
16003
|
-
const entry = manifestRef.current[key];
|
|
16004
|
-
if (!entry) return;
|
|
16005
|
-
const fadeDurationMs = entry.crossfadeDurationMs ?? 1500;
|
|
16006
|
-
const stepMs = 50;
|
|
16007
|
-
const totalSteps = Math.max(1, fadeDurationMs / stepMs);
|
|
16008
|
-
const targetVolume = Math.min(1, (entry.volume ?? 1) * volumeRef.current);
|
|
16009
|
-
clearMusicFade();
|
|
16010
|
-
const src = baseUrl + (Array.isArray(entry.path) ? entry.path[0] : entry.path);
|
|
16011
|
-
const incoming = new Audio(src);
|
|
16012
|
-
incoming.loop = true;
|
|
16013
|
-
incoming.volume = 0;
|
|
16014
|
-
const outgoing = currentMusicElRef.current;
|
|
16015
|
-
const outgoingStartVol = outgoing?.volume ?? 0;
|
|
16016
|
-
currentMusicKeyRef.current = key;
|
|
16017
|
-
currentMusicElRef.current = incoming;
|
|
16018
|
-
if (!mutedRef.current) {
|
|
16019
|
-
incoming.play().catch(() => {
|
|
16020
|
-
currentMusicKeyRef.current = null;
|
|
16021
|
-
currentMusicElRef.current = outgoing;
|
|
16022
|
-
});
|
|
16023
|
-
}
|
|
16024
|
-
let step = 0;
|
|
16025
|
-
musicFadeRef.current = setInterval(() => {
|
|
16026
|
-
step++;
|
|
16027
|
-
const progress = Math.min(step / totalSteps, 1);
|
|
16028
|
-
incoming.volume = Math.min(1, targetVolume * progress);
|
|
16029
|
-
if (outgoing) {
|
|
16030
|
-
outgoing.volume = Math.max(0, outgoingStartVol * (1 - progress));
|
|
16031
|
-
}
|
|
16032
|
-
if (progress >= 1) {
|
|
16033
|
-
clearMusicFade();
|
|
16034
|
-
if (outgoing) {
|
|
16035
|
-
outgoing.pause();
|
|
16036
|
-
outgoing.src = "";
|
|
16037
|
-
}
|
|
16038
|
-
}
|
|
16039
|
-
}, stepMs);
|
|
16040
|
-
}, [baseUrl, clearMusicFade]);
|
|
16041
|
-
const stopMusic = React114.useCallback((fadeDurationMs = 1e3) => {
|
|
16042
|
-
const outgoing = currentMusicElRef.current;
|
|
16043
|
-
if (!outgoing) return;
|
|
16044
|
-
currentMusicKeyRef.current = null;
|
|
16045
|
-
currentMusicElRef.current = null;
|
|
16046
|
-
pendingMusicKeyRef.current = null;
|
|
16047
|
-
clearMusicFade();
|
|
16048
|
-
const startVolume = outgoing.volume;
|
|
16049
|
-
const stepMs = 50;
|
|
16050
|
-
const totalSteps = Math.max(1, fadeDurationMs / stepMs);
|
|
16051
|
-
let step = 0;
|
|
16052
|
-
musicFadeRef.current = setInterval(() => {
|
|
16053
|
-
step++;
|
|
16054
|
-
const progress = step / totalSteps;
|
|
16055
|
-
outgoing.volume = Math.max(0, startVolume * (1 - progress));
|
|
16056
|
-
if (progress >= 1) {
|
|
16057
|
-
clearMusicFade();
|
|
16058
|
-
outgoing.pause();
|
|
16059
|
-
outgoing.src = "";
|
|
16060
|
-
}
|
|
16061
|
-
}, stepMs);
|
|
16062
|
-
}, [clearMusicFade]);
|
|
16063
|
-
const stopAll = React114.useCallback(() => {
|
|
16064
|
-
for (const pool of poolsRef.current.values()) {
|
|
16065
|
-
for (const audio of pool) {
|
|
16066
|
-
audio.pause();
|
|
16067
|
-
audio.currentTime = 0;
|
|
16068
|
-
}
|
|
16069
|
-
}
|
|
16070
|
-
stopMusic(0);
|
|
16071
|
-
}, [stopMusic]);
|
|
16072
|
-
const setMuted = React114.useCallback((value) => {
|
|
16073
|
-
setMutedState(value);
|
|
16074
|
-
if (value) {
|
|
16075
|
-
for (const [key, pool] of poolsRef.current.entries()) {
|
|
16076
|
-
if (manifestRef.current[key]?.loop) {
|
|
16077
|
-
for (const audio of pool) {
|
|
16078
|
-
if (!audio.paused) audio.pause();
|
|
16079
|
-
}
|
|
16080
|
-
}
|
|
16081
|
-
}
|
|
16082
|
-
currentMusicElRef.current?.pause();
|
|
16083
|
-
} else {
|
|
16084
|
-
for (const [key, pool] of poolsRef.current.entries()) {
|
|
16085
|
-
const entry = manifestRef.current[key];
|
|
16086
|
-
if (entry?.loop && entry?.autostart) {
|
|
16087
|
-
for (const audio of pool) {
|
|
16088
|
-
if (audio.paused) audio.play().catch(() => {
|
|
16089
|
-
});
|
|
16090
|
-
}
|
|
16091
|
-
}
|
|
16092
|
-
}
|
|
16093
|
-
const musicEl = currentMusicElRef.current;
|
|
16094
|
-
if (musicEl) {
|
|
16095
|
-
musicEl.play().catch(() => {
|
|
16096
|
-
});
|
|
16097
|
-
}
|
|
16098
|
-
}
|
|
16099
|
-
}, []);
|
|
16100
|
-
const setMasterVolume = React114.useCallback((volume) => {
|
|
16101
|
-
const clamped = Math.max(0, Math.min(1, volume));
|
|
16102
|
-
setMasterVolumeState(clamped);
|
|
16103
|
-
for (const [key, pool] of poolsRef.current.entries()) {
|
|
16104
|
-
const entryVol = manifestRef.current[key]?.volume ?? 1;
|
|
16105
|
-
for (const audio of pool) {
|
|
16106
|
-
audio.volume = Math.min(1, entryVol * clamped);
|
|
16107
|
-
}
|
|
16108
|
-
}
|
|
16109
|
-
if (!musicFadeRef.current && currentMusicElRef.current) {
|
|
16110
|
-
const key = currentMusicKeyRef.current;
|
|
16111
|
-
const entryVol = key ? manifestRef.current[key]?.volume ?? 1 : 1;
|
|
16112
|
-
currentMusicElRef.current.volume = Math.min(1, entryVol * clamped);
|
|
16113
|
-
}
|
|
16114
|
-
}, []);
|
|
16115
|
-
const unlockedRef = React114.useRef(false);
|
|
16116
|
-
React114.useEffect(() => {
|
|
16117
|
-
const autoKeys = Object.keys(manifest).filter((k) => manifest[k].autostart);
|
|
16118
|
-
const hasPendingMusic = () => pendingMusicKeyRef.current !== null;
|
|
16119
|
-
const hasAutoStart = autoKeys.length > 0;
|
|
16120
|
-
if (!hasAutoStart && !hasPendingMusic()) return;
|
|
16121
|
-
const unlock = () => {
|
|
16122
|
-
if (unlockedRef.current) return;
|
|
16123
|
-
unlockedRef.current = true;
|
|
16124
|
-
if (!mutedRef.current) {
|
|
16125
|
-
for (const key of autoKeys) {
|
|
16126
|
-
play(key);
|
|
16127
|
-
}
|
|
16128
|
-
const pending = pendingMusicKeyRef.current;
|
|
16129
|
-
if (pending && pending !== currentMusicKeyRef.current) {
|
|
16130
|
-
playMusic(pending);
|
|
16131
|
-
}
|
|
16132
|
-
}
|
|
16133
|
-
};
|
|
16134
|
-
document.addEventListener("click", unlock, { once: true });
|
|
16135
|
-
document.addEventListener("keydown", unlock, { once: true });
|
|
16136
|
-
document.addEventListener("touchstart", unlock, { once: true });
|
|
16137
|
-
return () => {
|
|
16138
|
-
document.removeEventListener("click", unlock);
|
|
16139
|
-
document.removeEventListener("keydown", unlock);
|
|
16140
|
-
document.removeEventListener("touchstart", unlock);
|
|
16141
|
-
};
|
|
16142
|
-
}, [manifest, play, playMusic]);
|
|
16143
|
-
React114.useEffect(() => {
|
|
16144
|
-
return () => {
|
|
16145
|
-
clearMusicFade();
|
|
16146
|
-
for (const pool of poolsRef.current.values()) {
|
|
16147
|
-
for (const audio of pool) {
|
|
16148
|
-
audio.pause();
|
|
16149
|
-
audio.src = "";
|
|
16150
|
-
}
|
|
16151
|
-
}
|
|
16152
|
-
poolsRef.current.clear();
|
|
16153
|
-
if (currentMusicElRef.current) {
|
|
16154
|
-
currentMusicElRef.current.pause();
|
|
16155
|
-
currentMusicElRef.current.src = "";
|
|
16156
|
-
currentMusicElRef.current = null;
|
|
16157
|
-
}
|
|
16158
|
-
};
|
|
16159
|
-
}, [clearMusicFade]);
|
|
16160
|
-
return {
|
|
16161
|
-
play,
|
|
16162
|
-
stop,
|
|
16163
|
-
stopAll,
|
|
16164
|
-
playMusic,
|
|
16165
|
-
stopMusic,
|
|
16166
|
-
muted,
|
|
16167
|
-
setMuted,
|
|
16168
|
-
masterVolume,
|
|
16169
|
-
setMasterVolume
|
|
16170
|
-
};
|
|
16171
|
-
}
|
|
16172
15999
|
var init_useGameAudio = __esm({
|
|
16173
16000
|
"components/game/shared/hooks/useGameAudio.ts"() {
|
|
16174
16001
|
"use client";
|
|
16175
|
-
useGameAudio.displayName = "useGameAudio";
|
|
16176
16002
|
}
|
|
16177
16003
|
});
|
|
16178
16004
|
function useGameAudioContextOptional() {
|
|
16179
16005
|
return React114.useContext(GameAudioContext);
|
|
16180
16006
|
}
|
|
16181
|
-
function GameAudioProvider({
|
|
16182
|
-
manifest,
|
|
16183
|
-
baseUrl = "",
|
|
16184
|
-
children,
|
|
16185
|
-
initialMuted = false
|
|
16186
|
-
}) {
|
|
16187
|
-
const eventBus = useEventBus();
|
|
16188
|
-
const { play, stop, stopAll, playMusic, stopMusic, muted, setMuted, masterVolume, setMasterVolume } = useGameAudio({ manifest, baseUrl, initialMuted });
|
|
16189
|
-
React114.useEffect(() => {
|
|
16190
|
-
const unsubPlay = eventBus.on("UI:PLAY_SOUND", (event) => {
|
|
16191
|
-
const key = event.payload?.key;
|
|
16192
|
-
if (key) play(key);
|
|
16193
|
-
});
|
|
16194
|
-
const unsubStop = eventBus.on("UI:STOP_SOUND", (event) => {
|
|
16195
|
-
const key = event.payload?.key;
|
|
16196
|
-
if (key) {
|
|
16197
|
-
stop(key);
|
|
16198
|
-
} else {
|
|
16199
|
-
stopAll();
|
|
16200
|
-
}
|
|
16201
|
-
});
|
|
16202
|
-
const unsubChangeMusic = eventBus.on("UI:CHANGE_MUSIC", (event) => {
|
|
16203
|
-
const key = event.payload?.key;
|
|
16204
|
-
if (key) {
|
|
16205
|
-
playMusic(key);
|
|
16206
|
-
} else {
|
|
16207
|
-
stopMusic();
|
|
16208
|
-
}
|
|
16209
|
-
});
|
|
16210
|
-
const unsubStopMusic = eventBus.on("UI:STOP_MUSIC", () => {
|
|
16211
|
-
stopMusic();
|
|
16212
|
-
});
|
|
16213
|
-
return () => {
|
|
16214
|
-
unsubPlay();
|
|
16215
|
-
unsubStop();
|
|
16216
|
-
unsubChangeMusic();
|
|
16217
|
-
unsubStopMusic();
|
|
16218
|
-
};
|
|
16219
|
-
}, [eventBus, play, stop, stopAll, playMusic, stopMusic]);
|
|
16220
|
-
const value = { muted, setMuted, masterVolume, setMasterVolume, play, playMusic, stopMusic };
|
|
16221
|
-
return /* @__PURE__ */ jsxRuntime.jsx(GameAudioContext.Provider, { value, children });
|
|
16222
|
-
}
|
|
16223
16007
|
var GameAudioContext;
|
|
16224
16008
|
var init_GameAudioProvider = __esm({
|
|
16225
16009
|
"components/game/shared/providers/GameAudioProvider.tsx"() {
|
|
@@ -16228,7 +16012,6 @@ var init_GameAudioProvider = __esm({
|
|
|
16228
16012
|
init_useGameAudio();
|
|
16229
16013
|
GameAudioContext = React114.createContext(null);
|
|
16230
16014
|
GameAudioContext.displayName = "GameAudioContext";
|
|
16231
|
-
GameAudioProvider.displayName = "GameAudioProvider";
|
|
16232
16015
|
}
|
|
16233
16016
|
});
|
|
16234
16017
|
function GameAudioToggle({
|
|
@@ -23864,6 +23647,315 @@ var init_molecules = __esm({
|
|
|
23864
23647
|
init_puzzleObject();
|
|
23865
23648
|
}
|
|
23866
23649
|
});
|
|
23650
|
+
function resolveColor2(color, ctx, fallback) {
|
|
23651
|
+
if (!color) return fallback;
|
|
23652
|
+
if (color.startsWith("var(")) {
|
|
23653
|
+
ctx.canvas.style;
|
|
23654
|
+
const m = /^var\((--[^,)]+)(?:,\s*([^)]+))?\)$/.exec(color);
|
|
23655
|
+
if (m) {
|
|
23656
|
+
const computed = getComputedStyle(ctx.canvas).getPropertyValue(m[1]).trim();
|
|
23657
|
+
return computed || m[2]?.trim() || fallback;
|
|
23658
|
+
}
|
|
23659
|
+
}
|
|
23660
|
+
return color;
|
|
23661
|
+
}
|
|
23662
|
+
function shapeBounds(shape) {
|
|
23663
|
+
switch (shape.type) {
|
|
23664
|
+
case "line":
|
|
23665
|
+
case "arrow":
|
|
23666
|
+
if (shape.x1 == null || shape.y1 == null || shape.x2 == null || shape.y2 == null) return null;
|
|
23667
|
+
return {
|
|
23668
|
+
x: Math.min(shape.x1, shape.x2) - 6,
|
|
23669
|
+
y: Math.min(shape.y1, shape.y2) - 6,
|
|
23670
|
+
w: Math.abs(shape.x2 - shape.x1) + 12,
|
|
23671
|
+
h: Math.abs(shape.y2 - shape.y1) + 12
|
|
23672
|
+
};
|
|
23673
|
+
case "circle":
|
|
23674
|
+
if (shape.x == null || shape.y == null || shape.radius == null) return null;
|
|
23675
|
+
return {
|
|
23676
|
+
x: shape.x - shape.radius - 4,
|
|
23677
|
+
y: shape.y - shape.radius - 4,
|
|
23678
|
+
w: shape.radius * 2 + 8,
|
|
23679
|
+
h: shape.radius * 2 + 8
|
|
23680
|
+
};
|
|
23681
|
+
case "rect":
|
|
23682
|
+
if (shape.x == null || shape.y == null || shape.width == null || shape.height == null) return null;
|
|
23683
|
+
return { x: shape.x - 4, y: shape.y - 4, w: shape.width + 8, h: shape.height + 8 };
|
|
23684
|
+
case "polygon":
|
|
23685
|
+
if (!shape.points || shape.points.length === 0) return null;
|
|
23686
|
+
{
|
|
23687
|
+
const xs = shape.points.map((p2) => p2.x);
|
|
23688
|
+
const ys = shape.points.map((p2) => p2.y);
|
|
23689
|
+
const minX = Math.min(...xs);
|
|
23690
|
+
const minY = Math.min(...ys);
|
|
23691
|
+
return {
|
|
23692
|
+
x: minX - 4,
|
|
23693
|
+
y: minY - 4,
|
|
23694
|
+
w: Math.max(...xs) - minX + 8,
|
|
23695
|
+
h: Math.max(...ys) - minY + 8
|
|
23696
|
+
};
|
|
23697
|
+
}
|
|
23698
|
+
case "text":
|
|
23699
|
+
if (shape.x == null || shape.y == null) return null;
|
|
23700
|
+
return { x: shape.x - 4, y: shape.y - (shape.fontSize ?? 14) - 4, w: 120, h: (shape.fontSize ?? 14) + 8 };
|
|
23701
|
+
default:
|
|
23702
|
+
return null;
|
|
23703
|
+
}
|
|
23704
|
+
}
|
|
23705
|
+
function drawArrowHead(ctx, x1, y1, x2, y2, size) {
|
|
23706
|
+
const angle = Math.atan2(y2 - y1, x2 - x1);
|
|
23707
|
+
ctx.beginPath();
|
|
23708
|
+
ctx.moveTo(x2, y2);
|
|
23709
|
+
ctx.lineTo(x2 - size * Math.cos(angle - Math.PI / 6), y2 - size * Math.sin(angle - Math.PI / 6));
|
|
23710
|
+
ctx.lineTo(x2 - size * Math.cos(angle + Math.PI / 6), y2 - size * Math.sin(angle + Math.PI / 6));
|
|
23711
|
+
ctx.closePath();
|
|
23712
|
+
ctx.fill();
|
|
23713
|
+
}
|
|
23714
|
+
function drawShape(ctx, shape, width, height) {
|
|
23715
|
+
ctx.save();
|
|
23716
|
+
const opacity = shape.opacity ?? 1;
|
|
23717
|
+
ctx.globalAlpha = opacity;
|
|
23718
|
+
const stroke = resolveColor2(shape.color, ctx, "#333333");
|
|
23719
|
+
const fill = shape.fill ? resolveColor2(shape.fill, ctx, "#cccccc") : void 0;
|
|
23720
|
+
ctx.lineWidth = shape.lineWidth ?? 2;
|
|
23721
|
+
switch (shape.type) {
|
|
23722
|
+
case "grid": {
|
|
23723
|
+
const step = shape.step ?? 40;
|
|
23724
|
+
ctx.strokeStyle = stroke;
|
|
23725
|
+
ctx.globalAlpha = opacity * 0.25;
|
|
23726
|
+
ctx.lineWidth = 1;
|
|
23727
|
+
ctx.beginPath();
|
|
23728
|
+
for (let x = 0; x <= width; x += step) {
|
|
23729
|
+
ctx.moveTo(x, 0);
|
|
23730
|
+
ctx.lineTo(x, height);
|
|
23731
|
+
}
|
|
23732
|
+
for (let y = 0; y <= height; y += step) {
|
|
23733
|
+
ctx.moveTo(0, y);
|
|
23734
|
+
ctx.lineTo(width, y);
|
|
23735
|
+
}
|
|
23736
|
+
ctx.stroke();
|
|
23737
|
+
break;
|
|
23738
|
+
}
|
|
23739
|
+
case "axis": {
|
|
23740
|
+
const axis = shape.axis ?? "x";
|
|
23741
|
+
ctx.strokeStyle = stroke;
|
|
23742
|
+
ctx.lineWidth = shape.lineWidth ?? 2;
|
|
23743
|
+
ctx.beginPath();
|
|
23744
|
+
if (axis === "x") {
|
|
23745
|
+
ctx.moveTo(0, height / 2);
|
|
23746
|
+
ctx.lineTo(width, height / 2);
|
|
23747
|
+
} else {
|
|
23748
|
+
ctx.moveTo(width / 2, 0);
|
|
23749
|
+
ctx.lineTo(width / 2, height);
|
|
23750
|
+
}
|
|
23751
|
+
ctx.stroke();
|
|
23752
|
+
break;
|
|
23753
|
+
}
|
|
23754
|
+
case "line": {
|
|
23755
|
+
if (shape.x1 == null || shape.y1 == null || shape.x2 == null || shape.y2 == null) break;
|
|
23756
|
+
ctx.strokeStyle = stroke;
|
|
23757
|
+
ctx.beginPath();
|
|
23758
|
+
ctx.moveTo(shape.x1, shape.y1);
|
|
23759
|
+
ctx.lineTo(shape.x2, shape.y2);
|
|
23760
|
+
ctx.stroke();
|
|
23761
|
+
break;
|
|
23762
|
+
}
|
|
23763
|
+
case "arrow": {
|
|
23764
|
+
if (shape.x1 == null || shape.y1 == null || shape.x2 == null || shape.y2 == null) break;
|
|
23765
|
+
ctx.strokeStyle = stroke;
|
|
23766
|
+
ctx.fillStyle = stroke;
|
|
23767
|
+
ctx.beginPath();
|
|
23768
|
+
ctx.moveTo(shape.x1, shape.y1);
|
|
23769
|
+
ctx.lineTo(shape.x2, shape.y2);
|
|
23770
|
+
ctx.stroke();
|
|
23771
|
+
drawArrowHead(ctx, shape.x1, shape.y1, shape.x2, shape.y2, 10);
|
|
23772
|
+
break;
|
|
23773
|
+
}
|
|
23774
|
+
case "circle": {
|
|
23775
|
+
if (shape.x == null || shape.y == null || shape.radius == null) break;
|
|
23776
|
+
ctx.beginPath();
|
|
23777
|
+
ctx.arc(shape.x, shape.y, shape.radius, 0, Math.PI * 2);
|
|
23778
|
+
if (fill) {
|
|
23779
|
+
ctx.fillStyle = fill;
|
|
23780
|
+
ctx.fill();
|
|
23781
|
+
}
|
|
23782
|
+
ctx.strokeStyle = stroke;
|
|
23783
|
+
ctx.stroke();
|
|
23784
|
+
break;
|
|
23785
|
+
}
|
|
23786
|
+
case "rect": {
|
|
23787
|
+
if (shape.x == null || shape.y == null || shape.width == null || shape.height == null) break;
|
|
23788
|
+
if (fill) {
|
|
23789
|
+
ctx.fillStyle = fill;
|
|
23790
|
+
ctx.fillRect(shape.x, shape.y, shape.width, shape.height);
|
|
23791
|
+
}
|
|
23792
|
+
ctx.strokeStyle = stroke;
|
|
23793
|
+
ctx.strokeRect(shape.x, shape.y, shape.width, shape.height);
|
|
23794
|
+
break;
|
|
23795
|
+
}
|
|
23796
|
+
case "polygon": {
|
|
23797
|
+
if (!shape.points || shape.points.length < 2) break;
|
|
23798
|
+
ctx.beginPath();
|
|
23799
|
+
ctx.moveTo(shape.points[0].x, shape.points[0].y);
|
|
23800
|
+
for (let i = 1; i < shape.points.length; i++) {
|
|
23801
|
+
ctx.lineTo(shape.points[i].x, shape.points[i].y);
|
|
23802
|
+
}
|
|
23803
|
+
ctx.closePath();
|
|
23804
|
+
if (fill) {
|
|
23805
|
+
ctx.fillStyle = fill;
|
|
23806
|
+
ctx.fill();
|
|
23807
|
+
}
|
|
23808
|
+
ctx.strokeStyle = stroke;
|
|
23809
|
+
ctx.stroke();
|
|
23810
|
+
break;
|
|
23811
|
+
}
|
|
23812
|
+
case "path": {
|
|
23813
|
+
if (!shape.path) break;
|
|
23814
|
+
const p2 = new Path2D(shape.path);
|
|
23815
|
+
if (fill) {
|
|
23816
|
+
ctx.fillStyle = fill;
|
|
23817
|
+
ctx.fill(p2);
|
|
23818
|
+
}
|
|
23819
|
+
ctx.strokeStyle = stroke;
|
|
23820
|
+
ctx.stroke(p2);
|
|
23821
|
+
break;
|
|
23822
|
+
}
|
|
23823
|
+
case "text": {
|
|
23824
|
+
if (shape.x == null || shape.y == null || !shape.text) break;
|
|
23825
|
+
ctx.fillStyle = stroke;
|
|
23826
|
+
ctx.font = `${shape.fontSize ?? 14}px system-ui, sans-serif`;
|
|
23827
|
+
ctx.textAlign = shape.align ?? "left";
|
|
23828
|
+
ctx.textBaseline = "middle";
|
|
23829
|
+
ctx.fillText(shape.text, shape.x, shape.y);
|
|
23830
|
+
break;
|
|
23831
|
+
}
|
|
23832
|
+
}
|
|
23833
|
+
ctx.restore();
|
|
23834
|
+
}
|
|
23835
|
+
var LearningCanvas;
|
|
23836
|
+
var init_LearningCanvas = __esm({
|
|
23837
|
+
"components/learning/atoms/LearningCanvas.tsx"() {
|
|
23838
|
+
"use client";
|
|
23839
|
+
init_cn();
|
|
23840
|
+
init_useEventBus();
|
|
23841
|
+
LearningCanvas = ({
|
|
23842
|
+
className,
|
|
23843
|
+
width = 600,
|
|
23844
|
+
height = 400,
|
|
23845
|
+
backgroundColor,
|
|
23846
|
+
shapes = [],
|
|
23847
|
+
interactive = false,
|
|
23848
|
+
animate = false,
|
|
23849
|
+
onShapeClick,
|
|
23850
|
+
onShapeHover,
|
|
23851
|
+
isLoading,
|
|
23852
|
+
error
|
|
23853
|
+
}) => {
|
|
23854
|
+
const canvasRef = React114.useRef(null);
|
|
23855
|
+
const eventBus = useEventBus();
|
|
23856
|
+
const animRef = React114.useRef(0);
|
|
23857
|
+
const hoverIndexRef = React114.useRef(-1);
|
|
23858
|
+
const findShapeAt = React114.useCallback((clientX, clientY) => {
|
|
23859
|
+
const canvas = canvasRef.current;
|
|
23860
|
+
if (!canvas) return -1;
|
|
23861
|
+
const rect = canvas.getBoundingClientRect();
|
|
23862
|
+
const x = clientX - rect.left;
|
|
23863
|
+
const y = clientY - rect.top;
|
|
23864
|
+
for (let i = shapes.length - 1; i >= 0; i--) {
|
|
23865
|
+
const b = shapeBounds(shapes[i]);
|
|
23866
|
+
if (b && x >= b.x && x <= b.x + b.w && y >= b.y && y <= b.y + b.h) {
|
|
23867
|
+
return i;
|
|
23868
|
+
}
|
|
23869
|
+
}
|
|
23870
|
+
return -1;
|
|
23871
|
+
}, [shapes]);
|
|
23872
|
+
const draw = React114.useCallback(() => {
|
|
23873
|
+
const canvas = canvasRef.current;
|
|
23874
|
+
if (!canvas) return;
|
|
23875
|
+
const ctx = canvas.getContext("2d");
|
|
23876
|
+
if (!ctx) return;
|
|
23877
|
+
const dpr = typeof window !== "undefined" ? window.devicePixelRatio || 1 : 1;
|
|
23878
|
+
canvas.width = Math.max(1, Math.floor(width * dpr));
|
|
23879
|
+
canvas.height = Math.max(1, Math.floor(height * dpr));
|
|
23880
|
+
canvas.style.width = `${width}px`;
|
|
23881
|
+
canvas.style.height = `${height}px`;
|
|
23882
|
+
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
|
|
23883
|
+
ctx.clearRect(0, 0, width, height);
|
|
23884
|
+
if (backgroundColor) {
|
|
23885
|
+
ctx.fillStyle = backgroundColor;
|
|
23886
|
+
ctx.fillRect(0, 0, width, height);
|
|
23887
|
+
}
|
|
23888
|
+
for (const shape of shapes) {
|
|
23889
|
+
drawShape(ctx, shape, width, height);
|
|
23890
|
+
}
|
|
23891
|
+
}, [width, height, backgroundColor, shapes]);
|
|
23892
|
+
React114.useEffect(() => {
|
|
23893
|
+
draw();
|
|
23894
|
+
}, [draw]);
|
|
23895
|
+
React114.useEffect(() => {
|
|
23896
|
+
if (!animate) return;
|
|
23897
|
+
const loop = () => {
|
|
23898
|
+
draw();
|
|
23899
|
+
animRef.current = requestAnimationFrame(loop);
|
|
23900
|
+
};
|
|
23901
|
+
animRef.current = requestAnimationFrame(loop);
|
|
23902
|
+
return () => cancelAnimationFrame(animRef.current);
|
|
23903
|
+
}, [animate, draw]);
|
|
23904
|
+
const handlePointerMove = React114.useCallback(
|
|
23905
|
+
(e) => {
|
|
23906
|
+
if (!interactive) return;
|
|
23907
|
+
const idx = findShapeAt(e.clientX, e.clientY);
|
|
23908
|
+
if (idx !== hoverIndexRef.current) {
|
|
23909
|
+
hoverIndexRef.current = idx;
|
|
23910
|
+
if (idx >= 0) {
|
|
23911
|
+
const shape = shapes[idx];
|
|
23912
|
+
const payload = { id: shape.id, type: shape.type, index: idx };
|
|
23913
|
+
if (onShapeHover) onShapeHover(payload);
|
|
23914
|
+
else if (eventBus) eventBus.emit(`UI:SHAPE_HOVER`, payload);
|
|
23915
|
+
}
|
|
23916
|
+
}
|
|
23917
|
+
},
|
|
23918
|
+
[interactive, onShapeHover, eventBus, findShapeAt, shapes]
|
|
23919
|
+
);
|
|
23920
|
+
const handleClick = React114.useCallback(
|
|
23921
|
+
(e) => {
|
|
23922
|
+
if (!interactive) return;
|
|
23923
|
+
const idx = findShapeAt(e.clientX, e.clientY);
|
|
23924
|
+
if (idx >= 0) {
|
|
23925
|
+
const shape = shapes[idx];
|
|
23926
|
+
const payload = { id: shape.id, type: shape.type, index: idx };
|
|
23927
|
+
if (onShapeClick) onShapeClick(payload);
|
|
23928
|
+
else if (eventBus) eventBus.emit(`UI:SHAPE_CLICK`, payload);
|
|
23929
|
+
}
|
|
23930
|
+
},
|
|
23931
|
+
[interactive, onShapeClick, eventBus, findShapeAt, shapes]
|
|
23932
|
+
);
|
|
23933
|
+
if (isLoading || error) {
|
|
23934
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
23935
|
+
"div",
|
|
23936
|
+
{
|
|
23937
|
+
className: cn(
|
|
23938
|
+
"flex items-center justify-center rounded border border-border bg-surface",
|
|
23939
|
+
className
|
|
23940
|
+
),
|
|
23941
|
+
style: { width, height },
|
|
23942
|
+
children: error ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-destructive", children: error.message }) : /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-muted-foreground", children: "Loading canvas\u2026" })
|
|
23943
|
+
}
|
|
23944
|
+
);
|
|
23945
|
+
}
|
|
23946
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
23947
|
+
"canvas",
|
|
23948
|
+
{
|
|
23949
|
+
ref: canvasRef,
|
|
23950
|
+
className: cn("block touch-none rounded border border-border", className),
|
|
23951
|
+
style: { width, height },
|
|
23952
|
+
onClick: handleClick,
|
|
23953
|
+
onPointerMove: handlePointerMove
|
|
23954
|
+
}
|
|
23955
|
+
);
|
|
23956
|
+
};
|
|
23957
|
+
}
|
|
23958
|
+
});
|
|
23867
23959
|
|
|
23868
23960
|
// components/core/atoms/index.ts
|
|
23869
23961
|
var init_atoms = __esm({
|
|
@@ -25349,91 +25441,6 @@ var init_ComponentPatterns = __esm({
|
|
|
25349
25441
|
AlertPattern.displayName = "AlertPattern";
|
|
25350
25442
|
}
|
|
25351
25443
|
});
|
|
25352
|
-
function parseValue(value) {
|
|
25353
|
-
if (value === "" || value == null) return { num: 0, prefix: "", suffix: "", decimals: 0 };
|
|
25354
|
-
const match = String(value).match(/^([^0-9]*)([0-9]+(?:\.[0-9]+)?)(.*)$/);
|
|
25355
|
-
if (!match) {
|
|
25356
|
-
return { num: 0, prefix: "", suffix: String(value), decimals: 0 };
|
|
25357
|
-
}
|
|
25358
|
-
const numStr = match[2];
|
|
25359
|
-
const decimalIdx = numStr.indexOf(".");
|
|
25360
|
-
const decimals = decimalIdx >= 0 ? numStr.length - decimalIdx - 1 : 0;
|
|
25361
|
-
return {
|
|
25362
|
-
prefix: match[1],
|
|
25363
|
-
num: parseFloat(numStr),
|
|
25364
|
-
suffix: match[3],
|
|
25365
|
-
decimals
|
|
25366
|
-
};
|
|
25367
|
-
}
|
|
25368
|
-
var AnimatedCounter;
|
|
25369
|
-
var init_AnimatedCounter = __esm({
|
|
25370
|
-
"components/core/molecules/AnimatedCounter.tsx"() {
|
|
25371
|
-
"use client";
|
|
25372
|
-
init_cn();
|
|
25373
|
-
init_Box();
|
|
25374
|
-
init_Typography();
|
|
25375
|
-
AnimatedCounter = ({
|
|
25376
|
-
value,
|
|
25377
|
-
label,
|
|
25378
|
-
duration = 1500,
|
|
25379
|
-
className
|
|
25380
|
-
}) => {
|
|
25381
|
-
const ref = React114.useRef(null);
|
|
25382
|
-
const [displayValue, setDisplayValue] = React114.useState("0");
|
|
25383
|
-
const [hasAnimated, setHasAnimated] = React114.useState(false);
|
|
25384
|
-
const animate = React114.useCallback(() => {
|
|
25385
|
-
const { num: num2, prefix, suffix, decimals } = parseValue(value);
|
|
25386
|
-
if (num2 === 0) {
|
|
25387
|
-
setDisplayValue(String(value));
|
|
25388
|
-
return;
|
|
25389
|
-
}
|
|
25390
|
-
const startTime = performance.now();
|
|
25391
|
-
const tick = (now2) => {
|
|
25392
|
-
const elapsed = now2 - startTime;
|
|
25393
|
-
const progress = Math.min(elapsed / duration, 1);
|
|
25394
|
-
const eased = 1 - Math.pow(1 - progress, 3);
|
|
25395
|
-
const current = eased * num2;
|
|
25396
|
-
setDisplayValue(`${prefix}${current.toFixed(decimals)}${suffix}`);
|
|
25397
|
-
if (progress < 1) {
|
|
25398
|
-
requestAnimationFrame(tick);
|
|
25399
|
-
} else {
|
|
25400
|
-
setDisplayValue(String(value));
|
|
25401
|
-
}
|
|
25402
|
-
};
|
|
25403
|
-
requestAnimationFrame(tick);
|
|
25404
|
-
}, [value, duration]);
|
|
25405
|
-
React114.useEffect(() => {
|
|
25406
|
-
if (hasAnimated) return;
|
|
25407
|
-
const el = ref.current;
|
|
25408
|
-
if (!el) return;
|
|
25409
|
-
const observer2 = new IntersectionObserver(
|
|
25410
|
-
(entries) => {
|
|
25411
|
-
if (entries[0].isIntersecting) {
|
|
25412
|
-
setHasAnimated(true);
|
|
25413
|
-
animate();
|
|
25414
|
-
observer2.disconnect();
|
|
25415
|
-
}
|
|
25416
|
-
},
|
|
25417
|
-
{ threshold: 0.3 }
|
|
25418
|
-
);
|
|
25419
|
-
observer2.observe(el);
|
|
25420
|
-
return () => observer2.disconnect();
|
|
25421
|
-
}, [hasAnimated, animate]);
|
|
25422
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(Box, { ref, className: cn("flex flex-col items-center gap-1 p-4", className), children: [
|
|
25423
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
25424
|
-
Typography,
|
|
25425
|
-
{
|
|
25426
|
-
variant: "h2",
|
|
25427
|
-
className: "text-primary font-bold tabular-nums",
|
|
25428
|
-
children: hasAnimated ? displayValue : "0"
|
|
25429
|
-
}
|
|
25430
|
-
),
|
|
25431
|
-
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", color: "muted", className: "text-center", children: label })
|
|
25432
|
-
] });
|
|
25433
|
-
};
|
|
25434
|
-
AnimatedCounter.displayName = "AnimatedCounter";
|
|
25435
|
-
}
|
|
25436
|
-
});
|
|
25437
25444
|
var AuthLayout;
|
|
25438
25445
|
var init_AuthLayout = __esm({
|
|
25439
25446
|
"components/marketing/templates/AuthLayout.tsx"() {
|
|
@@ -25575,10 +25582,107 @@ var init_AuthLayout = __esm({
|
|
|
25575
25582
|
AuthLayout.displayName = "AuthLayout";
|
|
25576
25583
|
}
|
|
25577
25584
|
});
|
|
25585
|
+
var BiologyCanvas;
|
|
25586
|
+
var init_BiologyCanvas = __esm({
|
|
25587
|
+
"components/learning/molecules/BiologyCanvas.tsx"() {
|
|
25588
|
+
"use client";
|
|
25589
|
+
init_atoms();
|
|
25590
|
+
init_Stack();
|
|
25591
|
+
init_LearningCanvas();
|
|
25592
|
+
BiologyCanvas = ({
|
|
25593
|
+
className,
|
|
25594
|
+
width = 600,
|
|
25595
|
+
height = 400,
|
|
25596
|
+
title,
|
|
25597
|
+
backgroundColor,
|
|
25598
|
+
nodes = [],
|
|
25599
|
+
edges = [],
|
|
25600
|
+
shapes = [],
|
|
25601
|
+
interactive = false,
|
|
25602
|
+
animate = false,
|
|
25603
|
+
onShapeClick,
|
|
25604
|
+
isLoading,
|
|
25605
|
+
error
|
|
25606
|
+
}) => {
|
|
25607
|
+
const derivedShapes = React114.useMemo(() => {
|
|
25608
|
+
const out = [];
|
|
25609
|
+
const nodeById = /* @__PURE__ */ new Map();
|
|
25610
|
+
for (const n of nodes) {
|
|
25611
|
+
if (n.id) nodeById.set(n.id, n);
|
|
25612
|
+
}
|
|
25613
|
+
for (const e of edges) {
|
|
25614
|
+
const a = nodeById.get(e.from);
|
|
25615
|
+
const b = nodeById.get(e.to);
|
|
25616
|
+
if (!a || !b) continue;
|
|
25617
|
+
out.push({
|
|
25618
|
+
type: "line",
|
|
25619
|
+
x1: a.x,
|
|
25620
|
+
y1: a.y,
|
|
25621
|
+
x2: b.x,
|
|
25622
|
+
y2: b.y,
|
|
25623
|
+
color: e.color ?? "#9ca3af",
|
|
25624
|
+
lineWidth: 2
|
|
25625
|
+
});
|
|
25626
|
+
if (e.label) {
|
|
25627
|
+
out.push({
|
|
25628
|
+
type: "text",
|
|
25629
|
+
x: (a.x + b.x) / 2 + 4,
|
|
25630
|
+
y: (a.y + b.y) / 2 - 4,
|
|
25631
|
+
text: e.label,
|
|
25632
|
+
color: "#374151",
|
|
25633
|
+
fontSize: 11
|
|
25634
|
+
});
|
|
25635
|
+
}
|
|
25636
|
+
}
|
|
25637
|
+
for (const n of nodes) {
|
|
25638
|
+
out.push({
|
|
25639
|
+
type: "circle",
|
|
25640
|
+
x: n.x,
|
|
25641
|
+
y: n.y,
|
|
25642
|
+
radius: n.radius ?? 16,
|
|
25643
|
+
color: n.color ?? "#16a34a",
|
|
25644
|
+
fill: `${n.color ?? "#16a34a"}33`,
|
|
25645
|
+
id: n.id
|
|
25646
|
+
});
|
|
25647
|
+
if (n.label) {
|
|
25648
|
+
out.push({
|
|
25649
|
+
type: "text",
|
|
25650
|
+
x: n.x,
|
|
25651
|
+
y: n.y + (n.radius ?? 16) + 14,
|
|
25652
|
+
text: n.label,
|
|
25653
|
+
color: "#111827",
|
|
25654
|
+
fontSize: 12,
|
|
25655
|
+
align: "center"
|
|
25656
|
+
});
|
|
25657
|
+
}
|
|
25658
|
+
}
|
|
25659
|
+
out.push(...shapes);
|
|
25660
|
+
return out;
|
|
25661
|
+
}, [nodes, edges, shapes]);
|
|
25662
|
+
return /* @__PURE__ */ jsxRuntime.jsx(Card, { className, children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "sm", children: [
|
|
25663
|
+
title ? /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h4", children: title }) : null,
|
|
25664
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
25665
|
+
LearningCanvas,
|
|
25666
|
+
{
|
|
25667
|
+
width,
|
|
25668
|
+
height,
|
|
25669
|
+
backgroundColor,
|
|
25670
|
+
shapes: derivedShapes,
|
|
25671
|
+
interactive,
|
|
25672
|
+
animate,
|
|
25673
|
+
onShapeClick,
|
|
25674
|
+
isLoading,
|
|
25675
|
+
error
|
|
25676
|
+
}
|
|
25677
|
+
)
|
|
25678
|
+
] }) });
|
|
25679
|
+
};
|
|
25680
|
+
}
|
|
25681
|
+
});
|
|
25578
25682
|
|
|
25579
|
-
// node_modules
|
|
25683
|
+
// node_modules/katex/dist/katex.min.css
|
|
25580
25684
|
var init_katex_min = __esm({
|
|
25581
|
-
"node_modules
|
|
25685
|
+
"node_modules/katex/dist/katex.min.css"() {
|
|
25582
25686
|
}
|
|
25583
25687
|
});
|
|
25584
25688
|
var Tabs;
|
|
@@ -29971,129 +30075,6 @@ var init_Breadcrumb = __esm({
|
|
|
29971
30075
|
Breadcrumb.displayName = "Breadcrumb";
|
|
29972
30076
|
}
|
|
29973
30077
|
});
|
|
29974
|
-
function useSafeEventBus2() {
|
|
29975
|
-
try {
|
|
29976
|
-
return useEventBus();
|
|
29977
|
-
} catch {
|
|
29978
|
-
return { emit: () => {
|
|
29979
|
-
}, on: () => () => {
|
|
29980
|
-
}, once: () => {
|
|
29981
|
-
} };
|
|
29982
|
-
}
|
|
29983
|
-
}
|
|
29984
|
-
var log5, lookStyles4, ButtonGroup;
|
|
29985
|
-
var init_ButtonGroup = __esm({
|
|
29986
|
-
"components/core/molecules/ButtonGroup.tsx"() {
|
|
29987
|
-
"use client";
|
|
29988
|
-
init_cn();
|
|
29989
|
-
init_atoms();
|
|
29990
|
-
init_useEventBus();
|
|
29991
|
-
log5 = logger.createLogger("almadar:ui:button-group");
|
|
29992
|
-
lookStyles4 = {
|
|
29993
|
-
"right-aligned-buttons": "",
|
|
29994
|
-
"floating-bar": "fixed bottom-section left-1/2 -translate-x-1/2 shadow-elevation-toast bg-card p-card-sm rounded-container",
|
|
29995
|
-
"inline-row": "gap-2 inline-flex",
|
|
29996
|
-
"dropdown-menu": "[&>button:not(:first-child)]:hidden",
|
|
29997
|
-
"command-palette-trigger": "[&>button:not(:first-child)]:hidden"
|
|
29998
|
-
};
|
|
29999
|
-
ButtonGroup = ({
|
|
30000
|
-
children,
|
|
30001
|
-
primary,
|
|
30002
|
-
secondary,
|
|
30003
|
-
variant = "default",
|
|
30004
|
-
orientation = "horizontal",
|
|
30005
|
-
className,
|
|
30006
|
-
// Filter-group pattern props (entity and filters are used for schema-driven filtering)
|
|
30007
|
-
entity: _entity,
|
|
30008
|
-
filters,
|
|
30009
|
-
look = "right-aligned-buttons"
|
|
30010
|
-
}) => {
|
|
30011
|
-
const eventBus = useSafeEventBus2();
|
|
30012
|
-
const variantClasses2 = {
|
|
30013
|
-
default: "gap-0",
|
|
30014
|
-
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",
|
|
30015
|
-
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"
|
|
30016
|
-
};
|
|
30017
|
-
const orientationClasses = {
|
|
30018
|
-
horizontal: "flex-row",
|
|
30019
|
-
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"
|
|
30020
|
-
};
|
|
30021
|
-
const handleActionClick = (action) => {
|
|
30022
|
-
if (action.event) {
|
|
30023
|
-
eventBus.emit("UI:DISPATCH", { event: action.event });
|
|
30024
|
-
}
|
|
30025
|
-
if (action.navigatesTo) {
|
|
30026
|
-
eventBus.emit("UI:NAVIGATE", { url: action.navigatesTo });
|
|
30027
|
-
}
|
|
30028
|
-
};
|
|
30029
|
-
const renderFormActions = () => {
|
|
30030
|
-
const buttons = [];
|
|
30031
|
-
if (secondary) {
|
|
30032
|
-
secondary.forEach((action, index) => {
|
|
30033
|
-
buttons.push(
|
|
30034
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
30035
|
-
Button,
|
|
30036
|
-
{
|
|
30037
|
-
type: action.actionType === "submit" ? "submit" : "button",
|
|
30038
|
-
variant: action.variant || "ghost",
|
|
30039
|
-
onClick: () => handleActionClick(action),
|
|
30040
|
-
children: action.label
|
|
30041
|
-
},
|
|
30042
|
-
`secondary-${index}`
|
|
30043
|
-
)
|
|
30044
|
-
);
|
|
30045
|
-
});
|
|
30046
|
-
}
|
|
30047
|
-
if (primary) {
|
|
30048
|
-
const isSubmit = primary.actionType === "submit";
|
|
30049
|
-
buttons.push(
|
|
30050
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
30051
|
-
Button,
|
|
30052
|
-
{
|
|
30053
|
-
type: isSubmit ? "submit" : "button",
|
|
30054
|
-
variant: primary.variant || "primary",
|
|
30055
|
-
onClick: () => handleActionClick(primary),
|
|
30056
|
-
"data-testid": isSubmit ? "form-submit" : void 0,
|
|
30057
|
-
children: primary.label
|
|
30058
|
-
},
|
|
30059
|
-
"primary"
|
|
30060
|
-
)
|
|
30061
|
-
);
|
|
30062
|
-
}
|
|
30063
|
-
return buttons;
|
|
30064
|
-
};
|
|
30065
|
-
const renderFilters = () => {
|
|
30066
|
-
if (!filters || filters.length === 0) return null;
|
|
30067
|
-
return filters.map((filter, index) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
30068
|
-
Button,
|
|
30069
|
-
{
|
|
30070
|
-
variant: "ghost",
|
|
30071
|
-
onClick: () => {
|
|
30072
|
-
log5.debug("Filter clicked", { field: filter.field });
|
|
30073
|
-
},
|
|
30074
|
-
children: filter.label
|
|
30075
|
-
},
|
|
30076
|
-
`filter-${filter.field}-${index}`
|
|
30077
|
-
));
|
|
30078
|
-
};
|
|
30079
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
30080
|
-
"div",
|
|
30081
|
-
{
|
|
30082
|
-
className: cn(
|
|
30083
|
-
"inline-flex gap-2",
|
|
30084
|
-
variantClasses2[variant],
|
|
30085
|
-
orientationClasses[orientation],
|
|
30086
|
-
lookStyles4[look],
|
|
30087
|
-
className
|
|
30088
|
-
),
|
|
30089
|
-
role: "group",
|
|
30090
|
-
children: children || renderFilters() || renderFormActions()
|
|
30091
|
-
}
|
|
30092
|
-
);
|
|
30093
|
-
};
|
|
30094
|
-
ButtonGroup.displayName = "ButtonGroup";
|
|
30095
|
-
}
|
|
30096
|
-
});
|
|
30097
30078
|
function useSwipeGesture(callbacks, options = {}) {
|
|
30098
30079
|
const { threshold = 50, velocityThreshold = 0.3, preventDefault = false } = options;
|
|
30099
30080
|
const startX = React114.useRef(0);
|
|
@@ -30903,7 +30884,7 @@ var init_CardGrid = __esm({
|
|
|
30903
30884
|
CardGrid.displayName = "CardGrid";
|
|
30904
30885
|
}
|
|
30905
30886
|
});
|
|
30906
|
-
function
|
|
30887
|
+
function useSafeEventBus2() {
|
|
30907
30888
|
try {
|
|
30908
30889
|
return useEventBus();
|
|
30909
30890
|
} catch {
|
|
@@ -30939,7 +30920,7 @@ var init_Carousel = __esm({
|
|
|
30939
30920
|
const [activeIndex, setActiveIndex] = React114.useState(0);
|
|
30940
30921
|
const scrollRef = React114.useRef(null);
|
|
30941
30922
|
const autoPlayRef = React114.useRef(null);
|
|
30942
|
-
const eventBus =
|
|
30923
|
+
const eventBus = useSafeEventBus2();
|
|
30943
30924
|
const { t } = hooks.useTranslate();
|
|
30944
30925
|
const safeItems = items ?? [];
|
|
30945
30926
|
const totalSlides = safeItems.length;
|
|
@@ -32187,6 +32168,122 @@ var init_ChatBar = __esm({
|
|
|
32187
32168
|
ChatBar.displayName = "ChatBar";
|
|
32188
32169
|
}
|
|
32189
32170
|
});
|
|
32171
|
+
var ChemistryCanvas;
|
|
32172
|
+
var init_ChemistryCanvas = __esm({
|
|
32173
|
+
"components/learning/molecules/ChemistryCanvas.tsx"() {
|
|
32174
|
+
"use client";
|
|
32175
|
+
init_atoms();
|
|
32176
|
+
init_Stack();
|
|
32177
|
+
init_LearningCanvas();
|
|
32178
|
+
ChemistryCanvas = ({
|
|
32179
|
+
className,
|
|
32180
|
+
width = 600,
|
|
32181
|
+
height = 400,
|
|
32182
|
+
title,
|
|
32183
|
+
backgroundColor,
|
|
32184
|
+
atoms = [],
|
|
32185
|
+
bonds = [],
|
|
32186
|
+
arrows = [],
|
|
32187
|
+
shapes = [],
|
|
32188
|
+
interactive = false,
|
|
32189
|
+
animate = false,
|
|
32190
|
+
onShapeClick,
|
|
32191
|
+
isLoading,
|
|
32192
|
+
error
|
|
32193
|
+
}) => {
|
|
32194
|
+
const derivedShapes = React114.useMemo(() => {
|
|
32195
|
+
const out = [];
|
|
32196
|
+
const atomById = /* @__PURE__ */ new Map();
|
|
32197
|
+
for (const a of atoms) {
|
|
32198
|
+
if (a.id) atomById.set(a.id, a);
|
|
32199
|
+
}
|
|
32200
|
+
for (const b of bonds) {
|
|
32201
|
+
const a = atomById.get(b.from);
|
|
32202
|
+
const c = atomById.get(b.to);
|
|
32203
|
+
if (!a || !c) continue;
|
|
32204
|
+
const color = b.color ?? "#6b7280";
|
|
32205
|
+
const strokeWidth = b.type === "double" ? 4 : b.type === "triple" ? 6 : 2;
|
|
32206
|
+
out.push({
|
|
32207
|
+
type: "line",
|
|
32208
|
+
x1: a.x,
|
|
32209
|
+
y1: a.y,
|
|
32210
|
+
x2: c.x,
|
|
32211
|
+
y2: c.y,
|
|
32212
|
+
color,
|
|
32213
|
+
lineWidth: strokeWidth
|
|
32214
|
+
});
|
|
32215
|
+
}
|
|
32216
|
+
for (const a of arrows) {
|
|
32217
|
+
const angle = (a.angle ?? 0) * (Math.PI / 180);
|
|
32218
|
+
const len = a.length ?? 60;
|
|
32219
|
+
const x2 = a.x + Math.cos(angle) * len;
|
|
32220
|
+
const y2 = a.y + Math.sin(angle) * len;
|
|
32221
|
+
out.push({
|
|
32222
|
+
type: "arrow",
|
|
32223
|
+
x1: a.x,
|
|
32224
|
+
y1: a.y,
|
|
32225
|
+
x2,
|
|
32226
|
+
y2,
|
|
32227
|
+
color: a.color ?? "#dc2626",
|
|
32228
|
+
lineWidth: 2
|
|
32229
|
+
});
|
|
32230
|
+
if (a.label) {
|
|
32231
|
+
out.push({
|
|
32232
|
+
type: "text",
|
|
32233
|
+
x: (a.x + x2) / 2,
|
|
32234
|
+
y: (a.y + y2) / 2 - 10,
|
|
32235
|
+
text: a.label,
|
|
32236
|
+
color: "#111827",
|
|
32237
|
+
fontSize: 12,
|
|
32238
|
+
align: "center"
|
|
32239
|
+
});
|
|
32240
|
+
}
|
|
32241
|
+
}
|
|
32242
|
+
for (const a of atoms) {
|
|
32243
|
+
out.push({
|
|
32244
|
+
type: "circle",
|
|
32245
|
+
x: a.x,
|
|
32246
|
+
y: a.y,
|
|
32247
|
+
radius: a.radius ?? 14,
|
|
32248
|
+
color: a.color ?? "#2563eb",
|
|
32249
|
+
fill: a.color ?? "#2563eb",
|
|
32250
|
+
id: a.id
|
|
32251
|
+
});
|
|
32252
|
+
if (a.element) {
|
|
32253
|
+
out.push({
|
|
32254
|
+
type: "text",
|
|
32255
|
+
x: a.x,
|
|
32256
|
+
y: a.y,
|
|
32257
|
+
text: a.element,
|
|
32258
|
+
color: "#ffffff",
|
|
32259
|
+
fontSize: 12,
|
|
32260
|
+
align: "center"
|
|
32261
|
+
});
|
|
32262
|
+
}
|
|
32263
|
+
}
|
|
32264
|
+
out.push(...shapes);
|
|
32265
|
+
return out;
|
|
32266
|
+
}, [atoms, bonds, arrows, shapes]);
|
|
32267
|
+
return /* @__PURE__ */ jsxRuntime.jsx(Card, { className, children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "sm", children: [
|
|
32268
|
+
title ? /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h4", children: title }) : null,
|
|
32269
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
32270
|
+
LearningCanvas,
|
|
32271
|
+
{
|
|
32272
|
+
width,
|
|
32273
|
+
height,
|
|
32274
|
+
backgroundColor,
|
|
32275
|
+
shapes: derivedShapes,
|
|
32276
|
+
interactive,
|
|
32277
|
+
animate,
|
|
32278
|
+
onShapeClick,
|
|
32279
|
+
isLoading,
|
|
32280
|
+
error
|
|
32281
|
+
}
|
|
32282
|
+
)
|
|
32283
|
+
] }) });
|
|
32284
|
+
};
|
|
32285
|
+
}
|
|
32286
|
+
});
|
|
32190
32287
|
var CodeRunnerPanel;
|
|
32191
32288
|
var init_CodeRunnerPanel = __esm({
|
|
32192
32289
|
"components/core/organisms/CodeRunnerPanel.tsx"() {
|
|
@@ -34275,7 +34372,7 @@ function DataGrid({
|
|
|
34275
34372
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
34276
34373
|
Box,
|
|
34277
34374
|
{
|
|
34278
|
-
className: cn("grid", gapStyles5[gap], scrollX ? "grid-flow-col overflow-x-auto" : colsClass,
|
|
34375
|
+
className: cn("grid", gapStyles5[gap], scrollX ? "grid-flow-col overflow-x-auto" : colsClass, lookStyles4[look], className),
|
|
34279
34376
|
style: scrollX ? { gridAutoFlow: "column", gridAutoColumns: `minmax(${minCardWidth}px, 1fr)` } : gridTemplateColumns ? { gridTemplateColumns } : void 0,
|
|
34280
34377
|
children: data.map((item, index) => {
|
|
34281
34378
|
const itemData = item;
|
|
@@ -34470,7 +34567,7 @@ function DataGrid({
|
|
|
34470
34567
|
] })
|
|
34471
34568
|
);
|
|
34472
34569
|
}
|
|
34473
|
-
var dataGridLog, BADGE_VARIANTS, gapStyles5,
|
|
34570
|
+
var dataGridLog, BADGE_VARIANTS, gapStyles5, lookStyles4;
|
|
34474
34571
|
var init_DataGrid = __esm({
|
|
34475
34572
|
"components/core/molecules/DataGrid.tsx"() {
|
|
34476
34573
|
"use client";
|
|
@@ -34505,7 +34602,7 @@ var init_DataGrid = __esm({
|
|
|
34505
34602
|
lg: "gap-6",
|
|
34506
34603
|
xl: "gap-8"
|
|
34507
34604
|
};
|
|
34508
|
-
|
|
34605
|
+
lookStyles4 = {
|
|
34509
34606
|
dense: "gap-2 [&>*]:p-card-sm",
|
|
34510
34607
|
spacious: "gap-8 [&>*]:p-card-lg",
|
|
34511
34608
|
striped: "[&>*:nth-child(even)]:bg-muted/30",
|
|
@@ -34932,6 +35029,133 @@ var init_DataList = __esm({
|
|
|
34932
35029
|
DataList.displayName = "DataList";
|
|
34933
35030
|
}
|
|
34934
35031
|
});
|
|
35032
|
+
var FormSection, FormLayout, FormActions;
|
|
35033
|
+
var init_FormSection = __esm({
|
|
35034
|
+
"components/core/molecules/FormSection.tsx"() {
|
|
35035
|
+
"use client";
|
|
35036
|
+
init_cn();
|
|
35037
|
+
init_atoms();
|
|
35038
|
+
init_Box();
|
|
35039
|
+
init_Typography();
|
|
35040
|
+
init_Button();
|
|
35041
|
+
init_Stack();
|
|
35042
|
+
init_Icon();
|
|
35043
|
+
init_useEventBus();
|
|
35044
|
+
FormSection = ({
|
|
35045
|
+
title,
|
|
35046
|
+
description,
|
|
35047
|
+
children,
|
|
35048
|
+
collapsible = false,
|
|
35049
|
+
defaultCollapsed = false,
|
|
35050
|
+
card = false,
|
|
35051
|
+
columns = 1,
|
|
35052
|
+
className
|
|
35053
|
+
}) => {
|
|
35054
|
+
const [collapsed, setCollapsed] = React114__namespace.default.useState(defaultCollapsed);
|
|
35055
|
+
const { t } = hooks.useTranslate();
|
|
35056
|
+
const eventBus = useEventBus();
|
|
35057
|
+
const gridClass = {
|
|
35058
|
+
1: "grid-cols-1",
|
|
35059
|
+
2: "grid-cols-1 md:grid-cols-2",
|
|
35060
|
+
3: "grid-cols-1 md:grid-cols-2 lg:grid-cols-3"
|
|
35061
|
+
}[columns];
|
|
35062
|
+
React114__namespace.default.useCallback(() => {
|
|
35063
|
+
if (collapsible) {
|
|
35064
|
+
setCollapsed((prev) => !prev);
|
|
35065
|
+
eventBus.emit("UI:TOGGLE_COLLAPSE", { collapsed: !collapsed });
|
|
35066
|
+
}
|
|
35067
|
+
}, [collapsible, collapsed, eventBus]);
|
|
35068
|
+
const content = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
35069
|
+
(title || description) && /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "xs", className: "mb-4", children: [
|
|
35070
|
+
title && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
35071
|
+
HStack,
|
|
35072
|
+
{
|
|
35073
|
+
justify: "between",
|
|
35074
|
+
align: "center",
|
|
35075
|
+
className: cn(collapsible && "cursor-pointer"),
|
|
35076
|
+
action: collapsible ? "TOGGLE_COLLAPSE" : void 0,
|
|
35077
|
+
children: [
|
|
35078
|
+
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h3", weight: "semibold", children: title }),
|
|
35079
|
+
collapsible && /* @__PURE__ */ jsxRuntime.jsx(
|
|
35080
|
+
Button,
|
|
35081
|
+
{
|
|
35082
|
+
variant: "ghost",
|
|
35083
|
+
size: "sm",
|
|
35084
|
+
action: "TOGGLE_COLLAPSE",
|
|
35085
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
35086
|
+
Icon,
|
|
35087
|
+
{
|
|
35088
|
+
icon: LucideIcons2.ChevronDown,
|
|
35089
|
+
size: "sm",
|
|
35090
|
+
className: cn(
|
|
35091
|
+
"text-muted-foreground transition-transform",
|
|
35092
|
+
collapsed && "rotate-180"
|
|
35093
|
+
)
|
|
35094
|
+
}
|
|
35095
|
+
)
|
|
35096
|
+
}
|
|
35097
|
+
)
|
|
35098
|
+
]
|
|
35099
|
+
}
|
|
35100
|
+
),
|
|
35101
|
+
description && /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", color: "secondary", children: description })
|
|
35102
|
+
] }),
|
|
35103
|
+
(!collapsible || !collapsed) && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: cn("grid gap-4", gridClass), children })
|
|
35104
|
+
] });
|
|
35105
|
+
if (card) {
|
|
35106
|
+
return /* @__PURE__ */ jsxRuntime.jsx(Card, { className: cn("p-6", className), children: content });
|
|
35107
|
+
}
|
|
35108
|
+
return /* @__PURE__ */ jsxRuntime.jsx(Box, { className, children: content });
|
|
35109
|
+
};
|
|
35110
|
+
FormSection.displayName = "FormSection";
|
|
35111
|
+
FormLayout = ({
|
|
35112
|
+
children,
|
|
35113
|
+
dividers = true,
|
|
35114
|
+
className
|
|
35115
|
+
}) => {
|
|
35116
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
35117
|
+
VStack,
|
|
35118
|
+
{
|
|
35119
|
+
gap: "lg",
|
|
35120
|
+
className: cn(
|
|
35121
|
+
dividers && "[&>*+*]:pt-8 [&>*+*]:border-t [&>*+*]:border-border",
|
|
35122
|
+
className
|
|
35123
|
+
),
|
|
35124
|
+
children
|
|
35125
|
+
}
|
|
35126
|
+
);
|
|
35127
|
+
};
|
|
35128
|
+
FormLayout.displayName = "FormLayout";
|
|
35129
|
+
FormActions = ({
|
|
35130
|
+
children,
|
|
35131
|
+
sticky = false,
|
|
35132
|
+
align = "right",
|
|
35133
|
+
className
|
|
35134
|
+
}) => {
|
|
35135
|
+
const alignClass2 = {
|
|
35136
|
+
left: "justify-start",
|
|
35137
|
+
right: "justify-end",
|
|
35138
|
+
between: "justify-between",
|
|
35139
|
+
center: "justify-center"
|
|
35140
|
+
}[align];
|
|
35141
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
35142
|
+
HStack,
|
|
35143
|
+
{
|
|
35144
|
+
gap: "sm",
|
|
35145
|
+
align: "center",
|
|
35146
|
+
className: cn(
|
|
35147
|
+
"pt-6 border-t border-border",
|
|
35148
|
+
alignClass2,
|
|
35149
|
+
sticky && "sticky bottom-0 bg-card py-4 -mx-6 px-6 shadow-[0_-4px_6px_-1px_rgb(0,0,0,0.05)]",
|
|
35150
|
+
className
|
|
35151
|
+
),
|
|
35152
|
+
children
|
|
35153
|
+
}
|
|
35154
|
+
);
|
|
35155
|
+
};
|
|
35156
|
+
FormActions.displayName = "FormActions";
|
|
35157
|
+
}
|
|
35158
|
+
});
|
|
34935
35159
|
function fileIcon(name) {
|
|
34936
35160
|
const ext = name.split(".").pop()?.toLowerCase() ?? "";
|
|
34937
35161
|
switch (ext) {
|
|
@@ -35093,6 +35317,129 @@ var init_FormField = __esm({
|
|
|
35093
35317
|
FormField.displayName = "FormField";
|
|
35094
35318
|
}
|
|
35095
35319
|
});
|
|
35320
|
+
function useSafeEventBus3() {
|
|
35321
|
+
try {
|
|
35322
|
+
return useEventBus();
|
|
35323
|
+
} catch {
|
|
35324
|
+
return { emit: () => {
|
|
35325
|
+
}, on: () => () => {
|
|
35326
|
+
}, once: () => {
|
|
35327
|
+
} };
|
|
35328
|
+
}
|
|
35329
|
+
}
|
|
35330
|
+
var log5, lookStyles5, ButtonGroup;
|
|
35331
|
+
var init_ButtonGroup = __esm({
|
|
35332
|
+
"components/core/molecules/ButtonGroup.tsx"() {
|
|
35333
|
+
"use client";
|
|
35334
|
+
init_cn();
|
|
35335
|
+
init_atoms();
|
|
35336
|
+
init_useEventBus();
|
|
35337
|
+
log5 = logger.createLogger("almadar:ui:button-group");
|
|
35338
|
+
lookStyles5 = {
|
|
35339
|
+
"right-aligned-buttons": "",
|
|
35340
|
+
"floating-bar": "fixed bottom-section left-1/2 -translate-x-1/2 shadow-elevation-toast bg-card p-card-sm rounded-container",
|
|
35341
|
+
"inline-row": "gap-2 inline-flex",
|
|
35342
|
+
"dropdown-menu": "[&>button:not(:first-child)]:hidden",
|
|
35343
|
+
"command-palette-trigger": "[&>button:not(:first-child)]:hidden"
|
|
35344
|
+
};
|
|
35345
|
+
ButtonGroup = ({
|
|
35346
|
+
children,
|
|
35347
|
+
primary,
|
|
35348
|
+
secondary,
|
|
35349
|
+
variant = "default",
|
|
35350
|
+
orientation = "horizontal",
|
|
35351
|
+
className,
|
|
35352
|
+
// Filter-group pattern props (entity and filters are used for schema-driven filtering)
|
|
35353
|
+
entity: _entity,
|
|
35354
|
+
filters,
|
|
35355
|
+
look = "right-aligned-buttons"
|
|
35356
|
+
}) => {
|
|
35357
|
+
const eventBus = useSafeEventBus3();
|
|
35358
|
+
const variantClasses2 = {
|
|
35359
|
+
default: "gap-0",
|
|
35360
|
+
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",
|
|
35361
|
+
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"
|
|
35362
|
+
};
|
|
35363
|
+
const orientationClasses = {
|
|
35364
|
+
horizontal: "flex-row",
|
|
35365
|
+
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"
|
|
35366
|
+
};
|
|
35367
|
+
const handleActionClick = (action) => {
|
|
35368
|
+
if (action.event) {
|
|
35369
|
+
eventBus.emit("UI:DISPATCH", { event: action.event });
|
|
35370
|
+
}
|
|
35371
|
+
if (action.navigatesTo) {
|
|
35372
|
+
eventBus.emit("UI:NAVIGATE", { url: action.navigatesTo });
|
|
35373
|
+
}
|
|
35374
|
+
};
|
|
35375
|
+
const renderFormActions = () => {
|
|
35376
|
+
const buttons = [];
|
|
35377
|
+
if (secondary) {
|
|
35378
|
+
secondary.forEach((action, index) => {
|
|
35379
|
+
buttons.push(
|
|
35380
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
35381
|
+
Button,
|
|
35382
|
+
{
|
|
35383
|
+
type: action.actionType === "submit" ? "submit" : "button",
|
|
35384
|
+
variant: action.variant || "ghost",
|
|
35385
|
+
onClick: () => handleActionClick(action),
|
|
35386
|
+
children: action.label
|
|
35387
|
+
},
|
|
35388
|
+
`secondary-${index}`
|
|
35389
|
+
)
|
|
35390
|
+
);
|
|
35391
|
+
});
|
|
35392
|
+
}
|
|
35393
|
+
if (primary) {
|
|
35394
|
+
const isSubmit = primary.actionType === "submit";
|
|
35395
|
+
buttons.push(
|
|
35396
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
35397
|
+
Button,
|
|
35398
|
+
{
|
|
35399
|
+
type: isSubmit ? "submit" : "button",
|
|
35400
|
+
variant: primary.variant || "primary",
|
|
35401
|
+
onClick: () => handleActionClick(primary),
|
|
35402
|
+
"data-testid": isSubmit ? "form-submit" : void 0,
|
|
35403
|
+
children: primary.label
|
|
35404
|
+
},
|
|
35405
|
+
"primary"
|
|
35406
|
+
)
|
|
35407
|
+
);
|
|
35408
|
+
}
|
|
35409
|
+
return buttons;
|
|
35410
|
+
};
|
|
35411
|
+
const renderFilters = () => {
|
|
35412
|
+
if (!filters || filters.length === 0) return null;
|
|
35413
|
+
return filters.map((filter, index) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
35414
|
+
Button,
|
|
35415
|
+
{
|
|
35416
|
+
variant: "ghost",
|
|
35417
|
+
onClick: () => {
|
|
35418
|
+
log5.debug("Filter clicked", { field: filter.field });
|
|
35419
|
+
},
|
|
35420
|
+
children: filter.label
|
|
35421
|
+
},
|
|
35422
|
+
`filter-${filter.field}-${index}`
|
|
35423
|
+
));
|
|
35424
|
+
};
|
|
35425
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
35426
|
+
"div",
|
|
35427
|
+
{
|
|
35428
|
+
className: cn(
|
|
35429
|
+
"inline-flex gap-2",
|
|
35430
|
+
variantClasses2[variant],
|
|
35431
|
+
orientationClasses[orientation],
|
|
35432
|
+
lookStyles5[look],
|
|
35433
|
+
className
|
|
35434
|
+
),
|
|
35435
|
+
role: "group",
|
|
35436
|
+
children: children || renderFilters() || renderFormActions()
|
|
35437
|
+
}
|
|
35438
|
+
);
|
|
35439
|
+
};
|
|
35440
|
+
ButtonGroup.displayName = "ButtonGroup";
|
|
35441
|
+
}
|
|
35442
|
+
});
|
|
35096
35443
|
function getOrCreateStore(query) {
|
|
35097
35444
|
if (!queryStores.has(query)) {
|
|
35098
35445
|
queryStores.set(query, {
|
|
@@ -37634,6 +37981,234 @@ var init_ProgressDots = __esm({
|
|
|
37634
37981
|
ProgressDots.displayName = "ProgressDots";
|
|
37635
37982
|
}
|
|
37636
37983
|
});
|
|
37984
|
+
var MathCanvas;
|
|
37985
|
+
var init_MathCanvas = __esm({
|
|
37986
|
+
"components/learning/molecules/MathCanvas.tsx"() {
|
|
37987
|
+
"use client";
|
|
37988
|
+
init_atoms();
|
|
37989
|
+
init_Stack();
|
|
37990
|
+
init_LearningCanvas();
|
|
37991
|
+
MathCanvas = ({
|
|
37992
|
+
className,
|
|
37993
|
+
width = 600,
|
|
37994
|
+
height = 400,
|
|
37995
|
+
title,
|
|
37996
|
+
xMin = -10,
|
|
37997
|
+
xMax = 10,
|
|
37998
|
+
yMin = -10,
|
|
37999
|
+
yMax = 10,
|
|
38000
|
+
showAxes = true,
|
|
38001
|
+
showGrid = true,
|
|
38002
|
+
gridStep = 1,
|
|
38003
|
+
curves = [],
|
|
38004
|
+
points = [],
|
|
38005
|
+
vectors = [],
|
|
38006
|
+
shapes = [],
|
|
38007
|
+
interactive = false,
|
|
38008
|
+
animate = false,
|
|
38009
|
+
onShapeClick,
|
|
38010
|
+
isLoading,
|
|
38011
|
+
error
|
|
38012
|
+
}) => {
|
|
38013
|
+
const derivedShapes = React114.useMemo(() => {
|
|
38014
|
+
const out = [];
|
|
38015
|
+
const margin = 24;
|
|
38016
|
+
const plotW = width - margin * 2;
|
|
38017
|
+
const plotH = height - margin * 2;
|
|
38018
|
+
const mapX = (x) => margin + (x - xMin) / (xMax - xMin) * plotW;
|
|
38019
|
+
const mapY = (y) => height - (margin + (y - yMin) / (yMax - yMin) * plotH);
|
|
38020
|
+
if (showGrid) {
|
|
38021
|
+
for (let x = Math.ceil(xMin / gridStep) * gridStep; x <= xMax; x += gridStep) {
|
|
38022
|
+
const px = mapX(x);
|
|
38023
|
+
out.push({ type: "line", x1: px, y1: margin, x2: px, y2: height - margin, color: "#e5e7eb", lineWidth: 1 });
|
|
38024
|
+
}
|
|
38025
|
+
for (let y = Math.ceil(yMin / gridStep) * gridStep; y <= yMax; y += gridStep) {
|
|
38026
|
+
const py = mapY(y);
|
|
38027
|
+
out.push({ type: "line", x1: margin, y1: py, x2: width - margin, y2: py, color: "#e5e7eb", lineWidth: 1 });
|
|
38028
|
+
}
|
|
38029
|
+
}
|
|
38030
|
+
if (showAxes) {
|
|
38031
|
+
const xAxisY = Math.max(margin, Math.min(height - margin, mapY(0)));
|
|
38032
|
+
const yAxisX = Math.max(margin, Math.min(width - margin, mapX(0)));
|
|
38033
|
+
out.push({ type: "line", x1: margin, y1: xAxisY, x2: width - margin, y2: xAxisY, color: "#374151", lineWidth: 2 });
|
|
38034
|
+
out.push({ type: "line", x1: yAxisX, y1: margin, x2: yAxisX, y2: height - margin, color: "#374151", lineWidth: 2 });
|
|
38035
|
+
}
|
|
38036
|
+
for (const curve of curves) {
|
|
38037
|
+
if (!curve.samples || curve.samples.length < 2) continue;
|
|
38038
|
+
for (let i = 1; i < curve.samples.length; i++) {
|
|
38039
|
+
const a = curve.samples[i - 1];
|
|
38040
|
+
const b = curve.samples[i];
|
|
38041
|
+
if (a.x < xMin || a.x > xMax || b.x < xMin || b.x > xMax) continue;
|
|
38042
|
+
out.push({
|
|
38043
|
+
type: "line",
|
|
38044
|
+
x1: mapX(a.x),
|
|
38045
|
+
y1: mapY(a.y),
|
|
38046
|
+
x2: mapX(b.x),
|
|
38047
|
+
y2: mapY(b.y),
|
|
38048
|
+
color: curve.color ?? "#2563eb",
|
|
38049
|
+
lineWidth: 2
|
|
38050
|
+
});
|
|
38051
|
+
}
|
|
38052
|
+
}
|
|
38053
|
+
for (const p2 of points) {
|
|
38054
|
+
if (p2.x < xMin || p2.x > xMax || p2.y < yMin || p2.y > yMax) continue;
|
|
38055
|
+
out.push({
|
|
38056
|
+
type: "circle",
|
|
38057
|
+
x: mapX(p2.x),
|
|
38058
|
+
y: mapY(p2.y),
|
|
38059
|
+
radius: p2.radius ?? 4,
|
|
38060
|
+
color: p2.color ?? "#dc2626",
|
|
38061
|
+
fill: p2.color ?? "#dc2626"
|
|
38062
|
+
});
|
|
38063
|
+
if (p2.label) {
|
|
38064
|
+
out.push({ type: "text", x: mapX(p2.x) + 8, y: mapY(p2.y) - 8, text: p2.label, color: "#111827", fontSize: 12 });
|
|
38065
|
+
}
|
|
38066
|
+
}
|
|
38067
|
+
for (const v of vectors) {
|
|
38068
|
+
if (v.x < xMin || v.x > xMax || v.y < yMin || v.y > yMax) continue;
|
|
38069
|
+
const x1 = mapX(v.x);
|
|
38070
|
+
const y1 = mapY(v.y);
|
|
38071
|
+
const x2 = mapX(v.x + v.vx);
|
|
38072
|
+
const y2 = mapY(v.y + v.vy);
|
|
38073
|
+
out.push({ type: "arrow", x1, y1, x2, y2, color: v.color ?? "#7c3aed", lineWidth: 2 });
|
|
38074
|
+
if (v.label) {
|
|
38075
|
+
out.push({ type: "text", x: x2 + 6, y: y2 - 6, text: v.label, color: "#111827", fontSize: 12 });
|
|
38076
|
+
}
|
|
38077
|
+
}
|
|
38078
|
+
out.push(...shapes);
|
|
38079
|
+
return out;
|
|
38080
|
+
}, [width, height, xMin, xMax, yMin, yMax, showAxes, showGrid, gridStep, curves, points, vectors, shapes]);
|
|
38081
|
+
return /* @__PURE__ */ jsxRuntime.jsx(Card, { className, children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "sm", children: [
|
|
38082
|
+
title ? /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h4", children: title }) : null,
|
|
38083
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
38084
|
+
LearningCanvas,
|
|
38085
|
+
{
|
|
38086
|
+
width,
|
|
38087
|
+
height,
|
|
38088
|
+
shapes: derivedShapes,
|
|
38089
|
+
interactive,
|
|
38090
|
+
animate,
|
|
38091
|
+
onShapeClick,
|
|
38092
|
+
isLoading,
|
|
38093
|
+
error
|
|
38094
|
+
}
|
|
38095
|
+
)
|
|
38096
|
+
] }) });
|
|
38097
|
+
};
|
|
38098
|
+
}
|
|
38099
|
+
});
|
|
38100
|
+
var PhysicsCanvas;
|
|
38101
|
+
var init_PhysicsCanvas = __esm({
|
|
38102
|
+
"components/learning/molecules/PhysicsCanvas.tsx"() {
|
|
38103
|
+
"use client";
|
|
38104
|
+
init_atoms();
|
|
38105
|
+
init_Stack();
|
|
38106
|
+
init_LearningCanvas();
|
|
38107
|
+
PhysicsCanvas = ({
|
|
38108
|
+
className,
|
|
38109
|
+
width = 600,
|
|
38110
|
+
height = 400,
|
|
38111
|
+
title,
|
|
38112
|
+
backgroundColor,
|
|
38113
|
+
bodies = [],
|
|
38114
|
+
constraints = [],
|
|
38115
|
+
showVelocity = true,
|
|
38116
|
+
showForces = false,
|
|
38117
|
+
velocityScale = 20,
|
|
38118
|
+
forceScale = 20,
|
|
38119
|
+
shapes = [],
|
|
38120
|
+
interactive = false,
|
|
38121
|
+
animate = false,
|
|
38122
|
+
onShapeClick,
|
|
38123
|
+
isLoading,
|
|
38124
|
+
error
|
|
38125
|
+
}) => {
|
|
38126
|
+
const derivedShapes = React114.useMemo(() => {
|
|
38127
|
+
const out = [];
|
|
38128
|
+
const bodyById = /* @__PURE__ */ new Map();
|
|
38129
|
+
for (const b of bodies) {
|
|
38130
|
+
if (b.id) bodyById.set(b.id, b);
|
|
38131
|
+
}
|
|
38132
|
+
for (const c of constraints) {
|
|
38133
|
+
const a = bodyById.get(c.from);
|
|
38134
|
+
const b = bodyById.get(c.to);
|
|
38135
|
+
if (!a || !b) continue;
|
|
38136
|
+
out.push({
|
|
38137
|
+
type: "line",
|
|
38138
|
+
x1: a.x,
|
|
38139
|
+
y1: a.y,
|
|
38140
|
+
x2: b.x,
|
|
38141
|
+
y2: b.y,
|
|
38142
|
+
color: c.color ?? "#9ca3af",
|
|
38143
|
+
lineWidth: 2
|
|
38144
|
+
});
|
|
38145
|
+
}
|
|
38146
|
+
for (const b of bodies) {
|
|
38147
|
+
out.push({
|
|
38148
|
+
type: "circle",
|
|
38149
|
+
x: b.x,
|
|
38150
|
+
y: b.y,
|
|
38151
|
+
radius: b.radius ?? 12,
|
|
38152
|
+
color: b.color ?? "#2563eb",
|
|
38153
|
+
fill: b.color ?? "#2563eb",
|
|
38154
|
+
id: b.id
|
|
38155
|
+
});
|
|
38156
|
+
if (b.label) {
|
|
38157
|
+
out.push({
|
|
38158
|
+
type: "text",
|
|
38159
|
+
x: b.x + (b.radius ?? 12) + 6,
|
|
38160
|
+
y: b.y - (b.radius ?? 12) - 6,
|
|
38161
|
+
text: b.label,
|
|
38162
|
+
color: "#111827",
|
|
38163
|
+
fontSize: 12
|
|
38164
|
+
});
|
|
38165
|
+
}
|
|
38166
|
+
if (showVelocity && b.vx != null && b.vy != null && (b.vx !== 0 || b.vy !== 0)) {
|
|
38167
|
+
out.push({
|
|
38168
|
+
type: "arrow",
|
|
38169
|
+
x1: b.x,
|
|
38170
|
+
y1: b.y,
|
|
38171
|
+
x2: b.x + b.vx * velocityScale,
|
|
38172
|
+
y2: b.y + b.vy * velocityScale,
|
|
38173
|
+
color: "#16a34a",
|
|
38174
|
+
lineWidth: 2
|
|
38175
|
+
});
|
|
38176
|
+
}
|
|
38177
|
+
if (showForces && b.fx != null && b.fy != null && (b.fx !== 0 || b.fy !== 0)) {
|
|
38178
|
+
out.push({
|
|
38179
|
+
type: "arrow",
|
|
38180
|
+
x1: b.x,
|
|
38181
|
+
y1: b.y,
|
|
38182
|
+
x2: b.x + b.fx * forceScale,
|
|
38183
|
+
y2: b.y + b.fy * forceScale,
|
|
38184
|
+
color: "#dc2626",
|
|
38185
|
+
lineWidth: 2
|
|
38186
|
+
});
|
|
38187
|
+
}
|
|
38188
|
+
}
|
|
38189
|
+
out.push(...shapes);
|
|
38190
|
+
return out;
|
|
38191
|
+
}, [bodies, constraints, showVelocity, showForces, velocityScale, forceScale, shapes]);
|
|
38192
|
+
return /* @__PURE__ */ jsxRuntime.jsx(Card, { className, children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "sm", children: [
|
|
38193
|
+
title ? /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h4", children: title }) : null,
|
|
38194
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
38195
|
+
LearningCanvas,
|
|
38196
|
+
{
|
|
38197
|
+
width,
|
|
38198
|
+
height,
|
|
38199
|
+
backgroundColor,
|
|
38200
|
+
shapes: derivedShapes,
|
|
38201
|
+
interactive,
|
|
38202
|
+
animate,
|
|
38203
|
+
onShapeClick,
|
|
38204
|
+
isLoading,
|
|
38205
|
+
error
|
|
38206
|
+
}
|
|
38207
|
+
)
|
|
38208
|
+
] }) });
|
|
38209
|
+
};
|
|
38210
|
+
}
|
|
38211
|
+
});
|
|
37637
38212
|
function resolveNodeColor(node, groups) {
|
|
37638
38213
|
if (node.color) return node.color;
|
|
37639
38214
|
if (node.group) {
|
|
@@ -38005,13 +38580,13 @@ var init_MapView = __esm({
|
|
|
38005
38580
|
shadowSize: [41, 41]
|
|
38006
38581
|
});
|
|
38007
38582
|
L.Marker.prototype.options.icon = defaultIcon;
|
|
38008
|
-
const { useEffect:
|
|
38583
|
+
const { useEffect: useEffect79, useRef: useRef79, useCallback: useCallback118, useState: useState115 } = React114__namespace.default;
|
|
38009
38584
|
const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
|
|
38010
38585
|
const { useEventBus: useEventBus2 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
|
|
38011
38586
|
function MapUpdater({ centerLat, centerLng, zoom }) {
|
|
38012
38587
|
const map = useMap();
|
|
38013
|
-
const prevRef =
|
|
38014
|
-
|
|
38588
|
+
const prevRef = useRef79({ centerLat, centerLng, zoom });
|
|
38589
|
+
useEffect79(() => {
|
|
38015
38590
|
const prev = prevRef.current;
|
|
38016
38591
|
if (prev.centerLat !== centerLat || prev.centerLng !== centerLng || prev.zoom !== zoom) {
|
|
38017
38592
|
map.setView([centerLat, centerLng], zoom);
|
|
@@ -38022,7 +38597,7 @@ var init_MapView = __esm({
|
|
|
38022
38597
|
}
|
|
38023
38598
|
function MapClickHandler({ onMapClick }) {
|
|
38024
38599
|
const map = useMap();
|
|
38025
|
-
|
|
38600
|
+
useEffect79(() => {
|
|
38026
38601
|
if (!onMapClick) return;
|
|
38027
38602
|
const handler = (e) => {
|
|
38028
38603
|
onMapClick(e.latlng.lat, e.latlng.lng);
|
|
@@ -46054,7 +46629,7 @@ function getGroupColor(group, groups) {
|
|
|
46054
46629
|
const idx = groups.indexOf(group);
|
|
46055
46630
|
return GROUP_COLORS2[idx % GROUP_COLORS2.length];
|
|
46056
46631
|
}
|
|
46057
|
-
function
|
|
46632
|
+
function resolveColor3(color, el) {
|
|
46058
46633
|
const m = /^var\((--[^,)]+)(?:,\s*([^)]+))?\)$/.exec(color.trim());
|
|
46059
46634
|
if (!m) return color;
|
|
46060
46635
|
const resolved = getComputedStyle(el).getPropertyValue(m[1]).trim();
|
|
@@ -46315,7 +46890,7 @@ var init_GraphCanvas = __esm({
|
|
|
46315
46890
|
const w = logicalW;
|
|
46316
46891
|
const h = height;
|
|
46317
46892
|
const nodes = nodesRef.current;
|
|
46318
|
-
const accentColor =
|
|
46893
|
+
const accentColor = resolveColor3("var(--color-accent)", canvas);
|
|
46319
46894
|
const dpr = typeof window !== "undefined" && window.devicePixelRatio || 1;
|
|
46320
46895
|
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
|
|
46321
46896
|
ctx.clearRect(0, 0, w, h);
|
|
@@ -46354,7 +46929,7 @@ var init_GraphCanvas = __esm({
|
|
|
46354
46929
|
ctx.globalAlpha = 1;
|
|
46355
46930
|
for (const node of nodes) {
|
|
46356
46931
|
const size = node.size || 8;
|
|
46357
|
-
const color =
|
|
46932
|
+
const color = resolveColor3(node.color || getGroupColor(node.group, groups), canvas);
|
|
46358
46933
|
const isHovered = hoveredNode === node.id;
|
|
46359
46934
|
const isSelected = selectedNodeId !== void 0 && node.id === selectedNodeId;
|
|
46360
46935
|
ctx.globalAlpha = hoveredNode && !neighbors.has(node.id) ? 0.2 : 1;
|
|
@@ -53252,6 +53827,7 @@ var init_component_registry_generated = __esm({
|
|
|
53252
53827
|
init_BattleBoard();
|
|
53253
53828
|
init_BattleTemplate();
|
|
53254
53829
|
init_BehaviorView();
|
|
53830
|
+
init_BiologyCanvas();
|
|
53255
53831
|
init_BloomQuizBlock();
|
|
53256
53832
|
init_BoardgameBoard();
|
|
53257
53833
|
init_BookChapterView();
|
|
@@ -53263,7 +53839,6 @@ var init_component_registry_generated = __esm({
|
|
|
53263
53839
|
init_BranchingLogicBuilder();
|
|
53264
53840
|
init_Breadcrumb();
|
|
53265
53841
|
init_BuilderBoard();
|
|
53266
|
-
init_ButtonGroup();
|
|
53267
53842
|
init_CTABanner();
|
|
53268
53843
|
init_CalendarGrid();
|
|
53269
53844
|
init_Canvas2D();
|
|
@@ -53282,6 +53857,7 @@ var init_component_registry_generated = __esm({
|
|
|
53282
53857
|
init_ChartLegend();
|
|
53283
53858
|
init_ChatBar();
|
|
53284
53859
|
init_Checkbox();
|
|
53860
|
+
init_ChemistryCanvas();
|
|
53285
53861
|
init_ChoiceButton();
|
|
53286
53862
|
init_CityBuilderBoard();
|
|
53287
53863
|
init_CityBuilderTemplate();
|
|
@@ -53340,9 +53916,9 @@ var init_component_registry_generated = __esm({
|
|
|
53340
53916
|
init_FlipContainer();
|
|
53341
53917
|
init_FloatingActionButton();
|
|
53342
53918
|
init_Form();
|
|
53919
|
+
init_FormSection();
|
|
53343
53920
|
init_FormField();
|
|
53344
53921
|
init_FormSectionHeader();
|
|
53345
|
-
init_GameAudioProvider();
|
|
53346
53922
|
init_GameAudioToggle();
|
|
53347
53923
|
init_GameCard();
|
|
53348
53924
|
init_GameHud();
|
|
@@ -53373,6 +53949,7 @@ var init_component_registry_generated = __esm({
|
|
|
53373
53949
|
init_JazariStateMachine();
|
|
53374
53950
|
init_LandingPageTemplate();
|
|
53375
53951
|
init_LawReferenceTooltip();
|
|
53952
|
+
init_LearningCanvas();
|
|
53376
53953
|
init_Lightbox();
|
|
53377
53954
|
init_LikertScale();
|
|
53378
53955
|
init_LineChart();
|
|
@@ -53381,9 +53958,11 @@ var init_component_registry_generated = __esm({
|
|
|
53381
53958
|
init_MapView();
|
|
53382
53959
|
init_MarkdownContent();
|
|
53383
53960
|
init_MarketingFooter();
|
|
53961
|
+
init_MarketingStatCard();
|
|
53384
53962
|
init_MasterDetail();
|
|
53385
53963
|
init_MasterDetailLayout();
|
|
53386
53964
|
init_MatchPuzzleBoard();
|
|
53965
|
+
init_MathCanvas();
|
|
53387
53966
|
init_MatrixQuestion();
|
|
53388
53967
|
init_MediaGallery();
|
|
53389
53968
|
init_Menu();
|
|
@@ -53402,6 +53981,7 @@ var init_component_registry_generated = __esm({
|
|
|
53402
53981
|
init_PageHeader();
|
|
53403
53982
|
init_Pagination();
|
|
53404
53983
|
init_PatternTile();
|
|
53984
|
+
init_PhysicsCanvas();
|
|
53405
53985
|
init_PinballBoard();
|
|
53406
53986
|
init_PirateBoard();
|
|
53407
53987
|
init_PlatformerBoard();
|
|
@@ -53578,6 +54158,7 @@ var init_component_registry_generated = __esm({
|
|
|
53578
54158
|
"BattleBoard": BattleBoard,
|
|
53579
54159
|
"BattleTemplate": BattleTemplate,
|
|
53580
54160
|
"BehaviorView": exports.BehaviorView,
|
|
54161
|
+
"BiologyCanvas": BiologyCanvas,
|
|
53581
54162
|
"BloomQuizBlock": BloomQuizBlock,
|
|
53582
54163
|
"BoardgameBoard": BoardgameBoard,
|
|
53583
54164
|
"BookChapterView": BookChapterView,
|
|
@@ -53591,7 +54172,6 @@ var init_component_registry_generated = __esm({
|
|
|
53591
54172
|
"Breadcrumb": Breadcrumb,
|
|
53592
54173
|
"BuilderBoard": BuilderBoard,
|
|
53593
54174
|
"Button": ButtonPattern,
|
|
53594
|
-
"ButtonGroup": ButtonGroup,
|
|
53595
54175
|
"ButtonPattern": ButtonPattern,
|
|
53596
54176
|
"CTABanner": CTABanner,
|
|
53597
54177
|
"CalendarGrid": CalendarGrid,
|
|
@@ -53611,6 +54191,7 @@ var init_component_registry_generated = __esm({
|
|
|
53611
54191
|
"ChartLegend": ChartLegend,
|
|
53612
54192
|
"ChatBar": ChatBar,
|
|
53613
54193
|
"Checkbox": Checkbox,
|
|
54194
|
+
"ChemistryCanvas": ChemistryCanvas,
|
|
53614
54195
|
"ChoiceButton": ChoiceButton,
|
|
53615
54196
|
"CityBuilderBoard": CityBuilderBoard,
|
|
53616
54197
|
"CityBuilderTemplate": CityBuilderTemplate,
|
|
@@ -53671,9 +54252,10 @@ var init_component_registry_generated = __esm({
|
|
|
53671
54252
|
"FlipContainer": FlipContainer,
|
|
53672
54253
|
"FloatingActionButton": FloatingActionButton,
|
|
53673
54254
|
"Form": Form,
|
|
54255
|
+
"FormActions": FormActions,
|
|
53674
54256
|
"FormField": FormField,
|
|
54257
|
+
"FormLayout": FormLayout,
|
|
53675
54258
|
"FormSectionHeader": FormSectionHeader,
|
|
53676
|
-
"GameAudioProvider": GameAudioProvider,
|
|
53677
54259
|
"GameAudioToggle": GameAudioToggle,
|
|
53678
54260
|
"GameBoard3D": GameBoard3D,
|
|
53679
54261
|
"GameCanvas3D": GameCanvas3D,
|
|
@@ -53711,6 +54293,7 @@ var init_component_registry_generated = __esm({
|
|
|
53711
54293
|
"LabelPattern": LabelPattern,
|
|
53712
54294
|
"LandingPageTemplate": LandingPageTemplate,
|
|
53713
54295
|
"LawReferenceTooltip": LawReferenceTooltip,
|
|
54296
|
+
"LearningCanvas": LearningCanvas,
|
|
53714
54297
|
"Lightbox": Lightbox,
|
|
53715
54298
|
"LikertScale": LikertScale,
|
|
53716
54299
|
"LineChart": LineChart2,
|
|
@@ -53719,9 +54302,11 @@ var init_component_registry_generated = __esm({
|
|
|
53719
54302
|
"MapView": MapView,
|
|
53720
54303
|
"MarkdownContent": MarkdownContent,
|
|
53721
54304
|
"MarketingFooter": MarketingFooter,
|
|
54305
|
+
"MarketingStatCard": MarketingStatCard,
|
|
53722
54306
|
"MasterDetail": MasterDetail,
|
|
53723
54307
|
"MasterDetailLayout": MasterDetailLayout,
|
|
53724
54308
|
"MatchPuzzleBoard": MatchPuzzleBoard,
|
|
54309
|
+
"MathCanvas": MathCanvas,
|
|
53725
54310
|
"MatrixQuestion": MatrixQuestion,
|
|
53726
54311
|
"MediaGallery": MediaGallery,
|
|
53727
54312
|
"Menu": Menu,
|
|
@@ -53740,6 +54325,7 @@ var init_component_registry_generated = __esm({
|
|
|
53740
54325
|
"PageHeader": PageHeader,
|
|
53741
54326
|
"Pagination": Pagination,
|
|
53742
54327
|
"PatternTile": PatternTile,
|
|
54328
|
+
"PhysicsCanvas": PhysicsCanvas,
|
|
53743
54329
|
"PinballBoard": PinballBoard,
|
|
53744
54330
|
"PirateBoard": PirateBoard,
|
|
53745
54331
|
"PlatformerBoard": PlatformerBoard,
|