@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.
@@ -8,9 +8,6 @@ var ELK = require('elkjs/lib/elk.bundled.js');
8
8
  var react = require('@xyflow/react');
9
9
  var hooks = require('@almadar/ui/hooks');
10
10
  var LucideIcons2 = require('lucide-react');
11
- var PhosphorIcons = require('@phosphor-icons/react');
12
- var TablerIcons = require('@tabler/icons-react');
13
- var FaIcons = require('react-icons/fa');
14
11
  var reactDom = require('react-dom');
15
12
  var evaluator = require('@almadar/evaluator');
16
13
  var context = require('@almadar/ui/context');
@@ -69,9 +66,6 @@ function _interopNamespace(e) {
69
66
  var React91__namespace = /*#__PURE__*/_interopNamespace(React91);
70
67
  var ELK__default = /*#__PURE__*/_interopDefault(ELK);
71
68
  var LucideIcons2__namespace = /*#__PURE__*/_interopNamespace(LucideIcons2);
72
- var PhosphorIcons__namespace = /*#__PURE__*/_interopNamespace(PhosphorIcons);
73
- var TablerIcons__namespace = /*#__PURE__*/_interopNamespace(TablerIcons);
74
- var FaIcons__namespace = /*#__PURE__*/_interopNamespace(FaIcons);
75
69
  var SyntaxHighlighter__default = /*#__PURE__*/_interopDefault(SyntaxHighlighter);
76
70
  var dark__default = /*#__PURE__*/_interopDefault(dark);
77
71
  var langJson__default = /*#__PURE__*/_interopDefault(langJson);
@@ -4522,6 +4516,41 @@ function kebabToPascal(name) {
4522
4516
  return part.charAt(0).toUpperCase() + part.slice(1);
4523
4517
  }).join("");
4524
4518
  }
