@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.
@@ -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
- const map = PhosphorIcons;
1242
- const PhosphorComp = map[target];
1243
- if (!PhosphorComp || typeof PhosphorComp !== "object") return null;
1244
- const Component = PhosphorComp;
1245
- const Adapter = (props) => /* @__PURE__ */ jsx(
1246
- Component,
1247
- {
1248
- weight,
1249
- className: props.className,
1250
- style: props.style,
1251
- size: props.size ?? "1em"
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
- const map = TablerIcons;
1261
- const TablerComp = map[target];
1262
- if (!TablerComp || typeof TablerComp !== "object") return null;
1263
- const Component = TablerComp;
1264
- const Adapter = (props) => /* @__PURE__ */ jsx(
1265
- Component,
1266
- {
1267
- stroke: props.strokeWidth ?? 1.5,
1268
- className: props.className,
1269
- style: props.style,
1270
- size: props.size ?? 24
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
- const map = FaIcons;
1280
- const FaComp = map[target];
1281
- if (!FaComp || typeof FaComp !== "function") return null;
1282
- const Component = FaComp;
1283
- const Adapter = (props) => /* @__PURE__ */ jsx(
1284
- Component,
1285
- {
1286
- className: props.className,
1287
- style: props.style,
1288
- size: props.size ?? "1em"
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
- case "phosphor-outline": {
1327
- const p2 = resolvePhosphor(name, "regular");
1328
- if (p2) return p2;
1329
- warnFallback(name, family);
1330
- return makeLucideAdapter(name, true);
1331
- }
1332
- case "phosphor-fill": {
1333
- const p2 = resolvePhosphor(name, "fill");
1334
- if (p2) return p2;
1335
- warnFallback(name, family);
1336
- return makeLucideAdapter(name, true);
1337
- }
1338
- case "phosphor-duotone": {
1339
- const p2 = resolvePhosphor(name, "duotone");
1340
- if (p2) return p2;
1341
- warnFallback(name, family);
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 [submitted, setSubmitted] = useState(false);
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 [selectedComponent, setSelectedComponent] = useState(null);
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.acceptsComponentId
15994
+ correct: placements[slot.id] === slot.requiredComponentId
15952
15995
  })) : [];
15953
- const allCorrect = results.length > 0 && results.every((r) => r.correct);
15996
+ const showHint = attempts >= 2 && Boolean(str(resolved?.hint));
15954
15997
  const handlePlaceComponent = (slotId) => {
15955
15998
  if (submitted || !selectedComponent) return;
15956
- setPlacements((prev) => ({ ...prev, [slotId]: selectedComponent }));
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
- setPlacements((prev) => {
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 = useCallback(() => {
15968
- setSubmitted(true);
15969
- setAttempts((a) => a + 1);
15970
- const correct = slots.every((slot) => placements[slot.id] === slot.acceptsComponentId);
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 handleFullReset = () => {
15982
- setPlacements({});
15983
- setSubmitted(false);
16013
+ const handlePlayAgain = () => {
15984
16014
  setSelectedComponent(null);
15985
- setAttempts(0);
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 result = results.find((r) => r.slot.id === slot.id);
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 ${result ? result.correct ? "border-success" : "border-error" : selectedComponent ? "border-dashed border-foreground cursor-pointer" : "border-border"}`,
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
- result && /* @__PURE__ */ jsx(Icon, { icon: result.correct ? CheckCircle : XCircle, size: "sm", className: result.correct ? "text-success" : "text-error" })
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: allCorrect ? CheckCircle : XCircle, size: "lg", className: allCorrect ? "text-success" : "text-error" }),
16067
- /* @__PURE__ */ jsx(Typography, { variant: "body", weight: "bold", children: allCorrect ? str(resolved.successMessage) || t("builder.success") : str(resolved.failMessage) || t("builder.incorrect") })
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 ? /* @__PURE__ */ jsxs(Button, { variant: "primary", onClick: handleSubmit, disabled: !allPlaced, children: [
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
- ] }) : !allCorrect ? /* @__PURE__ */ jsx(Button, { variant: "primary", onClick: handleReset, children: t("builder.tryAgain") }) : null,
16075
- /* @__PURE__ */ jsxs(Button, { variant: "secondary", onClick: handleFullReset, children: [
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 [assignments, setAssignments] = useState({});
19614
+ const [localAssignments, setLocalAssignments] = useState({});
19583
19615
  const [headerError, setHeaderError] = useState(false);
19584
- const [submitted, setSubmitted] = useState(false);
19585
- const [attempts, setAttempts] = useState(0);
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
- setAssignments((prev) => ({ ...prev, [itemId]: categoryId }));
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
- setAssignments((prev) => {
19605
- const next = { ...prev };
19606
- delete next[itemId];
19607
- return next;
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
- setSubmitted(true);
19612
- setAttempts((a) => a + 1);
19613
- const correct = items.every((item) => assignments[item.id] === item.correctCategory);
19614
- if (correct) {
19615
- emit(`UI:${completeEvent}`, { success: true, attempts: attempts + 1 });
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
- setSubmitted(false);
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
- setAssignments({});
19626
- setSubmitted(false);
19627
- setAttempts(0);
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: filtered.map((item, index) => {
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: useEffect72, useRef: useRef69, useCallback: useCallback115, useState: useState105 } = React77__default;
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 = useRef69({ centerLat, centerLng, zoom });
29172
- useEffect72(() => {
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
- useEffect72(() => {
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 = useCallback115((lat, lng) => {
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 = useCallback115((marker) => {
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
- setFlaggedLines((prev) => {
38668
- const next = new Set(prev);
38669
- if (next.has(lineId)) {
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
- setSubmitted(true);
38684
- setAttempts((a) => a + 1);
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
- setSubmitted(false);
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
- setFlaggedLines(/* @__PURE__ */ new Set());
38698
- setSubmitted(false);
38699
- setAttempts(0);
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 = flaggedLines.has(line.id);
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: flaggedLines.has(line.id) ? CheckCircle : XCircle,
38822
+ icon: line.isFlagged ? CheckCircle : XCircle,
38762
38823
  size: "xs",
38763
- className: flaggedLines.has(line.id) ? "text-success mt-0.5" : "text-warning mt-0.5"
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.size === 0, children: [
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?.totalRounds);
42806
+ const totalRounds = num(resolved?.maxRounds);
42743
42807
  const targetScore = num(resolved?.targetScore);
42744
- const currentRound = history.length;
42745
- const isComplete = currentRound >= totalRounds;
42746
- const playerTotal = history.reduce((s, r) => s + r.playerPayoff, 0);
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 result = {
42758
- round: currentRound + 1,
42759
- playerAction: actionId,
42760
- opponentAction,
42761
- playerPayoff: payoff?.playerPayoff ?? 0,
42762
- opponentPayoff: payoff?.opponentPayoff ?? 0
42763
- };
42764
- const newHistory = [...history, result];
42765
- setHistory(newHistory);
42766
- if (newHistory.length >= totalRounds) {
42767
- const total = newHistory.reduce((s, r) => s + r.playerPayoff, 0);
42768
- if (total >= targetScore) {
42769
- emit(`UI:${completeEvent}`, { success: true, score: total });
42770
- }
42771
- if (newHistory.length >= 3 && str(resolved?.hint)) {
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, targetScore, actions, payoffMatrix, history, currentRound, completeEvent, emit]);
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
- const [submitted, setSubmitted] = useState(false);
45795
- const [attempts, setAttempts] = useState(0);
45796
- const [showHint, setShowHint] = useState(false);
45797
- const computeOutput = useCallback((params) => {
45798
- try {
45799
- const fn = new Function("params", `return (${str(resolved?.computeExpression)})`);
45800
- return fn(params);
45801
- } catch {
45802
- return 0;
45803
- }
45804
- }, [resolved?.computeExpression]);
45805
- const output = useMemo(() => computeOutput(values) ?? 0, [computeOutput, values]);
45806
- const targetValue = num(resolved?.targetValue);
45807
- const targetTolerance = num(resolved?.targetTolerance);
45808
- const isCorrect = Math.abs(output - targetValue) <= targetTolerance;
45809
- const handleParameterChange = (id, value) => {
45810
- if (submitted) return;
45811
- setValues((prev) => ({ ...prev, [id]: value }));
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 handleReset = () => {
45821
- setSubmitted(false);
45822
- if (attempts >= 2 && str(resolved?.hint)) {
45823
- setShowHint(true);
45824
- }
45891
+ const handleCheck = () => {
45892
+ if (checkEvent) emit(`UI:${checkEvent}`, {});
45825
45893
  };
45826
- const handleFullReset = () => {
45827
- const init = {};
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
- parameters.map((param) => /* @__PURE__ */ jsxs(VStack, { gap: "xs", children: [
45861
- /* @__PURE__ */ jsxs(HStack, { justify: "between", align: "center", children: [
45862
- /* @__PURE__ */ jsx(Typography, { variant: "body", weight: "medium", children: param.label }),
45863
- /* @__PURE__ */ jsxs(Badge, { size: "sm", children: [
45864
- values[param.id],
45865
- " ",
45866
- param.unit
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__ */ jsxs(Typography, { variant: "caption", className: "text-muted-foreground", children: [
45889
- param.max,
45890
- " ",
45891
- param.unit
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
- ] }, param.id))
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
- submitted && /* @__PURE__ */ jsxs(HStack, { gap: "xs", align: "center", children: [
45904
- /* @__PURE__ */ jsx(Icon, { icon: isCorrect ? CheckCircle : XCircle, size: "sm", className: isCorrect ? "text-success" : "text-error" }),
45905
- /* @__PURE__ */ jsx(Typography, { variant: "body", className: isCorrect ? "text-success" : "text-error", children: isCorrect ? str(resolved.successMessage) || t("simulator.correct") : str(resolved.failMessage) || t("simulator.incorrect") })
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
- !submitted ? /* @__PURE__ */ jsxs(Button, { variant: "primary", onClick: handleSubmit, children: [
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
- ] }) : !isCorrect ? /* @__PURE__ */ jsx(Button, { variant: "primary", onClick: handleReset, children: t("simulator.tryAgain") }) : null,
45924
- /* @__PURE__ */ jsxs(Button, { variant: "secondary", onClick: handleFullReset, children: [
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
  ] })