@almadar/ui 5.76.7 → 5.78.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/avl/index.cjs +1121 -535
- package/dist/avl/index.css +3 -3
- package/dist/avl/index.js +1122 -536
- package/dist/components/core/atoms/FlipContainer.d.ts +1 -1
- package/dist/components/core/atoms/index.d.ts +2 -1
- package/dist/components/core/molecules/Card.d.ts +1 -1
- package/dist/components/core/molecules/DataGrid.d.ts +1 -1
- package/dist/components/core/molecules/DataList.d.ts +1 -1
- package/dist/components/core/molecules/DocPagination.d.ts +1 -8
- package/dist/components/core/molecules/DocSearch.d.ts +1 -8
- package/dist/components/core/molecules/FlipCard.d.ts +1 -1
- package/dist/components/core/molecules/MapView.d.ts +1 -18
- package/dist/components/core/molecules/SortableList.d.ts +1 -1
- package/dist/components/core/molecules/TableView.d.ts +1 -1
- package/dist/components/core/molecules/index.d.ts +4 -0
- package/dist/components/core/organisms/DataTable.d.ts +1 -1
- package/dist/components/core/organisms/debug/RuntimeDebugger.d.ts +1 -10
- package/dist/components/core/organisms/debug/tabs/EntitiesTab.d.ts +1 -2
- package/dist/components/core/organisms/debug/tabs/EventDispatcherTab.d.ts +1 -2
- package/dist/components/core/organisms/debug/tabs/EventFlowTab.d.ts +1 -6
- package/dist/components/core/organisms/debug/tabs/GuardsPanel.d.ts +1 -6
- package/dist/components/core/organisms/debug/tabs/ServerBridgeTab.d.ts +1 -12
- package/dist/components/core/organisms/debug/tabs/TicksTab.d.ts +1 -2
- package/dist/components/core/organisms/debug/tabs/TraitsTab.d.ts +1 -2
- package/dist/components/core/organisms/debug/tabs/TransitionTimeline.d.ts +1 -11
- package/dist/components/core/organisms/debug/tabs/VerificationTab.d.ts +1 -8
- package/dist/components/game/2d/atoms/ActionButton.d.ts +1 -2
- package/dist/components/game/2d/atoms/ChoiceButton.d.ts +1 -2
- package/dist/components/game/2d/atoms/ComboCounter.d.ts +1 -2
- package/dist/components/game/2d/atoms/ControlButton.d.ts +1 -2
- package/dist/components/game/2d/atoms/DamageNumber.d.ts +1 -2
- package/dist/components/game/2d/atoms/DialogueBubble.d.ts +1 -2
- package/dist/components/game/2d/atoms/GameIcon.d.ts +1 -2
- package/dist/components/game/2d/atoms/HealthBar.d.ts +1 -2
- package/dist/components/game/2d/atoms/ItemSlot.d.ts +1 -2
- package/dist/components/game/2d/atoms/MiniMap.d.ts +1 -2
- package/dist/components/game/2d/atoms/ResourceCounter.d.ts +1 -2
- package/dist/components/game/2d/atoms/ScoreDisplay.d.ts +1 -2
- package/dist/components/game/2d/atoms/StatusEffect.d.ts +1 -2
- package/dist/components/game/2d/atoms/TimerDisplay.d.ts +1 -2
- package/dist/components/game/2d/atoms/TurnIndicator.d.ts +1 -2
- package/dist/components/game/2d/atoms/WaypointMarker.d.ts +1 -2
- package/dist/components/game/2d/molecules/GameHud.d.ts +1 -2
- package/dist/components/game/2d/molecules/GameMenu.d.ts +1 -2
- package/dist/components/game/2d/molecules/InventoryGrid.d.ts +1 -2
- package/dist/components/game/2d/molecules/ResourceBar.d.ts +1 -2
- package/dist/components/game/2d/molecules/StatBadge.d.ts +1 -2
- package/dist/components/game/shared/lib/editorUtils.d.ts +8 -8
- package/dist/components/index.cjs +2334 -1653
- package/dist/components/index.css +3 -3
- package/dist/components/index.js +1124 -443
- package/dist/components/learning/atoms/LearningCanvas.d.ts +80 -0
- package/dist/components/learning/molecules/BiologyCanvas.d.ts +47 -0
- package/dist/components/learning/molecules/ChemistryCanvas.d.ts +55 -0
- package/dist/components/learning/molecules/MathCanvas.d.ts +61 -0
- package/dist/components/learning/molecules/PhysicsCanvas.d.ts +54 -0
- package/dist/components/marketing/atoms/MarketingStatCard.d.ts +2 -2
- package/dist/hooks/useGitHub.d.ts +7 -7
- package/dist/marketing/index.cjs +2 -2
- package/dist/marketing/index.js +2 -2
- package/dist/providers/EventBusProvider.d.ts +1 -1
- package/dist/providers/SelectionProvider.d.ts +1 -1
- package/dist/providers/ServerBridge.d.ts +1 -1
- package/dist/providers/index.cjs +1117 -531
- package/dist/providers/index.css +3 -3
- package/dist/providers/index.js +1118 -532
- package/dist/runtime/index.cjs +1117 -531
- package/dist/runtime/index.css +3 -3
- package/dist/runtime/index.js +1118 -532
- package/package.json +2 -2
package/dist/providers/index.cjs
CHANGED
|
@@ -5676,6 +5676,66 @@ var init_RangeSlider = __esm({
|
|
|
5676
5676
|
RangeSlider.displayName = "RangeSlider";
|
|
5677
5677
|
}
|
|
5678
5678
|
});
|
|
5679
|
+
function easeOut(t) {
|
|
5680
|
+
return t * (2 - t);
|
|
5681
|
+
}
|
|
5682
|
+
var AnimatedCounter;
|
|
5683
|
+
var init_AnimatedCounter = __esm({
|
|
5684
|
+
"components/marketing/atoms/AnimatedCounter.tsx"() {
|
|
5685
|
+
"use client";
|
|
5686
|
+
init_cn();
|
|
5687
|
+
init_Typography();
|
|
5688
|
+
AnimatedCounter = ({
|
|
5689
|
+
value: rawValue,
|
|
5690
|
+
duration = 600,
|
|
5691
|
+
prefix,
|
|
5692
|
+
suffix,
|
|
5693
|
+
className
|
|
5694
|
+
}) => {
|
|
5695
|
+
const numericRaw = typeof rawValue === "number" ? rawValue : Number.parseFloat(String(rawValue ?? ""));
|
|
5696
|
+
const value = !Number.isNaN(numericRaw) ? numericRaw : 0;
|
|
5697
|
+
const [displayValue, setDisplayValue] = React107.useState(value);
|
|
5698
|
+
const previousValueRef = React107.useRef(value);
|
|
5699
|
+
const animationFrameRef = React107.useRef(null);
|
|
5700
|
+
React107.useEffect(() => {
|
|
5701
|
+
const from = previousValueRef.current;
|
|
5702
|
+
const to = value;
|
|
5703
|
+
previousValueRef.current = value;
|
|
5704
|
+
if (from === to) {
|
|
5705
|
+
setDisplayValue(to);
|
|
5706
|
+
return;
|
|
5707
|
+
}
|
|
5708
|
+
const startTime = performance.now();
|
|
5709
|
+
const diff = to - from;
|
|
5710
|
+
function animate(currentTime) {
|
|
5711
|
+
const elapsed = currentTime - startTime;
|
|
5712
|
+
const progress = Math.min(elapsed / duration, 1);
|
|
5713
|
+
const easedProgress = easeOut(progress);
|
|
5714
|
+
setDisplayValue(from + diff * easedProgress);
|
|
5715
|
+
if (progress < 1) {
|
|
5716
|
+
animationFrameRef.current = requestAnimationFrame(animate);
|
|
5717
|
+
} else {
|
|
5718
|
+
setDisplayValue(to);
|
|
5719
|
+
}
|
|
5720
|
+
}
|
|
5721
|
+
animationFrameRef.current = requestAnimationFrame(animate);
|
|
5722
|
+
return () => {
|
|
5723
|
+
if (animationFrameRef.current !== null) {
|
|
5724
|
+
cancelAnimationFrame(animationFrameRef.current);
|
|
5725
|
+
}
|
|
5726
|
+
};
|
|
5727
|
+
}, [value, duration]);
|
|
5728
|
+
const decimalPlaces = Number.isInteger(value) ? 0 : String(value).split(".")[1]?.length ?? 0;
|
|
5729
|
+
const formattedValue = displayValue.toFixed(decimalPlaces);
|
|
5730
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(Typography, { variant: "h3", className: cn("tabular-nums", className), children: [
|
|
5731
|
+
prefix,
|
|
5732
|
+
formattedValue,
|
|
5733
|
+
suffix
|
|
5734
|
+
] });
|
|
5735
|
+
};
|
|
5736
|
+
AnimatedCounter.displayName = "AnimatedCounter";
|
|
5737
|
+
}
|
|
5738
|
+
});
|
|
5679
5739
|
function useInfiniteScroll(onLoadMore, options = {}) {
|
|
5680
5740
|
const { rootMargin = "200px", hasMore = true, isLoading = false } = options;
|
|
5681
5741
|
const observerRef = React107.useRef(null);
|
|
@@ -5972,6 +6032,34 @@ var init_SectionHeader = __esm({
|
|
|
5972
6032
|
SectionHeader.displayName = "SectionHeader";
|
|
5973
6033
|
}
|
|
5974
6034
|
});
|
|
6035
|
+
var sizeClasses6, MarketingStatCard;
|
|
6036
|
+
var init_MarketingStatCard = __esm({
|
|
6037
|
+
"components/marketing/atoms/MarketingStatCard.tsx"() {
|
|
6038
|
+
init_cn();
|
|
6039
|
+
init_Stack();
|
|
6040
|
+
init_Typography();
|
|
6041
|
+
sizeClasses6 = {
|
|
6042
|
+
sm: "text-2xl",
|
|
6043
|
+
md: "text-4xl",
|
|
6044
|
+
lg: "text-5xl"
|
|
6045
|
+
};
|
|
6046
|
+
MarketingStatCard = ({ value, label, size = "md", className }) => {
|
|
6047
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "xs", align: "center", className: cn(className), children: [
|
|
6048
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
6049
|
+
Typography,
|
|
6050
|
+
{
|
|
6051
|
+
weight: "bold",
|
|
6052
|
+
color: "primary",
|
|
6053
|
+
className: cn(sizeClasses6[size]),
|
|
6054
|
+
children: value
|
|
6055
|
+
}
|
|
6056
|
+
),
|
|
6057
|
+
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", color: "muted", children: label })
|
|
6058
|
+
] });
|
|
6059
|
+
};
|
|
6060
|
+
MarketingStatCard.displayName = "MarketingStatCard";
|
|
6061
|
+
}
|
|
6062
|
+
});
|
|
5975
6063
|
var backgroundClasses, paddingClasses, ContentSection;
|
|
5976
6064
|
var init_ContentSection = __esm({
|
|
5977
6065
|
"components/marketing/atoms/ContentSection.tsx"() {
|
|
@@ -12153,318 +12241,14 @@ var init_useCanvasEffects = __esm({
|
|
|
12153
12241
|
"use client";
|
|
12154
12242
|
}
|
|
12155
12243
|
});
|
|
12156
|
-
function pickPath(entry) {
|
|
12157
|
-
if (Array.isArray(entry.path)) {
|
|
12158
|
-
return entry.path[Math.floor(Math.random() * entry.path.length)];
|
|
12159
|
-
}
|
|
12160
|
-
return entry.path;
|
|
12161
|
-
}
|
|
12162
|
-
function useGameAudio({
|
|
12163
|
-
manifest,
|
|
12164
|
-
baseUrl = "",
|
|
12165
|
-
initialMuted = false,
|
|
12166
|
-
initialVolume = 1
|
|
12167
|
-
}) {
|
|
12168
|
-
const [muted, setMutedState] = React107.useState(initialMuted);
|
|
12169
|
-
const [masterVolume, setMasterVolumeState] = React107.useState(initialVolume);
|
|
12170
|
-
const mutedRef = React107.useRef(muted);
|
|
12171
|
-
const volumeRef = React107.useRef(masterVolume);
|
|
12172
|
-
const manifestRef = React107.useRef(manifest);
|
|
12173
|
-
mutedRef.current = muted;
|
|
12174
|
-
volumeRef.current = masterVolume;
|
|
12175
|
-
manifestRef.current = manifest;
|
|
12176
|
-
const poolsRef = React107.useRef(/* @__PURE__ */ new Map());
|
|
12177
|
-
const getOrCreateElement = React107.useCallback((key) => {
|
|
12178
|
-
const entry = manifestRef.current[key];
|
|
12179
|
-
if (!entry) return null;
|
|
12180
|
-
let pool = poolsRef.current.get(key);
|
|
12181
|
-
if (!pool) {
|
|
12182
|
-
pool = [];
|
|
12183
|
-
poolsRef.current.set(key, pool);
|
|
12184
|
-
}
|
|
12185
|
-
const maxSize = entry.poolSize ?? 1;
|
|
12186
|
-
for (const audio of pool) {
|
|
12187
|
-
if (audio.paused && (audio.ended || audio.currentTime === 0)) {
|
|
12188
|
-
return audio;
|
|
12189
|
-
}
|
|
12190
|
-
}
|
|
12191
|
-
if (pool.length < maxSize) {
|
|
12192
|
-
const src = baseUrl + pickPath(entry);
|
|
12193
|
-
const audio = new Audio(src);
|
|
12194
|
-
audio.loop = entry.loop ?? false;
|
|
12195
|
-
pool.push(audio);
|
|
12196
|
-
return audio;
|
|
12197
|
-
}
|
|
12198
|
-
if (!entry.loop) {
|
|
12199
|
-
let oldest = pool[0];
|
|
12200
|
-
for (const audio of pool) {
|
|
12201
|
-
if (audio.currentTime > oldest.currentTime) {
|
|
12202
|
-
oldest = audio;
|
|
12203
|
-
}
|
|
12204
|
-
}
|
|
12205
|
-
oldest.pause();
|
|
12206
|
-
oldest.currentTime = 0;
|
|
12207
|
-
return oldest;
|
|
12208
|
-
}
|
|
12209
|
-
return null;
|
|
12210
|
-
}, [baseUrl]);
|
|
12211
|
-
const play = React107.useCallback((key) => {
|
|
12212
|
-
if (mutedRef.current) return;
|
|
12213
|
-
const entry = manifestRef.current[key];
|
|
12214
|
-
if (!entry) return;
|
|
12215
|
-
const audio = getOrCreateElement(key);
|
|
12216
|
-
if (!audio) return;
|
|
12217
|
-
audio.volume = Math.min(1, (entry.volume ?? 1) * volumeRef.current);
|
|
12218
|
-
if (!entry.loop) {
|
|
12219
|
-
audio.currentTime = 0;
|
|
12220
|
-
}
|
|
12221
|
-
const promise = audio.play();
|
|
12222
|
-
if (promise) {
|
|
12223
|
-
promise.catch(() => {
|
|
12224
|
-
});
|
|
12225
|
-
}
|
|
12226
|
-
}, [getOrCreateElement]);
|
|
12227
|
-
const stop = React107.useCallback((key) => {
|
|
12228
|
-
const pool = poolsRef.current.get(key);
|
|
12229
|
-
if (!pool) return;
|
|
12230
|
-
for (const audio of pool) {
|
|
12231
|
-
audio.pause();
|
|
12232
|
-
audio.currentTime = 0;
|
|
12233
|
-
}
|
|
12234
|
-
}, []);
|
|
12235
|
-
const currentMusicKeyRef = React107.useRef(null);
|
|
12236
|
-
const currentMusicElRef = React107.useRef(null);
|
|
12237
|
-
const musicFadeRef = React107.useRef(null);
|
|
12238
|
-
const pendingMusicKeyRef = React107.useRef(null);
|
|
12239
|
-
const clearMusicFade = React107.useCallback(() => {
|
|
12240
|
-
if (musicFadeRef.current) {
|
|
12241
|
-
clearInterval(musicFadeRef.current);
|
|
12242
|
-
musicFadeRef.current = null;
|
|
12243
|
-
}
|
|
12244
|
-
}, []);
|
|
12245
|
-
const playMusic = React107.useCallback((key) => {
|
|
12246
|
-
if (key === currentMusicKeyRef.current) return;
|
|
12247
|
-
pendingMusicKeyRef.current = key;
|
|
12248
|
-
const entry = manifestRef.current[key];
|
|
12249
|
-
if (!entry) return;
|
|
12250
|
-
const fadeDurationMs = entry.crossfadeDurationMs ?? 1500;
|
|
12251
|
-
const stepMs = 50;
|
|
12252
|
-
const totalSteps = Math.max(1, fadeDurationMs / stepMs);
|
|
12253
|
-
const targetVolume = Math.min(1, (entry.volume ?? 1) * volumeRef.current);
|
|
12254
|
-
clearMusicFade();
|
|
12255
|
-
const src = baseUrl + (Array.isArray(entry.path) ? entry.path[0] : entry.path);
|
|
12256
|
-
const incoming = new Audio(src);
|
|
12257
|
-
incoming.loop = true;
|
|
12258
|
-
incoming.volume = 0;
|
|
12259
|
-
const outgoing = currentMusicElRef.current;
|
|
12260
|
-
const outgoingStartVol = outgoing?.volume ?? 0;
|
|
12261
|
-
currentMusicKeyRef.current = key;
|
|
12262
|
-
currentMusicElRef.current = incoming;
|
|
12263
|
-
if (!mutedRef.current) {
|
|
12264
|
-
incoming.play().catch(() => {
|
|
12265
|
-
currentMusicKeyRef.current = null;
|
|
12266
|
-
currentMusicElRef.current = outgoing;
|
|
12267
|
-
});
|
|
12268
|
-
}
|
|
12269
|
-
let step = 0;
|
|
12270
|
-
musicFadeRef.current = setInterval(() => {
|
|
12271
|
-
step++;
|
|
12272
|
-
const progress = Math.min(step / totalSteps, 1);
|
|
12273
|
-
incoming.volume = Math.min(1, targetVolume * progress);
|
|
12274
|
-
if (outgoing) {
|
|
12275
|
-
outgoing.volume = Math.max(0, outgoingStartVol * (1 - progress));
|
|
12276
|
-
}
|
|
12277
|
-
if (progress >= 1) {
|
|
12278
|
-
clearMusicFade();
|
|
12279
|
-
if (outgoing) {
|
|
12280
|
-
outgoing.pause();
|
|
12281
|
-
outgoing.src = "";
|
|
12282
|
-
}
|
|
12283
|
-
}
|
|
12284
|
-
}, stepMs);
|
|
12285
|
-
}, [baseUrl, clearMusicFade]);
|
|
12286
|
-
const stopMusic = React107.useCallback((fadeDurationMs = 1e3) => {
|
|
12287
|
-
const outgoing = currentMusicElRef.current;
|
|
12288
|
-
if (!outgoing) return;
|
|
12289
|
-
currentMusicKeyRef.current = null;
|
|
12290
|
-
currentMusicElRef.current = null;
|
|
12291
|
-
pendingMusicKeyRef.current = null;
|
|
12292
|
-
clearMusicFade();
|
|
12293
|
-
const startVolume = outgoing.volume;
|
|
12294
|
-
const stepMs = 50;
|
|
12295
|
-
const totalSteps = Math.max(1, fadeDurationMs / stepMs);
|
|
12296
|
-
let step = 0;
|
|
12297
|
-
musicFadeRef.current = setInterval(() => {
|
|
12298
|
-
step++;
|
|
12299
|
-
const progress = step / totalSteps;
|
|
12300
|
-
outgoing.volume = Math.max(0, startVolume * (1 - progress));
|
|
12301
|
-
if (progress >= 1) {
|
|
12302
|
-
clearMusicFade();
|
|
12303
|
-
outgoing.pause();
|
|
12304
|
-
outgoing.src = "";
|
|
12305
|
-
}
|
|
12306
|
-
}, stepMs);
|
|
12307
|
-
}, [clearMusicFade]);
|
|
12308
|
-
const stopAll = React107.useCallback(() => {
|
|
12309
|
-
for (const pool of poolsRef.current.values()) {
|
|
12310
|
-
for (const audio of pool) {
|
|
12311
|
-
audio.pause();
|
|
12312
|
-
audio.currentTime = 0;
|
|
12313
|
-
}
|
|
12314
|
-
}
|
|
12315
|
-
stopMusic(0);
|
|
12316
|
-
}, [stopMusic]);
|
|
12317
|
-
const setMuted = React107.useCallback((value) => {
|
|
12318
|
-
setMutedState(value);
|
|
12319
|
-
if (value) {
|
|
12320
|
-
for (const [key, pool] of poolsRef.current.entries()) {
|
|
12321
|
-
if (manifestRef.current[key]?.loop) {
|
|
12322
|
-
for (const audio of pool) {
|
|
12323
|
-
if (!audio.paused) audio.pause();
|
|
12324
|
-
}
|
|
12325
|
-
}
|
|
12326
|
-
}
|
|
12327
|
-
currentMusicElRef.current?.pause();
|
|
12328
|
-
} else {
|
|
12329
|
-
for (const [key, pool] of poolsRef.current.entries()) {
|
|
12330
|
-
const entry = manifestRef.current[key];
|
|
12331
|
-
if (entry?.loop && entry?.autostart) {
|
|
12332
|
-
for (const audio of pool) {
|
|
12333
|
-
if (audio.paused) audio.play().catch(() => {
|
|
12334
|
-
});
|
|
12335
|
-
}
|
|
12336
|
-
}
|
|
12337
|
-
}
|
|
12338
|
-
const musicEl = currentMusicElRef.current;
|
|
12339
|
-
if (musicEl) {
|
|
12340
|
-
musicEl.play().catch(() => {
|
|
12341
|
-
});
|
|
12342
|
-
}
|
|
12343
|
-
}
|
|
12344
|
-
}, []);
|
|
12345
|
-
const setMasterVolume = React107.useCallback((volume) => {
|
|
12346
|
-
const clamped = Math.max(0, Math.min(1, volume));
|
|
12347
|
-
setMasterVolumeState(clamped);
|
|
12348
|
-
for (const [key, pool] of poolsRef.current.entries()) {
|
|
12349
|
-
const entryVol = manifestRef.current[key]?.volume ?? 1;
|
|
12350
|
-
for (const audio of pool) {
|
|
12351
|
-
audio.volume = Math.min(1, entryVol * clamped);
|
|
12352
|
-
}
|
|
12353
|
-
}
|
|
12354
|
-
if (!musicFadeRef.current && currentMusicElRef.current) {
|
|
12355
|
-
const key = currentMusicKeyRef.current;
|
|
12356
|
-
const entryVol = key ? manifestRef.current[key]?.volume ?? 1 : 1;
|
|
12357
|
-
currentMusicElRef.current.volume = Math.min(1, entryVol * clamped);
|
|
12358
|
-
}
|
|
12359
|
-
}, []);
|
|
12360
|
-
const unlockedRef = React107.useRef(false);
|
|
12361
|
-
React107.useEffect(() => {
|
|
12362
|
-
const autoKeys = Object.keys(manifest).filter((k) => manifest[k].autostart);
|
|
12363
|
-
const hasPendingMusic = () => pendingMusicKeyRef.current !== null;
|
|
12364
|
-
const hasAutoStart = autoKeys.length > 0;
|
|
12365
|
-
if (!hasAutoStart && !hasPendingMusic()) return;
|
|
12366
|
-
const unlock = () => {
|
|
12367
|
-
if (unlockedRef.current) return;
|
|
12368
|
-
unlockedRef.current = true;
|
|
12369
|
-
if (!mutedRef.current) {
|
|
12370
|
-
for (const key of autoKeys) {
|
|
12371
|
-
play(key);
|
|
12372
|
-
}
|
|
12373
|
-
const pending = pendingMusicKeyRef.current;
|
|
12374
|
-
if (pending && pending !== currentMusicKeyRef.current) {
|
|
12375
|
-
playMusic(pending);
|
|
12376
|
-
}
|
|
12377
|
-
}
|
|
12378
|
-
};
|
|
12379
|
-
document.addEventListener("click", unlock, { once: true });
|
|
12380
|
-
document.addEventListener("keydown", unlock, { once: true });
|
|
12381
|
-
document.addEventListener("touchstart", unlock, { once: true });
|
|
12382
|
-
return () => {
|
|
12383
|
-
document.removeEventListener("click", unlock);
|
|
12384
|
-
document.removeEventListener("keydown", unlock);
|
|
12385
|
-
document.removeEventListener("touchstart", unlock);
|
|
12386
|
-
};
|
|
12387
|
-
}, [manifest, play, playMusic]);
|
|
12388
|
-
React107.useEffect(() => {
|
|
12389
|
-
return () => {
|
|
12390
|
-
clearMusicFade();
|
|
12391
|
-
for (const pool of poolsRef.current.values()) {
|
|
12392
|
-
for (const audio of pool) {
|
|
12393
|
-
audio.pause();
|
|
12394
|
-
audio.src = "";
|
|
12395
|
-
}
|
|
12396
|
-
}
|
|
12397
|
-
poolsRef.current.clear();
|
|
12398
|
-
if (currentMusicElRef.current) {
|
|
12399
|
-
currentMusicElRef.current.pause();
|
|
12400
|
-
currentMusicElRef.current.src = "";
|
|
12401
|
-
currentMusicElRef.current = null;
|
|
12402
|
-
}
|
|
12403
|
-
};
|
|
12404
|
-
}, [clearMusicFade]);
|
|
12405
|
-
return {
|
|
12406
|
-
play,
|
|
12407
|
-
stop,
|
|
12408
|
-
stopAll,
|
|
12409
|
-
playMusic,
|
|
12410
|
-
stopMusic,
|
|
12411
|
-
muted,
|
|
12412
|
-
setMuted,
|
|
12413
|
-
masterVolume,
|
|
12414
|
-
setMasterVolume
|
|
12415
|
-
};
|
|
12416
|
-
}
|
|
12417
12244
|
var init_useGameAudio = __esm({
|
|
12418
12245
|
"components/game/shared/hooks/useGameAudio.ts"() {
|
|
12419
12246
|
"use client";
|
|
12420
|
-
useGameAudio.displayName = "useGameAudio";
|
|
12421
12247
|
}
|
|
12422
12248
|
});
|
|
12423
12249
|
function useGameAudioContextOptional() {
|
|
12424
12250
|
return React107.useContext(GameAudioContext);
|
|
12425
12251
|
}
|
|
12426
|
-
function GameAudioProvider({
|
|
12427
|
-
manifest,
|
|
12428
|
-
baseUrl = "",
|
|
12429
|
-
children,
|
|
12430
|
-
initialMuted = false
|
|
12431
|
-
}) {
|
|
12432
|
-
const eventBus = useEventBus();
|
|
12433
|
-
const { play, stop, stopAll, playMusic, stopMusic, muted, setMuted, masterVolume, setMasterVolume } = useGameAudio({ manifest, baseUrl, initialMuted });
|
|
12434
|
-
React107.useEffect(() => {
|
|
12435
|
-
const unsubPlay = eventBus.on("UI:PLAY_SOUND", (event) => {
|
|
12436
|
-
const key = event.payload?.key;
|
|
12437
|
-
if (key) play(key);
|
|
12438
|
-
});
|
|
12439
|
-
const unsubStop = eventBus.on("UI:STOP_SOUND", (event) => {
|
|
12440
|
-
const key = event.payload?.key;
|
|
12441
|
-
if (key) {
|
|
12442
|
-
stop(key);
|
|
12443
|
-
} else {
|
|
12444
|
-
stopAll();
|
|
12445
|
-
}
|
|
12446
|
-
});
|
|
12447
|
-
const unsubChangeMusic = eventBus.on("UI:CHANGE_MUSIC", (event) => {
|
|
12448
|
-
const key = event.payload?.key;
|
|
12449
|
-
if (key) {
|
|
12450
|
-
playMusic(key);
|
|
12451
|
-
} else {
|
|
12452
|
-
stopMusic();
|
|
12453
|
-
}
|
|
12454
|
-
});
|
|
12455
|
-
const unsubStopMusic = eventBus.on("UI:STOP_MUSIC", () => {
|
|
12456
|
-
stopMusic();
|
|
12457
|
-
});
|
|
12458
|
-
return () => {
|
|
12459
|
-
unsubPlay();
|
|
12460
|
-
unsubStop();
|
|
12461
|
-
unsubChangeMusic();
|
|
12462
|
-
unsubStopMusic();
|
|
12463
|
-
};
|
|
12464
|
-
}, [eventBus, play, stop, stopAll, playMusic, stopMusic]);
|
|
12465
|
-
const value = { muted, setMuted, masterVolume, setMasterVolume, play, playMusic, stopMusic };
|
|
12466
|
-
return /* @__PURE__ */ jsxRuntime.jsx(GameAudioContext.Provider, { value, children });
|
|
12467
|
-
}
|
|
12468
12252
|
var GameAudioContext;
|
|
12469
12253
|
var init_GameAudioProvider = __esm({
|
|
12470
12254
|
"components/game/shared/providers/GameAudioProvider.tsx"() {
|
|
@@ -12473,7 +12257,6 @@ var init_GameAudioProvider = __esm({
|
|
|
12473
12257
|
init_useGameAudio();
|
|
12474
12258
|
GameAudioContext = React107.createContext(null);
|
|
12475
12259
|
GameAudioContext.displayName = "GameAudioContext";
|
|
12476
|
-
GameAudioProvider.displayName = "GameAudioProvider";
|
|
12477
12260
|
}
|
|
12478
12261
|
});
|
|
12479
12262
|
function GameAudioToggle({
|
|
@@ -20109,6 +19892,315 @@ var init_molecules = __esm({
|
|
|
20109
19892
|
init_puzzleObject();
|
|
20110
19893
|
}
|
|
20111
19894
|
});
|
|
19895
|
+
function resolveColor2(color, ctx, fallback) {
|
|
19896
|
+
if (!color) return fallback;
|
|
19897
|
+
if (color.startsWith("var(")) {
|
|
19898
|
+
ctx.canvas.style;
|
|
19899
|
+
const m = /^var\((--[^,)]+)(?:,\s*([^)]+))?\)$/.exec(color);
|
|
19900
|
+
if (m) {
|
|
19901
|
+
const computed = getComputedStyle(ctx.canvas).getPropertyValue(m[1]).trim();
|
|
19902
|
+
return computed || m[2]?.trim() || fallback;
|
|
19903
|
+
}
|
|
19904
|
+
}
|
|
19905
|
+
return color;
|
|
19906
|
+
}
|
|
19907
|
+
function shapeBounds(shape) {
|
|
19908
|
+
switch (shape.type) {
|
|
19909
|
+
case "line":
|
|
19910
|
+
case "arrow":
|
|
19911
|
+
if (shape.x1 == null || shape.y1 == null || shape.x2 == null || shape.y2 == null) return null;
|
|
19912
|
+
return {
|
|
19913
|
+
x: Math.min(shape.x1, shape.x2) - 6,
|
|
19914
|
+
y: Math.min(shape.y1, shape.y2) - 6,
|
|
19915
|
+
w: Math.abs(shape.x2 - shape.x1) + 12,
|
|
19916
|
+
h: Math.abs(shape.y2 - shape.y1) + 12
|
|
19917
|
+
};
|
|
19918
|
+
case "circle":
|
|
19919
|
+
if (shape.x == null || shape.y == null || shape.radius == null) return null;
|
|
19920
|
+
return {
|
|
19921
|
+
x: shape.x - shape.radius - 4,
|
|
19922
|
+
y: shape.y - shape.radius - 4,
|
|
19923
|
+
w: shape.radius * 2 + 8,
|
|
19924
|
+
h: shape.radius * 2 + 8
|
|
19925
|
+
};
|
|
19926
|
+
case "rect":
|
|
19927
|
+
if (shape.x == null || shape.y == null || shape.width == null || shape.height == null) return null;
|
|
19928
|
+
return { x: shape.x - 4, y: shape.y - 4, w: shape.width + 8, h: shape.height + 8 };
|
|
19929
|
+
case "polygon":
|
|
19930
|
+
if (!shape.points || shape.points.length === 0) return null;
|
|
19931
|
+
{
|
|
19932
|
+
const xs = shape.points.map((p2) => p2.x);
|
|
19933
|
+
const ys = shape.points.map((p2) => p2.y);
|
|
19934
|
+
const minX = Math.min(...xs);
|
|
19935
|
+
const minY = Math.min(...ys);
|
|
19936
|
+
return {
|
|
19937
|
+
x: minX - 4,
|
|
19938
|
+
y: minY - 4,
|
|
19939
|
+
w: Math.max(...xs) - minX + 8,
|
|
19940
|
+
h: Math.max(...ys) - minY + 8
|
|
19941
|
+
};
|
|
19942
|
+
}
|
|
19943
|
+
case "text":
|
|
19944
|
+
if (shape.x == null || shape.y == null) return null;
|
|
19945
|
+
return { x: shape.x - 4, y: shape.y - (shape.fontSize ?? 14) - 4, w: 120, h: (shape.fontSize ?? 14) + 8 };
|
|
19946
|
+
default:
|
|
19947
|
+
return null;
|
|
19948
|
+
}
|
|
19949
|
+
}
|
|
19950
|
+
function drawArrowHead(ctx, x1, y1, x2, y2, size) {
|
|
19951
|
+
const angle = Math.atan2(y2 - y1, x2 - x1);
|
|
19952
|
+
ctx.beginPath();
|
|
19953
|
+
ctx.moveTo(x2, y2);
|
|
19954
|
+
ctx.lineTo(x2 - size * Math.cos(angle - Math.PI / 6), y2 - size * Math.sin(angle - Math.PI / 6));
|
|
19955
|
+
ctx.lineTo(x2 - size * Math.cos(angle + Math.PI / 6), y2 - size * Math.sin(angle + Math.PI / 6));
|
|
19956
|
+
ctx.closePath();
|
|
19957
|
+
ctx.fill();
|
|
19958
|
+
}
|
|
19959
|
+
function drawShape(ctx, shape, width, height) {
|
|
19960
|
+
ctx.save();
|
|
19961
|
+
const opacity = shape.opacity ?? 1;
|
|
19962
|
+
ctx.globalAlpha = opacity;
|
|
19963
|
+
const stroke = resolveColor2(shape.color, ctx, "#333333");
|
|
19964
|
+
const fill = shape.fill ? resolveColor2(shape.fill, ctx, "#cccccc") : void 0;
|
|
19965
|
+
ctx.lineWidth = shape.lineWidth ?? 2;
|
|
19966
|
+
switch (shape.type) {
|
|
19967
|
+
case "grid": {
|
|
19968
|
+
const step = shape.step ?? 40;
|
|
19969
|
+
ctx.strokeStyle = stroke;
|
|
19970
|
+
ctx.globalAlpha = opacity * 0.25;
|
|
19971
|
+
ctx.lineWidth = 1;
|
|
19972
|
+
ctx.beginPath();
|
|
19973
|
+
for (let x = 0; x <= width; x += step) {
|
|
19974
|
+
ctx.moveTo(x, 0);
|
|
19975
|
+
ctx.lineTo(x, height);
|
|
19976
|
+
}
|
|
19977
|
+
for (let y = 0; y <= height; y += step) {
|
|
19978
|
+
ctx.moveTo(0, y);
|
|
19979
|
+
ctx.lineTo(width, y);
|
|
19980
|
+
}
|
|
19981
|
+
ctx.stroke();
|
|
19982
|
+
break;
|
|
19983
|
+
}
|
|
19984
|
+
case "axis": {
|
|
19985
|
+
const axis = shape.axis ?? "x";
|
|
19986
|
+
ctx.strokeStyle = stroke;
|
|
19987
|
+
ctx.lineWidth = shape.lineWidth ?? 2;
|
|
19988
|
+
ctx.beginPath();
|
|
19989
|
+
if (axis === "x") {
|
|
19990
|
+
ctx.moveTo(0, height / 2);
|
|
19991
|
+
ctx.lineTo(width, height / 2);
|
|
19992
|
+
} else {
|
|
19993
|
+
ctx.moveTo(width / 2, 0);
|
|
19994
|
+
ctx.lineTo(width / 2, height);
|
|
19995
|
+
}
|
|
19996
|
+
ctx.stroke();
|
|
19997
|
+
break;
|
|
19998
|
+
}
|
|
19999
|
+
case "line": {
|
|
20000
|
+
if (shape.x1 == null || shape.y1 == null || shape.x2 == null || shape.y2 == null) break;
|
|
20001
|
+
ctx.strokeStyle = stroke;
|
|
20002
|
+
ctx.beginPath();
|
|
20003
|
+
ctx.moveTo(shape.x1, shape.y1);
|
|
20004
|
+
ctx.lineTo(shape.x2, shape.y2);
|
|
20005
|
+
ctx.stroke();
|
|
20006
|
+
break;
|
|
20007
|
+
}
|
|
20008
|
+
case "arrow": {
|
|
20009
|
+
if (shape.x1 == null || shape.y1 == null || shape.x2 == null || shape.y2 == null) break;
|
|
20010
|
+
ctx.strokeStyle = stroke;
|
|
20011
|
+
ctx.fillStyle = stroke;
|
|
20012
|
+
ctx.beginPath();
|
|
20013
|
+
ctx.moveTo(shape.x1, shape.y1);
|
|
20014
|
+
ctx.lineTo(shape.x2, shape.y2);
|
|
20015
|
+
ctx.stroke();
|
|
20016
|
+
drawArrowHead(ctx, shape.x1, shape.y1, shape.x2, shape.y2, 10);
|
|
20017
|
+
break;
|
|
20018
|
+
}
|
|
20019
|
+
case "circle": {
|
|
20020
|
+
if (shape.x == null || shape.y == null || shape.radius == null) break;
|
|
20021
|
+
ctx.beginPath();
|
|
20022
|
+
ctx.arc(shape.x, shape.y, shape.radius, 0, Math.PI * 2);
|
|
20023
|
+
if (fill) {
|
|
20024
|
+
ctx.fillStyle = fill;
|
|
20025
|
+
ctx.fill();
|
|
20026
|
+
}
|
|
20027
|
+
ctx.strokeStyle = stroke;
|
|
20028
|
+
ctx.stroke();
|
|
20029
|
+
break;
|
|
20030
|
+
}
|
|
20031
|
+
case "rect": {
|
|
20032
|
+
if (shape.x == null || shape.y == null || shape.width == null || shape.height == null) break;
|
|
20033
|
+
if (fill) {
|
|
20034
|
+
ctx.fillStyle = fill;
|
|
20035
|
+
ctx.fillRect(shape.x, shape.y, shape.width, shape.height);
|
|
20036
|
+
}
|
|
20037
|
+
ctx.strokeStyle = stroke;
|
|
20038
|
+
ctx.strokeRect(shape.x, shape.y, shape.width, shape.height);
|
|
20039
|
+
break;
|
|
20040
|
+
}
|
|
20041
|
+
case "polygon": {
|
|
20042
|
+
if (!shape.points || shape.points.length < 2) break;
|
|
20043
|
+
ctx.beginPath();
|
|
20044
|
+
ctx.moveTo(shape.points[0].x, shape.points[0].y);
|
|
20045
|
+
for (let i = 1; i < shape.points.length; i++) {
|
|
20046
|
+
ctx.lineTo(shape.points[i].x, shape.points[i].y);
|
|
20047
|
+
}
|
|
20048
|
+
ctx.closePath();
|
|
20049
|
+
if (fill) {
|
|
20050
|
+
ctx.fillStyle = fill;
|
|
20051
|
+
ctx.fill();
|
|
20052
|
+
}
|
|
20053
|
+
ctx.strokeStyle = stroke;
|
|
20054
|
+
ctx.stroke();
|
|
20055
|
+
break;
|
|
20056
|
+
}
|
|
20057
|
+
case "path": {
|
|
20058
|
+
if (!shape.path) break;
|
|
20059
|
+
const p2 = new Path2D(shape.path);
|
|
20060
|
+
if (fill) {
|
|
20061
|
+
ctx.fillStyle = fill;
|
|
20062
|
+
ctx.fill(p2);
|
|
20063
|
+
}
|
|
20064
|
+
ctx.strokeStyle = stroke;
|
|
20065
|
+
ctx.stroke(p2);
|
|
20066
|
+
break;
|
|
20067
|
+
}
|
|
20068
|
+
case "text": {
|
|
20069
|
+
if (shape.x == null || shape.y == null || !shape.text) break;
|
|
20070
|
+
ctx.fillStyle = stroke;
|
|
20071
|
+
ctx.font = `${shape.fontSize ?? 14}px system-ui, sans-serif`;
|
|
20072
|
+
ctx.textAlign = shape.align ?? "left";
|
|
20073
|
+
ctx.textBaseline = "middle";
|
|
20074
|
+
ctx.fillText(shape.text, shape.x, shape.y);
|
|
20075
|
+
break;
|
|
20076
|
+
}
|
|
20077
|
+
}
|
|
20078
|
+
ctx.restore();
|
|
20079
|
+
}
|
|
20080
|
+
var LearningCanvas;
|
|
20081
|
+
var init_LearningCanvas = __esm({
|
|
20082
|
+
"components/learning/atoms/LearningCanvas.tsx"() {
|
|
20083
|
+
"use client";
|
|
20084
|
+
init_cn();
|
|
20085
|
+
init_useEventBus();
|
|
20086
|
+
LearningCanvas = ({
|
|
20087
|
+
className,
|
|
20088
|
+
width = 600,
|
|
20089
|
+
height = 400,
|
|
20090
|
+
backgroundColor,
|
|
20091
|
+
shapes = [],
|
|
20092
|
+
interactive = false,
|
|
20093
|
+
animate = false,
|
|
20094
|
+
onShapeClick,
|
|
20095
|
+
onShapeHover,
|
|
20096
|
+
isLoading,
|
|
20097
|
+
error
|
|
20098
|
+
}) => {
|
|
20099
|
+
const canvasRef = React107.useRef(null);
|
|
20100
|
+
const eventBus = useEventBus();
|
|
20101
|
+
const animRef = React107.useRef(0);
|
|
20102
|
+
const hoverIndexRef = React107.useRef(-1);
|
|
20103
|
+
const findShapeAt = React107.useCallback((clientX, clientY) => {
|
|
20104
|
+
const canvas = canvasRef.current;
|
|
20105
|
+
if (!canvas) return -1;
|
|
20106
|
+
const rect = canvas.getBoundingClientRect();
|
|
20107
|
+
const x = clientX - rect.left;
|
|
20108
|
+
const y = clientY - rect.top;
|
|
20109
|
+
for (let i = shapes.length - 1; i >= 0; i--) {
|
|
20110
|
+
const b = shapeBounds(shapes[i]);
|
|
20111
|
+
if (b && x >= b.x && x <= b.x + b.w && y >= b.y && y <= b.y + b.h) {
|
|
20112
|
+
return i;
|
|
20113
|
+
}
|
|
20114
|
+
}
|
|
20115
|
+
return -1;
|
|
20116
|
+
}, [shapes]);
|
|
20117
|
+
const draw = React107.useCallback(() => {
|
|
20118
|
+
const canvas = canvasRef.current;
|
|
20119
|
+
if (!canvas) return;
|
|
20120
|
+
const ctx = canvas.getContext("2d");
|
|
20121
|
+
if (!ctx) return;
|
|
20122
|
+
const dpr = typeof window !== "undefined" ? window.devicePixelRatio || 1 : 1;
|
|
20123
|
+
canvas.width = Math.max(1, Math.floor(width * dpr));
|
|
20124
|
+
canvas.height = Math.max(1, Math.floor(height * dpr));
|
|
20125
|
+
canvas.style.width = `${width}px`;
|
|
20126
|
+
canvas.style.height = `${height}px`;
|
|
20127
|
+
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
|
|
20128
|
+
ctx.clearRect(0, 0, width, height);
|
|
20129
|
+
if (backgroundColor) {
|
|
20130
|
+
ctx.fillStyle = backgroundColor;
|
|
20131
|
+
ctx.fillRect(0, 0, width, height);
|
|
20132
|
+
}
|
|
20133
|
+
for (const shape of shapes) {
|
|
20134
|
+
drawShape(ctx, shape, width, height);
|
|
20135
|
+
}
|
|
20136
|
+
}, [width, height, backgroundColor, shapes]);
|
|
20137
|
+
React107.useEffect(() => {
|
|
20138
|
+
draw();
|
|
20139
|
+
}, [draw]);
|
|
20140
|
+
React107.useEffect(() => {
|
|
20141
|
+
if (!animate) return;
|
|
20142
|
+
const loop = () => {
|
|
20143
|
+
draw();
|
|
20144
|
+
animRef.current = requestAnimationFrame(loop);
|
|
20145
|
+
};
|
|
20146
|
+
animRef.current = requestAnimationFrame(loop);
|
|
20147
|
+
return () => cancelAnimationFrame(animRef.current);
|
|
20148
|
+
}, [animate, draw]);
|
|
20149
|
+
const handlePointerMove = React107.useCallback(
|
|
20150
|
+
(e) => {
|
|
20151
|
+
if (!interactive) return;
|
|
20152
|
+
const idx = findShapeAt(e.clientX, e.clientY);
|
|
20153
|
+
if (idx !== hoverIndexRef.current) {
|
|
20154
|
+
hoverIndexRef.current = idx;
|
|
20155
|
+
if (idx >= 0) {
|
|
20156
|
+
const shape = shapes[idx];
|
|
20157
|
+
const payload = { id: shape.id, type: shape.type, index: idx };
|
|
20158
|
+
if (onShapeHover) onShapeHover(payload);
|
|
20159
|
+
else if (eventBus) eventBus.emit(`UI:SHAPE_HOVER`, payload);
|
|
20160
|
+
}
|
|
20161
|
+
}
|
|
20162
|
+
},
|
|
20163
|
+
[interactive, onShapeHover, eventBus, findShapeAt, shapes]
|
|
20164
|
+
);
|
|
20165
|
+
const handleClick = React107.useCallback(
|
|
20166
|
+
(e) => {
|
|
20167
|
+
if (!interactive) return;
|
|
20168
|
+
const idx = findShapeAt(e.clientX, e.clientY);
|
|
20169
|
+
if (idx >= 0) {
|
|
20170
|
+
const shape = shapes[idx];
|
|
20171
|
+
const payload = { id: shape.id, type: shape.type, index: idx };
|
|
20172
|
+
if (onShapeClick) onShapeClick(payload);
|
|
20173
|
+
else if (eventBus) eventBus.emit(`UI:SHAPE_CLICK`, payload);
|
|
20174
|
+
}
|
|
20175
|
+
},
|
|
20176
|
+
[interactive, onShapeClick, eventBus, findShapeAt, shapes]
|
|
20177
|
+
);
|
|
20178
|
+
if (isLoading || error) {
|
|
20179
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
20180
|
+
"div",
|
|
20181
|
+
{
|
|
20182
|
+
className: cn(
|
|
20183
|
+
"flex items-center justify-center rounded border border-border bg-surface",
|
|
20184
|
+
className
|
|
20185
|
+
),
|
|
20186
|
+
style: { width, height },
|
|
20187
|
+
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" })
|
|
20188
|
+
}
|
|
20189
|
+
);
|
|
20190
|
+
}
|
|
20191
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
20192
|
+
"canvas",
|
|
20193
|
+
{
|
|
20194
|
+
ref: canvasRef,
|
|
20195
|
+
className: cn("block touch-none rounded border border-border", className),
|
|
20196
|
+
style: { width, height },
|
|
20197
|
+
onClick: handleClick,
|
|
20198
|
+
onPointerMove: handlePointerMove
|
|
20199
|
+
}
|
|
20200
|
+
);
|
|
20201
|
+
};
|
|
20202
|
+
}
|
|
20203
|
+
});
|
|
20112
20204
|
|
|
20113
20205
|
// components/core/atoms/index.ts
|
|
20114
20206
|
var init_atoms = __esm({
|
|
@@ -21977,91 +22069,6 @@ var init_ComponentPatterns = __esm({
|
|
|
21977
22069
|
AlertPattern.displayName = "AlertPattern";
|
|
21978
22070
|
}
|
|
21979
22071
|
});
|
|
21980
|
-
function parseValue(value) {
|
|
21981
|
-
if (value === "" || value == null) return { num: 0, prefix: "", suffix: "", decimals: 0 };
|
|
21982
|
-
const match = String(value).match(/^([^0-9]*)([0-9]+(?:\.[0-9]+)?)(.*)$/);
|
|
21983
|
-
if (!match) {
|
|
21984
|
-
return { num: 0, prefix: "", suffix: String(value), decimals: 0 };
|
|
21985
|
-
}
|
|
21986
|
-
const numStr = match[2];
|
|
21987
|
-
const decimalIdx = numStr.indexOf(".");
|
|
21988
|
-
const decimals = decimalIdx >= 0 ? numStr.length - decimalIdx - 1 : 0;
|
|
21989
|
-
return {
|
|
21990
|
-
prefix: match[1],
|
|
21991
|
-
num: parseFloat(numStr),
|
|
21992
|
-
suffix: match[3],
|
|
21993
|
-
decimals
|
|
21994
|
-
};
|
|
21995
|
-
}
|
|
21996
|
-
var AnimatedCounter;
|
|
21997
|
-
var init_AnimatedCounter = __esm({
|
|
21998
|
-
"components/core/molecules/AnimatedCounter.tsx"() {
|
|
21999
|
-
"use client";
|
|
22000
|
-
init_cn();
|
|
22001
|
-
init_Box();
|
|
22002
|
-
init_Typography();
|
|
22003
|
-
AnimatedCounter = ({
|
|
22004
|
-
value,
|
|
22005
|
-
label,
|
|
22006
|
-
duration = 1500,
|
|
22007
|
-
className
|
|
22008
|
-
}) => {
|
|
22009
|
-
const ref = React107.useRef(null);
|
|
22010
|
-
const [displayValue, setDisplayValue] = React107.useState("0");
|
|
22011
|
-
const [hasAnimated, setHasAnimated] = React107.useState(false);
|
|
22012
|
-
const animate = React107.useCallback(() => {
|
|
22013
|
-
const { num: num2, prefix, suffix, decimals } = parseValue(value);
|
|
22014
|
-
if (num2 === 0) {
|
|
22015
|
-
setDisplayValue(String(value));
|
|
22016
|
-
return;
|
|
22017
|
-
}
|
|
22018
|
-
const startTime = performance.now();
|
|
22019
|
-
const tick = (now) => {
|
|
22020
|
-
const elapsed = now - startTime;
|
|
22021
|
-
const progress = Math.min(elapsed / duration, 1);
|
|
22022
|
-
const eased = 1 - Math.pow(1 - progress, 3);
|
|
22023
|
-
const current = eased * num2;
|
|
22024
|
-
setDisplayValue(`${prefix}${current.toFixed(decimals)}${suffix}`);
|
|
22025
|
-
if (progress < 1) {
|
|
22026
|
-
requestAnimationFrame(tick);
|
|
22027
|
-
} else {
|
|
22028
|
-
setDisplayValue(String(value));
|
|
22029
|
-
}
|
|
22030
|
-
};
|
|
22031
|
-
requestAnimationFrame(tick);
|
|
22032
|
-
}, [value, duration]);
|
|
22033
|
-
React107.useEffect(() => {
|
|
22034
|
-
if (hasAnimated) return;
|
|
22035
|
-
const el = ref.current;
|
|
22036
|
-
if (!el) return;
|
|
22037
|
-
const observer2 = new IntersectionObserver(
|
|
22038
|
-
(entries) => {
|
|
22039
|
-
if (entries[0].isIntersecting) {
|
|
22040
|
-
setHasAnimated(true);
|
|
22041
|
-
animate();
|
|
22042
|
-
observer2.disconnect();
|
|
22043
|
-
}
|
|
22044
|
-
},
|
|
22045
|
-
{ threshold: 0.3 }
|
|
22046
|
-
);
|
|
22047
|
-
observer2.observe(el);
|
|
22048
|
-
return () => observer2.disconnect();
|
|
22049
|
-
}, [hasAnimated, animate]);
|
|
22050
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(Box, { ref, className: cn("flex flex-col items-center gap-1 p-4", className), children: [
|
|
22051
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
22052
|
-
Typography,
|
|
22053
|
-
{
|
|
22054
|
-
variant: "h2",
|
|
22055
|
-
className: "text-primary font-bold tabular-nums",
|
|
22056
|
-
children: hasAnimated ? displayValue : "0"
|
|
22057
|
-
}
|
|
22058
|
-
),
|
|
22059
|
-
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", color: "muted", className: "text-center", children: label })
|
|
22060
|
-
] });
|
|
22061
|
-
};
|
|
22062
|
-
AnimatedCounter.displayName = "AnimatedCounter";
|
|
22063
|
-
}
|
|
22064
|
-
});
|
|
22065
22072
|
var AuthLayout;
|
|
22066
22073
|
var init_AuthLayout = __esm({
|
|
22067
22074
|
"components/marketing/templates/AuthLayout.tsx"() {
|
|
@@ -23030,10 +23037,107 @@ var init_BehaviorView = __esm({
|
|
|
23030
23037
|
BehaviorView.displayName = "BehaviorView";
|
|
23031
23038
|
}
|
|
23032
23039
|
});
|
|
23040
|
+
var BiologyCanvas;
|
|
23041
|
+
var init_BiologyCanvas = __esm({
|
|
23042
|
+
"components/learning/molecules/BiologyCanvas.tsx"() {
|
|
23043
|
+
"use client";
|
|
23044
|
+
init_atoms();
|
|
23045
|
+
init_Stack();
|
|
23046
|
+
init_LearningCanvas();
|
|
23047
|
+
BiologyCanvas = ({
|
|
23048
|
+
className,
|
|
23049
|
+
width = 600,
|
|
23050
|
+
height = 400,
|
|
23051
|
+
title,
|
|
23052
|
+
backgroundColor,
|
|
23053
|
+
nodes = [],
|
|
23054
|
+
edges = [],
|
|
23055
|
+
shapes = [],
|
|
23056
|
+
interactive = false,
|
|
23057
|
+
animate = false,
|
|
23058
|
+
onShapeClick,
|
|
23059
|
+
isLoading,
|
|
23060
|
+
error
|
|
23061
|
+
}) => {
|
|
23062
|
+
const derivedShapes = React107.useMemo(() => {
|
|
23063
|
+
const out = [];
|
|
23064
|
+
const nodeById = /* @__PURE__ */ new Map();
|
|
23065
|
+
for (const n of nodes) {
|
|
23066
|
+
if (n.id) nodeById.set(n.id, n);
|
|
23067
|
+
}
|
|
23068
|
+
for (const e of edges) {
|
|
23069
|
+
const a = nodeById.get(e.from);
|
|
23070
|
+
const b = nodeById.get(e.to);
|
|
23071
|
+
if (!a || !b) continue;
|
|
23072
|
+
out.push({
|
|
23073
|
+
type: "line",
|
|
23074
|
+
x1: a.x,
|
|
23075
|
+
y1: a.y,
|
|
23076
|
+
x2: b.x,
|
|
23077
|
+
y2: b.y,
|
|
23078
|
+
color: e.color ?? "#9ca3af",
|
|
23079
|
+
lineWidth: 2
|
|
23080
|
+
});
|
|
23081
|
+
if (e.label) {
|
|
23082
|
+
out.push({
|
|
23083
|
+
type: "text",
|
|
23084
|
+
x: (a.x + b.x) / 2 + 4,
|
|
23085
|
+
y: (a.y + b.y) / 2 - 4,
|
|
23086
|
+
text: e.label,
|
|
23087
|
+
color: "#374151",
|
|
23088
|
+
fontSize: 11
|
|
23089
|
+
});
|
|
23090
|
+
}
|
|
23091
|
+
}
|
|
23092
|
+
for (const n of nodes) {
|
|
23093
|
+
out.push({
|
|
23094
|
+
type: "circle",
|
|
23095
|
+
x: n.x,
|
|
23096
|
+
y: n.y,
|
|
23097
|
+
radius: n.radius ?? 16,
|
|
23098
|
+
color: n.color ?? "#16a34a",
|
|
23099
|
+
fill: `${n.color ?? "#16a34a"}33`,
|
|
23100
|
+
id: n.id
|
|
23101
|
+
});
|
|
23102
|
+
if (n.label) {
|
|
23103
|
+
out.push({
|
|
23104
|
+
type: "text",
|
|
23105
|
+
x: n.x,
|
|
23106
|
+
y: n.y + (n.radius ?? 16) + 14,
|
|
23107
|
+
text: n.label,
|
|
23108
|
+
color: "#111827",
|
|
23109
|
+
fontSize: 12,
|
|
23110
|
+
align: "center"
|
|
23111
|
+
});
|
|
23112
|
+
}
|
|
23113
|
+
}
|
|
23114
|
+
out.push(...shapes);
|
|
23115
|
+
return out;
|
|
23116
|
+
}, [nodes, edges, shapes]);
|
|
23117
|
+
return /* @__PURE__ */ jsxRuntime.jsx(Card, { className, children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "sm", children: [
|
|
23118
|
+
title ? /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h4", children: title }) : null,
|
|
23119
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
23120
|
+
LearningCanvas,
|
|
23121
|
+
{
|
|
23122
|
+
width,
|
|
23123
|
+
height,
|
|
23124
|
+
backgroundColor,
|
|
23125
|
+
shapes: derivedShapes,
|
|
23126
|
+
interactive,
|
|
23127
|
+
animate,
|
|
23128
|
+
onShapeClick,
|
|
23129
|
+
isLoading,
|
|
23130
|
+
error
|
|
23131
|
+
}
|
|
23132
|
+
)
|
|
23133
|
+
] }) });
|
|
23134
|
+
};
|
|
23135
|
+
}
|
|
23136
|
+
});
|
|
23033
23137
|
|
|
23034
|
-
// node_modules
|
|
23138
|
+
// node_modules/katex/dist/katex.min.css
|
|
23035
23139
|
var init_katex_min = __esm({
|
|
23036
|
-
"node_modules
|
|
23140
|
+
"node_modules/katex/dist/katex.min.css"() {
|
|
23037
23141
|
}
|
|
23038
23142
|
});
|
|
23039
23143
|
var Tabs;
|
|
@@ -27426,129 +27530,6 @@ var init_Breadcrumb = __esm({
|
|
|
27426
27530
|
Breadcrumb.displayName = "Breadcrumb";
|
|
27427
27531
|
}
|
|
27428
27532
|
});
|
|
27429
|
-
function useSafeEventBus2() {
|
|
27430
|
-
try {
|
|
27431
|
-
return useEventBus();
|
|
27432
|
-
} catch {
|
|
27433
|
-
return { emit: () => {
|
|
27434
|
-
}, on: () => () => {
|
|
27435
|
-
}, once: () => {
|
|
27436
|
-
} };
|
|
27437
|
-
}
|
|
27438
|
-
}
|
|
27439
|
-
var log8, lookStyles4, ButtonGroup;
|
|
27440
|
-
var init_ButtonGroup = __esm({
|
|
27441
|
-
"components/core/molecules/ButtonGroup.tsx"() {
|
|
27442
|
-
"use client";
|
|
27443
|
-
init_cn();
|
|
27444
|
-
init_atoms();
|
|
27445
|
-
init_useEventBus();
|
|
27446
|
-
log8 = logger.createLogger("almadar:ui:button-group");
|
|
27447
|
-
lookStyles4 = {
|
|
27448
|
-
"right-aligned-buttons": "",
|
|
27449
|
-
"floating-bar": "fixed bottom-section left-1/2 -translate-x-1/2 shadow-elevation-toast bg-card p-card-sm rounded-container",
|
|
27450
|
-
"inline-row": "gap-2 inline-flex",
|
|
27451
|
-
"dropdown-menu": "[&>button:not(:first-child)]:hidden",
|
|
27452
|
-
"command-palette-trigger": "[&>button:not(:first-child)]:hidden"
|
|
27453
|
-
};
|
|
27454
|
-
ButtonGroup = ({
|
|
27455
|
-
children,
|
|
27456
|
-
primary,
|
|
27457
|
-
secondary,
|
|
27458
|
-
variant = "default",
|
|
27459
|
-
orientation = "horizontal",
|
|
27460
|
-
className,
|
|
27461
|
-
// Filter-group pattern props (entity and filters are used for schema-driven filtering)
|
|
27462
|
-
entity: _entity,
|
|
27463
|
-
filters,
|
|
27464
|
-
look = "right-aligned-buttons"
|
|
27465
|
-
}) => {
|
|
27466
|
-
const eventBus = useSafeEventBus2();
|
|
27467
|
-
const variantClasses2 = {
|
|
27468
|
-
default: "gap-0",
|
|
27469
|
-
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",
|
|
27470
|
-
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"
|
|
27471
|
-
};
|
|
27472
|
-
const orientationClasses = {
|
|
27473
|
-
horizontal: "flex-row",
|
|
27474
|
-
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"
|
|
27475
|
-
};
|
|
27476
|
-
const handleActionClick = (action) => {
|
|
27477
|
-
if (action.event) {
|
|
27478
|
-
eventBus.emit("UI:DISPATCH", { event: action.event });
|
|
27479
|
-
}
|
|
27480
|
-
if (action.navigatesTo) {
|
|
27481
|
-
eventBus.emit("UI:NAVIGATE", { url: action.navigatesTo });
|
|
27482
|
-
}
|
|
27483
|
-
};
|
|
27484
|
-
const renderFormActions = () => {
|
|
27485
|
-
const buttons = [];
|
|
27486
|
-
if (secondary) {
|
|
27487
|
-
secondary.forEach((action, index) => {
|
|
27488
|
-
buttons.push(
|
|
27489
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
27490
|
-
Button,
|
|
27491
|
-
{
|
|
27492
|
-
type: action.actionType === "submit" ? "submit" : "button",
|
|
27493
|
-
variant: action.variant || "ghost",
|
|
27494
|
-
onClick: () => handleActionClick(action),
|
|
27495
|
-
children: action.label
|
|
27496
|
-
},
|
|
27497
|
-
`secondary-${index}`
|
|
27498
|
-
)
|
|
27499
|
-
);
|
|
27500
|
-
});
|
|
27501
|
-
}
|
|
27502
|
-
if (primary) {
|
|
27503
|
-
const isSubmit = primary.actionType === "submit";
|
|
27504
|
-
buttons.push(
|
|
27505
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
27506
|
-
Button,
|
|
27507
|
-
{
|
|
27508
|
-
type: isSubmit ? "submit" : "button",
|
|
27509
|
-
variant: primary.variant || "primary",
|
|
27510
|
-
onClick: () => handleActionClick(primary),
|
|
27511
|
-
"data-testid": isSubmit ? "form-submit" : void 0,
|
|
27512
|
-
children: primary.label
|
|
27513
|
-
},
|
|
27514
|
-
"primary"
|
|
27515
|
-
)
|
|
27516
|
-
);
|
|
27517
|
-
}
|
|
27518
|
-
return buttons;
|
|
27519
|
-
};
|
|
27520
|
-
const renderFilters = () => {
|
|
27521
|
-
if (!filters || filters.length === 0) return null;
|
|
27522
|
-
return filters.map((filter, index) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
27523
|
-
Button,
|
|
27524
|
-
{
|
|
27525
|
-
variant: "ghost",
|
|
27526
|
-
onClick: () => {
|
|
27527
|
-
log8.debug("Filter clicked", { field: filter.field });
|
|
27528
|
-
},
|
|
27529
|
-
children: filter.label
|
|
27530
|
-
},
|
|
27531
|
-
`filter-${filter.field}-${index}`
|
|
27532
|
-
));
|
|
27533
|
-
};
|
|
27534
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
27535
|
-
"div",
|
|
27536
|
-
{
|
|
27537
|
-
className: cn(
|
|
27538
|
-
"inline-flex gap-2",
|
|
27539
|
-
variantClasses2[variant],
|
|
27540
|
-
orientationClasses[orientation],
|
|
27541
|
-
lookStyles4[look],
|
|
27542
|
-
className
|
|
27543
|
-
),
|
|
27544
|
-
role: "group",
|
|
27545
|
-
children: children || renderFilters() || renderFormActions()
|
|
27546
|
-
}
|
|
27547
|
-
);
|
|
27548
|
-
};
|
|
27549
|
-
ButtonGroup.displayName = "ButtonGroup";
|
|
27550
|
-
}
|
|
27551
|
-
});
|
|
27552
27533
|
function useSwipeGesture(callbacks, options = {}) {
|
|
27553
27534
|
const { threshold = 50, velocityThreshold = 0.3, preventDefault = false } = options;
|
|
27554
27535
|
const startX = React107.useRef(0);
|
|
@@ -28358,7 +28339,7 @@ var init_CardGrid = __esm({
|
|
|
28358
28339
|
CardGrid.displayName = "CardGrid";
|
|
28359
28340
|
}
|
|
28360
28341
|
});
|
|
28361
|
-
function
|
|
28342
|
+
function useSafeEventBus2() {
|
|
28362
28343
|
try {
|
|
28363
28344
|
return useEventBus();
|
|
28364
28345
|
} catch {
|
|
@@ -28394,7 +28375,7 @@ var init_Carousel = __esm({
|
|
|
28394
28375
|
const [activeIndex, setActiveIndex] = React107.useState(0);
|
|
28395
28376
|
const scrollRef = React107.useRef(null);
|
|
28396
28377
|
const autoPlayRef = React107.useRef(null);
|
|
28397
|
-
const eventBus =
|
|
28378
|
+
const eventBus = useSafeEventBus2();
|
|
28398
28379
|
const { t } = hooks.useTranslate();
|
|
28399
28380
|
const safeItems = items ?? [];
|
|
28400
28381
|
const totalSlides = safeItems.length;
|
|
@@ -29642,6 +29623,122 @@ var init_ChatBar = __esm({
|
|
|
29642
29623
|
ChatBar.displayName = "ChatBar";
|
|
29643
29624
|
}
|
|
29644
29625
|
});
|
|
29626
|
+
var ChemistryCanvas;
|
|
29627
|
+
var init_ChemistryCanvas = __esm({
|
|
29628
|
+
"components/learning/molecules/ChemistryCanvas.tsx"() {
|
|
29629
|
+
"use client";
|
|
29630
|
+
init_atoms();
|
|
29631
|
+
init_Stack();
|
|
29632
|
+
init_LearningCanvas();
|
|
29633
|
+
ChemistryCanvas = ({
|
|
29634
|
+
className,
|
|
29635
|
+
width = 600,
|
|
29636
|
+
height = 400,
|
|
29637
|
+
title,
|
|
29638
|
+
backgroundColor,
|
|
29639
|
+
atoms = [],
|
|
29640
|
+
bonds = [],
|
|
29641
|
+
arrows = [],
|
|
29642
|
+
shapes = [],
|
|
29643
|
+
interactive = false,
|
|
29644
|
+
animate = false,
|
|
29645
|
+
onShapeClick,
|
|
29646
|
+
isLoading,
|
|
29647
|
+
error
|
|
29648
|
+
}) => {
|
|
29649
|
+
const derivedShapes = React107.useMemo(() => {
|
|
29650
|
+
const out = [];
|
|
29651
|
+
const atomById = /* @__PURE__ */ new Map();
|
|
29652
|
+
for (const a of atoms) {
|
|
29653
|
+
if (a.id) atomById.set(a.id, a);
|
|
29654
|
+
}
|
|
29655
|
+
for (const b of bonds) {
|
|
29656
|
+
const a = atomById.get(b.from);
|
|
29657
|
+
const c = atomById.get(b.to);
|
|
29658
|
+
if (!a || !c) continue;
|
|
29659
|
+
const color = b.color ?? "#6b7280";
|
|
29660
|
+
const strokeWidth = b.type === "double" ? 4 : b.type === "triple" ? 6 : 2;
|
|
29661
|
+
out.push({
|
|
29662
|
+
type: "line",
|
|
29663
|
+
x1: a.x,
|
|
29664
|
+
y1: a.y,
|
|
29665
|
+
x2: c.x,
|
|
29666
|
+
y2: c.y,
|
|
29667
|
+
color,
|
|
29668
|
+
lineWidth: strokeWidth
|
|
29669
|
+
});
|
|
29670
|
+
}
|
|
29671
|
+
for (const a of arrows) {
|
|
29672
|
+
const angle = (a.angle ?? 0) * (Math.PI / 180);
|
|
29673
|
+
const len = a.length ?? 60;
|
|
29674
|
+
const x2 = a.x + Math.cos(angle) * len;
|
|
29675
|
+
const y2 = a.y + Math.sin(angle) * len;
|
|
29676
|
+
out.push({
|
|
29677
|
+
type: "arrow",
|
|
29678
|
+
x1: a.x,
|
|
29679
|
+
y1: a.y,
|
|
29680
|
+
x2,
|
|
29681
|
+
y2,
|
|
29682
|
+
color: a.color ?? "#dc2626",
|
|
29683
|
+
lineWidth: 2
|
|
29684
|
+
});
|
|
29685
|
+
if (a.label) {
|
|
29686
|
+
out.push({
|
|
29687
|
+
type: "text",
|
|
29688
|
+
x: (a.x + x2) / 2,
|
|
29689
|
+
y: (a.y + y2) / 2 - 10,
|
|
29690
|
+
text: a.label,
|
|
29691
|
+
color: "#111827",
|
|
29692
|
+
fontSize: 12,
|
|
29693
|
+
align: "center"
|
|
29694
|
+
});
|
|
29695
|
+
}
|
|
29696
|
+
}
|
|
29697
|
+
for (const a of atoms) {
|
|
29698
|
+
out.push({
|
|
29699
|
+
type: "circle",
|
|
29700
|
+
x: a.x,
|
|
29701
|
+
y: a.y,
|
|
29702
|
+
radius: a.radius ?? 14,
|
|
29703
|
+
color: a.color ?? "#2563eb",
|
|
29704
|
+
fill: a.color ?? "#2563eb",
|
|
29705
|
+
id: a.id
|
|
29706
|
+
});
|
|
29707
|
+
if (a.element) {
|
|
29708
|
+
out.push({
|
|
29709
|
+
type: "text",
|
|
29710
|
+
x: a.x,
|
|
29711
|
+
y: a.y,
|
|
29712
|
+
text: a.element,
|
|
29713
|
+
color: "#ffffff",
|
|
29714
|
+
fontSize: 12,
|
|
29715
|
+
align: "center"
|
|
29716
|
+
});
|
|
29717
|
+
}
|
|
29718
|
+
}
|
|
29719
|
+
out.push(...shapes);
|
|
29720
|
+
return out;
|
|
29721
|
+
}, [atoms, bonds, arrows, shapes]);
|
|
29722
|
+
return /* @__PURE__ */ jsxRuntime.jsx(Card, { className, children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "sm", children: [
|
|
29723
|
+
title ? /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h4", children: title }) : null,
|
|
29724
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
29725
|
+
LearningCanvas,
|
|
29726
|
+
{
|
|
29727
|
+
width,
|
|
29728
|
+
height,
|
|
29729
|
+
backgroundColor,
|
|
29730
|
+
shapes: derivedShapes,
|
|
29731
|
+
interactive,
|
|
29732
|
+
animate,
|
|
29733
|
+
onShapeClick,
|
|
29734
|
+
isLoading,
|
|
29735
|
+
error
|
|
29736
|
+
}
|
|
29737
|
+
)
|
|
29738
|
+
] }) });
|
|
29739
|
+
};
|
|
29740
|
+
}
|
|
29741
|
+
});
|
|
29645
29742
|
var CodeRunnerPanel;
|
|
29646
29743
|
var init_CodeRunnerPanel = __esm({
|
|
29647
29744
|
"components/core/organisms/CodeRunnerPanel.tsx"() {
|
|
@@ -31730,7 +31827,7 @@ function DataGrid({
|
|
|
31730
31827
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
31731
31828
|
Box,
|
|
31732
31829
|
{
|
|
31733
|
-
className: cn("grid", gapStyles5[gap], scrollX ? "grid-flow-col overflow-x-auto" : colsClass,
|
|
31830
|
+
className: cn("grid", gapStyles5[gap], scrollX ? "grid-flow-col overflow-x-auto" : colsClass, lookStyles4[look], className),
|
|
31734
31831
|
style: scrollX ? { gridAutoFlow: "column", gridAutoColumns: `minmax(${minCardWidth}px, 1fr)` } : gridTemplateColumns ? { gridTemplateColumns } : void 0,
|
|
31735
31832
|
children: data.map((item, index) => {
|
|
31736
31833
|
const itemData = item;
|
|
@@ -31925,7 +32022,7 @@ function DataGrid({
|
|
|
31925
32022
|
] })
|
|
31926
32023
|
);
|
|
31927
32024
|
}
|
|
31928
|
-
var dataGridLog, BADGE_VARIANTS, gapStyles5,
|
|
32025
|
+
var dataGridLog, BADGE_VARIANTS, gapStyles5, lookStyles4;
|
|
31929
32026
|
var init_DataGrid = __esm({
|
|
31930
32027
|
"components/core/molecules/DataGrid.tsx"() {
|
|
31931
32028
|
"use client";
|
|
@@ -31960,7 +32057,7 @@ var init_DataGrid = __esm({
|
|
|
31960
32057
|
lg: "gap-6",
|
|
31961
32058
|
xl: "gap-8"
|
|
31962
32059
|
};
|
|
31963
|
-
|
|
32060
|
+
lookStyles4 = {
|
|
31964
32061
|
dense: "gap-2 [&>*]:p-card-sm",
|
|
31965
32062
|
spacious: "gap-8 [&>*]:p-card-lg",
|
|
31966
32063
|
striped: "[&>*:nth-child(even)]:bg-muted/30",
|
|
@@ -32387,6 +32484,133 @@ var init_DataList = __esm({
|
|
|
32387
32484
|
DataList.displayName = "DataList";
|
|
32388
32485
|
}
|
|
32389
32486
|
});
|
|
32487
|
+
var FormSection, FormLayout, FormActions;
|
|
32488
|
+
var init_FormSection = __esm({
|
|
32489
|
+
"components/core/molecules/FormSection.tsx"() {
|
|
32490
|
+
"use client";
|
|
32491
|
+
init_cn();
|
|
32492
|
+
init_atoms();
|
|
32493
|
+
init_Box();
|
|
32494
|
+
init_Typography();
|
|
32495
|
+
init_Button();
|
|
32496
|
+
init_Stack();
|
|
32497
|
+
init_Icon();
|
|
32498
|
+
init_useEventBus();
|
|
32499
|
+
FormSection = ({
|
|
32500
|
+
title,
|
|
32501
|
+
description,
|
|
32502
|
+
children,
|
|
32503
|
+
collapsible = false,
|
|
32504
|
+
defaultCollapsed = false,
|
|
32505
|
+
card = false,
|
|
32506
|
+
columns = 1,
|
|
32507
|
+
className
|
|
32508
|
+
}) => {
|
|
32509
|
+
const [collapsed, setCollapsed] = React107__namespace.default.useState(defaultCollapsed);
|
|
32510
|
+
const { t } = hooks.useTranslate();
|
|
32511
|
+
const eventBus = useEventBus();
|
|
32512
|
+
const gridClass = {
|
|
32513
|
+
1: "grid-cols-1",
|
|
32514
|
+
2: "grid-cols-1 md:grid-cols-2",
|
|
32515
|
+
3: "grid-cols-1 md:grid-cols-2 lg:grid-cols-3"
|
|
32516
|
+
}[columns];
|
|
32517
|
+
React107__namespace.default.useCallback(() => {
|
|
32518
|
+
if (collapsible) {
|
|
32519
|
+
setCollapsed((prev) => !prev);
|
|
32520
|
+
eventBus.emit("UI:TOGGLE_COLLAPSE", { collapsed: !collapsed });
|
|
32521
|
+
}
|
|
32522
|
+
}, [collapsible, collapsed, eventBus]);
|
|
32523
|
+
const content = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
32524
|
+
(title || description) && /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "xs", className: "mb-4", children: [
|
|
32525
|
+
title && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
32526
|
+
HStack,
|
|
32527
|
+
{
|
|
32528
|
+
justify: "between",
|
|
32529
|
+
align: "center",
|
|
32530
|
+
className: cn(collapsible && "cursor-pointer"),
|
|
32531
|
+
action: collapsible ? "TOGGLE_COLLAPSE" : void 0,
|
|
32532
|
+
children: [
|
|
32533
|
+
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h3", weight: "semibold", children: title }),
|
|
32534
|
+
collapsible && /* @__PURE__ */ jsxRuntime.jsx(
|
|
32535
|
+
Button,
|
|
32536
|
+
{
|
|
32537
|
+
variant: "ghost",
|
|
32538
|
+
size: "sm",
|
|
32539
|
+
action: "TOGGLE_COLLAPSE",
|
|
32540
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
32541
|
+
Icon,
|
|
32542
|
+
{
|
|
32543
|
+
icon: LucideIcons2.ChevronDown,
|
|
32544
|
+
size: "sm",
|
|
32545
|
+
className: cn(
|
|
32546
|
+
"text-muted-foreground transition-transform",
|
|
32547
|
+
collapsed && "rotate-180"
|
|
32548
|
+
)
|
|
32549
|
+
}
|
|
32550
|
+
)
|
|
32551
|
+
}
|
|
32552
|
+
)
|
|
32553
|
+
]
|
|
32554
|
+
}
|
|
32555
|
+
),
|
|
32556
|
+
description && /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", color: "secondary", children: description })
|
|
32557
|
+
] }),
|
|
32558
|
+
(!collapsible || !collapsed) && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: cn("grid gap-4", gridClass), children })
|
|
32559
|
+
] });
|
|
32560
|
+
if (card) {
|
|
32561
|
+
return /* @__PURE__ */ jsxRuntime.jsx(Card, { className: cn("p-6", className), children: content });
|
|
32562
|
+
}
|
|
32563
|
+
return /* @__PURE__ */ jsxRuntime.jsx(Box, { className, children: content });
|
|
32564
|
+
};
|
|
32565
|
+
FormSection.displayName = "FormSection";
|
|
32566
|
+
FormLayout = ({
|
|
32567
|
+
children,
|
|
32568
|
+
dividers = true,
|
|
32569
|
+
className
|
|
32570
|
+
}) => {
|
|
32571
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
32572
|
+
VStack,
|
|
32573
|
+
{
|
|
32574
|
+
gap: "lg",
|
|
32575
|
+
className: cn(
|
|
32576
|
+
dividers && "[&>*+*]:pt-8 [&>*+*]:border-t [&>*+*]:border-border",
|
|
32577
|
+
className
|
|
32578
|
+
),
|
|
32579
|
+
children
|
|
32580
|
+
}
|
|
32581
|
+
);
|
|
32582
|
+
};
|
|
32583
|
+
FormLayout.displayName = "FormLayout";
|
|
32584
|
+
FormActions = ({
|
|
32585
|
+
children,
|
|
32586
|
+
sticky = false,
|
|
32587
|
+
align = "right",
|
|
32588
|
+
className
|
|
32589
|
+
}) => {
|
|
32590
|
+
const alignClass2 = {
|
|
32591
|
+
left: "justify-start",
|
|
32592
|
+
right: "justify-end",
|
|
32593
|
+
between: "justify-between",
|
|
32594
|
+
center: "justify-center"
|
|
32595
|
+
}[align];
|
|
32596
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
32597
|
+
HStack,
|
|
32598
|
+
{
|
|
32599
|
+
gap: "sm",
|
|
32600
|
+
align: "center",
|
|
32601
|
+
className: cn(
|
|
32602
|
+
"pt-6 border-t border-border",
|
|
32603
|
+
alignClass2,
|
|
32604
|
+
sticky && "sticky bottom-0 bg-card py-4 -mx-6 px-6 shadow-[0_-4px_6px_-1px_rgb(0,0,0,0.05)]",
|
|
32605
|
+
className
|
|
32606
|
+
),
|
|
32607
|
+
children
|
|
32608
|
+
}
|
|
32609
|
+
);
|
|
32610
|
+
};
|
|
32611
|
+
FormActions.displayName = "FormActions";
|
|
32612
|
+
}
|
|
32613
|
+
});
|
|
32390
32614
|
function fileIcon(name) {
|
|
32391
32615
|
const ext = name.split(".").pop()?.toLowerCase() ?? "";
|
|
32392
32616
|
switch (ext) {
|
|
@@ -32548,6 +32772,129 @@ var init_FormField = __esm({
|
|
|
32548
32772
|
FormField.displayName = "FormField";
|
|
32549
32773
|
}
|
|
32550
32774
|
});
|
|
32775
|
+
function useSafeEventBus3() {
|
|
32776
|
+
try {
|
|
32777
|
+
return useEventBus();
|
|
32778
|
+
} catch {
|
|
32779
|
+
return { emit: () => {
|
|
32780
|
+
}, on: () => () => {
|
|
32781
|
+
}, once: () => {
|
|
32782
|
+
} };
|
|
32783
|
+
}
|
|
32784
|
+
}
|
|
32785
|
+
var log8, lookStyles5, ButtonGroup;
|
|
32786
|
+
var init_ButtonGroup = __esm({
|
|
32787
|
+
"components/core/molecules/ButtonGroup.tsx"() {
|
|
32788
|
+
"use client";
|
|
32789
|
+
init_cn();
|
|
32790
|
+
init_atoms();
|
|
32791
|
+
init_useEventBus();
|
|
32792
|
+
log8 = logger.createLogger("almadar:ui:button-group");
|
|
32793
|
+
lookStyles5 = {
|
|
32794
|
+
"right-aligned-buttons": "",
|
|
32795
|
+
"floating-bar": "fixed bottom-section left-1/2 -translate-x-1/2 shadow-elevation-toast bg-card p-card-sm rounded-container",
|
|
32796
|
+
"inline-row": "gap-2 inline-flex",
|
|
32797
|
+
"dropdown-menu": "[&>button:not(:first-child)]:hidden",
|
|
32798
|
+
"command-palette-trigger": "[&>button:not(:first-child)]:hidden"
|
|
32799
|
+
};
|
|
32800
|
+
ButtonGroup = ({
|
|
32801
|
+
children,
|
|
32802
|
+
primary,
|
|
32803
|
+
secondary,
|
|
32804
|
+
variant = "default",
|
|
32805
|
+
orientation = "horizontal",
|
|
32806
|
+
className,
|
|
32807
|
+
// Filter-group pattern props (entity and filters are used for schema-driven filtering)
|
|
32808
|
+
entity: _entity,
|
|
32809
|
+
filters,
|
|
32810
|
+
look = "right-aligned-buttons"
|
|
32811
|
+
}) => {
|
|
32812
|
+
const eventBus = useSafeEventBus3();
|
|
32813
|
+
const variantClasses2 = {
|
|
32814
|
+
default: "gap-0",
|
|
32815
|
+
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",
|
|
32816
|
+
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"
|
|
32817
|
+
};
|
|
32818
|
+
const orientationClasses = {
|
|
32819
|
+
horizontal: "flex-row",
|
|
32820
|
+
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"
|
|
32821
|
+
};
|
|
32822
|
+
const handleActionClick = (action) => {
|
|
32823
|
+
if (action.event) {
|
|
32824
|
+
eventBus.emit("UI:DISPATCH", { event: action.event });
|
|
32825
|
+
}
|
|
32826
|
+
if (action.navigatesTo) {
|
|
32827
|
+
eventBus.emit("UI:NAVIGATE", { url: action.navigatesTo });
|
|
32828
|
+
}
|
|
32829
|
+
};
|
|
32830
|
+
const renderFormActions = () => {
|
|
32831
|
+
const buttons = [];
|
|
32832
|
+
if (secondary) {
|
|
32833
|
+
secondary.forEach((action, index) => {
|
|
32834
|
+
buttons.push(
|
|
32835
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
32836
|
+
Button,
|
|
32837
|
+
{
|
|
32838
|
+
type: action.actionType === "submit" ? "submit" : "button",
|
|
32839
|
+
variant: action.variant || "ghost",
|
|
32840
|
+
onClick: () => handleActionClick(action),
|
|
32841
|
+
children: action.label
|
|
32842
|
+
},
|
|
32843
|
+
`secondary-${index}`
|
|
32844
|
+
)
|
|
32845
|
+
);
|
|
32846
|
+
});
|
|
32847
|
+
}
|
|
32848
|
+
if (primary) {
|
|
32849
|
+
const isSubmit = primary.actionType === "submit";
|
|
32850
|
+
buttons.push(
|
|
32851
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
32852
|
+
Button,
|
|
32853
|
+
{
|
|
32854
|
+
type: isSubmit ? "submit" : "button",
|
|
32855
|
+
variant: primary.variant || "primary",
|
|
32856
|
+
onClick: () => handleActionClick(primary),
|
|
32857
|
+
"data-testid": isSubmit ? "form-submit" : void 0,
|
|
32858
|
+
children: primary.label
|
|
32859
|
+
},
|
|
32860
|
+
"primary"
|
|
32861
|
+
)
|
|
32862
|
+
);
|
|
32863
|
+
}
|
|
32864
|
+
return buttons;
|
|
32865
|
+
};
|
|
32866
|
+
const renderFilters = () => {
|
|
32867
|
+
if (!filters || filters.length === 0) return null;
|
|
32868
|
+
return filters.map((filter, index) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
32869
|
+
Button,
|
|
32870
|
+
{
|
|
32871
|
+
variant: "ghost",
|
|
32872
|
+
onClick: () => {
|
|
32873
|
+
log8.debug("Filter clicked", { field: filter.field });
|
|
32874
|
+
},
|
|
32875
|
+
children: filter.label
|
|
32876
|
+
},
|
|
32877
|
+
`filter-${filter.field}-${index}`
|
|
32878
|
+
));
|
|
32879
|
+
};
|
|
32880
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
32881
|
+
"div",
|
|
32882
|
+
{
|
|
32883
|
+
className: cn(
|
|
32884
|
+
"inline-flex gap-2",
|
|
32885
|
+
variantClasses2[variant],
|
|
32886
|
+
orientationClasses[orientation],
|
|
32887
|
+
lookStyles5[look],
|
|
32888
|
+
className
|
|
32889
|
+
),
|
|
32890
|
+
role: "group",
|
|
32891
|
+
children: children || renderFilters() || renderFormActions()
|
|
32892
|
+
}
|
|
32893
|
+
);
|
|
32894
|
+
};
|
|
32895
|
+
ButtonGroup.displayName = "ButtonGroup";
|
|
32896
|
+
}
|
|
32897
|
+
});
|
|
32551
32898
|
function getOrCreateStore(query) {
|
|
32552
32899
|
if (!queryStores.has(query)) {
|
|
32553
32900
|
queryStores.set(query, {
|
|
@@ -35089,6 +35436,234 @@ var init_ProgressDots = __esm({
|
|
|
35089
35436
|
ProgressDots.displayName = "ProgressDots";
|
|
35090
35437
|
}
|
|
35091
35438
|
});
|
|
35439
|
+
var MathCanvas;
|
|
35440
|
+
var init_MathCanvas = __esm({
|
|
35441
|
+
"components/learning/molecules/MathCanvas.tsx"() {
|
|
35442
|
+
"use client";
|
|
35443
|
+
init_atoms();
|
|
35444
|
+
init_Stack();
|
|
35445
|
+
init_LearningCanvas();
|
|
35446
|
+
MathCanvas = ({
|
|
35447
|
+
className,
|
|
35448
|
+
width = 600,
|
|
35449
|
+
height = 400,
|
|
35450
|
+
title,
|
|
35451
|
+
xMin = -10,
|
|
35452
|
+
xMax = 10,
|
|
35453
|
+
yMin = -10,
|
|
35454
|
+
yMax = 10,
|
|
35455
|
+
showAxes = true,
|
|
35456
|
+
showGrid = true,
|
|
35457
|
+
gridStep = 1,
|
|
35458
|
+
curves = [],
|
|
35459
|
+
points = [],
|
|
35460
|
+
vectors = [],
|
|
35461
|
+
shapes = [],
|
|
35462
|
+
interactive = false,
|
|
35463
|
+
animate = false,
|
|
35464
|
+
onShapeClick,
|
|
35465
|
+
isLoading,
|
|
35466
|
+
error
|
|
35467
|
+
}) => {
|
|
35468
|
+
const derivedShapes = React107.useMemo(() => {
|
|
35469
|
+
const out = [];
|
|
35470
|
+
const margin = 24;
|
|
35471
|
+
const plotW = width - margin * 2;
|
|
35472
|
+
const plotH = height - margin * 2;
|
|
35473
|
+
const mapX = (x) => margin + (x - xMin) / (xMax - xMin) * plotW;
|
|
35474
|
+
const mapY = (y) => height - (margin + (y - yMin) / (yMax - yMin) * plotH);
|
|
35475
|
+
if (showGrid) {
|
|
35476
|
+
for (let x = Math.ceil(xMin / gridStep) * gridStep; x <= xMax; x += gridStep) {
|
|
35477
|
+
const px = mapX(x);
|
|
35478
|
+
out.push({ type: "line", x1: px, y1: margin, x2: px, y2: height - margin, color: "#e5e7eb", lineWidth: 1 });
|
|
35479
|
+
}
|
|
35480
|
+
for (let y = Math.ceil(yMin / gridStep) * gridStep; y <= yMax; y += gridStep) {
|
|
35481
|
+
const py = mapY(y);
|
|
35482
|
+
out.push({ type: "line", x1: margin, y1: py, x2: width - margin, y2: py, color: "#e5e7eb", lineWidth: 1 });
|
|
35483
|
+
}
|
|
35484
|
+
}
|
|
35485
|
+
if (showAxes) {
|
|
35486
|
+
const xAxisY = Math.max(margin, Math.min(height - margin, mapY(0)));
|
|
35487
|
+
const yAxisX = Math.max(margin, Math.min(width - margin, mapX(0)));
|
|
35488
|
+
out.push({ type: "line", x1: margin, y1: xAxisY, x2: width - margin, y2: xAxisY, color: "#374151", lineWidth: 2 });
|
|
35489
|
+
out.push({ type: "line", x1: yAxisX, y1: margin, x2: yAxisX, y2: height - margin, color: "#374151", lineWidth: 2 });
|
|
35490
|
+
}
|
|
35491
|
+
for (const curve of curves) {
|
|
35492
|
+
if (!curve.samples || curve.samples.length < 2) continue;
|
|
35493
|
+
for (let i = 1; i < curve.samples.length; i++) {
|
|
35494
|
+
const a = curve.samples[i - 1];
|
|
35495
|
+
const b = curve.samples[i];
|
|
35496
|
+
if (a.x < xMin || a.x > xMax || b.x < xMin || b.x > xMax) continue;
|
|
35497
|
+
out.push({
|
|
35498
|
+
type: "line",
|
|
35499
|
+
x1: mapX(a.x),
|
|
35500
|
+
y1: mapY(a.y),
|
|
35501
|
+
x2: mapX(b.x),
|
|
35502
|
+
y2: mapY(b.y),
|
|
35503
|
+
color: curve.color ?? "#2563eb",
|
|
35504
|
+
lineWidth: 2
|
|
35505
|
+
});
|
|
35506
|
+
}
|
|
35507
|
+
}
|
|
35508
|
+
for (const p2 of points) {
|
|
35509
|
+
if (p2.x < xMin || p2.x > xMax || p2.y < yMin || p2.y > yMax) continue;
|
|
35510
|
+
out.push({
|
|
35511
|
+
type: "circle",
|
|
35512
|
+
x: mapX(p2.x),
|
|
35513
|
+
y: mapY(p2.y),
|
|
35514
|
+
radius: p2.radius ?? 4,
|
|
35515
|
+
color: p2.color ?? "#dc2626",
|
|
35516
|
+
fill: p2.color ?? "#dc2626"
|
|
35517
|
+
});
|
|
35518
|
+
if (p2.label) {
|
|
35519
|
+
out.push({ type: "text", x: mapX(p2.x) + 8, y: mapY(p2.y) - 8, text: p2.label, color: "#111827", fontSize: 12 });
|
|
35520
|
+
}
|
|
35521
|
+
}
|
|
35522
|
+
for (const v of vectors) {
|
|
35523
|
+
if (v.x < xMin || v.x > xMax || v.y < yMin || v.y > yMax) continue;
|
|
35524
|
+
const x1 = mapX(v.x);
|
|
35525
|
+
const y1 = mapY(v.y);
|
|
35526
|
+
const x2 = mapX(v.x + v.vx);
|
|
35527
|
+
const y2 = mapY(v.y + v.vy);
|
|
35528
|
+
out.push({ type: "arrow", x1, y1, x2, y2, color: v.color ?? "#7c3aed", lineWidth: 2 });
|
|
35529
|
+
if (v.label) {
|
|
35530
|
+
out.push({ type: "text", x: x2 + 6, y: y2 - 6, text: v.label, color: "#111827", fontSize: 12 });
|
|
35531
|
+
}
|
|
35532
|
+
}
|
|
35533
|
+
out.push(...shapes);
|
|
35534
|
+
return out;
|
|
35535
|
+
}, [width, height, xMin, xMax, yMin, yMax, showAxes, showGrid, gridStep, curves, points, vectors, shapes]);
|
|
35536
|
+
return /* @__PURE__ */ jsxRuntime.jsx(Card, { className, children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "sm", children: [
|
|
35537
|
+
title ? /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h4", children: title }) : null,
|
|
35538
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
35539
|
+
LearningCanvas,
|
|
35540
|
+
{
|
|
35541
|
+
width,
|
|
35542
|
+
height,
|
|
35543
|
+
shapes: derivedShapes,
|
|
35544
|
+
interactive,
|
|
35545
|
+
animate,
|
|
35546
|
+
onShapeClick,
|
|
35547
|
+
isLoading,
|
|
35548
|
+
error
|
|
35549
|
+
}
|
|
35550
|
+
)
|
|
35551
|
+
] }) });
|
|
35552
|
+
};
|
|
35553
|
+
}
|
|
35554
|
+
});
|
|
35555
|
+
var PhysicsCanvas;
|
|
35556
|
+
var init_PhysicsCanvas = __esm({
|
|
35557
|
+
"components/learning/molecules/PhysicsCanvas.tsx"() {
|
|
35558
|
+
"use client";
|
|
35559
|
+
init_atoms();
|
|
35560
|
+
init_Stack();
|
|
35561
|
+
init_LearningCanvas();
|
|
35562
|
+
PhysicsCanvas = ({
|
|
35563
|
+
className,
|
|
35564
|
+
width = 600,
|
|
35565
|
+
height = 400,
|
|
35566
|
+
title,
|
|
35567
|
+
backgroundColor,
|
|
35568
|
+
bodies = [],
|
|
35569
|
+
constraints = [],
|
|
35570
|
+
showVelocity = true,
|
|
35571
|
+
showForces = false,
|
|
35572
|
+
velocityScale = 20,
|
|
35573
|
+
forceScale = 20,
|
|
35574
|
+
shapes = [],
|
|
35575
|
+
interactive = false,
|
|
35576
|
+
animate = false,
|
|
35577
|
+
onShapeClick,
|
|
35578
|
+
isLoading,
|
|
35579
|
+
error
|
|
35580
|
+
}) => {
|
|
35581
|
+
const derivedShapes = React107.useMemo(() => {
|
|
35582
|
+
const out = [];
|
|
35583
|
+
const bodyById = /* @__PURE__ */ new Map();
|
|
35584
|
+
for (const b of bodies) {
|
|
35585
|
+
if (b.id) bodyById.set(b.id, b);
|
|
35586
|
+
}
|
|
35587
|
+
for (const c of constraints) {
|
|
35588
|
+
const a = bodyById.get(c.from);
|
|
35589
|
+
const b = bodyById.get(c.to);
|
|
35590
|
+
if (!a || !b) continue;
|
|
35591
|
+
out.push({
|
|
35592
|
+
type: "line",
|
|
35593
|
+
x1: a.x,
|
|
35594
|
+
y1: a.y,
|
|
35595
|
+
x2: b.x,
|
|
35596
|
+
y2: b.y,
|
|
35597
|
+
color: c.color ?? "#9ca3af",
|
|
35598
|
+
lineWidth: 2
|
|
35599
|
+
});
|
|
35600
|
+
}
|
|
35601
|
+
for (const b of bodies) {
|
|
35602
|
+
out.push({
|
|
35603
|
+
type: "circle",
|
|
35604
|
+
x: b.x,
|
|
35605
|
+
y: b.y,
|
|
35606
|
+
radius: b.radius ?? 12,
|
|
35607
|
+
color: b.color ?? "#2563eb",
|
|
35608
|
+
fill: b.color ?? "#2563eb",
|
|
35609
|
+
id: b.id
|
|
35610
|
+
});
|
|
35611
|
+
if (b.label) {
|
|
35612
|
+
out.push({
|
|
35613
|
+
type: "text",
|
|
35614
|
+
x: b.x + (b.radius ?? 12) + 6,
|
|
35615
|
+
y: b.y - (b.radius ?? 12) - 6,
|
|
35616
|
+
text: b.label,
|
|
35617
|
+
color: "#111827",
|
|
35618
|
+
fontSize: 12
|
|
35619
|
+
});
|
|
35620
|
+
}
|
|
35621
|
+
if (showVelocity && b.vx != null && b.vy != null && (b.vx !== 0 || b.vy !== 0)) {
|
|
35622
|
+
out.push({
|
|
35623
|
+
type: "arrow",
|
|
35624
|
+
x1: b.x,
|
|
35625
|
+
y1: b.y,
|
|
35626
|
+
x2: b.x + b.vx * velocityScale,
|
|
35627
|
+
y2: b.y + b.vy * velocityScale,
|
|
35628
|
+
color: "#16a34a",
|
|
35629
|
+
lineWidth: 2
|
|
35630
|
+
});
|
|
35631
|
+
}
|
|
35632
|
+
if (showForces && b.fx != null && b.fy != null && (b.fx !== 0 || b.fy !== 0)) {
|
|
35633
|
+
out.push({
|
|
35634
|
+
type: "arrow",
|
|
35635
|
+
x1: b.x,
|
|
35636
|
+
y1: b.y,
|
|
35637
|
+
x2: b.x + b.fx * forceScale,
|
|
35638
|
+
y2: b.y + b.fy * forceScale,
|
|
35639
|
+
color: "#dc2626",
|
|
35640
|
+
lineWidth: 2
|
|
35641
|
+
});
|
|
35642
|
+
}
|
|
35643
|
+
}
|
|
35644
|
+
out.push(...shapes);
|
|
35645
|
+
return out;
|
|
35646
|
+
}, [bodies, constraints, showVelocity, showForces, velocityScale, forceScale, shapes]);
|
|
35647
|
+
return /* @__PURE__ */ jsxRuntime.jsx(Card, { className, children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "sm", children: [
|
|
35648
|
+
title ? /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h4", children: title }) : null,
|
|
35649
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
35650
|
+
LearningCanvas,
|
|
35651
|
+
{
|
|
35652
|
+
width,
|
|
35653
|
+
height,
|
|
35654
|
+
backgroundColor,
|
|
35655
|
+
shapes: derivedShapes,
|
|
35656
|
+
interactive,
|
|
35657
|
+
animate,
|
|
35658
|
+
onShapeClick,
|
|
35659
|
+
isLoading,
|
|
35660
|
+
error
|
|
35661
|
+
}
|
|
35662
|
+
)
|
|
35663
|
+
] }) });
|
|
35664
|
+
};
|
|
35665
|
+
}
|
|
35666
|
+
});
|
|
35092
35667
|
function resolveNodeColor(node, groups) {
|
|
35093
35668
|
if (node.color) return node.color;
|
|
35094
35669
|
if (node.group) {
|
|
@@ -35460,13 +36035,13 @@ var init_MapView = __esm({
|
|
|
35460
36035
|
shadowSize: [41, 41]
|
|
35461
36036
|
});
|
|
35462
36037
|
L.Marker.prototype.options.icon = defaultIcon;
|
|
35463
|
-
const { useEffect:
|
|
36038
|
+
const { useEffect: useEffect76, useRef: useRef77, useCallback: useCallback117, useState: useState108 } = React107__namespace.default;
|
|
35464
36039
|
const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
|
|
35465
36040
|
const { useEventBus: useEventBus2 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
|
|
35466
36041
|
function MapUpdater({ centerLat, centerLng, zoom }) {
|
|
35467
36042
|
const map = useMap();
|
|
35468
|
-
const prevRef =
|
|
35469
|
-
|
|
36043
|
+
const prevRef = useRef77({ centerLat, centerLng, zoom });
|
|
36044
|
+
useEffect76(() => {
|
|
35470
36045
|
const prev = prevRef.current;
|
|
35471
36046
|
if (prev.centerLat !== centerLat || prev.centerLng !== centerLng || prev.zoom !== zoom) {
|
|
35472
36047
|
map.setView([centerLat, centerLng], zoom);
|
|
@@ -35477,7 +36052,7 @@ var init_MapView = __esm({
|
|
|
35477
36052
|
}
|
|
35478
36053
|
function MapClickHandler({ onMapClick }) {
|
|
35479
36054
|
const map = useMap();
|
|
35480
|
-
|
|
36055
|
+
useEffect76(() => {
|
|
35481
36056
|
if (!onMapClick) return;
|
|
35482
36057
|
const handler = (e) => {
|
|
35483
36058
|
onMapClick(e.latlng.lat, e.latlng.lng);
|
|
@@ -43918,7 +44493,7 @@ function getGroupColor(group, groups) {
|
|
|
43918
44493
|
const idx = groups.indexOf(group);
|
|
43919
44494
|
return GROUP_COLORS2[idx % GROUP_COLORS2.length];
|
|
43920
44495
|
}
|
|
43921
|
-
function
|
|
44496
|
+
function resolveColor3(color, el) {
|
|
43922
44497
|
const m = /^var\((--[^,)]+)(?:,\s*([^)]+))?\)$/.exec(color.trim());
|
|
43923
44498
|
if (!m) return color;
|
|
43924
44499
|
const resolved = getComputedStyle(el).getPropertyValue(m[1]).trim();
|
|
@@ -44179,7 +44754,7 @@ var init_GraphCanvas = __esm({
|
|
|
44179
44754
|
const w = logicalW;
|
|
44180
44755
|
const h = height;
|
|
44181
44756
|
const nodes = nodesRef.current;
|
|
44182
|
-
const accentColor =
|
|
44757
|
+
const accentColor = resolveColor3("var(--color-accent)", canvas);
|
|
44183
44758
|
const dpr = typeof window !== "undefined" && window.devicePixelRatio || 1;
|
|
44184
44759
|
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
|
|
44185
44760
|
ctx.clearRect(0, 0, w, h);
|
|
@@ -44218,7 +44793,7 @@ var init_GraphCanvas = __esm({
|
|
|
44218
44793
|
ctx.globalAlpha = 1;
|
|
44219
44794
|
for (const node of nodes) {
|
|
44220
44795
|
const size = node.size || 8;
|
|
44221
|
-
const color =
|
|
44796
|
+
const color = resolveColor3(node.color || getGroupColor(node.group, groups), canvas);
|
|
44222
44797
|
const isHovered = hoveredNode === node.id;
|
|
44223
44798
|
const isSelected = selectedNodeId !== void 0 && node.id === selectedNodeId;
|
|
44224
44799
|
ctx.globalAlpha = hoveredNode && !neighbors.has(node.id) ? 0.2 : 1;
|
|
@@ -51097,6 +51672,7 @@ var init_component_registry_generated = __esm({
|
|
|
51097
51672
|
init_BattleBoard();
|
|
51098
51673
|
init_BattleTemplate();
|
|
51099
51674
|
init_BehaviorView();
|
|
51675
|
+
init_BiologyCanvas();
|
|
51100
51676
|
init_BloomQuizBlock();
|
|
51101
51677
|
init_BoardgameBoard();
|
|
51102
51678
|
init_BookChapterView();
|
|
@@ -51108,7 +51684,6 @@ var init_component_registry_generated = __esm({
|
|
|
51108
51684
|
init_BranchingLogicBuilder();
|
|
51109
51685
|
init_Breadcrumb();
|
|
51110
51686
|
init_BuilderBoard();
|
|
51111
|
-
init_ButtonGroup();
|
|
51112
51687
|
init_CTABanner();
|
|
51113
51688
|
init_CalendarGrid();
|
|
51114
51689
|
init_Canvas2D();
|
|
@@ -51127,6 +51702,7 @@ var init_component_registry_generated = __esm({
|
|
|
51127
51702
|
init_ChartLegend();
|
|
51128
51703
|
init_ChatBar();
|
|
51129
51704
|
init_Checkbox();
|
|
51705
|
+
init_ChemistryCanvas();
|
|
51130
51706
|
init_ChoiceButton();
|
|
51131
51707
|
init_CityBuilderBoard();
|
|
51132
51708
|
init_CityBuilderTemplate();
|
|
@@ -51185,9 +51761,9 @@ var init_component_registry_generated = __esm({
|
|
|
51185
51761
|
init_FlipContainer();
|
|
51186
51762
|
init_FloatingActionButton();
|
|
51187
51763
|
init_Form();
|
|
51764
|
+
init_FormSection();
|
|
51188
51765
|
init_FormField();
|
|
51189
51766
|
init_FormSectionHeader();
|
|
51190
|
-
init_GameAudioProvider();
|
|
51191
51767
|
init_GameAudioToggle();
|
|
51192
51768
|
init_GameCard();
|
|
51193
51769
|
init_GameHud();
|
|
@@ -51218,6 +51794,7 @@ var init_component_registry_generated = __esm({
|
|
|
51218
51794
|
init_JazariStateMachine();
|
|
51219
51795
|
init_LandingPageTemplate();
|
|
51220
51796
|
init_LawReferenceTooltip();
|
|
51797
|
+
init_LearningCanvas();
|
|
51221
51798
|
init_Lightbox();
|
|
51222
51799
|
init_LikertScale();
|
|
51223
51800
|
init_LineChart();
|
|
@@ -51226,9 +51803,11 @@ var init_component_registry_generated = __esm({
|
|
|
51226
51803
|
init_MapView();
|
|
51227
51804
|
init_MarkdownContent();
|
|
51228
51805
|
init_MarketingFooter();
|
|
51806
|
+
init_MarketingStatCard();
|
|
51229
51807
|
init_MasterDetail();
|
|
51230
51808
|
init_MasterDetailLayout();
|
|
51231
51809
|
init_MatchPuzzleBoard();
|
|
51810
|
+
init_MathCanvas();
|
|
51232
51811
|
init_MatrixQuestion();
|
|
51233
51812
|
init_MediaGallery();
|
|
51234
51813
|
init_Menu();
|
|
@@ -51247,6 +51826,7 @@ var init_component_registry_generated = __esm({
|
|
|
51247
51826
|
init_PageHeader();
|
|
51248
51827
|
init_Pagination();
|
|
51249
51828
|
init_PatternTile();
|
|
51829
|
+
init_PhysicsCanvas();
|
|
51250
51830
|
init_PinballBoard();
|
|
51251
51831
|
init_PirateBoard();
|
|
51252
51832
|
init_PlatformerBoard();
|
|
@@ -51423,6 +52003,7 @@ var init_component_registry_generated = __esm({
|
|
|
51423
52003
|
"BattleBoard": BattleBoard,
|
|
51424
52004
|
"BattleTemplate": BattleTemplate,
|
|
51425
52005
|
"BehaviorView": BehaviorView,
|
|
52006
|
+
"BiologyCanvas": BiologyCanvas,
|
|
51426
52007
|
"BloomQuizBlock": BloomQuizBlock,
|
|
51427
52008
|
"BoardgameBoard": BoardgameBoard,
|
|
51428
52009
|
"BookChapterView": BookChapterView,
|
|
@@ -51436,7 +52017,6 @@ var init_component_registry_generated = __esm({
|
|
|
51436
52017
|
"Breadcrumb": Breadcrumb,
|
|
51437
52018
|
"BuilderBoard": BuilderBoard,
|
|
51438
52019
|
"Button": ButtonPattern,
|
|
51439
|
-
"ButtonGroup": ButtonGroup,
|
|
51440
52020
|
"ButtonPattern": ButtonPattern,
|
|
51441
52021
|
"CTABanner": CTABanner,
|
|
51442
52022
|
"CalendarGrid": CalendarGrid,
|
|
@@ -51456,6 +52036,7 @@ var init_component_registry_generated = __esm({
|
|
|
51456
52036
|
"ChartLegend": ChartLegend,
|
|
51457
52037
|
"ChatBar": ChatBar,
|
|
51458
52038
|
"Checkbox": Checkbox,
|
|
52039
|
+
"ChemistryCanvas": ChemistryCanvas,
|
|
51459
52040
|
"ChoiceButton": ChoiceButton,
|
|
51460
52041
|
"CityBuilderBoard": CityBuilderBoard,
|
|
51461
52042
|
"CityBuilderTemplate": CityBuilderTemplate,
|
|
@@ -51516,9 +52097,10 @@ var init_component_registry_generated = __esm({
|
|
|
51516
52097
|
"FlipContainer": FlipContainer,
|
|
51517
52098
|
"FloatingActionButton": FloatingActionButton,
|
|
51518
52099
|
"Form": Form,
|
|
52100
|
+
"FormActions": FormActions,
|
|
51519
52101
|
"FormField": FormField,
|
|
52102
|
+
"FormLayout": FormLayout,
|
|
51520
52103
|
"FormSectionHeader": FormSectionHeader,
|
|
51521
|
-
"GameAudioProvider": GameAudioProvider,
|
|
51522
52104
|
"GameAudioToggle": GameAudioToggle,
|
|
51523
52105
|
"GameBoard3D": GameBoard3D,
|
|
51524
52106
|
"GameCanvas3D": GameCanvas3D,
|
|
@@ -51556,6 +52138,7 @@ var init_component_registry_generated = __esm({
|
|
|
51556
52138
|
"LabelPattern": LabelPattern,
|
|
51557
52139
|
"LandingPageTemplate": LandingPageTemplate,
|
|
51558
52140
|
"LawReferenceTooltip": LawReferenceTooltip,
|
|
52141
|
+
"LearningCanvas": LearningCanvas,
|
|
51559
52142
|
"Lightbox": Lightbox,
|
|
51560
52143
|
"LikertScale": LikertScale,
|
|
51561
52144
|
"LineChart": LineChart2,
|
|
@@ -51564,9 +52147,11 @@ var init_component_registry_generated = __esm({
|
|
|
51564
52147
|
"MapView": MapView,
|
|
51565
52148
|
"MarkdownContent": MarkdownContent,
|
|
51566
52149
|
"MarketingFooter": MarketingFooter,
|
|
52150
|
+
"MarketingStatCard": MarketingStatCard,
|
|
51567
52151
|
"MasterDetail": MasterDetail,
|
|
51568
52152
|
"MasterDetailLayout": MasterDetailLayout,
|
|
51569
52153
|
"MatchPuzzleBoard": MatchPuzzleBoard,
|
|
52154
|
+
"MathCanvas": MathCanvas,
|
|
51570
52155
|
"MatrixQuestion": MatrixQuestion,
|
|
51571
52156
|
"MediaGallery": MediaGallery,
|
|
51572
52157
|
"Menu": Menu,
|
|
@@ -51585,6 +52170,7 @@ var init_component_registry_generated = __esm({
|
|
|
51585
52170
|
"PageHeader": PageHeader,
|
|
51586
52171
|
"Pagination": Pagination,
|
|
51587
52172
|
"PatternTile": PatternTile,
|
|
52173
|
+
"PhysicsCanvas": PhysicsCanvas,
|
|
51588
52174
|
"PinballBoard": PinballBoard,
|
|
51589
52175
|
"PirateBoard": PirateBoard,
|
|
51590
52176
|
"PlatformerBoard": PlatformerBoard,
|