4519
+ function loadLib(key, importer) {
4520
+ let p2 = libPromises.get(key);
4521
+ if (!p2) {
4522
+ p2 = importer().then((m) => m);
4523
+ libPromises.set(key, p2);
4524
+ }
4525
+ return p2;
4526
+ }
4527
+ function lazyFamilyIcon(libKey, importer, pick, fallbackName, family) {
4528
+ const Lazy = React91__namespace.default.lazy(async () => {
4529
+ const lib = await loadLib(libKey, importer);
4530
+ const Comp = pick(lib);
4531
+ if (!Comp) {
4532
+ warnFallback(fallbackName, family);
4533
+ return { default: makeLucideAdapter(fallbackName, true) };
4534
+ }
4535
+ return { default: Comp };
4536
+ });
4537
+ const Wrapped = (props) => /* @__PURE__ */ jsxRuntime.jsx(
4538
+ React91__namespace.default.Suspense,
4539
+ {
4540
+ fallback: /* @__PURE__ */ jsxRuntime.jsx(
4541
+ "span",
4542
+ {
4543
+ "aria-hidden": true,
4544
+ className: props.className,
4545
+ style: { display: "inline-block", ...props.style }
4546
+ }
4547
+ ),
4548
+ children: /* @__PURE__ */ jsxRuntime.jsx(Lazy, { ...props })
4549
+ }
4550
+ );
4551
+ Wrapped.displayName = `Lazy.${libKey}.${fallbackName}`;
4552
+ return Wrapped;
4553
+ }
4525
4554
  function resolveLucide(name) {
4526
4555
  if (lucideAliases[name]) return lucideAliases[name];
4527
4556
  const pascal = kebabToPascal(name);
@@ -4532,60 +4561,81 @@ function resolveLucide(name) {
4532
4561
  if (asIs && typeof asIs === "object") return asIs;
4533
4562
  return LucideIcons2__namespace.HelpCircle;
4534
4563
  }
4535
- function resolvePhosphor(name, weight) {
4564
+ function resolvePhosphor(name, weight, family) {
4536
4565
  const target = phosphorAliases[name] ?? kebabToPascal(name);
4537
- const map = PhosphorIcons__namespace;
4538
- const PhosphorComp = map[target];
4539
- if (!PhosphorComp || typeof PhosphorComp !== "object") return null;
4540
- const Component = PhosphorComp;
4541
- const Adapter = (props) => /* @__PURE__ */ jsxRuntime.jsx(
4542
- Component,
4543
- {
4544
- weight,
4545
- className: props.className,
4546
- style: props.style,
4547
- size: props.size ?? "1em"
4548
- }
4566
+ return lazyFamilyIcon(
4567
+ "phosphor",
4568
+ () => import('@phosphor-icons/react'),
4569
+ (lib) => {
4570
+ const PhosphorComp = lib[target];
4571
+ if (!PhosphorComp || typeof PhosphorComp !== "object") return null;
4572
+ const Component = PhosphorComp;
4573
+ const Adapter = (props) => /* @__PURE__ */ jsxRuntime.jsx(
4574
+ Component,
4575
+ {
4576
+ weight,
4577
+ className: props.className,
4578
+ style: props.style,
4579
+ size: props.size ?? "1em"
4580
+ }
4581
+ );
4582
+ Adapter.displayName = `Phosphor.${target}.${weight}`;
4583
+ return Adapter;
4584
+ },
4585
+ name,
4586
+ family
4549
4587
  );
4550
- Adapter.displayName = `Phosphor.${target}.${weight}`;
4551
- return Adapter;
4552
4588
  }
4553
- function resolveTabler(name) {
4589
+ function resolveTabler(name, family) {
4554
4590
  const suffix = tablerAliases[name] ?? kebabToPascal(name);
4555
4591
  const target = `Icon${suffix}`;
4556
- const map = TablerIcons__namespace;
4557
- const TablerComp = map[target];
4558
- if (!TablerComp || typeof TablerComp !== "object") return null;
4559
- const Component = TablerComp;
4560
- const Adapter = (props) => /* @__PURE__ */ jsxRuntime.jsx(
4561
- Component,
4562
- {
4563
- stroke: props.strokeWidth ?? 1.5,
4564
- className: props.className,
4565
- style: props.style,
4566
- size: props.size ?? 24
4567
- }
4592
+ return lazyFamilyIcon(
4593
+ "tabler",
4594
+ () => import('@tabler/icons-react'),
4595
+ (lib) => {
4596
+ const TablerComp = lib[target];
4597
+ if (!TablerComp || typeof TablerComp !== "object") return null;
4598
+ const Component = TablerComp;
4599
+ const Adapter = (props) => /* @__PURE__ */ jsxRuntime.jsx(
4600
+ Component,
4601
+ {
4602
+ stroke: props.strokeWidth ?? 1.5,
4603
+ className: props.className,
4604
+ style: props.style,
4605
+ size: props.size ?? 24
4606
+ }
4607
+ );
4608
+ Adapter.displayName = `Tabler.${target}`;
4609
+ return Adapter;
4610
+ },
4611
+ name,
4612
+ family
4568
4613
  );
4569
- Adapter.displayName = `Tabler.${target}`;
4570
- return Adapter;
4571
4614
  }
4572
- function resolveFa(name) {
4615
+ function resolveFa(name, family) {
4573
4616
  const suffix = faAliases[name] ?? kebabToPascal(name);
4574
4617
  const target = `Fa${suffix}`;
4575
- const map = FaIcons__namespace;
4576
- const FaComp = map[target];
4577
- if (!FaComp || typeof FaComp !== "function") return null;
4578
- const Component = FaComp;
4579
- const Adapter = (props) => /* @__PURE__ */ jsxRuntime.jsx(
4580
- Component,
4581
- {
4582
- className: props.className,
4583
- style: props.style,
4584
- size: props.size ?? "1em"
4585
- }
4618
+ return lazyFamilyIcon(
4619
+ "fa",
4620
+ () => import('react-icons/fa'),
4621
+ (lib) => {
4622
+ const FaComp = lib[target];
4623
+ if (!FaComp || typeof FaComp !== "function") return null;
4624
+ const Component = FaComp;
4625
+ const Adapter = (props) => /* @__PURE__ */ jsxRuntime.jsx(
4626
+ Component,
4627
+ {
4628
+ className: props.className,
4629
+ style: props.style,
4630
+ size: props.size ?? "1em"
4631
+ }
4632
+ );
4633
+ Adapter.displayName = `Fa.${target}`;
4634
+ return Adapter;
4635
+ },
4636
+ name,
4637
+ family
4586
4638
  );
4587
- Adapter.displayName = `Fa.${target}`;
4588
- return Adapter;
4589
4639
  }
4590
4640
  function warnFallback(name, family) {
4591
4641
  const key = `${family}::${name}`;
@@ -4619,39 +4669,22 @@ function resolveIconForFamily(name, family) {
4619
4669
  switch (family) {
4620
4670
  case "lucide":
4621
4671
  return makeLucideAdapter(name, false);
4622
- case "phosphor-outline": {
4623
- const p2 = resolvePhosphor(name, "regular");
4624
- if (p2) return p2;
4625
- warnFallback(name, family);
4626
- return makeLucideAdapter(name, true);
4627
- }
4628
- case "phosphor-fill": {
4629
- const p2 = resolvePhosphor(name, "fill");
4630
- if (p2) return p2;
4631
- warnFallback(name, family);
4632
- return makeLucideAdapter(name, true);
4633
- }
4634
- case "phosphor-duotone": {
4635
- const p2 = resolvePhosphor(name, "duotone");
4636
- if (p2) return p2;
4637
- warnFallback(name, family);
4638
- return makeLucideAdapter(name, true);
4639
- }
4640
- case "tabler": {
4641
- const t = resolveTabler(name);
4642
- if (t) return t;
4643
- warnFallback(name, family);
4644
- return makeLucideAdapter(name, true);
4645
- }
4646
- case "fa-solid": {
4647
- const f3 = resolveFa(name);
4648
- if (f3) return f3;
4649
- warnFallback(name, family);
4650
- return makeLucideAdapter(name, true);
4651
- }
4652
- }
4653
- }
4654
- var DEFAULT_FAMILY, VALID_FAMILIES, cachedFamily, listeners, observer, lucideAliases, phosphorAliases, tablerAliases, faAliases, warned;
4672
+ // Non-lucide families resolve to a lazy, Suspense-wrapped component that
4673
+ // dynamic-imports the library on first render and falls back to lucide
4674
+ // internally when the family lacks the icon (see lazyFamilyIcon).
4675
+ case "phosphor-outline":
4676
+ return resolvePhosphor(name, "regular", family);
4677
+ case "phosphor-fill":
4678
+ return resolvePhosphor(name, "fill", family);
4679
+ case "phosphor-duotone":
4680
+ return resolvePhosphor(name, "duotone", family);
4681
+ case "tabler":
4682
+ return resolveTabler(name, family);
4683
+ case "fa-solid":
4684
+ return resolveFa(name, family);
4685
+ }
4686
+ }
4687
+ var DEFAULT_FAMILY, VALID_FAMILIES, cachedFamily, listeners, observer, libPromises, lucideAliases, phosphorAliases, tablerAliases, faAliases, warned;
4655
4688
  var init_iconFamily = __esm({
4656
4689
  "lib/iconFamily.tsx"() {
4657
4690
  "use client";
@@ -4667,6 +4700,7 @@ var init_iconFamily = __esm({
4667
4700
  cachedFamily = null;
4668
4701
  listeners = /* @__PURE__ */ new Set();
4669
4702
  observer = null;
4703
+ libPromises = /* @__PURE__ */ new Map();
4670
4704
  lucideAliases = {
4671
4705
  close: LucideIcons2__namespace.X,
4672
4706
  trash: LucideIcons2__namespace.Trash2,
@@ -19898,61 +19932,53 @@ var init_Breadcrumb = __esm({
19898
19932
  function BuilderBoard({
19899
19933
  entity,
19900
19934
  completeEvent = "PUZZLE_COMPLETE",
19935
+ placeEvent,
19936
+ checkEvent,
19937
+ playAgainEvent,
19901
19938
  className
19902
19939
  }) {
19903
19940
  const { emit } = useEventBus();
19904
19941
  const { t } = hooks.useTranslate();
19905
19942
  const resolved = boardEntity(entity);
19906
- const [placements, setPlacements] = React91.useState({});
19907
19943
  const [headerError, setHeaderError] = React91.useState(false);
19908
- const [submitted, setSubmitted] = React91.useState(false);
19909
- const [attempts, setAttempts] = React91.useState(0);
19910
- const [showHint, setShowHint] = React91.useState(false);
19944
+ const [selectedComponent, setSelectedComponent] = React91.useState(null);
19911
19945
  const components = Array.isArray(resolved?.components) ? resolved.components : [];
19912
19946
  const slots = Array.isArray(resolved?.slots) ? resolved.slots : [];
19947
+ const placements = {};
19948
+ for (const slot of slots) {
19949
+ if (slot.placedComponentId) placements[slot.id] = slot.placedComponentId;
19950
+ }
19951
+ const attempts = num(resolved?.attempts);
19952
+ const result = str(resolved?.result) || "none";
19953
+ const submitted = result === "win";
19913
19954
  const usedComponentIds = new Set(Object.values(placements));
19914
19955
  const availableComponents = components.filter((c) => !usedComponentIds.has(c.id));
19915
- const [selectedComponent, setSelectedComponent] = React91.useState(null);
19916
- const allPlaced = Object.keys(placements).length === slots.length;
19956
+ const allPlaced = slots.length > 0 && slots.every((s) => Boolean(placements[s.id]));
19917
19957
  const results = submitted ? slots.map((slot) => ({
19918
19958
  slot,
19919
19959
  placed: placements[slot.id],
19920
- correct: placements[slot.id] === slot.acceptsComponentId
19960
+ correct: placements[slot.id] === slot.requiredComponentId
19921
19961
  })) : [];
19922
- const allCorrect = results.length > 0 && results.every((r2) => r2.correct);
19962
+ const showHint = attempts >= 2 && Boolean(str(resolved?.hint));
19923
19963
  const handlePlaceComponent = (slotId) => {
19924
19964
  if (submitted || !selectedComponent) return;
19925
- setPlacements((prev) => ({ ...prev, [slotId]: selectedComponent }));
19965
+ if (placeEvent) emit(`UI:${placeEvent}`, { slotId, componentId: selectedComponent });
19926
19966
  setSelectedComponent(null);
19927
19967
  };
19928
19968
  const handleRemoveFromSlot = (slotId) => {
19929
19969
  if (submitted) return;
19930
- setPlacements((prev) => {
19931
- const next = { ...prev };
19932
- delete next[slotId];
19933
- return next;
19934
- });
19970
+ if (placeEvent) emit(`UI:${placeEvent}`, { slotId, componentId: "" });
19935
19971
  };
19936
- const handleSubmit = React91.useCallback(() => {
19937
- setSubmitted(true);
19938
- setAttempts((a) => a + 1);
19939
- const correct = slots.every((slot) => placements[slot.id] === slot.acceptsComponentId);
19940
- if (correct) {
19972
+ const handleSubmit = () => {
19973
+ if (checkEvent) emit(`UI:${checkEvent}`, {});
19974
+ const solved = slots.length > 0 && slots.every((s) => placements[s.id] === s.requiredComponentId);
19975
+ if (solved && completeEvent) {
19941
19976
  emit(`UI:${completeEvent}`, { success: true, attempts: attempts + 1 });
19942
19977
  }
19943
- }, [slots, placements, attempts, completeEvent, emit]);
19944
- const handleReset = () => {
19945
- setSubmitted(false);
19946
- if (attempts >= 2 && str(resolved?.hint)) {
19947
- setShowHint(true);
19948
- }
19949
19978
  };
19950
- const handleFullReset = () => {
19951
- setPlacements({});
19952
- setSubmitted(false);
19979
+ const handlePlayAgain = () => {
19953
19980
  setSelectedComponent(null);
19954
- setAttempts(0);
19955
- setShowHint(false);
19981
+ if (playAgainEvent) emit(`UI:${playAgainEvent}`, {});
19956
19982
  };
19957
19983
  const getComponentById = (id) => components.find((c) => c.id === id);
19958
19984
  if (!resolved) return null;
@@ -20002,13 +20028,13 @@ function BuilderBoard({
20002
20028
  /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", weight: "bold", className: "uppercase tracking-wider text-muted-foreground", children: t("builder.blueprint") }),
20003
20029
  /* @__PURE__ */ jsxRuntime.jsx(VStack, { gap: "sm", children: slots.map((slot) => {
20004
20030
  const placedComp = placements[slot.id] ? getComponentById(placements[slot.id]) : null;
20005
- const result = results.find((r2) => r2.slot.id === slot.id);
20031
+ const result2 = results.find((r2) => r2.slot.id === slot.id);
20006
20032
  return /* @__PURE__ */ jsxRuntime.jsxs(
20007
20033
  HStack,
20008
20034
  {
20009
20035
  gap: "sm",
20010
20036
  align: "center",
20011
- className: `p-3 border-2 rounded ${result ? result.correct ? "border-success" : "border-error" : selectedComponent ? "border-dashed border-foreground cursor-pointer" : "border-border"}`,
20037
+ className: `p-3 border-2 rounded ${result2 ? result2.correct ? "border-success" : "border-error" : selectedComponent ? "border-dashed border-foreground cursor-pointer" : "border-border"}`,
20012
20038
  onClick: () => handlePlaceComponent(slot.id),
20013
20039
  children: [
20014
20040
  /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "none", className: "flex-1", children: [
@@ -20023,7 +20049,7 @@ function BuilderBoard({
20023
20049
  ] }) : null,
20024
20050
  placedComp.label
20025
20051
  ] }),
20026
- result && /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: result.correct ? LucideIcons2.CheckCircle : LucideIcons2.XCircle, size: "sm", className: result.correct ? "text-success" : "text-error" })
20052
+ result2 && /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: result2.correct ? LucideIcons2.CheckCircle : LucideIcons2.XCircle, size: "sm", className: result2.correct ? "text-success" : "text-error" })
20027
20053
  ] }) : /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", className: "text-muted-foreground", children: t("builder.empty") })
20028
20054
  ]
20029
20055
  },
@@ -20032,16 +20058,16 @@ function BuilderBoard({
20032
20058
  }) })
20033
20059
  ] }) }),
20034
20060
  submitted && /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "sm", align: "center", children: [
20035
- /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: allCorrect ? LucideIcons2.CheckCircle : LucideIcons2.XCircle, size: "lg", className: allCorrect ? "text-success" : "text-error" }),
20036
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", weight: "bold", children: allCorrect ? str(resolved.successMessage) || t("builder.success") : str(resolved.failMessage) || t("builder.incorrect") })
20061
+ /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: LucideIcons2.CheckCircle, size: "lg", className: "text-success" }),
20062
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", weight: "bold", children: str(resolved.successMessage) || t("builder.success") })
20037
20063
  ] }) }),
