@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 @@ var tailwindMerge = require('tailwind-merge');
7
7
  var providers = require('@almadar/ui/providers');
8
8
  var logger = require('@almadar/logger');
9
9
  var LucideIcons2 = require('lucide-react');
10
- var PhosphorIcons = require('@phosphor-icons/react');
11
- var TablerIcons = require('@tabler/icons-react');
12
- var FaIcons = require('react-icons/fa');
13
10
  var hooks = require('@almadar/ui/hooks');
14
11
  var evaluator = require('@almadar/evaluator');
15
12
  var reactDom = require('react-dom');
@@ -68,9 +65,6 @@ function _interopNamespace(e) {
68
65
 
69
66
  var React77__namespace = /*#__PURE__*/_interopNamespace(React77);
70
67
  var LucideIcons2__namespace = /*#__PURE__*/_interopNamespace(LucideIcons2);
71
- var PhosphorIcons__namespace = /*#__PURE__*/_interopNamespace(PhosphorIcons);
72
- var TablerIcons__namespace = /*#__PURE__*/_interopNamespace(TablerIcons);
73
- var FaIcons__namespace = /*#__PURE__*/_interopNamespace(FaIcons);
74
68
  var ELK__default = /*#__PURE__*/_interopDefault(ELK);
75
69
  var SyntaxHighlighter__default = /*#__PURE__*/_interopDefault(SyntaxHighlighter);
76
70
  var dark__default = /*#__PURE__*/_interopDefault(dark);
@@ -1274,6 +1268,41 @@ function kebabToPascal(name) {
1274
1268
  return part.charAt(0).toUpperCase() + part.slice(1);
1275
1269
  }).join("");
1276
1270
  }
