@almadar/ui 5.35.0 → 5.37.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 +341 -285
- package/dist/avl/index.js +341 -282
- package/dist/components/game/organisms/puzzles/builder/BuilderBoard.d.ts +18 -5
- package/dist/components/game/organisms/puzzles/classifier/ClassifierBoard.d.ts +21 -3
- package/dist/components/game/organisms/puzzles/debugger/DebuggerBoard.d.ts +12 -2
- package/dist/components/game/organisms/puzzles/negotiator/NegotiatorBoard.d.ts +13 -3
- package/dist/components/game/organisms/puzzles/simulator/SimulatorBoard.d.ts +22 -7
- package/dist/components/index.cjs +348 -288
- package/dist/components/index.js +348 -285
- package/dist/docs/index.cjs +115 -81
- package/dist/docs/index.js +115 -78
- package/dist/marketing/index.cjs +114 -80
- package/dist/marketing/index.js +114 -77
- package/dist/providers/index.cjs +341 -285
- package/dist/providers/index.js +341 -282
- package/dist/runtime/index.cjs +341 -285
- package/dist/runtime/index.js +341 -282
- package/package.json +1 -1
package/dist/components/index.js
CHANGED
|
@@ -7,9 +7,6 @@ import { EventBusContext, useTraitScope, TraitScopeProvider } from '@almadar/ui/
|
|
|
7
7
|
import { createLogger, isLogLevelEnabled } from '@almadar/logger';
|
|
8
8
|
import * as LucideIcons2 from 'lucide-react';
|
|
9
9
|
import { Loader2, X, List, Printer, ChevronRight, ChevronLeft, CheckCircle, XCircle, Wrench, RotateCcw, Send, Play, Terminal, Search, ChevronUp, ChevronDown, MoreHorizontal, Bug, Package, Calendar, Pencil, Eye, Image as Image$1, Upload, ZoomIn, ArrowRight, Pause, SkipForward, TrendingUp, TrendingDown, Minus, AlertCircle, Circle, Clock, CheckCircle2, Code, FileText, WrapText, Check, Copy, HelpCircle, Type, Heading1, Heading2, Heading3, ListOrdered, Quote, GitBranch, Plus, Trash, ArrowLeft, Menu as Menu$1, AlertTriangle, Trash2, Eraser, ZoomOut, Download, Lightbulb, PauseCircle, Link2, Tag, User, DollarSign, Zap, Sword, Move, Heart, Shield } from 'lucide-react';
|
|
10
|
-
import * as PhosphorIcons from '@phosphor-icons/react';
|
|
11
|
-
import * as TablerIcons from '@tabler/icons-react';
|
|
12
|
-
import * as FaIcons from 'react-icons/fa';
|
|
13
10
|
import { useTranslate } from '@almadar/ui/hooks';
|
|
14
11
|
export * from '@almadar/ui/hooks';
|
|
15
12
|
import { evaluate, createMinimalContext } from '@almadar/evaluator';
|
|
@@ -1226,6 +1223,41 @@ function kebabToPascal(name) {
|
|
|
1226
1223
|
return part.charAt(0).toUpperCase() + part.slice(1);
|
|
1227
1224
|
}).join("");
|
|
1228
1225
|
}
|
|
1226
|
+
function loadLib(key, importer) {
|
|
1227
|
+
let p2 = libPromises.get(key);
|
|
1228
|
+
if (!p2) {
|
|
1229
|
+
p2 = importer().then((m) => m);
|
|
1230
|
+
libPromises.set(key, p2);
|
|
1231
|
+
}
|
|
1232
|
+
return p2;
|
|
1233
|
+
}
|
|
1234
|
+
function lazyFamilyIcon(libKey, importer, pick, fallbackName, family) {
|
|
1235
|
+
const Lazy = React77__default.lazy(async () => {
|
|
1236
|
+
const lib = await loadLib(libKey, importer);
|
|
1237
|
+
const Comp = pick(lib);
|
|
1238
|
+
if (!Comp) {
|
|
1239
|
+
warnFallback(fallbackName, family);
|
|
1240
|
+
return { default: makeLucideAdapter(fallbackName, true) };
|
|
1241
|
+
}
|
|
1242
|
+
return { default: Comp };
|
|
1243
|
+
});
|
|
1244
|
+
const Wrapped = (props) => /* @__PURE__ */ jsx(
|
|
1245
|
+
React77__default.Suspense,
|
|
1246
|
+
{
|
|
1247
|
+
fallback: /* @__PURE__ */ jsx(
|
|
1248
|
+
"span",
|
|
1249
|
+
{
|
|
1250
|
+
"aria-hidden": true,
|
|
1251
|
+
className: props.className,
|
|
1252
|
+
style: { display: "inline-block", ...props.style }
|
|
1253
|
+
}
|
|
1254
|
+
),
|
|
1255
|
+
children: /* @__PURE__ */ jsx(Lazy, { ...props })
|
|
1256
|
+
}
|
|
1257
|
+
);
|
|
1258
|
+
Wrapped.displayName = `Lazy.${libKey}.${fallbackName}`;
|
|
1259
|
+
return Wrapped;
|
|
1260
|
+
}
|
|
1229
1261
|
function resolveLucide(name) {
|
|
1230
1262
|
if (lucideAliases[name]) return lucideAliases[name];
|
|
1231
1263
|
const pascal = kebabToPascal(name);
|
|
@@ -1236,60 +1268,81 @@ function resolveLucide(name) {
|
|
|
1236
1268
|
if (asIs && typeof asIs === "object") return asIs;
|
|
1237
1269
|
return LucideIcons2.HelpCircle;
|
|
1238
1270
|
}
|
|
1239
|
-
function resolvePhosphor(name, weight) {
|
|
1271
|
+
function resolvePhosphor(name, weight, family) {
|
|
1240
1272
|
const target = phosphorAliases[name] ?? kebabToPascal(name);
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1273
|
+
return lazyFamilyIcon(
|
|
1274
|
+
"phosphor",
|
|
1275
|
+
() => import('@phosphor-icons/react'),
|
|
1276
|
+
(lib) => {
|
|
1277
|
+
const PhosphorComp = lib[target];
|
|
1278
|
+
if (!PhosphorComp || typeof PhosphorComp !== "object") return null;
|
|
1279
|
+
const Component = PhosphorComp;
|
|
1280
|
+
const Adapter = (props) => /* @__PURE__ */ jsx(
|
|
1281
|
+
Component,
|
|
1282
|
+
{
|
|
1283
|
+
weight,
|
|
1284
|
+
className: props.className,
|
|
1285
|
+
style: props.style,
|
|
1286
|
+
size: props.size ?? "1em"
|
|
1287
|
+
}
|
|
1288
|
+
);
|
|
1289
|
+
Adapter.displayName = `Phosphor.${target}.${weight}`;
|
|
1290
|
+
return Adapter;
|
|
1291
|
+
},
|
|
1292
|
+
name,
|
|
1293
|
+
family
|
|
1253
1294
|
);
|
|
1254
|
-
Adapter.displayName = `Phosphor.${target}.${weight}`;
|
|
1255
|
-
return Adapter;
|
|
1256
1295
|
}
|
|
1257
|
-
function resolveTabler(name) {
|
|
1296
|
+
function resolveTabler(name, family) {
|
|
1258
1297
|
const suffix = tablerAliases[name] ?? kebabToPascal(name);
|
|
1259
1298
|
const target = `Icon${suffix}`;
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1299
|
+
return lazyFamilyIcon(
|
|
1300
|
+
"tabler",
|
|
1301
|
+
() => import('@tabler/icons-react'),
|
|
1302
|
+
(lib) => {
|
|
1303
|
+
const TablerComp = lib[target];
|
|
1304
|
+
if (!TablerComp || typeof TablerComp !== "object") return null;
|
|
1305
|
+
const Component = TablerComp;
|
|
1306
|
+
const Adapter = (props) => /* @__PURE__ */ jsx(
|
|
1307
|
+
Component,
|
|
1308
|
+
{
|
|
1309
|
+
stroke: props.strokeWidth ?? 1.5,
|
|
1310
|
+
className: props.className,
|
|
1311
|
+
style: props.style,
|
|
1312
|
+
size: props.size ?? 24
|
|
1313
|
+
}
|
|
1314
|
+
);
|
|
1315
|
+
Adapter.displayName = `Tabler.${target}`;
|
|
1316
|
+
return Adapter;
|
|
1317
|
+
},
|
|
1318
|
+
name,
|
|
1319
|
+
family
|
|
1272
1320
|
);
|
|
1273
|
-
Adapter.displayName = `Tabler.${target}`;
|
|
1274
|
-
return Adapter;
|
|
1275
1321
|
}
|
|
1276
|
-
function resolveFa(name) {
|
|
1322
|
+
function resolveFa(name, family) {
|
|
1277
1323
|
const suffix = faAliases[name] ?? kebabToPascal(name);
|
|
1278
1324
|
const target = `Fa${suffix}`;
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1325
|
+
return lazyFamilyIcon(
|
|
1326
|
+
"fa",
|
|
1327
|
+
() => import('react-icons/fa'),
|
|
1328
|
+
(lib) => {
|
|
1329
|
+
const FaComp = lib[target];
|
|
1330
|
+
if (!FaComp || typeof FaComp !== "function") return null;
|
|
1331
|
+
const Component = FaComp;
|
|
1332
|
+
const Adapter = (props) => /* @__PURE__ */ jsx(
|
|
1333
|
+
Component,
|
|
1334
|
+
{
|
|
1335
|
+
className: props.className,
|
|
1336
|
+
style: props.style,
|
|
1337
|
+
size: props.size ?? "1em"
|
|
1338
|
+
}
|
|
1339
|
+
);
|
|
1340
|
+
Adapter.displayName = `Fa.${target}`;
|
|
1341
|
+
return Adapter;
|
|
1342
|
+
},
|
|
1343
|
+
name,
|
|
1344
|
+
family
|
|
1290
1345
|
);
|
|
1291
|
-
Adapter.displayName = `Fa.${target}`;
|
|
1292
|
-
return Adapter;
|
|
1293
1346
|
}
|
|
1294
1347
|
function warnFallback(name, family) {
|
|
1295
1348
|
const key = `${family}::${name}`;
|
|
@@ -1323,39 +1376,22 @@ function resolveIconForFamily(name, family) {
|
|
|
1323
1376
|
switch (family) {
|
|
1324
1377
|
case "lucide":
|
|
1325
1378
|
return makeLucideAdapter(name, false);
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
return
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
return
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
return makeLucideAdapter(name, true);
|
|
1343
|
-
}
|
|
1344
|
-
case "tabler": {
|
|
1345
|
-
const t = resolveTabler(name);
|
|
1346
|
-
if (t) return t;
|
|
1347
|
-
warnFallback(name, family);
|
|
1348
|
-
return makeLucideAdapter(name, true);
|
|
1349
|
-
}
|
|
1350
|
-
case "fa-solid": {
|
|
1351
|
-
const f3 = resolveFa(name);
|
|
1352
|
-
if (f3) return f3;
|
|
1353
|
-
warnFallback(name, family);
|
|
1354
|
-
return makeLucideAdapter(name, true);
|
|
1355
|
-
}
|
|
1356
|
-
}
|
|
1357
|
-
}
|
|
1358
|
-
var DEFAULT_FAMILY, VALID_FAMILIES, cachedFamily, listeners, observer, lucideAliases, phosphorAliases, tablerAliases, faAliases, warned;
|
|
1379
|
+
// Non-lucide families resolve to a lazy, Suspense-wrapped component that
|
|
1380
|
+
// dynamic-imports the library on first render and falls back to lucide
|
|
1381
|
+
// internally when the family lacks the icon (see lazyFamilyIcon).
|
|
1382
|
+
case "phosphor-outline":
|
|
1383
|
+
return resolvePhosphor(name, "regular", family);
|
|
1384
|
+
case "phosphor-fill":
|
|
1385
|
+
return resolvePhosphor(name, "fill", family);
|
|
1386
|
+
case "phosphor-duotone":
|
|
1387
|
+
return resolvePhosphor(name, "duotone", family);
|
|
1388
|
+
case "tabler":
|
|
1389
|
+
return resolveTabler(name, family);
|
|
1390
|
+
case "fa-solid":
|
|
1391
|
+
return resolveFa(name, family);
|
|
1392
|
+
}
|
|
1393
|
+
}
|
|
1394
|
+
var DEFAULT_FAMILY, VALID_FAMILIES, cachedFamily, listeners, observer, libPromises, lucideAliases, phosphorAliases, tablerAliases, faAliases, warned;
|
|
1359
1395
|
var init_iconFamily = __esm({
|
|
1360
1396
|
"lib/iconFamily.tsx"() {
|
|
1361
1397
|
"use client";
|
|
@@ -1371,6 +1407,7 @@ var init_iconFamily = __esm({
|
|
|
1371
1407
|
cachedFamily = null;
|
|
1372
1408
|
listeners = /* @__PURE__ */ new Set();
|
|
1373
1409
|
observer = null;
|
|
1410
|
+
libPromises = /* @__PURE__ */ new Map();
|
|
1374
1411
|
lucideAliases = {
|
|
1375
1412
|
close: LucideIcons2.X,
|
|
1376
1413
|
trash: LucideIcons2.Trash2,
|
|
@@ -15929,61 +15966,53 @@ var init_Breadcrumb = __esm({
|
|
|
15929
15966
|
function BuilderBoard({
|
|
15930
15967
|
entity,
|
|
15931
15968
|
completeEvent = "PUZZLE_COMPLETE",
|
|
15969
|
+
placeEvent,
|
|
15970
|
+
checkEvent,
|
|
15971
|
+
playAgainEvent,
|
|
15932
15972
|
className
|
|
15933
15973
|
}) {
|
|
15934
15974
|
const { emit } = useEventBus();
|
|
15935
15975
|
const { t } = useTranslate();
|
|
15936
15976
|
const resolved = boardEntity(entity);
|
|
15937
|
-
const [placements, setPlacements] = useState({});
|
|
15938
15977
|
const [headerError, setHeaderError] = useState(false);
|
|
15939
|
-
const [
|
|
15940
|
-
const [attempts, setAttempts] = useState(0);
|
|
15941
|
-
const [showHint, setShowHint] = useState(false);
|
|
15978
|
+
const [selectedComponent, setSelectedComponent] = useState(null);
|
|
15942
15979
|
const components = Array.isArray(resolved?.components) ? resolved.components : [];
|
|
15943
15980
|
const slots = Array.isArray(resolved?.slots) ? resolved.slots : [];
|
|
15981
|
+
const placements = {};
|
|
15982
|
+
for (const slot of slots) {
|
|
15983
|
+
if (slot.placedComponentId) placements[slot.id] = slot.placedComponentId;
|
|
15984
|
+
}
|
|
15985
|
+
const attempts = num(resolved?.attempts);
|
|
15986
|
+
const result = str(resolved?.result) || "none";
|
|
15987
|
+
const submitted = result === "win";
|
|
15944
15988
|
const usedComponentIds = new Set(Object.values(placements));
|
|
15945
15989
|
const availableComponents = components.filter((c) => !usedComponentIds.has(c.id));
|
|
15946
|
-
const
|
|
15947
|
-
const allPlaced = Object.keys(placements).length === slots.length;
|
|
15990
|
+
const allPlaced = slots.length > 0 && slots.every((s) => Boolean(placements[s.id]));
|
|
15948
15991
|
const results = submitted ? slots.map((slot) => ({
|
|
15949
15992
|
slot,
|
|
15950
15993
|
placed: placements[slot.id],
|
|
15951
|
-
correct: placements[slot.id] === slot.
|
|
15994
|
+
correct: placements[slot.id] === slot.requiredComponentId
|
|
15952
15995
|
})) : [];
|
|
15953
|
-
const
|
|
15996
|
+
const showHint = attempts >= 2 && Boolean(str(resolved?.hint));
|
|
15954
15997
|
const handlePlaceComponent = (slotId) => {
|
|
15955
15998
|
if (submitted || !selectedComponent) return;
|
|
15956
|
-
|
|
15999
|
+
if (placeEvent) emit(`UI:${placeEvent}`, { slotId, componentId: selectedComponent });
|
|
15957
16000
|
setSelectedComponent(null);
|
|
15958
16001
|
};
|
|
15959
16002
|
const handleRemoveFromSlot = (slotId) => {
|
|
15960
16003
|
if (submitted) return;
|
|
15961
|
-
|
|
15962
|
-
const next = { ...prev };
|
|
15963
|
-
delete next[slotId];
|
|
15964
|
-
return next;
|
|
15965
|
-
});
|
|
16004
|
+
if (placeEvent) emit(`UI:${placeEvent}`, { slotId, componentId: "" });
|
|
15966
16005
|
};
|
|
15967
|
-
const handleSubmit =
|
|
15968
|
-
|
|
15969
|
-
|
|
15970
|
-
|
|
15971
|
-
if (correct) {
|
|
16006
|
+
const handleSubmit = () => {
|
|
16007
|
+
if (checkEvent) emit(`UI:${checkEvent}`, {});
|
|
16008
|
+
const solved = slots.length > 0 && slots.every((s) => placements[s.id] === s.requiredComponentId);
|
|
16009
|
+
if (solved && completeEvent) {
|
|
15972
16010
|
emit(`UI:${completeEvent}`, { success: true, attempts: attempts + 1 });
|
|
15973
16011
|
}
|
|
15974
|
-
}, [slots, placements, attempts, completeEvent, emit]);
|
|
15975
|
-
const handleReset = () => {
|
|
15976
|
-
setSubmitted(false);
|
|
15977
|
-
if (attempts >= 2 && str(resolved?.hint)) {
|
|
15978
|
-
setShowHint(true);
|
|
15979
|
-
}
|
|
15980
16012
|
};
|
|
15981
|
-
const
|
|
15982
|
-
setPlacements({});
|
|
15983
|
-
setSubmitted(false);
|
|
16013
|
+
const handlePlayAgain = () => {
|
|
15984
16014
|
setSelectedComponent(null);
|
|
15985
|
-
|
|
15986
|
-
setShowHint(false);
|
|
16015
|
+
if (playAgainEvent) emit(`UI:${playAgainEvent}`, {});
|
|
15987
16016
|
};
|
|
15988
16017
|
const getComponentById = (id) => components.find((c) => c.id === id);
|
|
15989
16018
|
if (!resolved) return null;
|
|
@@ -16033,13 +16062,13 @@ function BuilderBoard({
|
|
|
16033
16062
|
/* @__PURE__ */ jsx(Typography, { variant: "small", weight: "bold", className: "uppercase tracking-wider text-muted-foreground", children: t("builder.blueprint") }),
|
|
16034
16063
|
/* @__PURE__ */ jsx(VStack, { gap: "sm", children: slots.map((slot) => {
|
|
16035
16064
|
const placedComp = placements[slot.id] ? getComponentById(placements[slot.id]) : null;
|
|
16036
|
-
const
|
|
16065
|
+
const result2 = results.find((r) => r.slot.id === slot.id);
|
|
16037
16066
|
return /* @__PURE__ */ jsxs(
|
|
16038
16067
|
HStack,
|
|
16039
16068
|
{
|
|
16040
16069
|
gap: "sm",
|
|
16041
16070
|
align: "center",
|
|
16042
|
-
className: `p-3 border-2 rounded ${
|
|
16071
|
+
className: `p-3 border-2 rounded ${result2 ? result2.correct ? "border-success" : "border-error" : selectedComponent ? "border-dashed border-foreground cursor-pointer" : "border-border"}`,
|
|
16043
16072
|
onClick: () => handlePlaceComponent(slot.id),
|
|
16044
16073
|
children: [
|
|
16045
16074
|
/* @__PURE__ */ jsxs(VStack, { gap: "none", className: "flex-1", children: [
|
|
@@ -16054,7 +16083,7 @@ function BuilderBoard({
|
|
|
16054
16083
|
] }) : null,
|
|
16055
16084
|
placedComp.label
|
|
16056
16085
|
] }),
|
|
16057
|
-
|
|
16086
|
+
result2 && /* @__PURE__ */ jsx(Icon, { icon: result2.correct ? CheckCircle : XCircle, size: "sm", className: result2.correct ? "text-success" : "text-error" })
|
|
16058
16087
|
] }) : /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-muted-foreground", children: t("builder.empty") })
|
|
16059
16088
|
]
|
|
16060
16089
|
},
|
|
@@ -16063,16 +16092,16 @@ function BuilderBoard({
|
|
|
16063
16092
|
}) })
|
|
16064
16093
|
] }) }),
|
|
16065
16094
|
submitted && /* @__PURE__ */ jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", align: "center", children: [
|
|
16066
|
-
/* @__PURE__ */ jsx(Icon, { icon:
|
|
16067
|
-
/* @__PURE__ */ jsx(Typography, { variant: "body", weight: "bold", children:
|
|
16095
|
+
/* @__PURE__ */ jsx(Icon, { icon: CheckCircle, size: "lg", className: "text-success" }),
|
|
16096
|
+
/* @__PURE__ */ jsx(Typography, { variant: "body", weight: "bold", children: str(resolved.successMessage) || t("builder.success") })
|
|
16068
16097
|
] }) }),
|
|
16069
16098
|
showHint && hint && /* @__PURE__ */ jsx(Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsx(Typography, { variant: "body", children: hint }) }),
|
|
16070
16099
|
/* @__PURE__ */ jsxs(HStack, { gap: "sm", justify: "center", children: [
|
|
16071
|
-
!submitted
|
|
16100
|
+
!submitted && /* @__PURE__ */ jsxs(Button, { variant: "primary", onClick: handleSubmit, disabled: !allPlaced, children: [
|
|
16072
16101
|
/* @__PURE__ */ jsx(Icon, { icon: Wrench, size: "sm" }),
|
|
16073
16102
|
t("builder.build")
|
|
16074
|
-
] })
|
|
16075
|
-
/* @__PURE__ */ jsxs(Button, { variant: "secondary", onClick:
|
|
16103
|
+
] }),
|
|
16104
|
+
/* @__PURE__ */ jsxs(Button, { variant: "secondary", onClick: handlePlayAgain, children: [
|
|
16076
16105
|
/* @__PURE__ */ jsx(Icon, { icon: RotateCcw, size: "sm" }),
|
|
16077
16106
|
t("builder.reset")
|
|
16078
16107
|
] })
|
|
@@ -19574,57 +19603,84 @@ var init_ChoiceButton = __esm({
|
|
|
19574
19603
|
function ClassifierBoard({
|
|
19575
19604
|
entity,
|
|
19576
19605
|
completeEvent = "PUZZLE_COMPLETE",
|
|
19606
|
+
assignEvent,
|
|
19607
|
+
checkEvent,
|
|
19608
|
+
playAgainEvent,
|
|
19577
19609
|
className
|
|
19578
19610
|
}) {
|
|
19579
19611
|
const { emit } = useEventBus();
|
|
19580
19612
|
const { t } = useTranslate();
|
|
19581
19613
|
const resolved = boardEntity(entity);
|
|
19582
|
-
const [
|
|
19614
|
+
const [localAssignments, setLocalAssignments] = useState({});
|
|
19583
19615
|
const [headerError, setHeaderError] = useState(false);
|
|
19584
|
-
const [
|
|
19585
|
-
const [
|
|
19616
|
+
const [localSubmitted, setLocalSubmitted] = useState(false);
|
|
19617
|
+
const [localAttempts, setLocalAttempts] = useState(0);
|
|
19586
19618
|
const [showHint, setShowHint] = useState(false);
|
|
19587
19619
|
const items = Array.isArray(resolved?.items) ? resolved.items : [];
|
|
19588
19620
|
const categories = Array.isArray(resolved?.categories) ? resolved.categories : [];
|
|
19621
|
+
const entityResult = str(resolved?.result);
|
|
19622
|
+
const entityDrivesResult = entityResult.length > 0;
|
|
19623
|
+
const entityHasAssignments = items.some((item) => item.assignedCategory != null);
|
|
19624
|
+
const assignments = entityHasAssignments ? items.reduce((acc, item) => {
|
|
19625
|
+
if (item.assignedCategory != null) acc[item.id] = item.assignedCategory;
|
|
19626
|
+
return acc;
|
|
19627
|
+
}, {}) : localAssignments;
|
|
19628
|
+
const attempts = entityDrivesResult ? num(resolved?.attempts) : localAttempts;
|
|
19629
|
+
const submitted = entityDrivesResult || localSubmitted;
|
|
19589
19630
|
const unassignedItems = items.filter((item) => !assignments[item.id]);
|
|
19590
|
-
const allAssigned = Object.keys(assignments).length === items.length;
|
|
19631
|
+
const allAssigned = items.length > 0 && Object.keys(assignments).length === items.length;
|
|
19591
19632
|
const results = submitted ? items.map((item) => ({
|
|
19592
19633
|
item,
|
|
19593
19634
|
assigned: assignments[item.id],
|
|
19594
19635
|
correct: assignments[item.id] === item.correctCategory
|
|
19595
19636
|
})) : [];
|
|
19596
|
-
const allCorrect = results.length > 0 && results.every((r) => r.correct);
|
|
19637
|
+
const allCorrect = entityDrivesResult ? entityResult === "success" : results.length > 0 && results.every((r) => r.correct);
|
|
19597
19638
|
const correctCount = results.filter((r) => r.correct).length;
|
|
19598
19639
|
const handleAssign = (itemId, categoryId) => {
|
|
19599
19640
|
if (submitted) return;
|
|
19600
|
-
|
|
19641
|
+
if (assignEvent) {
|
|
19642
|
+
emit(`UI:${assignEvent}`, { itemId, categoryId });
|
|
19643
|
+
}
|
|
19644
|
+
if (!entityHasAssignments) {
|
|
19645
|
+
setLocalAssignments((prev) => ({ ...prev, [itemId]: categoryId }));
|
|
19646
|
+
}
|
|
19601
19647
|
};
|
|
19602
19648
|
const handleUnassign = (itemId) => {
|
|
19603
19649
|
if (submitted) return;
|
|
19604
|
-
|
|
19605
|
-
|
|
19606
|
-
|
|
19607
|
-
|
|
19608
|
-
|
|
19650
|
+
if (!entityHasAssignments) {
|
|
19651
|
+
setLocalAssignments((prev) => {
|
|
19652
|
+
const next = { ...prev };
|
|
19653
|
+
delete next[itemId];
|
|
19654
|
+
return next;
|
|
19655
|
+
});
|
|
19656
|
+
}
|
|
19609
19657
|
};
|
|
19610
19658
|
const handleSubmit = useCallback(() => {
|
|
19611
|
-
|
|
19612
|
-
|
|
19613
|
-
|
|
19614
|
-
if (
|
|
19615
|
-
|
|
19659
|
+
if (checkEvent) {
|
|
19660
|
+
emit(`UI:${checkEvent}`, {});
|
|
19661
|
+
}
|
|
19662
|
+
if (!entityDrivesResult) {
|
|
19663
|
+
setLocalSubmitted(true);
|
|
19664
|
+
setLocalAttempts((a) => a + 1);
|
|
19665
|
+
const correct = items.every((item) => assignments[item.id] === item.correctCategory);
|
|
19666
|
+
if (correct) {
|
|
19667
|
+
emit(`UI:${completeEvent}`, { success: true, attempts: attempts + 1 });
|
|
19668
|
+
}
|
|
19616
19669
|
}
|
|
19617
|
-
}, [items, assignments, attempts, completeEvent, emit]);
|
|
19670
|
+
}, [checkEvent, entityDrivesResult, items, assignments, attempts, completeEvent, emit]);
|
|
19618
19671
|
const handleReset = () => {
|
|
19619
|
-
|
|
19672
|
+
if (!entityDrivesResult) setLocalSubmitted(false);
|
|
19620
19673
|
if (attempts >= 2 && str(resolved?.hint)) {
|
|
19621
19674
|
setShowHint(true);
|
|
19622
19675
|
}
|
|
19623
19676
|
};
|
|
19624
19677
|
const handleFullReset = () => {
|
|
19625
|
-
|
|
19626
|
-
|
|
19627
|
-
|
|
19678
|
+
if (playAgainEvent) {
|
|
19679
|
+
emit(`UI:${playAgainEvent}`, {});
|
|
19680
|
+
}
|
|
19681
|
+
setLocalAssignments({});
|
|
19682
|
+
setLocalSubmitted(false);
|
|
19683
|
+
setLocalAttempts(0);
|
|
19628
19684
|
setShowHint(false);
|
|
19629
19685
|
};
|
|
19630
19686
|
if (!resolved) return null;
|
|
@@ -23276,7 +23332,7 @@ var init_FormSection = __esm({
|
|
|
23276
23332
|
FormActions.displayName = "FormActions";
|
|
23277
23333
|
}
|
|
23278
23334
|
});
|
|
23279
|
-
var ALL_CATEGORY, GridPicker;
|
|
23335
|
+
var ALL_CATEGORY, MAX_RENDERED, GridPicker;
|
|
23280
23336
|
var init_GridPicker = __esm({
|
|
23281
23337
|
"components/core/molecules/GridPicker.tsx"() {
|
|
23282
23338
|
"use client";
|
|
@@ -23285,6 +23341,7 @@ var init_GridPicker = __esm({
|
|
|
23285
23341
|
init_Badge();
|
|
23286
23342
|
init_Stack();
|
|
23287
23343
|
ALL_CATEGORY = "__all__";
|
|
23344
|
+
MAX_RENDERED = 300;
|
|
23288
23345
|
GridPicker = ({
|
|
23289
23346
|
items,
|
|
23290
23347
|
value,
|
|
@@ -23314,6 +23371,8 @@ var init_GridPicker = __esm({
|
|
|
23314
23371
|
return matchesCategory && matchesSearch;
|
|
23315
23372
|
});
|
|
23316
23373
|
}, [items, search, activeCategory]);
|
|
23374
|
+
const visible = useMemo(() => filtered.slice(0, MAX_RENDERED), [filtered]);
|
|
23375
|
+
const truncated = filtered.length - visible.length;
|
|
23317
23376
|
const select = useCallback(
|
|
23318
23377
|
(item) => {
|
|
23319
23378
|
onChange(item.id);
|
|
@@ -23415,7 +23474,7 @@ var init_GridPicker = __esm({
|
|
|
23415
23474
|
style: {
|
|
23416
23475
|
gridTemplateColumns: `repeat(auto-fill, minmax(${cellSize}px, 1fr))`
|
|
23417
23476
|
},
|
|
23418
|
-
children:
|
|
23477
|
+
children: visible.map((item, index) => {
|
|
23419
23478
|
const selected = item.id === value;
|
|
23420
23479
|
return /* @__PURE__ */ jsx(
|
|
23421
23480
|
"button",
|
|
@@ -23442,7 +23501,8 @@ var init_GridPicker = __esm({
|
|
|
23442
23501
|
);
|
|
23443
23502
|
})
|
|
23444
23503
|
}
|
|
23445
|
-
)
|
|
23504
|
+
),
|
|
23505
|
+
truncated > 0 && /* @__PURE__ */ jsx("div", { className: "px-1 text-xs text-muted-foreground", children: `+${truncated} more \u2014 refine your search` })
|
|
23446
23506
|
] });
|
|
23447
23507
|
};
|
|
23448
23508
|
GridPicker.displayName = "GridPicker";
|
|
@@ -29163,13 +29223,13 @@ var init_MapView = __esm({
|
|
|
29163
29223
|
shadowSize: [41, 41]
|
|
29164
29224
|
});
|
|
29165
29225
|
L.Marker.prototype.options.icon = defaultIcon;
|
|
29166
|
-
const { useEffect:
|
|
29226
|
+
const { useEffect: useEffect73, useRef: useRef70, useCallback: useCallback113, useState: useState105 } = React77__default;
|
|
29167
29227
|
const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
|
|
29168
29228
|
const { useEventBus: useEventBus2 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
|
|
29169
29229
|
function MapUpdater({ centerLat, centerLng, zoom }) {
|
|
29170
29230
|
const map = useMap();
|
|
29171
|
-
const prevRef =
|
|
29172
|
-
|
|
29231
|
+
const prevRef = useRef70({ centerLat, centerLng, zoom });
|
|
29232
|
+
useEffect73(() => {
|
|
29173
29233
|
const prev = prevRef.current;
|
|
29174
29234
|
if (prev.centerLat !== centerLat || prev.centerLng !== centerLng || prev.zoom !== zoom) {
|
|
29175
29235
|
map.setView([centerLat, centerLng], zoom);
|
|
@@ -29180,7 +29240,7 @@ var init_MapView = __esm({
|
|
|
29180
29240
|
}
|
|
29181
29241
|
function MapClickHandler({ onMapClick }) {
|
|
29182
29242
|
const map = useMap();
|
|
29183
|
-
|
|
29243
|
+
useEffect73(() => {
|
|
29184
29244
|
if (!onMapClick) return;
|
|
29185
29245
|
const handler = (e) => {
|
|
29186
29246
|
onMapClick(e.latlng.lat, e.latlng.lng);
|
|
@@ -29209,7 +29269,7 @@ var init_MapView = __esm({
|
|
|
29209
29269
|
}) {
|
|
29210
29270
|
const eventBus = useEventBus2();
|
|
29211
29271
|
const [clickedPosition, setClickedPosition] = useState105(null);
|
|
29212
|
-
const handleMapClick =
|
|
29272
|
+
const handleMapClick = useCallback113((lat, lng) => {
|
|
29213
29273
|
if (showClickedPin) {
|
|
29214
29274
|
setClickedPosition({ lat, lng });
|
|
29215
29275
|
}
|
|
@@ -29218,7 +29278,7 @@ var init_MapView = __esm({
|
|
|
29218
29278
|
eventBus.emit(`UI:${mapClickEvent}`, { latitude: lat, longitude: lng });
|
|
29219
29279
|
}
|
|
29220
29280
|
}, [onMapClick, mapClickEvent, eventBus, showClickedPin]);
|
|
29221
|
-
const handleMarkerClick =
|
|
29281
|
+
const handleMarkerClick = useCallback113((marker) => {
|
|
29222
29282
|
onMarkerClick?.(marker);
|
|
29223
29283
|
if (markerClickEvent) {
|
|
29224
29284
|
eventBus.emit(`UI:${markerClickEvent}`, { ...marker });
|
|
@@ -38652,51 +38712,52 @@ var init_DataTable = __esm({
|
|
|
38652
38712
|
function DebuggerBoard({
|
|
38653
38713
|
entity,
|
|
38654
38714
|
completeEvent = "PUZZLE_COMPLETE",
|
|
38715
|
+
toggleFlagEvent,
|
|
38716
|
+
checkEvent,
|
|
38717
|
+
playAgainEvent,
|
|
38655
38718
|
className
|
|
38656
38719
|
}) {
|
|
38657
38720
|
const { emit } = useEventBus();
|
|
38658
38721
|
const { t } = useTranslate();
|
|
38659
38722
|
const resolved = boardEntity(entity);
|
|
38660
|
-
const [flaggedLines, setFlaggedLines] = useState(/* @__PURE__ */ new Set());
|
|
38661
38723
|
const [headerError, setHeaderError] = useState(false);
|
|
38662
|
-
const [submitted, setSubmitted] = useState(false);
|
|
38663
|
-
const [attempts, setAttempts] = useState(0);
|
|
38664
38724
|
const [showHint, setShowHint] = useState(false);
|
|
38725
|
+
const lines = Array.isArray(resolved?.lines) ? resolved.lines : [];
|
|
38726
|
+
const result = resolved?.result ?? null;
|
|
38727
|
+
const attempts = num(resolved?.attempts);
|
|
38728
|
+
const submitted = result != null;
|
|
38729
|
+
const bugLines = lines.filter((l) => l.isBug);
|
|
38730
|
+
const flaggedLines = lines.filter((l) => l.isFlagged);
|
|
38731
|
+
const correctFlags = lines.filter((l) => l.isBug && l.isFlagged);
|
|
38732
|
+
const falseFlags = lines.filter((l) => !l.isBug && l.isFlagged);
|
|
38733
|
+
const allCorrect = result === "win";
|
|
38665
38734
|
const toggleLine = (lineId) => {
|
|
38666
38735
|
if (submitted) return;
|
|
38667
|
-
|
|
38668
|
-
|
|
38669
|
-
|
|
38670
|
-
next.delete(lineId);
|
|
38671
|
-
} else {
|
|
38672
|
-
next.add(lineId);
|
|
38673
|
-
}
|
|
38674
|
-
return next;
|
|
38675
|
-
});
|
|
38736
|
+
if (toggleFlagEvent) {
|
|
38737
|
+
emit(`UI:${toggleFlagEvent}`, { lineId });
|
|
38738
|
+
}
|
|
38676
38739
|
};
|
|
38677
|
-
const lines = Array.isArray(resolved?.lines) ? resolved.lines : [];
|
|
38678
|
-
const bugLines = lines.filter((l) => l.isBug);
|
|
38679
|
-
const correctFlags = lines.filter((l) => l.isBug && flaggedLines.has(l.id));
|
|
38680
|
-
const falseFlags = lines.filter((l) => !l.isBug && flaggedLines.has(l.id));
|
|
38681
|
-
const allCorrect = submitted && correctFlags.length === bugLines.length && falseFlags.length === 0;
|
|
38682
38740
|
const handleSubmit = useCallback(() => {
|
|
38683
|
-
|
|
38684
|
-
|
|
38741
|
+
if (checkEvent) {
|
|
38742
|
+
emit(`UI:${checkEvent}`, {});
|
|
38743
|
+
}
|
|
38685
38744
|
const correct = correctFlags.length === bugLines.length && falseFlags.length === 0;
|
|
38686
38745
|
if (correct) {
|
|
38687
38746
|
emit(`UI:${completeEvent}`, { success: true, attempts: attempts + 1 });
|
|
38688
38747
|
}
|
|
38689
|
-
}, [correctFlags.length, bugLines.length, falseFlags.length, attempts, completeEvent, emit]);
|
|
38748
|
+
}, [checkEvent, correctFlags.length, bugLines.length, falseFlags.length, attempts, completeEvent, emit]);
|
|
38690
38749
|
const handleReset = () => {
|
|
38691
|
-
|
|
38750
|
+
if (playAgainEvent) {
|
|
38751
|
+
emit(`UI:${playAgainEvent}`, {});
|
|
38752
|
+
}
|
|
38692
38753
|
if (attempts >= 2 && str(resolved?.hint)) {
|
|
38693
38754
|
setShowHint(true);
|
|
38694
38755
|
}
|
|
38695
38756
|
};
|
|
38696
38757
|
const handleFullReset = () => {
|
|
38697
|
-
|
|
38698
|
-
|
|
38699
|
-
|
|
38758
|
+
if (playAgainEvent) {
|
|
38759
|
+
emit(`UI:${playAgainEvent}`, {});
|
|
38760
|
+
}
|
|
38700
38761
|
setShowHint(false);
|
|
38701
38762
|
};
|
|
38702
38763
|
if (!resolved) return null;
|
|
@@ -38724,7 +38785,7 @@ function DebuggerBoard({
|
|
|
38724
38785
|
/* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-muted-foreground", children: t("debugger.findBugs", { count: String(num(resolved.bugCount)) }) })
|
|
38725
38786
|
] }) }),
|
|
38726
38787
|
/* @__PURE__ */ jsx(Card, { className: "p-0 overflow-hidden", children: /* @__PURE__ */ jsx(VStack, { gap: "none", children: lines.map((line, i) => {
|
|
38727
|
-
const isFlagged =
|
|
38788
|
+
const isFlagged = !!line.isFlagged;
|
|
38728
38789
|
let lineStyle = "";
|
|
38729
38790
|
if (submitted) {
|
|
38730
38791
|
if (line.isBug && isFlagged) lineStyle = "bg-success/10";
|
|
@@ -38758,9 +38819,9 @@ function DebuggerBoard({
|
|
|
38758
38819
|
/* @__PURE__ */ jsx(
|
|
38759
38820
|
Icon,
|
|
38760
38821
|
{
|
|
38761
|
-
icon:
|
|
38822
|
+
icon: line.isFlagged ? CheckCircle : XCircle,
|
|
38762
38823
|
size: "xs",
|
|
38763
|
-
className:
|
|
38824
|
+
className: line.isFlagged ? "text-success mt-0.5" : "text-warning mt-0.5"
|
|
38764
38825
|
}
|
|
38765
38826
|
),
|
|
38766
38827
|
/* @__PURE__ */ jsxs(VStack, { gap: "none", children: [
|
|
@@ -38771,7 +38832,7 @@ function DebuggerBoard({
|
|
|
38771
38832
|
] }) }),
|
|
38772
38833
|
showHint && hint && /* @__PURE__ */ jsx(Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsx(Typography, { variant: "body", children: hint }) }),
|
|
38773
38834
|
/* @__PURE__ */ jsxs(HStack, { gap: "sm", justify: "center", children: [
|
|
38774
|
-
!submitted ? /* @__PURE__ */ jsxs(Button, { variant: "primary", onClick: handleSubmit, disabled: flaggedLines.
|
|
38835
|
+
!submitted ? /* @__PURE__ */ jsxs(Button, { variant: "primary", onClick: handleSubmit, disabled: flaggedLines.length === 0, children: [
|
|
38775
38836
|
/* @__PURE__ */ jsx(Icon, { icon: Send, size: "sm" }),
|
|
38776
38837
|
t("debugger.submit")
|
|
38777
38838
|
] }) : !allCorrect ? /* @__PURE__ */ jsx(Button, { variant: "primary", onClick: handleReset, children: t("debugger.tryAgain") }) : null,
|
|
@@ -42731,6 +42792,9 @@ function getOpponentAction(strategy, actions, history) {
|
|
|
42731
42792
|
function NegotiatorBoard({
|
|
42732
42793
|
entity,
|
|
42733
42794
|
completeEvent = "PUZZLE_COMPLETE",
|
|
42795
|
+
playRoundEvent,
|
|
42796
|
+
finishEvent,
|
|
42797
|
+
playAgainEvent,
|
|
42734
42798
|
className
|
|
42735
42799
|
}) {
|
|
42736
42800
|
const { emit } = useEventBus();
|
|
@@ -42739,13 +42803,14 @@ function NegotiatorBoard({
|
|
|
42739
42803
|
const [history, setHistory] = useState([]);
|
|
42740
42804
|
const [headerError, setHeaderError] = useState(false);
|
|
42741
42805
|
const [showHint, setShowHint] = useState(false);
|
|
42742
|
-
const totalRounds = num(resolved?.
|
|
42806
|
+
const totalRounds = num(resolved?.maxRounds);
|
|
42743
42807
|
const targetScore = num(resolved?.targetScore);
|
|
42744
|
-
const currentRound =
|
|
42745
|
-
const
|
|
42746
|
-
const playerTotal =
|
|
42808
|
+
const currentRound = num(resolved?.round);
|
|
42809
|
+
const result = str(resolved?.result) || "none";
|
|
42810
|
+
const playerTotal = num(resolved?.score);
|
|
42811
|
+
const isComplete = result !== "none" || totalRounds > 0 && currentRound >= totalRounds;
|
|
42812
|
+
const won = result === "win";
|
|
42747
42813
|
const opponentTotal = history.reduce((s, r) => s + r.opponentPayoff, 0);
|
|
42748
|
-
const won = isComplete && playerTotal >= targetScore;
|
|
42749
42814
|
const actions = Array.isArray(resolved?.actions) ? resolved.actions : [];
|
|
42750
42815
|
const payoffMatrix = Array.isArray(resolved?.payoffMatrix) ? resolved.payoffMatrix : [];
|
|
42751
42816
|
const handleAction = useCallback((actionId) => {
|
|
@@ -42754,29 +42819,45 @@ function NegotiatorBoard({
|
|
|
42754
42819
|
const payoff = payoffMatrix.find(
|
|
42755
42820
|
(p2) => p2.playerAction === actionId && p2.opponentAction === opponentAction
|
|
42756
42821
|
);
|
|
42757
|
-
const
|
|
42758
|
-
|
|
42759
|
-
|
|
42760
|
-
|
|
42761
|
-
|
|
42762
|
-
|
|
42763
|
-
|
|
42764
|
-
|
|
42765
|
-
|
|
42766
|
-
|
|
42767
|
-
|
|
42768
|
-
|
|
42769
|
-
|
|
42770
|
-
|
|
42771
|
-
|
|
42822
|
+
const playerPayoff = payoff?.playerPayoff ?? 0;
|
|
42823
|
+
setHistory((prev) => [
|
|
42824
|
+
...prev,
|
|
42825
|
+
{
|
|
42826
|
+
round: prev.length + 1,
|
|
42827
|
+
playerAction: actionId,
|
|
42828
|
+
opponentAction,
|
|
42829
|
+
playerPayoff,
|
|
42830
|
+
opponentPayoff: payoff?.opponentPayoff ?? 0
|
|
42831
|
+
}
|
|
42832
|
+
]);
|
|
42833
|
+
if (playRoundEvent) {
|
|
42834
|
+
emit(`UI:${playRoundEvent}`, { playerAction: actionId, payoff: playerPayoff });
|
|
42835
|
+
}
|
|
42836
|
+
if (totalRounds > 0 && currentRound + 1 >= totalRounds) {
|
|
42837
|
+
if (finishEvent) {
|
|
42838
|
+
emit(`UI:${finishEvent}`, {});
|
|
42839
|
+
}
|
|
42840
|
+
if (str(resolved?.hint)) {
|
|
42772
42841
|
setShowHint(true);
|
|
42773
42842
|
}
|
|
42774
42843
|
}
|
|
42775
|
-
}, [isComplete, resolved, totalRounds,
|
|
42776
|
-
const handleReset = () => {
|
|
42844
|
+
}, [isComplete, resolved, totalRounds, currentRound, actions, payoffMatrix, history, playRoundEvent, finishEvent, emit]);
|
|
42845
|
+
const handleReset = useCallback(() => {
|
|
42777
42846
|
setHistory([]);
|
|
42778
42847
|
setShowHint(false);
|
|
42779
|
-
|
|
42848
|
+
if (playAgainEvent) {
|
|
42849
|
+
emit(`UI:${playAgainEvent}`, {});
|
|
42850
|
+
}
|
|
42851
|
+
}, [playAgainEvent, emit]);
|
|
42852
|
+
const completedRef = useRef(false);
|
|
42853
|
+
useEffect(() => {
|
|
42854
|
+
if (result === "win" && !completedRef.current) {
|
|
42855
|
+
completedRef.current = true;
|
|
42856
|
+
emit(`UI:${completeEvent}`, { success: true, score: playerTotal });
|
|
42857
|
+
} else if (result === "none") {
|
|
42858
|
+
completedRef.current = false;
|
|
42859
|
+
}
|
|
42860
|
+
}, [result, playerTotal, completeEvent, emit]);
|
|
42780
42861
|
const getActionLabel = (id) => actions.find((a) => a.id === id)?.label ?? id;
|
|
42781
42862
|
if (!resolved) return null;
|
|
42782
42863
|
const theme = resolved.theme ?? void 0;
|
|
@@ -45777,67 +45858,47 @@ var init_SimulationGraph = __esm({
|
|
|
45777
45858
|
function SimulatorBoard({
|
|
45778
45859
|
entity,
|
|
45779
45860
|
completeEvent = "PUZZLE_COMPLETE",
|
|
45861
|
+
setAEvent,
|
|
45862
|
+
setBEvent,
|
|
45863
|
+
checkEvent,
|
|
45864
|
+
playAgainEvent,
|
|
45780
45865
|
className
|
|
45781
45866
|
}) {
|
|
45782
45867
|
const { emit } = useEventBus();
|
|
45783
45868
|
const { t } = useTranslate();
|
|
45784
45869
|
const resolved = boardEntity(entity);
|
|
45785
45870
|
const parameters = Array.isArray(resolved?.parameters) ? resolved.parameters : [];
|
|
45786
|
-
const [values, setValues] = useState(() => {
|
|
45787
|
-
const init = {};
|
|
45788
|
-
for (const p2 of parameters) {
|
|
45789
|
-
init[p2.id] = p2.initial;
|
|
45790
|
-
}
|
|
45791
|
-
return init;
|
|
45792
|
-
});
|
|
45793
45871
|
const [headerError, setHeaderError] = useState(false);
|
|
45794
|
-
|
|
45795
|
-
const
|
|
45796
|
-
const
|
|
45797
|
-
const
|
|
45798
|
-
|
|
45799
|
-
|
|
45800
|
-
|
|
45801
|
-
|
|
45802
|
-
|
|
45803
|
-
|
|
45804
|
-
|
|
45805
|
-
const
|
|
45806
|
-
const
|
|
45807
|
-
const
|
|
45808
|
-
const
|
|
45809
|
-
|
|
45810
|
-
|
|
45811
|
-
|
|
45812
|
-
};
|
|
45813
|
-
const handleSubmit = () => {
|
|
45814
|
-
setSubmitted(true);
|
|
45815
|
-
setAttempts((a) => a + 1);
|
|
45816
|
-
if (isCorrect) {
|
|
45817
|
-
emit(`UI:${completeEvent}`, { success: true, attempts: attempts + 1 });
|
|
45818
|
-
}
|
|
45872
|
+
if (!resolved) return null;
|
|
45873
|
+
const paramA = num(resolved.paramA);
|
|
45874
|
+
const paramB = num(resolved.paramB);
|
|
45875
|
+
const output = num(resolved.output);
|
|
45876
|
+
const targetValue = num(resolved.target);
|
|
45877
|
+
const targetTolerance = num(resolved.tolerance);
|
|
45878
|
+
const attempts = num(resolved.attempts);
|
|
45879
|
+
const result = str(resolved.result);
|
|
45880
|
+
const isWin = result === "win";
|
|
45881
|
+
const isComplete = result !== "none" && result !== "";
|
|
45882
|
+
const paramAValue = parameters[0];
|
|
45883
|
+
const paramBValue = parameters[1];
|
|
45884
|
+
const sliderValues = [paramA, paramB];
|
|
45885
|
+
const sliderEvents = [setAEvent, setBEvent];
|
|
45886
|
+
const handleParameterChange = (index, value) => {
|
|
45887
|
+
if (isComplete) return;
|
|
45888
|
+
const ev = sliderEvents[index];
|
|
45889
|
+
if (ev) emit(`UI:${ev}`, { value });
|
|
45819
45890
|
};
|
|
45820
|
-
const
|
|
45821
|
-
|
|
45822
|
-
if (attempts >= 2 && str(resolved?.hint)) {
|
|
45823
|
-
setShowHint(true);
|
|
45824
|
-
}
|
|
45891
|
+
const handleCheck = () => {
|
|
45892
|
+
if (checkEvent) emit(`UI:${checkEvent}`, {});
|
|
45825
45893
|
};
|
|
45826
|
-
const
|
|
45827
|
-
|
|
45828
|
-
for (const p2 of parameters) {
|
|
45829
|
-
init[p2.id] = p2.initial;
|
|
45830
|
-
}
|
|
45831
|
-
setValues(init);
|
|
45832
|
-
setSubmitted(false);
|
|
45833
|
-
setAttempts(0);
|
|
45834
|
-
setShowHint(false);
|
|
45894
|
+
const handlePlayAgain = () => {
|
|
45895
|
+
if (playAgainEvent) emit(`UI:${playAgainEvent}`, {});
|
|
45835
45896
|
};
|
|
45836
|
-
if (!resolved) return null;
|
|
45837
45897
|
const theme = resolved.theme ?? void 0;
|
|
45838
45898
|
const themeBackground = theme?.background;
|
|
45839
45899
|
const headerImage = str(resolved.headerImage);
|
|
45840
45900
|
const hint = str(resolved.hint);
|
|
45901
|
+
const showHint = isComplete && !isWin && attempts >= 2 && Boolean(hint);
|
|
45841
45902
|
const outputLabel = str(resolved.outputLabel);
|
|
45842
45903
|
const outputUnit = str(resolved.outputUnit);
|
|
45843
45904
|
return /* @__PURE__ */ jsx(
|
|
@@ -45857,41 +45918,43 @@ function SimulatorBoard({
|
|
|
45857
45918
|
] }) }),
|
|
45858
45919
|
/* @__PURE__ */ jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxs(VStack, { gap: "md", children: [
|
|
45859
45920
|
/* @__PURE__ */ jsx(Typography, { variant: "small", weight: "bold", className: "uppercase tracking-wider text-muted-foreground", children: t("simulator.parameters") }),
|
|
45860
|
-
|
|
45861
|
-
/* @__PURE__ */ jsxs(
|
|
45862
|
-
/* @__PURE__ */
|
|
45863
|
-
|
|
45864
|
-
|
|
45865
|
-
|
|
45866
|
-
|
|
45867
|
-
|
|
45868
|
-
|
|
45869
|
-
/* @__PURE__ */ jsx(
|
|
45870
|
-
"input",
|
|
45871
|
-
{
|
|
45872
|
-
type: "range",
|
|
45873
|
-
min: param.min,
|
|
45874
|
-
max: param.max,
|
|
45875
|
-
step: param.step,
|
|
45876
|
-
value: values[param.id],
|
|
45877
|
-
onChange: (e) => handleParameterChange(param.id, Number(e.target.value)),
|
|
45878
|
-
disabled: submitted,
|
|
45879
|
-
className: "w-full accent-foreground"
|
|
45880
|
-
}
|
|
45881
|
-
),
|
|
45882
|
-
/* @__PURE__ */ jsxs(HStack, { justify: "between", children: [
|
|
45883
|
-
/* @__PURE__ */ jsxs(Typography, { variant: "caption", className: "text-muted-foreground", children: [
|
|
45884
|
-
param.min,
|
|
45885
|
-
" ",
|
|
45886
|
-
param.unit
|
|
45921
|
+
[paramAValue, paramBValue].map(
|
|
45922
|
+
(param, index) => param ? /* @__PURE__ */ jsxs(VStack, { gap: "xs", children: [
|
|
45923
|
+
/* @__PURE__ */ jsxs(HStack, { justify: "between", align: "center", children: [
|
|
45924
|
+
/* @__PURE__ */ jsx(Typography, { variant: "body", weight: "medium", children: param.label }),
|
|
45925
|
+
/* @__PURE__ */ jsxs(Badge, { size: "sm", children: [
|
|
45926
|
+
sliderValues[index],
|
|
45927
|
+
" ",
|
|
45928
|
+
param.unit
|
|
45929
|
+
] })
|
|
45887
45930
|
] }),
|
|
45888
|
-
/* @__PURE__ */
|
|
45889
|
-
|
|
45890
|
-
|
|
45891
|
-
|
|
45931
|
+
/* @__PURE__ */ jsx(
|
|
45932
|
+
"input",
|
|
45933
|
+
{
|
|
45934
|
+
type: "range",
|
|
45935
|
+
min: param.min,
|
|
45936
|
+
max: param.max,
|
|
45937
|
+
step: param.step,
|
|
45938
|
+
value: sliderValues[index],
|
|
45939
|
+
onChange: (e) => handleParameterChange(index, Number(e.target.value)),
|
|
45940
|
+
disabled: isComplete,
|
|
45941
|
+
className: "w-full accent-foreground"
|
|
45942
|
+
}
|
|
45943
|
+
),
|
|
45944
|
+
/* @__PURE__ */ jsxs(HStack, { justify: "between", children: [
|
|
45945
|
+
/* @__PURE__ */ jsxs(Typography, { variant: "caption", className: "text-muted-foreground", children: [
|
|
45946
|
+
param.min,
|
|
45947
|
+
" ",
|
|
45948
|
+
param.unit
|
|
45949
|
+
] }),
|
|
45950
|
+
/* @__PURE__ */ jsxs(Typography, { variant: "caption", className: "text-muted-foreground", children: [
|
|
45951
|
+
param.max,
|
|
45952
|
+
" ",
|
|
45953
|
+
param.unit
|
|
45954
|
+
] })
|
|
45892
45955
|
] })
|
|
45893
|
-
] })
|
|
45894
|
-
|
|
45956
|
+
] }, param.id ?? index) : null
|
|
45957
|
+
)
|
|
45895
45958
|
] }) }),
|
|
45896
45959
|
/* @__PURE__ */ jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", align: "center", children: [
|
|
45897
45960
|
/* @__PURE__ */ jsx(Typography, { variant: "small", weight: "bold", className: "uppercase tracking-wider text-muted-foreground", children: outputLabel }),
|
|
@@ -45900,9 +45963,9 @@ function SimulatorBoard({
|
|
|
45900
45963
|
" ",
|
|
45901
45964
|
outputUnit
|
|
45902
45965
|
] }),
|
|
45903
|
-
|
|
45904
|
-
/* @__PURE__ */ jsx(Icon, { icon:
|
|
45905
|
-
/* @__PURE__ */ jsx(Typography, { variant: "body", className:
|
|
45966
|
+
isComplete && /* @__PURE__ */ jsxs(HStack, { gap: "xs", align: "center", children: [
|
|
45967
|
+
/* @__PURE__ */ jsx(Icon, { icon: isWin ? CheckCircle : XCircle, size: "sm", className: isWin ? "text-success" : "text-error" }),
|
|
45968
|
+
/* @__PURE__ */ jsx(Typography, { variant: "body", className: isWin ? "text-success" : "text-error", children: isWin ? str(resolved.successMessage) || t("simulator.correct") : str(resolved.failMessage) || t("simulator.incorrect") })
|
|
45906
45969
|
] }),
|
|
45907
45970
|
/* @__PURE__ */ jsxs(Typography, { variant: "caption", className: "text-muted-foreground", children: [
|
|
45908
45971
|
t("simulator.target"),
|
|
@@ -45917,11 +45980,11 @@ function SimulatorBoard({
|
|
|
45917
45980
|
] }) }),
|
|
45918
45981
|
showHint && hint && /* @__PURE__ */ jsx(Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsx(Typography, { variant: "body", children: hint }) }),
|
|
45919
45982
|
/* @__PURE__ */ jsxs(HStack, { gap: "sm", justify: "center", children: [
|
|
45920
|
-
!
|
|
45983
|
+
!isComplete ? /* @__PURE__ */ jsxs(Button, { variant: "primary", onClick: handleCheck, children: [
|
|
45921
45984
|
/* @__PURE__ */ jsx(Icon, { icon: Play, size: "sm" }),
|
|
45922
45985
|
t("simulator.simulate")
|
|
45923
|
-
] }) :
|
|
45924
|
-
/* @__PURE__ */ jsxs(Button, { variant: "secondary", onClick:
|
|
45986
|
+
] }) : null,
|
|
45987
|
+
/* @__PURE__ */ jsxs(Button, { variant: "secondary", onClick: handlePlayAgain, children: [
|
|
45925
45988
|
/* @__PURE__ */ jsx(Icon, { icon: RotateCcw, size: "sm" }),
|
|
45926
45989
|
t("simulator.reset")
|
|
45927
45990
|
] })
|