20038
20064
  showHint && hint && /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", children: hint }) }),
20039
20065
  /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "sm", justify: "center", children: [
20040
- !submitted ? /* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "primary", onClick: handleSubmit, disabled: !allPlaced, children: [
20066
+ !submitted && /* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "primary", onClick: handleSubmit, disabled: !allPlaced, children: [
20041
20067
  /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: LucideIcons2.Wrench, size: "sm" }),
20042
20068
  t("builder.build")
20043
- ] }) : !allCorrect ? /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "primary", onClick: handleReset, children: t("builder.tryAgain") }) : null,
20044
- /* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "secondary", onClick: handleFullReset, children: [
20069
+ ] }),
20070
+ /* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "secondary", onClick: handlePlayAgain, children: [
20045
20071
  /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: LucideIcons2.RotateCcw, size: "sm" }),
20046
20072
  t("builder.reset")
20047
20073
  ] })
@@ -23497,57 +23523,84 @@ var init_ChartLegend = __esm({
23497
23523
  function ClassifierBoard({
23498
23524
  entity,
23499
23525
  completeEvent = "PUZZLE_COMPLETE",
23526
+ assignEvent,
23527
+ checkEvent,
23528
+ playAgainEvent,
23500
23529
  className
23501
23530
  }) {
23502
23531
  const { emit } = useEventBus();
23503
23532
  const { t } = hooks.useTranslate();
23504
23533
  const resolved = boardEntity(entity);
23505
- const [assignments, setAssignments] = React91.useState({});
23534
+ const [localAssignments, setLocalAssignments] = React91.useState({});
23506
23535
  const [headerError, setHeaderError] = React91.useState(false);
23507
- const [submitted, setSubmitted] = React91.useState(false);
23508
- const [attempts, setAttempts] = React91.useState(0);
23536
+ const [localSubmitted, setLocalSubmitted] = React91.useState(false);
23537
+ const [localAttempts, setLocalAttempts] = React91.useState(0);
23509
23538
  const [showHint, setShowHint] = React91.useState(false);
23510
23539
  const items = Array.isArray(resolved?.items) ? resolved.items : [];
23511
23540
  const categories = Array.isArray(resolved?.categories) ? resolved.categories : [];
23541
+ const entityResult = str(resolved?.result);
23542
+ const entityDrivesResult = entityResult.length > 0;
23543
+ const entityHasAssignments = items.some((item) => item.assignedCategory != null);
23544
+ const assignments = entityHasAssignments ? items.reduce((acc, item) => {
23545
+ if (item.assignedCategory != null) acc[item.id] = item.assignedCategory;
23546
+ return acc;
23547
+ }, {}) : localAssignments;
23548
+ const attempts = entityDrivesResult ? num(resolved?.attempts) : localAttempts;
23549
+ const submitted = entityDrivesResult || localSubmitted;
23512
23550
  const unassignedItems = items.filter((item) => !assignments[item.id]);
23513
- const allAssigned = Object.keys(assignments).length === items.length;
23551
+ const allAssigned = items.length > 0 && Object.keys(assignments).length === items.length;
23514
23552
  const results = submitted ? items.map((item) => ({
23515
23553
  item,
23516
23554
  assigned: assignments[item.id],
23517
23555
  correct: assignments[item.id] === item.correctCategory
23518
23556
  })) : [];
23519
- const allCorrect = results.length > 0 && results.every((r2) => r2.correct);
23557
+ const allCorrect = entityDrivesResult ? entityResult === "success" : results.length > 0 && results.every((r2) => r2.correct);
23520
23558
  const correctCount = results.filter((r2) => r2.correct).length;
23521
23559
  const handleAssign = (itemId, categoryId) => {
23522
23560
  if (submitted) return;
23523
- setAssignments((prev) => ({ ...prev, [itemId]: categoryId }));
23561
+ if (assignEvent) {
23562
+ emit(`UI:${assignEvent}`, { itemId, categoryId });
23563
+ }
23564
+ if (!entityHasAssignments) {
23565
+ setLocalAssignments((prev) => ({ ...prev, [itemId]: categoryId }));
23566
+ }
23524
23567
  };
23525
23568
  const handleUnassign = (itemId) => {
23526
23569
  if (submitted) return;
23527
- setAssignments((prev) => {
23528
- const next = { ...prev };
23529
- delete next[itemId];
23530
- return next;
23531
- });
23570
+ if (!entityHasAssignments) {
23571
+ setLocalAssignments((prev) => {
23572
+ const next = { ...prev };
23573
+ delete next[itemId];
23574
+ return next;
23575
+ });
23576
+ }
23532
23577
  };
23533
23578
  const handleSubmit = React91.useCallback(() => {
23534
- setSubmitted(true);
23535
- setAttempts((a) => a + 1);
23536
- const correct = items.every((item) => assignments[item.id] === item.correctCategory);
23537
- if (correct) {
23538
- emit(`UI:${completeEvent}`, { success: true, attempts: attempts + 1 });
23579
+ if (checkEvent) {
23580
+ emit(`UI:${checkEvent}`, {});
23539
23581
  }
23540
- }, [items, assignments, attempts, completeEvent, emit]);
23582
+ if (!entityDrivesResult) {
23583
+ setLocalSubmitted(true);
23584
+ setLocalAttempts((a) => a + 1);
23585
+ const correct = items.every((item) => assignments[item.id] === item.correctCategory);
23586
+ if (correct) {
23587
+ emit(`UI:${completeEvent}`, { success: true, attempts: attempts + 1 });
23588
+ }
23589
+ }
23590
+ }, [checkEvent, entityDrivesResult, items, assignments, attempts, completeEvent, emit]);
23541
23591
  const handleReset = () => {
23542
- setSubmitted(false);
23592
+ if (!entityDrivesResult) setLocalSubmitted(false);
23543
23593
  if (attempts >= 2 && str(resolved?.hint)) {
23544
23594
  setShowHint(true);
23545
23595
  }
23546
23596
  };
23547
23597
  const handleFullReset = () => {
23548
- setAssignments({});
23549
- setSubmitted(false);
23550
- setAttempts(0);
23598
+ if (playAgainEvent) {
23599
+ emit(`UI:${playAgainEvent}`, {});
23600
+ }
23601
+ setLocalAssignments({});
23602
+ setLocalSubmitted(false);
23603
+ setLocalAttempts(0);
23551
23604
  setShowHint(false);
23552
23605
  };
23553
23606
  if (!resolved) return null;
@@ -31430,13 +31483,13 @@ var init_MapView = __esm({
31430
31483
  shadowSize: [41, 41]
31431
31484
  });
31432
31485
  L.Marker.prototype.options.icon = defaultIcon;
31433
- const { useEffect: useEffect77, useRef: useRef69, useCallback: useCallback119, useState: useState115 } = React91__namespace.default;
31486
+ const { useEffect: useEffect78, useRef: useRef70, useCallback: useCallback117, useState: useState115 } = React91__namespace.default;
31434
31487
  const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
31435
31488
  const { useEventBus: useEventBus3 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
31436
31489
  function MapUpdater({ centerLat, centerLng, zoom }) {
31437
31490
  const map = useMap();
31438
- const prevRef = useRef69({ centerLat, centerLng, zoom });
31439
- useEffect77(() => {
31491
+ const prevRef = useRef70({ centerLat, centerLng, zoom });
31492
+ useEffect78(() => {
31440
31493
  const prev = prevRef.current;
31441
31494
  if (prev.centerLat !== centerLat || prev.centerLng !== centerLng || prev.zoom !== zoom) {
31442
31495
  map.setView([centerLat, centerLng], zoom);
@@ -31447,7 +31500,7 @@ var init_MapView = __esm({
31447
31500
  }
31448
31501
  function MapClickHandler({ onMapClick }) {
31449
31502
  const map = useMap();
31450
- useEffect77(() => {
31503
+ useEffect78(() => {
31451
31504
  if (!onMapClick) return;
31452
31505
  const handler = (e) => {
31453
31506
  onMapClick(e.latlng.lat, e.latlng.lng);
@@ -31476,7 +31529,7 @@ var init_MapView = __esm({
31476
31529
  }) {
31477
31530
  const eventBus = useEventBus3();
31478
31531
  const [clickedPosition, setClickedPosition] = useState115(null);
31479
- const handleMapClick = useCallback119((lat, lng) => {
31532
+ const handleMapClick = useCallback117((lat, lng) => {
31480
31533
  if (showClickedPin) {
31481
31534
  setClickedPosition({ lat, lng });
31482
31535
  }
@@ -31485,7 +31538,7 @@ var init_MapView = __esm({
31485
31538
  eventBus.emit(`UI:${mapClickEvent}`, { latitude: lat, longitude: lng });
31486
31539
  }
31487
31540
  }, [onMapClick, mapClickEvent, eventBus, showClickedPin]);
31488
- const handleMarkerClick = useCallback119((marker) => {
31541
+ const handleMarkerClick = useCallback117((marker) => {
31489
31542
  onMarkerClick?.(marker);
31490
31543
  if (markerClickEvent) {
31491
31544
  eventBus.emit(`UI:${markerClickEvent}`, { ...marker });
@@ -40298,51 +40351,52 @@ var init_DataTable = __esm({
40298
40351
  function DebuggerBoard({
40299
40352
  entity,
40300
40353
  completeEvent = "PUZZLE_COMPLETE",
40354
+ toggleFlagEvent,
40355
+ checkEvent,
40356
+ playAgainEvent,
40301
40357
  className
40302
40358
  }) {
40303
40359
  const { emit } = useEventBus();
40304
40360
  const { t } = hooks.useTranslate();
40305
40361
  const resolved = boardEntity(entity);
40306
- const [flaggedLines, setFlaggedLines] = React91.useState(/* @__PURE__ */ new Set());
40307
40362
  const [headerError, setHeaderError] = React91.useState(false);