1271
+ function loadLib(key, importer) {
1272
+ let p2 = libPromises.get(key);
1273
+ if (!p2) {
1274
+ p2 = importer().then((m) => m);
1275
+ libPromises.set(key, p2);
1276
+ }
1277
+ return p2;
1278
+ }
1279
+ function lazyFamilyIcon(libKey, importer, pick, fallbackName, family) {
1280
+ const Lazy = React77__namespace.default.lazy(async () => {
1281
+ const lib = await loadLib(libKey, importer);
1282
+ const Comp = pick(lib);
1283
+ if (!Comp) {
1284
+ warnFallback(fallbackName, family);
1285
+ return { default: makeLucideAdapter(fallbackName, true) };
1286
+ }
1287
+ return { default: Comp };
1288
+ });
1289
+ const Wrapped = (props) => /* @__PURE__ */ jsxRuntime.jsx(
1290
+ React77__namespace.default.Suspense,
1291
+ {
1292
+ fallback: /* @__PURE__ */ jsxRuntime.jsx(
1293
+ "span",
1294
+ {
1295
+ "aria-hidden": true,
1296
+ className: props.className,
1297
+ style: { display: "inline-block", ...props.style }
1298
+ }
1299
+ ),
1300
+ children: /* @__PURE__ */ jsxRuntime.jsx(Lazy, { ...props })
1301
+ }
1302
+ );
1303
+ Wrapped.displayName = `Lazy.${libKey}.${fallbackName}`;
1304
+ return Wrapped;
1305
+ }
1277
1306
  function resolveLucide(name) {
1278
1307
  if (lucideAliases[name]) return lucideAliases[name];
1279
1308
  const pascal = kebabToPascal(name);
@@ -1284,60 +1313,81 @@ function resolveLucide(name) {
1284
1313
  if (asIs && typeof asIs === "object") return asIs;
1285
1314
  return LucideIcons2__namespace.HelpCircle;
1286
1315
  }
1287
- function resolvePhosphor(name, weight) {
1316
+ function resolvePhosphor(name, weight, family) {
1288
1317
  const target = phosphorAliases[name] ?? kebabToPascal(name);
1289
- const map = PhosphorIcons__namespace;
1290
- const PhosphorComp = map[target];
1291
- if (!PhosphorComp || typeof PhosphorComp !== "object") return null;
1292
- const Component = PhosphorComp;
1293
- const Adapter = (props) => /* @__PURE__ */ jsxRuntime.jsx(
1294
- Component,
1295
- {
1296
- weight,
1297
- className: props.className,
1298
- style: props.style,
1299
- size: props.size ?? "1em"
1300
- }
1318
+ return lazyFamilyIcon(
1319
+ "phosphor",
1320
+ () => import('@phosphor-icons/react'),
1321
+ (lib) => {
1322
+ const PhosphorComp = lib[target];
1323
+ if (!PhosphorComp || typeof PhosphorComp !== "object") return null;
1324
+ const Component = PhosphorComp;
1325
+ const Adapter = (props) => /* @__PURE__ */ jsxRuntime.jsx(
1326
+ Component,
1327
+ {
1328
+ weight,
1329
+ className: props.className,
1330
+ style: props.style,
1331
+ size: props.size ?? "1em"
1332
+ }
1333
+ );
1334
+ Adapter.displayName = `Phosphor.${target}.${weight}`;
1335
+ return Adapter;
1336
+ },
1337
+ name,
1338
+ family
1301
1339
  );
1302
- Adapter.displayName = `Phosphor.${target}.${weight}`;
1303
- return Adapter;
1304
1340
  }
1305
- function resolveTabler(name) {
1341
+ function resolveTabler(name, family) {
1306
1342
  const suffix = tablerAliases[name] ?? kebabToPascal(name);
1307
1343
  const target = `Icon${suffix}`;
1308
- const map = TablerIcons__namespace;
1309
- const TablerComp = map[target];
1310
- if (!TablerComp || typeof TablerComp !== "object") return null;
1311
- const Component = TablerComp;
1312
- const Adapter = (props) => /* @__PURE__ */ jsxRuntime.jsx(
1313
- Component,
1314
- {
1315
- stroke: props.strokeWidth ?? 1.5,
1316
- className: props.className,
1317
- style: props.style,
1318
- size: props.size ?? 24
1319
- }
1344
+ return lazyFamilyIcon(
1345
+ "tabler",
1346
+ () => import('@tabler/icons-react'),
1347
+ (lib) => {
1348
+ const TablerComp = lib[target];
1349
+ if (!TablerComp || typeof TablerComp !== "object") return null;
1350
+ const Component = TablerComp;
1351
+ const Adapter = (props) => /* @__PURE__ */ jsxRuntime.jsx(
1352
+ Component,
1353
+ {
1354
+ stroke: props.strokeWidth ?? 1.5,
1355
+ className: props.className,
1356
+ style: props.style,
1357
+ size: props.size ?? 24
1358
+ }
1359
+ );
1360
+ Adapter.displayName = `Tabler.${target}`;
1361
+ return Adapter;
1362
+ },
1363
+ name,
1364
+ family
1320
1365
  );
1321
- Adapter.displayName = `Tabler.${target}`;
1322
- return Adapter;
1323
1366
  }
1324
- function resolveFa(name) {
1367
+ function resolveFa(name, family) {
1325
1368
  const suffix = faAliases[name] ?? kebabToPascal(name);
1326
1369
  const target = `Fa${suffix}`;
1327
- const map = FaIcons__namespace;
1328
- const FaComp = map[target];
1329
- if (!FaComp || typeof FaComp !== "function") return null;
1330
- const Component = FaComp;
1331
- const Adapter = (props) => /* @__PURE__ */ jsxRuntime.jsx(
1332
- Component,
1333
- {
1334
- className: props.className,
1335
- style: props.style,
1336
- size: props.size ?? "1em"
1337
- }
1370
+ return lazyFamilyIcon(
1371
+ "fa",
1372
+ () => import('react-icons/fa'),
1373
+ (lib) => {
1374
+ const FaComp = lib[target];
1375
+ if (!FaComp || typeof FaComp !== "function") return null;
1376
+ const Component = FaComp;
1377
+ const Adapter = (props) => /* @__PURE__ */ jsxRuntime.jsx(
1378
+ Component,
1379
+ {
1380
+ className: props.className,
1381
+ style: props.style,
1382
+ size: props.size ?? "1em"
1383
+ }
1384
+ );
1385
+ Adapter.displayName = `Fa.${target}`;
1386
+ return Adapter;
1387
+ },
1388
+ name,
1389
+ family
1338
1390
  );
1339
- Adapter.displayName = `Fa.${target}`;
1340
- return Adapter;
1341
1391
  }
1342
1392
  function warnFallback(name, family) {
1343
1393
  const key = `${family}::${name}`;
@@ -1371,39 +1421,22 @@ function resolveIconForFamily(name, family) {
1371
1421
  switch (family) {
1372
1422
  case "lucide":
1373
1423
  return makeLucideAdapter(name, false);
1374
- case "phosphor-outline": {
1375
- const p2 = resolvePhosphor(name, "regular");
1376
- if (p2) return p2;
1377
- warnFallback(name, family);
1378
- return makeLucideAdapter(name, true);
1379
- }
1380
- case "phosphor-fill": {
1381
- const p2 = resolvePhosphor(name, "fill");
1382
- if (p2) return p2;
1383
- warnFallback(name, family);
1384
- return makeLucideAdapter(name, true);
1385
- }
1386
- case "phosphor-duotone": {
1387
- const p2 = resolvePhosphor(name, "duotone");
1388
- if (p2) return p2;
1389
- warnFallback(name, family);
1390
- return makeLucideAdapter(name, true);
1391
- }
1392
- case "tabler": {
1393
- const t = resolveTabler(name);
1394
- if (t) return t;
1395
- warnFallback(name, family);
1396
- return makeLucideAdapter(name, true);
1397
- }
1398
- case "fa-solid": {
1399
- const f3 = resolveFa(name);
1400
- if (f3) return f3;
1401
- warnFallback(name, family);
1402
- return makeLucideAdapter(name, true);
1403
- }
1404
- }
1405
- }
1406
- var DEFAULT_FAMILY, VALID_FAMILIES, cachedFamily, listeners, observer, lucideAliases, phosphorAliases, tablerAliases, faAliases, warned;
1424
+ // Non-lucide families resolve to a lazy, Suspense-wrapped component that
1425
+ // dynamic-imports the library on first render and falls back to lucide
1426
+ // internally when the family lacks the icon (see lazyFamilyIcon).
1427
+ case "phosphor-outline":
1428
+ return resolvePhosphor(name, "regular", family);
1429
+ case "phosphor-fill":
1430
+ return resolvePhosphor(name, "fill", family);
1431
+ case "phosphor-duotone":
1432
+ return resolvePhosphor(name, "duotone", family);
1433
+ case "tabler":
1434
+ return resolveTabler(name, family);
1435
+ case "fa-solid":
1436
+ return resolveFa(name, family);
1437
+ }
1438
+ }
1439
+ var DEFAULT_FAMILY, VALID_FAMILIES, cachedFamily, listeners, observer, libPromises, lucideAliases, phosphorAliases, tablerAliases, faAliases, warned;
1407
1440
  var init_iconFamily = __esm({
1408
1441
  "lib/iconFamily.tsx"() {
1409
1442
  "use client";
@@ -1419,6 +1452,7 @@ var init_iconFamily = __esm({
1419
1452
  cachedFamily = null;
1420
1453
  listeners = /* @__PURE__ */ new Set();
1421
1454
  observer = null;
1455
+ libPromises = /* @__PURE__ */ new Map();
1422
1456
  lucideAliases = {
1423
1457
  close: LucideIcons2__namespace.X,
1424
1458
  trash: LucideIcons2__namespace.Trash2,
@@ -15977,61 +16011,53 @@ var init_Breadcrumb = __esm({
15977
16011
  function BuilderBoard({
15978
16012
  entity,
15979
16013
  completeEvent = "PUZZLE_COMPLETE",
16014
+ placeEvent,
16015
+ checkEvent,
16016
+ playAgainEvent,
15980
16017
  className
15981
16018
  }) {
15982
16019
  const { emit } = useEventBus();
15983
16020
  const { t } = hooks.useTranslate();
15984
16021
  const resolved = boardEntity(entity);
15985
- const [placements, setPlacements] = React77.useState({});
15986
16022
  const [headerError, setHeaderError] = React77.useState(false);
15987
- const [submitted, setSubmitted] = React77.useState(false);
15988
- const [attempts, setAttempts] = React77.useState(0);
15989
- const [showHint, setShowHint] = React77.useState(false);
16023
+ const [selectedComponent, setSelectedComponent] = React77.useState(null);
15990
16024
  const components = Array.isArray(resolved?.components) ? resolved.components : [];
15991
16025
  const slots = Array.isArray(resolved?.slots) ? resolved.slots : [];
16026
+ const placements = {};
16027
+ for (const slot of slots) {
16028
+ if (slot.placedComponentId) placements[slot.id] = slot.placedComponentId;
16029
+ }
16030
+ const attempts = num(resolved?.attempts);
16031
+ const result = str(resolved?.result) || "none";
16032
+ const submitted = result === "win";
15992
16033
  const usedComponentIds = new Set(Object.values(placements));
15993
16034
  const availableComponents = components.filter((c) => !usedComponentIds.has(c.id));
15994
- const [selectedComponent, setSelectedComponent] = React77.useState(null);
15995
- const allPlaced = Object.keys(placements).length === slots.length;
16035
+ const allPlaced = slots.length > 0 && slots.every((s) => Boolean(placements[s.id]));
15996
16036
  const results = submitted ? slots.map((slot) => ({
15997
16037
  slot,
15998
16038
  placed: placements[slot.id],
15999
- correct: placements[slot.id] === slot.acceptsComponentId
16039
+ correct: placements[slot.id] === slot.requiredComponentId
16000
16040
  })) : [];
16001
- const allCorrect = results.length > 0 && results.every((r) => r.correct);
16041
+ const showHint = attempts >= 2 && Boolean(str(resolved?.hint));
16002
16042
  const handlePlaceComponent = (slotId) => {
16003
16043
  if (submitted || !selectedComponent) return;
16004
- setPlacements((prev) => ({ ...prev, [slotId]: selectedComponent }));
16044
+ if (placeEvent) emit(`UI:${placeEvent}`, { slotId, componentId: selectedComponent });
16005
16045
  setSelectedComponent(null);
16006
16046
  };
16007
16047
  const handleRemoveFromSlot = (slotId) => {
16008
16048
  if (submitted) return;
16009
- setPlacements((prev) => {
16010
- const next = { ...prev };
16011
- delete next[slotId];
16012
- return next;
16013
- });
16049
+ if (placeEvent) emit(`UI:${placeEvent}`, { slotId, componentId: "" });
16014
16050
  };
16015
- const handleSubmit = React77.useCallback(() => {
16016
- setSubmitted(true);
16017
- setAttempts((a) => a + 1);
16018
- const correct = slots.every((slot) => placements[slot.id] === slot.acceptsComponentId);
16019
- if (correct) {
16051
+ const handleSubmit = () => {
16052
+ if (checkEvent) emit(`UI:${checkEvent}`, {});
16053
+ const solved = slots.length > 0 && slots.every((s) => placements[s.id] === s.requiredComponentId);
16054
+ if (solved && completeEvent) {
16020
16055
  emit(`UI:${completeEvent}`, { success: true, attempts: attempts + 1 });
16021
16056
  }
16022
- }, [slots, placements, attempts, completeEvent, emit]);
16023
- const handleReset = () => {
16024
- setSubmitted(false);
16025
- if (attempts >= 2 && str(resolved?.hint)) {
16026
- setShowHint(true);
16027
- }
16028
16057
  };
16029
- const handleFullReset = () => {
16030
- setPlacements({});
16031
- setSubmitted(false);
16058
+ const handlePlayAgain = () => {
16032
16059
  setSelectedComponent(null);
16033
- setAttempts(0);
16034
- setShowHint(false);
16060
+ if (playAgainEvent) emit(`UI:${playAgainEvent}`, {});
16035
16061
  };
16036
16062
  const getComponentById = (id) => components.find((c) => c.id === id);
16037
16063
  if (!resolved) return null;
@@ -16081,13 +16107,13 @@ function BuilderBoard({
16081
16107
  /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "small", weight: "bold", className: "uppercase tracking-wider text-muted-foreground", children: t("builder.blueprint") }),
16082
16108
  /* @__PURE__ */ jsxRuntime.jsx(exports.VStack, { gap: "sm", children: slots.map((slot) => {
16083
16109
  const placedComp = placements[slot.id] ? getComponentById(placements[slot.id]) : null;
16084
- const result = results.find((r) => r.slot.id === slot.id);
16110
+ const result2 = results.find((r) => r.slot.id === slot.id);
16085
16111
  return /* @__PURE__ */ jsxRuntime.jsxs(
16086
16112
  exports.HStack,
16087
16113
  {
16088
16114
  gap: "sm",
16089
16115
  align: "center",
16090
- className: `p-3 border-2 rounded ${result ? result.correct ? "border-success" : "border-error" : selectedComponent ? "border-dashed border-foreground cursor-pointer" : "border-border"}`,
16116
+ className: `p-3 border-2 rounded ${result2 ? result2.correct ? "border-success" : "border-error" : selectedComponent ? "border-dashed border-foreground cursor-pointer" : "border-border"}`,
16091
16117
  onClick: () => handlePlaceComponent(slot.id),
16092
16118
  children: [
16093
16119
  /* @__PURE__ */ jsxRuntime.jsxs(exports.VStack, { gap: "none", className: "flex-1", children: [
@@ -16102,7 +16128,7 @@ function BuilderBoard({
16102
16128
  ] }) : null,
16103
16129
  placedComp.label
16104
16130
  ] }),
16105
- result && /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { icon: result.correct ? LucideIcons2.CheckCircle : LucideIcons2.XCircle, size: "sm", className: result.correct ? "text-success" : "text-error" })
16131
+ result2 && /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { icon: result2.correct ? LucideIcons2.CheckCircle : LucideIcons2.XCircle, size: "sm", className: result2.correct ? "text-success" : "text-error" })
16106
16132
  ] }) : /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "caption", className: "text-muted-foreground", children: t("builder.empty") })
16107
16133
  ]
16108
16134
  },
@@ -16111,16 +16137,16 @@ function BuilderBoard({
16111
16137
  }) })
16112
16138
  ] }) }),
16113
16139
  submitted && /* @__PURE__ */ jsxRuntime.jsx(exports.Card, { className: "p-4", children: /* @__PURE__ */ jsxRuntime.jsxs(exports.VStack, { gap: "sm", align: "center", children: [
16114
- /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { icon: allCorrect ? LucideIcons2.CheckCircle : LucideIcons2.XCircle, size: "lg", className: allCorrect ? "text-success" : "text-error" }),
16115
- /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "body", weight: "bold", children: allCorrect ? str(resolved.successMessage) || t("builder.success") : str(resolved.failMessage) || t("builder.incorrect") })
16140
+ /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { icon: LucideIcons2.CheckCircle, size: "lg", className: "text-success" }),
16141
+ /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "body", weight: "bold", children: str(resolved.successMessage) || t("builder.success") })
16116
16142
  ] }) }),
16117
16143
  showHint && hint && /* @__PURE__ */ jsxRuntime.jsx(exports.Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "body", children: hint }) }),
16118
16144
  /* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { gap: "sm", justify: "center", children: [
16119
- !submitted ? /* @__PURE__ */ jsxRuntime.jsxs(exports.Button, { variant: "primary", onClick: handleSubmit, disabled: !allPlaced, children: [
16145
+ !submitted && /* @__PURE__ */ jsxRuntime.jsxs(exports.Button, { variant: "primary", onClick: handleSubmit, disabled: !allPlaced, children: [
16120
16146
  /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { icon: LucideIcons2.Wrench, size: "sm" }),
16121
16147
  t("builder.build")
16122
- ] }) : !allCorrect ? /* @__PURE__ */ jsxRuntime.jsx(exports.Button, { variant: "primary", onClick: handleReset, children: t("builder.tryAgain") }) : null,
16123
- /* @__PURE__ */ jsxRuntime.jsxs(exports.Button, { variant: "secondary", onClick: handleFullReset, children: [
16148
+ ] }),
16149
+ /* @__PURE__ */ jsxRuntime.jsxs(exports.Button, { variant: "secondary", onClick: handlePlayAgain, children: [
16124
16150
  /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { icon: LucideIcons2.RotateCcw, size: "sm" }),
16125
16151
  t("builder.reset")
16126
16152
  ] })
@@ -19622,57 +19648,84 @@ var init_ChoiceButton = __esm({
19622
19648
  function ClassifierBoard({
19623
19649
  entity,
19624
19650
  completeEvent = "PUZZLE_COMPLETE",
19651
+ assignEvent,
19652
+ checkEvent,
19653
+ playAgainEvent,
19625
19654
  className
19626
19655
  }) {
19627
19656
  const { emit } = useEventBus();
19628
19657
  const { t } = hooks.useTranslate();
19629
19658
  const resolved = boardEntity(entity);
19630
- const [assignments, setAssignments] = React77.useState({});
19659
+ const [localAssignments, setLocalAssignments] = React77.useState({});
19631
19660
  const [headerError, setHeaderError] = React77.useState(false);
19632
- const [submitted, setSubmitted] = React77.useState(false);
19633
- const [attempts, setAttempts] = React77.useState(0);
19661
+ const [localSubmitted, setLocalSubmitted] = React77.useState(false);
19662
+ const [localAttempts, setLocalAttempts] = React77.useState(0);
19634
19663
  const [showHint, setShowHint] = React77.useState(false);
19635
19664
  const items = Array.isArray(resolved?.items) ? resolved.items : [];
19636
19665
  const categories = Array.isArray(resolved?.categories) ? resolved.categories : [];
19666
+ const entityResult = str(resolved?.result);
19667
+ const entityDrivesResult = entityResult.length > 0;
19668
+ const entityHasAssignments = items.some((item) => item.assignedCategory != null);
19669
+ const assignments = entityHasAssignments ? items.reduce((acc, item) => {
19670
+ if (item.assignedCategory != null) acc[item.id] = item.assignedCategory;
19671
+ return acc;
19672
+ }, {}) : localAssignments;
19673
+ const attempts = entityDrivesResult ? num(resolved?.attempts) : localAttempts;
19674
+ const submitted = entityDrivesResult || localSubmitted;
19637
19675
  const unassignedItems = items.filter((item) => !assignments[item.id]);
19638
- const allAssigned = Object.keys(assignments).length === items.length;
19676
+ const allAssigned = items.length > 0 && Object.keys(assignments).length === items.length;
19639
19677
  const results = submitted ? items.map((item) => ({
19640
19678
  item,
19641
19679
  assigned: assignments[item.id],
19642
19680
  correct: assignments[item.id] === item.correctCategory
19643
19681
  })) : [];
19644
- const allCorrect = results.length > 0 && results.every((r) => r.correct);
19682
+ const allCorrect = entityDrivesResult ? entityResult === "success" : results.length > 0 && results.every((r) => r.correct);
19645
19683
  const correctCount = results.filter((r) => r.correct).length;
19646
19684
  const handleAssign = (itemId, categoryId) => {
19647
19685
  if (submitted) return;
19648
- setAssignments((prev) => ({ ...prev, [itemId]: categoryId }));
19686
+ if (assignEvent) {
19687
+ emit(`UI:${assignEvent}`, { itemId, categoryId });
19688
+ }
19689
+ if (!entityHasAssignments) {
19690
+ setLocalAssignments((prev) => ({ ...prev, [itemId]: categoryId }));
19691
+ }
19649
19692
  };
19650
19693
  const handleUnassign = (itemId) => {
19651
19694
  if (submitted) return;
19652
- setAssignments((prev) => {
19653
- const next = { ...prev };
19654
- delete next[itemId];
19655
- return next;
19656
- });
19695
+ if (!entityHasAssignments) {
19696
+ setLocalAssignments((prev) => {
19697
+ const next = { ...prev };
19698
+ delete next[itemId];
19699
+ return next;
19700
+ });
19701
+ }
19657
19702
  };
19658
19703
  const handleSubmit = React77.useCallback(() => {
19659
- setSubmitted(true);
19660
- setAttempts((a) => a + 1);
19661
- const correct = items.every((item) => assignments[item.id] === item.correctCategory);
19662
- if (correct) {
19663
- emit(`UI:${completeEvent}`, { success: true, attempts: attempts + 1 });
19704
+ if (checkEvent) {
19705
+ emit(`UI:${checkEvent}`, {});
19706
+ }
19707
+ if (!entityDrivesResult) {
19708
+ setLocalSubmitted(true);
19709
+ setLocalAttempts((a) => a + 1);
19710
+ const correct = items.every((item) => assignments[item.id] === item.correctCategory);
19711
+ if (correct) {
19712
+ emit(`UI:${completeEvent}`, { success: true, attempts: attempts + 1 });
19713
+ }
19664
19714
  }
19665
- }, [items, assignments, attempts, completeEvent, emit]);
19715
+ }, [checkEvent, entityDrivesResult, items, assignments, attempts, completeEvent, emit]);
19666
19716
  const handleReset = () => {
19667
- setSubmitted(false);
19717
+ if (!entityDrivesResult) setLocalSubmitted(false);
19668
19718
  if (attempts >= 2 && str(resolved?.hint)) {
19669
19719
  setShowHint(true);
19670
19720
  }
19671
19721
  };
19672
19722
  const handleFullReset = () => {
19673
- setAssignments({});
19674
- setSubmitted(false);
19675
- setAttempts(0);
19723
+ if (playAgainEvent) {
19724
+ emit(`UI:${playAgainEvent}`, {});
19725
+ }
19726
+ setLocalAssignments({});
19727
+ setLocalSubmitted(false);
19728
+ setLocalAttempts(0);
19676
19729
  setShowHint(false);
19677
19730
  };
19678
19731
  if (!resolved) return null;
@@ -23324,7 +23377,7 @@ var init_FormSection = __esm({
23324
23377
  exports.FormActions.displayName = "FormActions";
23325
23378
  }
23326
23379
  });
23327
- var ALL_CATEGORY; exports.GridPicker = void 0;
23380
+ var ALL_CATEGORY, MAX_RENDERED; exports.GridPicker = void 0;
23328
23381
  var init_GridPicker = __esm({
23329
23382
  "components/core/molecules/GridPicker.tsx"() {
23330
23383
  "use client";
@@ -23333,6 +23386,7 @@ var init_GridPicker = __esm({
23333
23386
  init_Badge();
23334
23387
  init_Stack();
23335
23388
  ALL_CATEGORY = "__all__";
23389
+ MAX_RENDERED = 300;
23336
23390
  exports.GridPicker = ({
23337
23391
  items,
23338
23392
  value,
@@ -23362,6 +23416,8 @@ var init_GridPicker = __esm({
23362
23416
  return matchesCategory && matchesSearch;
23363
23417
  });
23364
23418
  }, [items, search, activeCategory]);
23419
+ const visible = React77.useMemo(() => filtered.slice(0, MAX_RENDERED), [filtered]);
23420
+ const truncated = filtered.length - visible.length;
23365
23421
  const select = React77.useCallback(
23366
23422
  (item) => {
23367
23423
  onChange(item.id);
@@ -23463,7 +23519,7 @@ var init_GridPicker = __esm({
23463
23519
  style: {
23464
23520
  gridTemplateColumns: `repeat(auto-fill, minmax(${cellSize}px, 1fr))`
23465
23521
  },
23466
- children: filtered.map((item, index) => {
23522
+ children: visible.map((item, index) => {
23467
23523
  const selected = item.id === value;
23468
23524
  return /* @__PURE__ */ jsxRuntime.jsx(
23469
23525
  "button",
@@ -23490,7 +23546,8 @@ var init_GridPicker = __esm({
23490
23546
  );
23491
23547
  })
23492
23548
  }
23493
- )
23549
+ ),
23550
+ truncated > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-1 text-xs text-muted-foreground", children: `+${truncated} more \u2014 refine your search` })
23494
23551
  ] });
23495
23552
  };
23496
23553
  exports.GridPicker.displayName = "GridPicker";
@@ -29211,13 +29268,13 @@ var init_MapView = __esm({
29211
29268
  shadowSize: [41, 41]
29212
29269
  });
29213
29270
  L.Marker.prototype.options.icon = defaultIcon;
29214
- const { useEffect: useEffect72, useRef: useRef69, useCallback: useCallback115, useState: useState105 } = React77__namespace.default;
29271
+ const { useEffect: useEffect73, useRef: useRef70, useCallback: useCallback113, useState: useState105 } = React77__namespace.default;
29215
29272
  const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
29216
29273
  const { useEventBus: useEventBus2 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
29217
29274
  function MapUpdater({ centerLat, centerLng, zoom }) {
29218
29275
  const map = useMap();
29219
- const prevRef = useRef69({ centerLat, centerLng, zoom });
29220
- useEffect72(() => {
29276
+ const prevRef = useRef70({ centerLat, centerLng, zoom });
29277
+ useEffect73(() => {
29221
29278
  const prev = prevRef.current;
29222
29279
  if (prev.centerLat !== centerLat || prev.centerLng !== centerLng || prev.zoom !== zoom) {
29223
29280
  map.setView([centerLat, centerLng], zoom);
@@ -29228,7 +29285,7 @@ var init_MapView = __esm({
29228
29285
  }
29229
29286
  function MapClickHandler({ onMapClick }) {
29230
29287
  const map = useMap();
29231
- useEffect72(() => {
29288
+ useEffect73(() => {
29232
29289
  if (!onMapClick) return;
29233
29290
  const handler = (e) => {
29234
29291
  onMapClick(e.latlng.lat, e.latlng.lng);
@@ -29257,7 +29314,7 @@ var init_MapView = __esm({
29257
29314
  }) {
29258
29315
  const eventBus = useEventBus2();
29259
29316
  const [clickedPosition, setClickedPosition] = useState105(null);
29260
- const handleMapClick = useCallback115((lat, lng) => {
29317
+ const handleMapClick = useCallback113((lat, lng) => {
29261
29318
  if (showClickedPin) {
29262
29319
  setClickedPosition({ lat, lng });
29263
29320
  }
@@ -29266,7 +29323,7 @@ var init_MapView = __esm({
29266
29323
  eventBus.emit(`UI:${mapClickEvent}`, { latitude: lat, longitude: lng });
29267
29324
  }
29268
29325
  }, [onMapClick, mapClickEvent, eventBus, showClickedPin]);
29269
- const handleMarkerClick = useCallback115((marker) => {
29326
+ const handleMarkerClick = useCallback113((marker) => {
29270
29327
  onMarkerClick?.(marker);
29271
29328
  if (markerClickEvent) {
29272
29329
  eventBus.emit(`UI:${markerClickEvent}`, { ...marker });
@@ -38700,51 +38757,52 @@ var init_DataTable = __esm({
38700
38757
  function DebuggerBoard({
38701
38758
  entity,
38702
38759
  completeEvent = "PUZZLE_COMPLETE",
38760
+ toggleFlagEvent,
38761
+ checkEvent,
38762
+ playAgainEvent,
38703
38763
  className
38704
38764
  }) {
38705
38765
  const { emit } = useEventBus();
38706
38766
  const { t } = hooks.useTranslate();
38707
38767
  const resolved = boardEntity(entity);
38708
- const [flaggedLines, setFlaggedLines] = React77.useState(/* @__PURE__ */ new Set());
38709
38768
  const [headerError, setHeaderError] = React77.useState(false);
38710
- const [submitted, setSubmitted] = React77.useState(false);
38711
- const [attempts, setAttempts] = React77.useState(0);
38712
38769
  const [showHint, setShowHint] = React77.useState(false);
38770
+ const lines = Array.isArray(resolved?.lines) ? resolved.lines : [];
38771
+ const result = resolved?.result ?? null;
38772
+ const attempts = num(resolved?.attempts);
38773
+ const submitted = result != null;
38774
+ const bugLines = lines.filter((l) => l.isBug);
38775
+ const flaggedLines = lines.filter((l) => l.isFlagged);
38776
+ const correctFlags = lines.filter((l) => l.isBug && l.isFlagged);
38777
+ const falseFlags = lines.filter((l) => !l.isBug && l.isFlagged);
38778
+ const allCorrect = result === "win";
38713
38779
  const toggleLine = (lineId) => {
38714
38780
  if (submitted) return;
38715
- setFlaggedLines((prev) => {
38716
- const next = new Set(prev);
38717
- if (next.has(lineId)) {
38718
- next.delete(lineId);
38719
- } else {
38720
- next.add(lineId);
38721
- }
38722
- return next;
38723
- });
38781
+ if (toggleFlagEvent) {
38782
+ emit(`UI:${toggleFlagEvent}`, { lineId });
38783
+ }
38724
38784
  };
38725
- const lines = Array.isArray(resolved?.lines) ? resolved.lines : [];
38726
- const bugLines = lines.filter((l) => l.isBug);
38727
- const correctFlags = lines.filter((l) => l.isBug && flaggedLines.has(l.id));
38728
- const falseFlags = lines.filter((l) => !l.isBug && flaggedLines.has(l.id));
38729
- const allCorrect = submitted && correctFlags.length === bugLines.length && falseFlags.length === 0;
38730
38785
  const handleSubmit = React77.useCallback(() => {
38731
- setSubmitted(true);
38732
- setAttempts((a) => a + 1);
38786
+ if (checkEvent) {
38787
+ emit(`UI:${checkEvent}`, {});
38788
+ }
38733
38789
  const correct = correctFlags.length === bugLines.length && falseFlags.length === 0;
38734
38790
  if (correct) {
38735
38791
  emit(`UI:${completeEvent}`, { success: true, attempts: attempts + 1 });
38736
38792
  }
38737
- }, [correctFlags.length, bugLines.length, falseFlags.length, attempts, completeEvent, emit]);
38793
+ }, [checkEvent, correctFlags.length, bugLines.length, falseFlags.length, attempts, completeEvent, emit]);
38738
38794
  const handleReset = () => {
38739
- setSubmitted(false);
38795
+ if (playAgainEvent) {
38796
+ emit(`UI:${playAgainEvent}`, {});
38797
+ }
38740
38798
  if (attempts >= 2 && str(resolved?.hint)) {
38741
38799
  setShowHint(true);
38742
38800
  }
38743
38801
  };
38744
38802
  const handleFullReset = () => {
38745
- setFlaggedLines(/* @__PURE__ */ new Set());
38746
- setSubmitted(false);
38747
- setAttempts(0);
38803
+ if (playAgainEvent) {
38804
+ emit(`UI:${playAgainEvent}`, {});
38805
+ }
38748
38806
  setShowHint(false);
38749
38807
  };
38750
38808
  if (!resolved) return null;
@@ -38772,7 +38830,7 @@ function DebuggerBoard({
38772
38830
  /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "caption", className: "text-muted-foreground", children: t("debugger.findBugs", { count: String(num(resolved.bugCount)) }) })
38773
38831
  ] }) }),
38774
38832
  /* @__PURE__ */ jsxRuntime.jsx(exports.Card, { className: "p-0 overflow-hidden", children: /* @__PURE__ */ jsxRuntime.jsx(exports.VStack, { gap: "none", children: lines.map((line, i) => {
38775
- const isFlagged = flaggedLines.has(line.id);
38833
+ const isFlagged = !!line.isFlagged;
38776
38834
  let lineStyle = "";
38777
38835
  if (submitted) {
38778
38836
  if (line.isBug && isFlagged) lineStyle = "bg-success/10";
@@ -38806,9 +38864,9 @@ function DebuggerBoard({
38806
38864
  /* @__PURE__ */ jsxRuntime.jsx(
38807
38865
  exports.Icon,
38808
38866
  {
38809
- icon: flaggedLines.has(line.id) ? LucideIcons2.CheckCircle : LucideIcons2.XCircle,
38867
+ icon: line.isFlagged ? LucideIcons2.CheckCircle : LucideIcons2.XCircle,
38810
38868
  size: "xs",
38811
- className: flaggedLines.has(line.id) ? "text-success mt-0.5" : "text-warning mt-0.5"
38869
+ className: line.isFlagged ? "text-success mt-0.5" : "text-warning mt-0.5"
38812
38870
  }
38813
38871
  ),
38814
38872
  /* @__PURE__ */ jsxRuntime.jsxs(exports.VStack, { gap: "none", children: [
@@ -38819,7 +38877,7 @@ function DebuggerBoard({
38819
38877
  ] }) }),
38820
38878
  showHint && hint && /* @__PURE__ */ jsxRuntime.jsx(exports.Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "body", children: hint }) }),
38821
38879
  /* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { gap: "sm", justify: "center", children: [
38822
- !submitted ? /* @__PURE__ */ jsxRuntime.jsxs(exports.Button, { variant: "primary", onClick: handleSubmit, disabled: flaggedLines.size === 0, children: [
38880
+ !submitted ? /* @__PURE__ */ jsxRuntime.jsxs(exports.Button, { variant: "primary", onClick: handleSubmit, disabled: flaggedLines.length === 0, children: [
38823
38881
  /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { icon: LucideIcons2.Send, size: "sm" }),
38824
38882
  t("debugger.submit")
38825
38883
  ] }) : !allCorrect ? /* @__PURE__ */ jsxRuntime.jsx(exports.Button, { variant: "primary", onClick: handleReset, children: t("debugger.tryAgain") }) : null,
@@ -42779,6 +42837,9 @@ function getOpponentAction(strategy, actions, history) {
42779
42837
  function NegotiatorBoard({
42780
42838
  entity,
42781
42839
  completeEvent = "PUZZLE_COMPLETE",
42840
+ playRoundEvent,
42841
+ finishEvent,
42842
+ playAgainEvent,
42782
42843
  className
42783
42844
  }) {
42784
42845
  const { emit } = useEventBus();
@@ -42787,13 +42848,14 @@ function NegotiatorBoard({
42787
42848
  const [history, setHistory] = React77.useState([]);
42788
42849
  const [headerError, setHeaderError] = React77.useState(false);
42789
42850
  const [showHint, setShowHint] = React77.useState(false);
42790
- const totalRounds = num(resolved?.totalRounds);
42851
+ const totalRounds = num(resolved?.maxRounds);
42791
42852
  const targetScore = num(resolved?.targetScore);
42792
- const currentRound = history.length;
42793
- const isComplete = currentRound >= totalRounds;
42794
- const playerTotal = history.reduce((s, r) => s + r.playerPayoff, 0);
42853
+ const currentRound = num(resolved?.round);
42854
+ const result = str(resolved?.result) || "none";
42855
+ const playerTotal = num(resolved?.score);
42856
+ const isComplete = result !== "none" || totalRounds > 0 && currentRound >= totalRounds;
42857
+ const won = result === "win";
42795
42858
  const opponentTotal = history.reduce((s, r) => s + r.opponentPayoff, 0);
42796
- const won = isComplete && playerTotal >= targetScore;
42797
42859
  const actions = Array.isArray(resolved?.actions) ? resolved.actions : [];
42798
42860
  const payoffMatrix = Array.isArray(resolved?.payoffMatrix) ? resolved.payoffMatrix : [];
42799
42861
  const handleAction = React77.useCallback((actionId) => {
@@ -42802,29 +42864,45 @@ function NegotiatorBoard({
42802
42864
  const payoff = payoffMatrix.find(
42803
42865
  (p2) => p2.playerAction === actionId && p2.opponentAction === opponentAction
42804
42866
  );
42805
- const result = {
42806
- round: currentRound + 1,
42807
- playerAction: actionId,
42808
- opponentAction,
42809
- playerPayoff: payoff?.playerPayoff ?? 0,
42810
- opponentPayoff: payoff?.opponentPayoff ?? 0
42811
- };
42812
- const newHistory = [...history, result];
42813
- setHistory(newHistory);
42814
- if (newHistory.length >= totalRounds) {
42815
- const total = newHistory.reduce((s, r) => s + r.playerPayoff, 0);
42816
- if (total >= targetScore) {
42817
- emit(`UI:${completeEvent}`, { success: true, score: total });
42818
- }
42819
- if (newHistory.length >= 3 && str(resolved?.hint)) {
42867
+ const playerPayoff = payoff?.playerPayoff ?? 0;
42868
+ setHistory((prev) => [
42869
+ ...prev,
42870
+ {
42871
+ round: prev.length + 1,
42872
+ playerAction: actionId,
42873
+ opponentAction,
42874
+ playerPayoff,
42875
+ opponentPayoff: payoff?.opponentPayoff ?? 0
42876
+ }
42877
+ ]);
42878
+ if (playRoundEvent) {
42879
+ emit(`UI:${playRoundEvent}`, { playerAction: actionId, payoff: playerPayoff });
42880
+ }
42881
+ if (totalRounds > 0 && currentRound + 1 >= totalRounds) {
42882
+ if (finishEvent) {
42883
+ emit(`UI:${finishEvent}`, {});
42884
+ }
42885
+ if (str(resolved?.hint)) {
42820
42886
  setShowHint(true);
42821
42887
  }
42822
42888
  }
42823
- }, [isComplete, resolved, totalRounds, targetScore, actions, payoffMatrix, history, currentRound, completeEvent, emit]);
42824
- const handleReset = () => {
42889
+ }, [isComplete, resolved, totalRounds, currentRound, actions, payoffMatrix, history, playRoundEvent, finishEvent, emit]);
42890
+ const handleReset = React77.useCallback(() => {
42825
42891
  setHistory([]);
42826
42892
  setShowHint(false);
42827
- };
42893
+ if (playAgainEvent) {
42894
+ emit(`UI:${playAgainEvent}`, {});
42895
+ }
42896
+ }, [playAgainEvent, emit]);
42897
+ const completedRef = React77.useRef(false);
42898
+ React77.useEffect(() => {
42899
+ if (result === "win" && !completedRef.current) {
42900
+ completedRef.current = true;
42901
+ emit(`UI:${completeEvent}`, { success: true, score: playerTotal });
42902
+ } else if (result === "none") {
42903
+ completedRef.current = false;
42904
+ }
42905
+ }, [result, playerTotal, completeEvent, emit]);
42828
42906
  const getActionLabel = (id) => actions.find((a) => a.id === id)?.label ?? id;
42829
42907
  if (!resolved) return null;
42830
42908
  const theme = resolved.theme ?? void 0;
@@ -45825,67 +45903,47 @@ var init_SimulationGraph = __esm({
45825
45903
  function SimulatorBoard({
45826
45904
  entity,
45827
45905
  completeEvent = "PUZZLE_COMPLETE",
45906
+ setAEvent,
45907
+ setBEvent,
45908
+ checkEvent,
45909
+ playAgainEvent,
45828
45910
  className
45829
45911
  }) {
45830
45912
  const { emit } = useEventBus();
45831
45913
  const { t } = hooks.useTranslate();
45832
45914
  const resolved = boardEntity(entity);
45833
45915
  const parameters = Array.isArray(resolved?.parameters) ? resolved.parameters : [];
45834
- const [values, setValues] = React77.useState(() => {
45835
- const init = {};
45836
- for (const p2 of parameters) {
45837
- init[p2.id] = p2.initial;
45838
- }
45839
- return init;
45840
- });
45841
45916
  const [headerError, setHeaderError] = React77.useState(false);
45842
- const [submitted, setSubmitted] = React77.useState(false);
45843
- const [attempts, setAttempts] = React77.useState(0);
45844
- const [showHint, setShowHint] = React77.useState(false);
45845
- const computeOutput = React77.useCallback((params) => {
45846
- try {
45847
- const fn = new Function("params", `return (${str(resolved?.computeExpression)})`);
45848
- return fn(params);
45849
- } catch {
45850
- return 0;
45851
- }
45852
- }, [resolved?.computeExpression]);
45853
- const output = React77.useMemo(() => computeOutput(values) ?? 0, [computeOutput, values]);
45854
- const targetValue = num(resolved?.targetValue);
45855
- const targetTolerance = num(resolved?.targetTolerance);
45856
- const isCorrect = Math.abs(output - targetValue) <= targetTolerance;
45857
- const handleParameterChange = (id, value) => {
45858
- if (submitted) return;
45859
- setValues((prev) => ({ ...prev, [id]: value }));
45860
- };
45861
- const handleSubmit = () => {
45862
- setSubmitted(true);
45863
- setAttempts((a) => a + 1);
45864
- if (isCorrect) {
45865
- emit(`UI:${completeEvent}`, { success: true, attempts: attempts + 1 });
45866
- }
45917
+ if (!resolved) return null;
45918
+ const paramA = num(resolved.paramA);
45919
+ const paramB = num(resolved.paramB);
45920
+ const output = num(resolved.output);
45921
+ const targetValue = num(resolved.target);
45922
+ const targetTolerance = num(resolved.tolerance);
45923
+ const attempts = num(resolved.attempts);
45924
+ const result = str(resolved.result);
45925
+ const isWin = result === "win";
45926
+ const isComplete = result !== "none" && result !== "";
45927
+ const paramAValue = parameters[0];
45928
+ const paramBValue = parameters[1];
45929
+ const sliderValues = [paramA, paramB];
45930
+ const sliderEvents = [setAEvent, setBEvent];
45931
+ const handleParameterChange = (index, value) => {
45932
+ if (isComplete) return;
45933
+ const ev = sliderEvents[index];
45934
+ if (ev) emit(`UI:${ev}`, { value });
45867
45935
  };
45868
- const handleReset = () => {
45869
- setSubmitted(false);
45870
- if (attempts >= 2 && str(resolved?.hint)) {
45871
- setShowHint(true);
45872
- }
45936
+ const handleCheck = () => {
45937
+ if (checkEvent) emit(`UI:${checkEvent}`, {});
45873
45938
  };
45874
- const handleFullReset = () => {
45875
- const init = {};
45876
- for (const p2 of parameters) {
45877
- init[p2.id] = p2.initial;
45878
- }
45879
- setValues(init);
45880
- setSubmitted(false);
45881
- setAttempts(0);
45882
- setShowHint(false);
45939
+ const handlePlayAgain = () => {
45940
+ if (playAgainEvent) emit(`UI:${playAgainEvent}`, {});
45883
45941
  };
45884
- if (!resolved) return null;
45885
45942
  const theme = resolved.theme ?? void 0;
45886
45943
  const themeBackground = theme?.background;
45887
45944
  const headerImage = str(resolved.headerImage);
45888
45945
  const hint = str(resolved.hint);
45946
+ const showHint = isComplete && !isWin && attempts >= 2 && Boolean(hint);
45889
45947
  const outputLabel = str(resolved.outputLabel);
45890
45948
  const outputUnit = str(resolved.outputUnit);
45891
45949
  return /* @__PURE__ */ jsxRuntime.jsx(
@@ -45905,41 +45963,43 @@ function SimulatorBoard({
45905
45963
  ] }) }),
45906
45964
  /* @__PURE__ */ jsxRuntime.jsx(exports.Card, { className: "p-4", children: /* @__PURE__ */ jsxRuntime.jsxs(exports.VStack, { gap: "md", children: [
45907
45965
  /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "small", weight: "bold", className: "uppercase tracking-wider text-muted-foreground", children: t("simulator.parameters") }),
45908
- parameters.map((param) => /* @__PURE__ */ jsxRuntime.jsxs(exports.VStack, { gap: "xs", children: [
45909
- /* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { justify: "between", align: "center", children: [
45910
- /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "body", weight: "medium", children: param.label }),
45911
- /* @__PURE__ */ jsxRuntime.jsxs(exports.Badge, { size: "sm", children: [
45912
- values[param.id],
45913
- " ",
45914
- param.unit
45915
- ] })
45916
- ] }),
45917
- /* @__PURE__ */ jsxRuntime.jsx(
45918
- "input",
45919
- {
45920
- type: "range",
45921
- min: param.min,
45922
- max: param.max,
45923
- step: param.step,
45924
- value: values[param.id],
45925
- onChange: (e) => handleParameterChange(param.id, Number(e.target.value)),
45926
- disabled: submitted,
45927
- className: "w-full accent-foreground"
45928
- }
45929
- ),
45930
- /* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { justify: "between", children: [
45931
- /* @__PURE__ */ jsxRuntime.jsxs(exports.Typography, { variant: "caption", className: "text-muted-foreground", children: [
45932
- param.min,
45933
- " ",
45934
- param.unit
45966
+ [paramAValue, paramBValue].map(
45967
+ (param, index) => param ? /* @__PURE__ */ jsxRuntime.jsxs(exports.VStack, { gap: "xs", children: [
45968
+ /* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { justify: "between", align: "center", children: [
45969
+ /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "body", weight: "medium", children: param.label }),
45970
+ /* @__PURE__ */ jsxRuntime.jsxs(exports.Badge, { size: "sm", children: [
45971
+ sliderValues[index],
45972
+ " ",
45973
+ param.unit
45974
+ ] })
45935
45975
  ] }),
45936
- /* @__PURE__ */ jsxRuntime.jsxs(exports.Typography, { variant: "caption", className: "text-muted-foreground", children: [
45937
- param.max,
45938
- " ",
45939
- param.unit
45976
+ /* @__PURE__ */ jsxRuntime.jsx(
45977
+ "input",
45978
+ {
45979
+ type: "range",
45980
+ min: param.min,
45981
+ max: param.max,
45982
+ step: param.step,
45983
+ value: sliderValues[index],
45984
+ onChange: (e) => handleParameterChange(index, Number(e.target.value)),
45985
+ disabled: isComplete,
45986
+ className: "w-full accent-foreground"
45987
+ }
45988
+ ),
45989
+ /* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { justify: "between", children: [
45990
+ /* @__PURE__ */ jsxRuntime.jsxs(exports.Typography, { variant: "caption", className: "text-muted-foreground", children: [
45991
+ param.min,
45992
+ " ",
45993
+ param.unit
45994
+ ] }),
45995
+ /* @__PURE__ */ jsxRuntime.jsxs(exports.Typography, { variant: "caption", className: "text-muted-foreground", children: [
45996
+ param.max,
45997
+ " ",
45998
+ param.unit
45999
+ ] })
45940
46000
  ] })
45941
- ] })
45942
- ] }, param.id))
46001
+ ] }, param.id ?? index) : null
46002
+ )
45943
46003
  ] }) }),
45944
46004
  /* @__PURE__ */ jsxRuntime.jsx(exports.Card, { className: "p-4", children: /* @__PURE__ */ jsxRuntime.jsxs(exports.VStack, { gap: "sm", align: "center", children: [
45945
46005
  /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "small", weight: "bold", className: "uppercase tracking-wider text-muted-foreground", children: outputLabel }),
@@ -45948,9 +46008,9 @@ function SimulatorBoard({
45948
46008
  " ",
45949
46009
  outputUnit
45950
46010
  ] }),
45951
- submitted && /* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { gap: "xs", align: "center", children: [
45952
- /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { icon: isCorrect ? LucideIcons2.CheckCircle : LucideIcons2.XCircle, size: "sm", className: isCorrect ? "text-success" : "text-error" }),
45953
- /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "body", className: isCorrect ? "text-success" : "text-error", children: isCorrect ? str(resolved.successMessage) || t("simulator.correct") : str(resolved.failMessage) || t("simulator.incorrect") })
46011
+ isComplete && /* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { gap: "xs", align: "center", children: [
46012
+ /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { icon: isWin ? LucideIcons2.CheckCircle : LucideIcons2.XCircle, size: "sm", className: isWin ? "text-success" : "text-error" }),
46013
+ /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "body", className: isWin ? "text-success" : "text-error", children: isWin ? str(resolved.successMessage) || t("simulator.correct") : str(resolved.failMessage) || t("simulator.incorrect") })
45954
46014
  ] }),
45955
46015
  /* @__PURE__ */ jsxRuntime.jsxs(exports.Typography, { variant: "caption", className: "text-muted-foreground", children: [
45956
46016
  t("simulator.target"),
@@ -45965,11 +46025,11 @@ function SimulatorBoard({
45965
46025
  ] }) }),
45966
46026
  showHint && hint && /* @__PURE__ */ jsxRuntime.jsx(exports.Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "body", children: hint }) }),
45967
46027
  /* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { gap: "sm", justify: "center", children: [
45968
- !submitted ? /* @__PURE__ */ jsxRuntime.jsxs(exports.Button, { variant: "primary", onClick: handleSubmit, children: [
46028
+ !isComplete ? /* @__PURE__ */ jsxRuntime.jsxs(exports.Button, { variant: "primary", onClick: handleCheck, children: [
45969
46029
  /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { icon: LucideIcons2.Play, size: "sm" }),
45970
46030
  t("simulator.simulate")
45971
- ] }) : !isCorrect ? /* @__PURE__ */ jsxRuntime.jsx(exports.Button, { variant: "primary", onClick: handleReset, children: t("simulator.tryAgain") }) : null,
45972
- /* @__PURE__ */ jsxRuntime.jsxs(exports.Button, { variant: "secondary", onClick: handleFullReset, children: [
46031
+ ] }) : null,
46032
+ /* @__PURE__ */ jsxRuntime.jsxs(exports.Button, { variant: "secondary", onClick: handlePlayAgain, children: [
45973
46033
  /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { icon: LucideIcons2.RotateCcw, size: "sm" }),
45974
46034
  t("simulator.reset")
45975
46035
  ] })