40308
- const [submitted, setSubmitted] = React91.useState(false);
40309
- const [attempts, setAttempts] = React91.useState(0);
40310
40363
  const [showHint, setShowHint] = React91.useState(false);
40364
+ const lines = Array.isArray(resolved?.lines) ? resolved.lines : [];
40365
+ const result = resolved?.result ?? null;
40366
+ const attempts = num(resolved?.attempts);
40367
+ const submitted = result != null;
40368
+ const bugLines = lines.filter((l) => l.isBug);
40369
+ const flaggedLines = lines.filter((l) => l.isFlagged);
40370
+ const correctFlags = lines.filter((l) => l.isBug && l.isFlagged);
40371
+ const falseFlags = lines.filter((l) => !l.isBug && l.isFlagged);
40372
+ const allCorrect = result === "win";
40311
40373
  const toggleLine = (lineId) => {
40312
40374
  if (submitted) return;
40313
- setFlaggedLines((prev) => {
40314
- const next = new Set(prev);
40315
- if (next.has(lineId)) {
40316
- next.delete(lineId);
40317
- } else {
40318
- next.add(lineId);
40319
- }
40320
- return next;
40321
- });
40375
+ if (toggleFlagEvent) {
40376
+ emit(`UI:${toggleFlagEvent}`, { lineId });
40377
+ }
40322
40378
  };
40323
- const lines = Array.isArray(resolved?.lines) ? resolved.lines : [];
40324
- const bugLines = lines.filter((l) => l.isBug);
40325
- const correctFlags = lines.filter((l) => l.isBug && flaggedLines.has(l.id));
40326
- const falseFlags = lines.filter((l) => !l.isBug && flaggedLines.has(l.id));
40327
- const allCorrect = submitted && correctFlags.length === bugLines.length && falseFlags.length === 0;
40328
40379
  const handleSubmit = React91.useCallback(() => {
40329
- setSubmitted(true);
40330
- setAttempts((a) => a + 1);
40380
+ if (checkEvent) {
40381
+ emit(`UI:${checkEvent}`, {});
40382
+ }
40331
40383
  const correct = correctFlags.length === bugLines.length && falseFlags.length === 0;
40332
40384
  if (correct) {
40333
40385
  emit(`UI:${completeEvent}`, { success: true, attempts: attempts + 1 });
40334
40386
  }
40335
- }, [correctFlags.length, bugLines.length, falseFlags.length, attempts, completeEvent, emit]);
40387
+ }, [checkEvent, correctFlags.length, bugLines.length, falseFlags.length, attempts, completeEvent, emit]);
40336
40388
  const handleReset = () => {
40337
- setSubmitted(false);
40389
+ if (playAgainEvent) {
40390
+ emit(`UI:${playAgainEvent}`, {});
40391
+ }
40338
40392
  if (attempts >= 2 && str(resolved?.hint)) {
40339
40393
  setShowHint(true);
40340
40394
  }
40341
40395
  };
40342
40396
  const handleFullReset = () => {
40343
- setFlaggedLines(/* @__PURE__ */ new Set());
40344
- setSubmitted(false);
40345
- setAttempts(0);
40397
+ if (playAgainEvent) {
40398
+ emit(`UI:${playAgainEvent}`, {});
40399
+ }
40346
40400
  setShowHint(false);
40347
40401
  };
40348
40402
  if (!resolved) return null;
@@ -40370,7 +40424,7 @@ function DebuggerBoard({
40370
40424
  /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", className: "text-muted-foreground", children: t("debugger.findBugs", { count: String(num(resolved.bugCount)) }) })
40371
40425
  ] }) }),
40372
40426
  /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "p-0 overflow-hidden", children: /* @__PURE__ */ jsxRuntime.jsx(VStack, { gap: "none", children: lines.map((line, i) => {
40373
- const isFlagged = flaggedLines.has(line.id);
40427
+ const isFlagged = !!line.isFlagged;
40374
40428
  let lineStyle = "";
40375
40429
  if (submitted) {
40376
40430
  if (line.isBug && isFlagged) lineStyle = "bg-success/10";
@@ -40404,9 +40458,9 @@ function DebuggerBoard({
40404
40458
  /* @__PURE__ */ jsxRuntime.jsx(
40405
40459
  Icon,
40406
40460
  {
40407
- icon: flaggedLines.has(line.id) ? LucideIcons2.CheckCircle : LucideIcons2.XCircle,
40461
+ icon: line.isFlagged ? LucideIcons2.CheckCircle : LucideIcons2.XCircle,
40408
40462
  size: "xs",
40409
- className: flaggedLines.has(line.id) ? "text-success mt-0.5" : "text-warning mt-0.5"
40463
+ className: line.isFlagged ? "text-success mt-0.5" : "text-warning mt-0.5"
40410
40464
  }
40411
40465
  ),
40412
40466
  /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "none", children: [
@@ -40417,7 +40471,7 @@ function DebuggerBoard({
40417
40471
  ] }) }),
40418
40472
  showHint && hint && /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", children: hint }) }),
40419
40473
  /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "sm", justify: "center", children: [
40420
- !submitted ? /* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "primary", onClick: handleSubmit, disabled: flaggedLines.size === 0, children: [
40474
+ !submitted ? /* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "primary", onClick: handleSubmit, disabled: flaggedLines.length === 0, children: [
40421
40475
  /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: LucideIcons2.Send, size: "sm" }),
40422
40476
  t("debugger.submit")
40423
40477
  ] }) : !allCorrect ? /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "primary", onClick: handleReset, children: t("debugger.tryAgain") }) : null,
@@ -44181,6 +44235,9 @@ function getOpponentAction(strategy, actions, history) {
44181
44235
  function NegotiatorBoard({
44182
44236
  entity,
44183
44237
  completeEvent = "PUZZLE_COMPLETE",
44238
+ playRoundEvent,
44239
+ finishEvent,
44240
+ playAgainEvent,
44184
44241
  className
44185
44242
  }) {
44186
44243
  const { emit } = useEventBus();
@@ -44189,13 +44246,14 @@ function NegotiatorBoard({
44189
44246
  const [history, setHistory] = React91.useState([]);
44190
44247
  const [headerError, setHeaderError] = React91.useState(false);
44191
44248
  const [showHint, setShowHint] = React91.useState(false);
44192
- const totalRounds = num(resolved?.totalRounds);
44249
+ const totalRounds = num(resolved?.maxRounds);
44193
44250
  const targetScore = num(resolved?.targetScore);
44194
- const currentRound = history.length;
44195
- const isComplete = currentRound >= totalRounds;
44196
- const playerTotal = history.reduce((s, r2) => s + r2.playerPayoff, 0);
44251
+ const currentRound = num(resolved?.round);
44252
+ const result = str(resolved?.result) || "none";
44253
+ const playerTotal = num(resolved?.score);
44254
+ const isComplete = result !== "none" || totalRounds > 0 && currentRound >= totalRounds;
44255
+ const won = result === "win";
44197
44256
  const opponentTotal = history.reduce((s, r2) => s + r2.opponentPayoff, 0);
44198
- const won = isComplete && playerTotal >= targetScore;
44199
44257
  const actions = Array.isArray(resolved?.actions) ? resolved.actions : [];
44200
44258
  const payoffMatrix = Array.isArray(resolved?.payoffMatrix) ? resolved.payoffMatrix : [];
44201
44259
  const handleAction = React91.useCallback((actionId) => {
@@ -44204,29 +44262,45 @@ function NegotiatorBoard({
44204
44262
  const payoff = payoffMatrix.find(
44205
44263
  (p2) => p2.playerAction === actionId && p2.opponentAction === opponentAction
44206
44264
  );
44207
- const result = {
44208
- round: currentRound + 1,
44209
- playerAction: actionId,
44210
- opponentAction,
44211
- playerPayoff: payoff?.playerPayoff ?? 0,
44212
- opponentPayoff: payoff?.opponentPayoff ?? 0
44213
- };
44214
- const newHistory = [...history, result];
44215
- setHistory(newHistory);
44216
- if (newHistory.length >= totalRounds) {
44217
- const total = newHistory.reduce((s, r2) => s + r2.playerPayoff, 0);
44218
- if (total >= targetScore) {
44219
- emit(`UI:${completeEvent}`, { success: true, score: total });
44220
- }
44221
- if (newHistory.length >= 3 && str(resolved?.hint)) {
44265
+ const playerPayoff = payoff?.playerPayoff ?? 0;
44266
+ setHistory((prev) => [
44267
+ ...prev,
44268
+ {
44269
+ round: prev.length + 1,
44270
+ playerAction: actionId,
44271
+ opponentAction,
44272
+ playerPayoff,
44273
+ opponentPayoff: payoff?.opponentPayoff ?? 0
44274
+ }
44275
+ ]);
44276
+ if (playRoundEvent) {
44277
+ emit(`UI:${playRoundEvent}`, { playerAction: actionId, payoff: playerPayoff });
44278
+ }
44279
+ if (totalRounds > 0 && currentRound + 1 >= totalRounds) {
44280
+ if (finishEvent) {
44281
+ emit(`UI:${finishEvent}`, {});
44282
+ }
44283
+ if (str(resolved?.hint)) {
44222
44284
  setShowHint(true);
44223
44285
  }
44224
44286
  }
44225
- }, [isComplete, resolved, totalRounds, targetScore, actions, payoffMatrix, history, currentRound, completeEvent, emit]);
44226
- const handleReset = () => {
44287
+ }, [isComplete, resolved, totalRounds, currentRound, actions, payoffMatrix, history, playRoundEvent, finishEvent, emit]);
44288
+ const handleReset = React91.useCallback(() => {
44227
44289
  setHistory([]);
44228
44290
  setShowHint(false);
44229
- };
44291
+ if (playAgainEvent) {
44292
+ emit(`UI:${playAgainEvent}`, {});
44293
+ }
44294
+ }, [playAgainEvent, emit]);
44295
+ const completedRef = React91.useRef(false);
44296
+ React91.useEffect(() => {
44297
+ if (result === "win" && !completedRef.current) {
44298
+ completedRef.current = true;
44299
+ emit(`UI:${completeEvent}`, { success: true, score: playerTotal });
44300
+ } else if (result === "none") {
44301
+ completedRef.current = false;
44302
+ }
44303
+ }, [result, playerTotal, completeEvent, emit]);
44230
44304
  const getActionLabel = (id) => actions.find((a) => a.id === id)?.label ?? id;
44231
44305
  if (!resolved) return null;
44232
44306
  const theme = resolved.theme ?? void 0;
@@ -47192,67 +47266,47 @@ var init_SimulationGraph = __esm({
47192
47266
  function SimulatorBoard({
47193
47267
  entity,
47194
47268
  completeEvent = "PUZZLE_COMPLETE",
47269
+ setAEvent,
47270
+ setBEvent,
47271
+ checkEvent,
47272
+ playAgainEvent,
47195
47273
  className
47196
47274
  }) {
47197
47275
  const { emit } = useEventBus();
47198
47276
  const { t } = hooks.useTranslate();
47199
47277
  const resolved = boardEntity(entity);
47200
47278
  const parameters = Array.isArray(resolved?.parameters) ? resolved.parameters : [];
47201
- const [values, setValues] = React91.useState(() => {
47202
- const init = {};
47203
- for (const p2 of parameters) {
47204
- init[p2.id] = p2.initial;
47205
- }
47206
- return init;
47207
- });
47208
47279
  const [headerError, setHeaderError] = React91.useState(false);
47209
- const [submitted, setSubmitted] = React91.useState(false);
47210
- const [attempts, setAttempts] = React91.useState(0);
47211
- const [showHint, setShowHint] = React91.useState(false);
47212
- const computeOutput = React91.useCallback((params) => {
47213
- try {
47214
- const fn = new Function("params", `return (${str(resolved?.computeExpression)})`);
47215
- return fn(params);
47216
- } catch {
47217
- return 0;
47218
- }
47219
- }, [resolved?.computeExpression]);
47220
- const output = React91.useMemo(() => computeOutput(values) ?? 0, [computeOutput, values]);
47221
- const targetValue = num(resolved?.targetValue);
47222
- const targetTolerance = num(resolved?.targetTolerance);
47223
- const isCorrect = Math.abs(output - targetValue) <= targetTolerance;
47224
- const handleParameterChange = (id, value) => {
47225
- if (submitted) return;
47226
- setValues((prev) => ({ ...prev, [id]: value }));
47227
- };
47228
- const handleSubmit = () => {
47229
- setSubmitted(true);
47230
- setAttempts((a) => a + 1);
47231
- if (isCorrect) {
47232
- emit(`UI:${completeEvent}`, { success: true, attempts: attempts + 1 });
47233
- }
47280
+ if (!resolved) return null;
47281
+ const paramA = num(resolved.paramA);
47282
+ const paramB = num(resolved.paramB);
47283
+ const output = num(resolved.output);
47284
+ const targetValue = num(resolved.target);
47285
+ const targetTolerance = num(resolved.tolerance);
47286
+ const attempts = num(resolved.attempts);
47287
+ const result = str(resolved.result);
47288
+ const isWin = result === "win";
47289
+ const isComplete = result !== "none" && result !== "";
47290
+ const paramAValue = parameters[0];
47291
+ const paramBValue = parameters[1];
47292
+ const sliderValues = [paramA, paramB];
47293
+ const sliderEvents = [setAEvent, setBEvent];
47294
+ const handleParameterChange = (index, value) => {
47295
+ if (isComplete) return;
47296
+ const ev = sliderEvents[index];
47297
+ if (ev) emit(`UI:${ev}`, { value });
47234
47298
  };
47235
- const handleReset = () => {
47236
- setSubmitted(false);
47237
- if (attempts >= 2 && str(resolved?.hint)) {
47238
- setShowHint(true);
47239
- }
47299
+ const handleCheck = () => {
47300
+ if (checkEvent) emit(`UI:${checkEvent}`, {});
47240
47301
  };
47241
- const handleFullReset = () => {
47242
- const init = {};
47243
- for (const p2 of parameters) {
47244
- init[p2.id] = p2.initial;
47245
- }
47246
- setValues(init);
47247
- setSubmitted(false);
47248
- setAttempts(0);
47249
- setShowHint(false);
47302
+ const handlePlayAgain = () => {
47303
+ if (playAgainEvent) emit(`UI:${playAgainEvent}`, {});
47250
47304
  };
47251
- if (!resolved) return null;
47252
47305
  const theme = resolved.theme ?? void 0;
47253
47306
  const themeBackground = theme?.background;
47254
47307
  const headerImage = str(resolved.headerImage);
47255
47308
  const hint = str(resolved.hint);
47309
+ const showHint = isComplete && !isWin && attempts >= 2 && Boolean(hint);
47256
47310
  const outputLabel = str(resolved.outputLabel);
47257
47311
  const outputUnit = str(resolved.outputUnit);
47258
47312
  return /* @__PURE__ */ jsxRuntime.jsx(
@@ -47272,41 +47326,43 @@ function SimulatorBoard({
47272
47326
  ] }) }),
47273
47327
  /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "md", children: [
47274
47328
  /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", weight: "bold", className: "uppercase tracking-wider text-muted-foreground", children: t("simulator.parameters") }),
47275
- parameters.map((param) => /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "xs", children: [
47276
- /* @__PURE__ */ jsxRuntime.jsxs(HStack, { justify: "between", align: "center", children: [
47277
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", weight: "medium", children: param.label }),
47278
- /* @__PURE__ */ jsxRuntime.jsxs(Badge, { size: "sm", children: [
47279
- values[param.id],
47280
- " ",
47281
- param.unit
47282
- ] })
47283
- ] }),
47284
- /* @__PURE__ */ jsxRuntime.jsx(
47285
- "input",
47286
- {
47287
- type: "range",
47288
- min: param.min,
47289
- max: param.max,
47290
- step: param.step,
47291
- value: values[param.id],
47292
- onChange: (e) => handleParameterChange(param.id, Number(e.target.value)),
47293
- disabled: submitted,
47294
- className: "w-full accent-foreground"
47295
- }
47296
- ),
47297
- /* @__PURE__ */ jsxRuntime.jsxs(HStack, { justify: "between", children: [
47298
- /* @__PURE__ */ jsxRuntime.jsxs(Typography, { variant: "caption", className: "text-muted-foreground", children: [
47299
- param.min,
47300
- " ",
47301
- param.unit
47329
+ [paramAValue, paramBValue].map(
47330
+ (param, index) => param ? /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "xs", children: [
47331
+ /* @__PURE__ */ jsxRuntime.jsxs(HStack, { justify: "between", align: "center", children: [
47332
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", weight: "medium", children: param.label }),
47333
+ /* @__PURE__ */ jsxRuntime.jsxs(Badge, { size: "sm", children: [
47334
+ sliderValues[index],
47335
+ " ",
47336
+ param.unit
47337
+ ] })
47302
47338
  ] }),
47303
- /* @__PURE__ */ jsxRuntime.jsxs(Typography, { variant: "caption", className: "text-muted-foreground", children: [
47304
- param.max,
47305
- " ",
47306
- param.unit
47339
+ /* @__PURE__ */ jsxRuntime.jsx(
47340
+ "input",
47341
+ {
47342
+ type: "range",
47343
+ min: param.min,
47344
+ max: param.max,
47345
+ step: param.step,
47346
+ value: sliderValues[index],
47347
+ onChange: (e) => handleParameterChange(index, Number(e.target.value)),
47348
+ disabled: isComplete,
47349
+ className: "w-full accent-foreground"
47350
+ }
47351
+ ),
47352
+ /* @__PURE__ */ jsxRuntime.jsxs(HStack, { justify: "between", children: [
47353
+ /* @__PURE__ */ jsxRuntime.jsxs(Typography, { variant: "caption", className: "text-muted-foreground", children: [
47354
+ param.min,
47355
+ " ",
47356
+ param.unit
47357
+ ] }),
47358
+ /* @__PURE__ */ jsxRuntime.jsxs(Typography, { variant: "caption", className: "text-muted-foreground", children: [
47359
+ param.max,
47360
+ " ",
47361
+ param.unit
47362
+ ] })
47307
47363
  ] })
47308
- ] })
47309
- ] }, param.id))
47364
+ ] }, param.id ?? index) : null
47365
+ )
47310
47366
  ] }) }),
47311
47367
  /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "sm", align: "center", children: [
47312
47368
  /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", weight: "bold", className: "uppercase tracking-wider text-muted-foreground", children: outputLabel }),
@@ -47315,9 +47371,9 @@ function SimulatorBoard({
47315
47371
  " ",
47316
47372
  outputUnit
47317
47373
  ] }),
47318
- submitted && /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "xs", align: "center", children: [
47319
- /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: isCorrect ? LucideIcons2.CheckCircle : LucideIcons2.XCircle, size: "sm", className: isCorrect ? "text-success" : "text-error" }),
47320
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", className: isCorrect ? "text-success" : "text-error", children: isCorrect ? str(resolved.successMessage) || t("simulator.correct") : str(resolved.failMessage) || t("simulator.incorrect") })
47374
+ isComplete && /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "xs", align: "center", children: [
47375
+ /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: isWin ? LucideIcons2.CheckCircle : LucideIcons2.XCircle, size: "sm", className: isWin ? "text-success" : "text-error" }),
47376
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", className: isWin ? "text-success" : "text-error", children: isWin ? str(resolved.successMessage) || t("simulator.correct") : str(resolved.failMessage) || t("simulator.incorrect") })
47321
47377
  ] }),
47322
47378
  /* @__PURE__ */ jsxRuntime.jsxs(Typography, { variant: "caption", className: "text-muted-foreground", children: [
47323
47379
  t("simulator.target"),
@@ -47332,11 +47388,11 @@ function SimulatorBoard({
47332
47388
  ] }) }),
47333
47389
  showHint && hint && /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", children: hint }) }),
47334
47390
  /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "sm", justify: "center", children: [
47335
- !submitted ? /* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "primary", onClick: handleSubmit, children: [
47391
+ !isComplete ? /* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "primary", onClick: handleCheck, children: [
47336
47392
  /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: LucideIcons2.Play, size: "sm" }),
47337
47393
  t("simulator.simulate")
47338
- ] }) : !isCorrect ? /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "primary", onClick: handleReset, children: t("simulator.tryAgain") }) : null,
47339
- /* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "secondary", onClick: handleFullReset, children: [
47394
+ ] }) : null,
47395
+ /* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "secondary", onClick: handlePlayAgain, children: [
47340
47396
  /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: LucideIcons2.RotateCcw, size: "sm" }),
47341
47397
  t("simulator.reset")
47342
47398
  ] })