@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 reactDom = require('react-dom');
14
11
  var hooks = require('@almadar/ui/hooks');
15
12
  var evaluator = require('@almadar/evaluator');
@@ -70,9 +67,6 @@ function _interopNamespace(e) {
70
67
 
71
68
  var React82__namespace = /*#__PURE__*/_interopNamespace(React82);
72
69
  var LucideIcons2__namespace = /*#__PURE__*/_interopNamespace(LucideIcons2);
73
- var PhosphorIcons__namespace = /*#__PURE__*/_interopNamespace(PhosphorIcons);
74
- var TablerIcons__namespace = /*#__PURE__*/_interopNamespace(TablerIcons);
75
- var FaIcons__namespace = /*#__PURE__*/_interopNamespace(FaIcons);
76
70
  var ELK__default = /*#__PURE__*/_interopDefault(ELK);
77
71
  var SyntaxHighlighter__default = /*#__PURE__*/_interopDefault(SyntaxHighlighter);
78
72
  var dark__default = /*#__PURE__*/_interopDefault(dark);
@@ -578,6 +572,41 @@ function kebabToPascal(name) {
578
572
  return part.charAt(0).toUpperCase() + part.slice(1);
579
573
  }).join("");
580
574
  }
575
+ function loadLib(key, importer) {
576
+ let p2 = libPromises.get(key);
577
+ if (!p2) {
578
+ p2 = importer().then((m) => m);
579
+ libPromises.set(key, p2);
580
+ }
581
+ return p2;
582
+ }
583
+ function lazyFamilyIcon(libKey, importer, pick, fallbackName, family) {
584
+ const Lazy = React82__namespace.default.lazy(async () => {
585
+ const lib = await loadLib(libKey, importer);
586
+ const Comp = pick(lib);
587
+ if (!Comp) {
588
+ warnFallback(fallbackName, family);
589
+ return { default: makeLucideAdapter(fallbackName, true) };
590
+ }
591
+ return { default: Comp };
592
+ });
593
+ const Wrapped = (props) => /* @__PURE__ */ jsxRuntime.jsx(
594
+ React82__namespace.default.Suspense,
595
+ {
596
+ fallback: /* @__PURE__ */ jsxRuntime.jsx(
597
+ "span",
598
+ {
599
+ "aria-hidden": true,
600
+ className: props.className,
601
+ style: { display: "inline-block", ...props.style }
602
+ }
603
+ ),
604
+ children: /* @__PURE__ */ jsxRuntime.jsx(Lazy, { ...props })
605
+ }
606
+ );
607
+ Wrapped.displayName = `Lazy.${libKey}.${fallbackName}`;
608
+ return Wrapped;
609
+ }
581
610
  function resolveLucide(name) {
582
611
  if (lucideAliases[name]) return lucideAliases[name];
583
612
  const pascal = kebabToPascal(name);
@@ -588,60 +617,81 @@ function resolveLucide(name) {
588
617
  if (asIs && typeof asIs === "object") return asIs;
589
618
  return LucideIcons2__namespace.HelpCircle;
590
619
  }
591
- function resolvePhosphor(name, weight) {
620
+ function resolvePhosphor(name, weight, family) {
592
621
  const target = phosphorAliases[name] ?? kebabToPascal(name);
593
- const map = PhosphorIcons__namespace;
594
- const PhosphorComp = map[target];
595
- if (!PhosphorComp || typeof PhosphorComp !== "object") return null;
596
- const Component = PhosphorComp;
597
- const Adapter = (props) => /* @__PURE__ */ jsxRuntime.jsx(
598
- Component,
599
- {
600
- weight,
601
- className: props.className,
602
- style: props.style,
603
- size: props.size ?? "1em"
604
- }
622
+ return lazyFamilyIcon(
623
+ "phosphor",
624
+ () => import('@phosphor-icons/react'),
625
+ (lib) => {
626
+ const PhosphorComp = lib[target];
627
+ if (!PhosphorComp || typeof PhosphorComp !== "object") return null;
628
+ const Component = PhosphorComp;
629
+ const Adapter = (props) => /* @__PURE__ */ jsxRuntime.jsx(
630
+ Component,
631
+ {
632
+ weight,
633
+ className: props.className,
634
+ style: props.style,
635
+ size: props.size ?? "1em"
636
+ }
637
+ );
638
+ Adapter.displayName = `Phosphor.${target}.${weight}`;
639
+ return Adapter;
640
+ },
641
+ name,
642
+ family
605
643
  );
606
- Adapter.displayName = `Phosphor.${target}.${weight}`;
607
- return Adapter;
608
644
  }
609
- function resolveTabler(name) {
645
+ function resolveTabler(name, family) {
610
646
  const suffix = tablerAliases[name] ?? kebabToPascal(name);
611
647
  const target = `Icon${suffix}`;
612
- const map = TablerIcons__namespace;
613
- const TablerComp = map[target];
614
- if (!TablerComp || typeof TablerComp !== "object") return null;
615
- const Component = TablerComp;
616
- const Adapter = (props) => /* @__PURE__ */ jsxRuntime.jsx(
617
- Component,
618
- {
619
- stroke: props.strokeWidth ?? 1.5,
620
- className: props.className,
621
- style: props.style,
622
- size: props.size ?? 24
623
- }
648
+ return lazyFamilyIcon(
649
+ "tabler",
650
+ () => import('@tabler/icons-react'),
651
+ (lib) => {
652
+ const TablerComp = lib[target];
653
+ if (!TablerComp || typeof TablerComp !== "object") return null;
654
+ const Component = TablerComp;
655
+ const Adapter = (props) => /* @__PURE__ */ jsxRuntime.jsx(
656
+ Component,
657
+ {
658
+ stroke: props.strokeWidth ?? 1.5,
659
+ className: props.className,
660
+ style: props.style,
661
+ size: props.size ?? 24
662
+ }
663
+ );
664
+ Adapter.displayName = `Tabler.${target}`;
665
+ return Adapter;
666
+ },
667
+ name,
668
+ family
624
669
  );
625
- Adapter.displayName = `Tabler.${target}`;
626
- return Adapter;
627
670
  }
628
- function resolveFa(name) {
671
+ function resolveFa(name, family) {
629
672
  const suffix = faAliases[name] ?? kebabToPascal(name);
630
673
  const target = `Fa${suffix}`;
631
- const map = FaIcons__namespace;
632
- const FaComp = map[target];
633
- if (!FaComp || typeof FaComp !== "function") return null;
634
- const Component = FaComp;
635
- const Adapter = (props) => /* @__PURE__ */ jsxRuntime.jsx(
636
- Component,
637
- {
638
- className: props.className,
639
- style: props.style,
640
- size: props.size ?? "1em"
641
- }
674
+ return lazyFamilyIcon(
675
+ "fa",
676
+ () => import('react-icons/fa'),
677
+ (lib) => {
678
+ const FaComp = lib[target];
679
+ if (!FaComp || typeof FaComp !== "function") return null;
680
+ const Component = FaComp;
681
+ const Adapter = (props) => /* @__PURE__ */ jsxRuntime.jsx(
682
+ Component,
683
+ {
684
+ className: props.className,
685
+ style: props.style,
686
+ size: props.size ?? "1em"
687
+ }
688
+ );
689
+ Adapter.displayName = `Fa.${target}`;
690
+ return Adapter;
691
+ },
692
+ name,
693
+ family
642
694
  );
643
- Adapter.displayName = `Fa.${target}`;
644
- return Adapter;
645
695
  }
646
696
  function warnFallback(name, family) {
647
697
  const key = `${family}::${name}`;
@@ -675,39 +725,22 @@ function resolveIconForFamily(name, family) {
675
725
  switch (family) {
676
726
  case "lucide":
677
727
  return makeLucideAdapter(name, false);
678
- case "phosphor-outline": {
679
- const p2 = resolvePhosphor(name, "regular");
680
- if (p2) return p2;
681
- warnFallback(name, family);
682
- return makeLucideAdapter(name, true);
683
- }
684
- case "phosphor-fill": {
685
- const p2 = resolvePhosphor(name, "fill");
686
- if (p2) return p2;
687
- warnFallback(name, family);
688
- return makeLucideAdapter(name, true);
689
- }
690
- case "phosphor-duotone": {
691
- const p2 = resolvePhosphor(name, "duotone");
692
- if (p2) return p2;
693
- warnFallback(name, family);
694
- return makeLucideAdapter(name, true);
695
- }
696
- case "tabler": {
697
- const t = resolveTabler(name);
698
- if (t) return t;
699
- warnFallback(name, family);
700
- return makeLucideAdapter(name, true);
701
- }
702
- case "fa-solid": {
703
- const f3 = resolveFa(name);
704
- if (f3) return f3;
705
- warnFallback(name, family);
706
- return makeLucideAdapter(name, true);
707
- }
708
- }
709
- }
710
- var DEFAULT_FAMILY, VALID_FAMILIES, cachedFamily, listeners, observer, lucideAliases, phosphorAliases, tablerAliases, faAliases, warned;
728
+ // Non-lucide families resolve to a lazy, Suspense-wrapped component that
729
+ // dynamic-imports the library on first render and falls back to lucide
730
+ // internally when the family lacks the icon (see lazyFamilyIcon).
731
+ case "phosphor-outline":
732
+ return resolvePhosphor(name, "regular", family);
733
+ case "phosphor-fill":
734
+ return resolvePhosphor(name, "fill", family);
735
+ case "phosphor-duotone":
736
+ return resolvePhosphor(name, "duotone", family);
737
+ case "tabler":
738
+ return resolveTabler(name, family);
739
+ case "fa-solid":
740
+ return resolveFa(name, family);
741
+ }
742
+ }
743
+ var DEFAULT_FAMILY, VALID_FAMILIES, cachedFamily, listeners, observer, libPromises, lucideAliases, phosphorAliases, tablerAliases, faAliases, warned;
711
744
  var init_iconFamily = __esm({
712
745
  "lib/iconFamily.tsx"() {
713
746
  "use client";
@@ -723,6 +756,7 @@ var init_iconFamily = __esm({
723
756
  cachedFamily = null;
724
757
  listeners = /* @__PURE__ */ new Set();
725
758
  observer = null;
759
+ libPromises = /* @__PURE__ */ new Map();
726
760
  lucideAliases = {
727
761
  close: LucideIcons2__namespace.X,
728
762
  trash: LucideIcons2__namespace.Trash2,
@@ -16988,61 +17022,53 @@ var init_Breadcrumb = __esm({
16988
17022
  function BuilderBoard({
16989
17023
  entity,
16990
17024
  completeEvent = "PUZZLE_COMPLETE",
17025
+ placeEvent,
17026
+ checkEvent,
17027
+ playAgainEvent,
16991
17028
  className
16992
17029
  }) {
16993
17030
  const { emit } = useEventBus();
16994
17031
  const { t } = hooks.useTranslate();
16995
17032
  const resolved = boardEntity(entity);
16996
- const [placements, setPlacements] = React82.useState({});
16997
17033
  const [headerError, setHeaderError] = React82.useState(false);
16998
- const [submitted, setSubmitted] = React82.useState(false);
16999
- const [attempts, setAttempts] = React82.useState(0);
17000
- const [showHint, setShowHint] = React82.useState(false);
17034
+ const [selectedComponent, setSelectedComponent] = React82.useState(null);
17001
17035
  const components = Array.isArray(resolved?.components) ? resolved.components : [];
17002
17036
  const slots = Array.isArray(resolved?.slots) ? resolved.slots : [];
17037
+ const placements = {};
17038
+ for (const slot of slots) {
17039
+ if (slot.placedComponentId) placements[slot.id] = slot.placedComponentId;
17040
+ }
17041
+ const attempts = num(resolved?.attempts);
17042
+ const result = str(resolved?.result) || "none";
17043
+ const submitted = result === "win";
17003
17044
  const usedComponentIds = new Set(Object.values(placements));
17004
17045
  const availableComponents = components.filter((c) => !usedComponentIds.has(c.id));
17005
- const [selectedComponent, setSelectedComponent] = React82.useState(null);
17006
- const allPlaced = Object.keys(placements).length === slots.length;
17046
+ const allPlaced = slots.length > 0 && slots.every((s) => Boolean(placements[s.id]));
17007
17047
  const results = submitted ? slots.map((slot) => ({
17008
17048
  slot,
17009
17049
  placed: placements[slot.id],
17010
- correct: placements[slot.id] === slot.acceptsComponentId
17050
+ correct: placements[slot.id] === slot.requiredComponentId
17011
17051
  })) : [];
17012
- const allCorrect = results.length > 0 && results.every((r) => r.correct);
17052
+ const showHint = attempts >= 2 && Boolean(str(resolved?.hint));
17013
17053
  const handlePlaceComponent = (slotId) => {
17014
17054
  if (submitted || !selectedComponent) return;
17015
- setPlacements((prev) => ({ ...prev, [slotId]: selectedComponent }));
17055
+ if (placeEvent) emit(`UI:${placeEvent}`, { slotId, componentId: selectedComponent });
17016
17056
  setSelectedComponent(null);
17017
17057
  };
17018
17058
  const handleRemoveFromSlot = (slotId) => {
17019
17059
  if (submitted) return;
17020
- setPlacements((prev) => {
17021
- const next = { ...prev };
17022
- delete next[slotId];
17023
- return next;
17024
- });
17060
+ if (placeEvent) emit(`UI:${placeEvent}`, { slotId, componentId: "" });
17025
17061
  };
17026
- const handleSubmit = React82.useCallback(() => {
17027
- setSubmitted(true);
17028
- setAttempts((a) => a + 1);
17029
- const correct = slots.every((slot) => placements[slot.id] === slot.acceptsComponentId);
17030
- if (correct) {
17062
+ const handleSubmit = () => {
17063
+ if (checkEvent) emit(`UI:${checkEvent}`, {});
17064
+ const solved = slots.length > 0 && slots.every((s) => placements[s.id] === s.requiredComponentId);
17065
+ if (solved && completeEvent) {
17031
17066
  emit(`UI:${completeEvent}`, { success: true, attempts: attempts + 1 });
17032
17067
  }
17033
- }, [slots, placements, attempts, completeEvent, emit]);
17034
- const handleReset = () => {
17035
- setSubmitted(false);
17036
- if (attempts >= 2 && str(resolved?.hint)) {
17037
- setShowHint(true);
17038
- }
17039
17068
  };
17040
- const handleFullReset = () => {
17041
- setPlacements({});
17042
- setSubmitted(false);
17069
+ const handlePlayAgain = () => {
17043
17070
  setSelectedComponent(null);
17044
- setAttempts(0);
17045
- setShowHint(false);
17071
+ if (playAgainEvent) emit(`UI:${playAgainEvent}`, {});
17046
17072
  };
17047
17073
  const getComponentById = (id) => components.find((c) => c.id === id);
17048
17074
  if (!resolved) return null;
@@ -17092,13 +17118,13 @@ function BuilderBoard({
17092
17118
  /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", weight: "bold", className: "uppercase tracking-wider text-muted-foreground", children: t("builder.blueprint") }),
17093
17119
  /* @__PURE__ */ jsxRuntime.jsx(VStack, { gap: "sm", children: slots.map((slot) => {
17094
17120
  const placedComp = placements[slot.id] ? getComponentById(placements[slot.id]) : null;
17095
- const result = results.find((r) => r.slot.id === slot.id);
17121
+ const result2 = results.find((r) => r.slot.id === slot.id);
17096
17122
  return /* @__PURE__ */ jsxRuntime.jsxs(
17097
17123
  HStack,
17098
17124
  {
17099
17125
  gap: "sm",
17100
17126
  align: "center",
17101
- className: `p-3 border-2 rounded ${result ? result.correct ? "border-success" : "border-error" : selectedComponent ? "border-dashed border-foreground cursor-pointer" : "border-border"}`,
17127
+ className: `p-3 border-2 rounded ${result2 ? result2.correct ? "border-success" : "border-error" : selectedComponent ? "border-dashed border-foreground cursor-pointer" : "border-border"}`,
17102
17128
  onClick: () => handlePlaceComponent(slot.id),
17103
17129
  children: [
17104
17130
  /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "none", className: "flex-1", children: [
@@ -17113,7 +17139,7 @@ function BuilderBoard({
17113
17139
  ] }) : null,
17114
17140
  placedComp.label
17115
17141
  ] }),
17116
- result && /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: result.correct ? LucideIcons2.CheckCircle : LucideIcons2.XCircle, size: "sm", className: result.correct ? "text-success" : "text-error" })
17142
+ result2 && /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: result2.correct ? LucideIcons2.CheckCircle : LucideIcons2.XCircle, size: "sm", className: result2.correct ? "text-success" : "text-error" })
17117
17143
  ] }) : /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", className: "text-muted-foreground", children: t("builder.empty") })
17118
17144
  ]
17119
17145
  },
@@ -17122,16 +17148,16 @@ function BuilderBoard({
17122
17148
  }) })
17123
17149
  ] }) }),
17124
17150
  submitted && /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "sm", align: "center", children: [
17125
- /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: allCorrect ? LucideIcons2.CheckCircle : LucideIcons2.XCircle, size: "lg", className: allCorrect ? "text-success" : "text-error" }),
17126
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", weight: "bold", children: allCorrect ? str(resolved.successMessage) || t("builder.success") : str(resolved.failMessage) || t("builder.incorrect") })
17151
+ /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: LucideIcons2.CheckCircle, size: "lg", className: "text-success" }),
17152
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", weight: "bold", children: str(resolved.successMessage) || t("builder.success") })
17127
17153
  ] }) }),
17128
17154
  showHint && hint && /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", children: hint }) }),
17129
17155
  /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "sm", justify: "center", children: [
17130
- !submitted ? /* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "primary", onClick: handleSubmit, disabled: !allPlaced, children: [
17156
+ !submitted && /* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "primary", onClick: handleSubmit, disabled: !allPlaced, children: [
17131
17157
  /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: LucideIcons2.Wrench, size: "sm" }),
17132
17158
  t("builder.build")
17133
- ] }) : !allCorrect ? /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "primary", onClick: handleReset, children: t("builder.tryAgain") }) : null,
17134
- /* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "secondary", onClick: handleFullReset, children: [
17159
+ ] }),
17160
+ /* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "secondary", onClick: handlePlayAgain, children: [
17135
17161
  /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: LucideIcons2.RotateCcw, size: "sm" }),
17136
17162
  t("builder.reset")
17137
17163
  ] })
@@ -20587,57 +20613,84 @@ var init_ChartLegend = __esm({
20587
20613
  function ClassifierBoard({
20588
20614
  entity,
20589
20615
  completeEvent = "PUZZLE_COMPLETE",
20616
+ assignEvent,
20617
+ checkEvent,
20618
+ playAgainEvent,
20590
20619
  className
20591
20620
  }) {
20592
20621
  const { emit } = useEventBus();
20593
20622
  const { t } = hooks.useTranslate();
20594
20623
  const resolved = boardEntity(entity);
20595
- const [assignments, setAssignments] = React82.useState({});
20624
+ const [localAssignments, setLocalAssignments] = React82.useState({});
20596
20625
  const [headerError, setHeaderError] = React82.useState(false);
20597
- const [submitted, setSubmitted] = React82.useState(false);
20598
- const [attempts, setAttempts] = React82.useState(0);
20626
+ const [localSubmitted, setLocalSubmitted] = React82.useState(false);
20627
+ const [localAttempts, setLocalAttempts] = React82.useState(0);
20599
20628
  const [showHint, setShowHint] = React82.useState(false);
20600
20629
  const items = Array.isArray(resolved?.items) ? resolved.items : [];
20601
20630
  const categories = Array.isArray(resolved?.categories) ? resolved.categories : [];
20631
+ const entityResult = str(resolved?.result);
20632
+ const entityDrivesResult = entityResult.length > 0;
20633
+ const entityHasAssignments = items.some((item) => item.assignedCategory != null);
20634
+ const assignments = entityHasAssignments ? items.reduce((acc, item) => {
20635
+ if (item.assignedCategory != null) acc[item.id] = item.assignedCategory;
20636
+ return acc;
20637
+ }, {}) : localAssignments;
20638
+ const attempts = entityDrivesResult ? num(resolved?.attempts) : localAttempts;
20639
+ const submitted = entityDrivesResult || localSubmitted;
20602
20640
  const unassignedItems = items.filter((item) => !assignments[item.id]);
20603
- const allAssigned = Object.keys(assignments).length === items.length;
20641
+ const allAssigned = items.length > 0 && Object.keys(assignments).length === items.length;
20604
20642
  const results = submitted ? items.map((item) => ({
20605
20643
  item,
20606
20644
  assigned: assignments[item.id],
20607
20645
  correct: assignments[item.id] === item.correctCategory
20608
20646
  })) : [];
20609
- const allCorrect = results.length > 0 && results.every((r) => r.correct);
20647
+ const allCorrect = entityDrivesResult ? entityResult === "success" : results.length > 0 && results.every((r) => r.correct);
20610
20648
  const correctCount = results.filter((r) => r.correct).length;
20611
20649
  const handleAssign = (itemId, categoryId) => {
20612
20650
  if (submitted) return;
20613
- setAssignments((prev) => ({ ...prev, [itemId]: categoryId }));
20651
+ if (assignEvent) {
20652
+ emit(`UI:${assignEvent}`, { itemId, categoryId });
20653
+ }
20654
+ if (!entityHasAssignments) {
20655
+ setLocalAssignments((prev) => ({ ...prev, [itemId]: categoryId }));
20656
+ }
20614
20657
  };
20615
20658
  const handleUnassign = (itemId) => {
20616
20659
  if (submitted) return;
20617
- setAssignments((prev) => {
20618
- const next = { ...prev };
20619
- delete next[itemId];
20620
- return next;
20621
- });
20660
+ if (!entityHasAssignments) {
20661
+ setLocalAssignments((prev) => {
20662
+ const next = { ...prev };
20663
+ delete next[itemId];
20664
+ return next;
20665
+ });
20666
+ }
20622
20667
  };
20623
20668
  const handleSubmit = React82.useCallback(() => {
20624
- setSubmitted(true);
20625
- setAttempts((a) => a + 1);
20626
- const correct = items.every((item) => assignments[item.id] === item.correctCategory);
20627
- if (correct) {
20628
- emit(`UI:${completeEvent}`, { success: true, attempts: attempts + 1 });
20669
+ if (checkEvent) {
20670
+ emit(`UI:${checkEvent}`, {});
20671
+ }
20672
+ if (!entityDrivesResult) {
20673
+ setLocalSubmitted(true);
20674
+ setLocalAttempts((a) => a + 1);
20675
+ const correct = items.every((item) => assignments[item.id] === item.correctCategory);
20676
+ if (correct) {
20677
+ emit(`UI:${completeEvent}`, { success: true, attempts: attempts + 1 });
20678
+ }
20629
20679
  }
20630
- }, [items, assignments, attempts, completeEvent, emit]);
20680
+ }, [checkEvent, entityDrivesResult, items, assignments, attempts, completeEvent, emit]);
20631
20681
  const handleReset = () => {
20632
- setSubmitted(false);
20682
+ if (!entityDrivesResult) setLocalSubmitted(false);
20633
20683
  if (attempts >= 2 && str(resolved?.hint)) {
20634
20684
  setShowHint(true);
20635
20685
  }
20636
20686
  };
20637
20687
  const handleFullReset = () => {
20638
- setAssignments({});
20639
- setSubmitted(false);
20640
- setAttempts(0);
20688
+ if (playAgainEvent) {
20689
+ emit(`UI:${playAgainEvent}`, {});
20690
+ }
20691
+ setLocalAssignments({});
20692
+ setLocalSubmitted(false);
20693
+ setLocalAttempts(0);
20641
20694
  setShowHint(false);
20642
20695
  };
20643
20696
  if (!resolved) return null;
@@ -28520,13 +28573,13 @@ var init_MapView = __esm({
28520
28573
  shadowSize: [41, 41]
28521
28574
  });
28522
28575
  L.Marker.prototype.options.icon = defaultIcon;
28523
- const { useEffect: useEffect73, useRef: useRef67, useCallback: useCallback113, useState: useState107 } = React82__namespace.default;
28576
+ const { useEffect: useEffect74, useRef: useRef68, useCallback: useCallback111, useState: useState107 } = React82__namespace.default;
28524
28577
  const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
28525
28578
  const { useEventBus: useEventBus3 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
28526
28579
  function MapUpdater({ centerLat, centerLng, zoom }) {
28527
28580
  const map = useMap();
28528
- const prevRef = useRef67({ centerLat, centerLng, zoom });
28529
- useEffect73(() => {
28581
+ const prevRef = useRef68({ centerLat, centerLng, zoom });
28582
+ useEffect74(() => {
28530
28583
  const prev = prevRef.current;
28531
28584
  if (prev.centerLat !== centerLat || prev.centerLng !== centerLng || prev.zoom !== zoom) {
28532
28585
  map.setView([centerLat, centerLng], zoom);
@@ -28537,7 +28590,7 @@ var init_MapView = __esm({
28537
28590
  }
28538
28591
  function MapClickHandler({ onMapClick }) {
28539
28592
  const map = useMap();
28540
- useEffect73(() => {
28593
+ useEffect74(() => {
28541
28594
  if (!onMapClick) return;
28542
28595
  const handler = (e) => {
28543
28596
  onMapClick(e.latlng.lat, e.latlng.lng);
@@ -28566,7 +28619,7 @@ var init_MapView = __esm({
28566
28619
  }) {
28567
28620
  const eventBus = useEventBus3();
28568
28621
  const [clickedPosition, setClickedPosition] = useState107(null);
28569
- const handleMapClick = useCallback113((lat, lng) => {
28622
+ const handleMapClick = useCallback111((lat, lng) => {
28570
28623
  if (showClickedPin) {
28571
28624
  setClickedPosition({ lat, lng });
28572
28625
  }
@@ -28575,7 +28628,7 @@ var init_MapView = __esm({
28575
28628
  eventBus.emit(`UI:${mapClickEvent}`, { latitude: lat, longitude: lng });
28576
28629
  }
28577
28630
  }, [onMapClick, mapClickEvent, eventBus, showClickedPin]);
28578
- const handleMarkerClick = useCallback113((marker) => {
28631
+ const handleMarkerClick = useCallback111((marker) => {
28579
28632
  onMarkerClick?.(marker);
28580
28633
  if (markerClickEvent) {
28581
28634
  eventBus.emit(`UI:${markerClickEvent}`, { ...marker });
@@ -37797,51 +37850,52 @@ var init_DataTable = __esm({
37797
37850
  function DebuggerBoard({
37798
37851
  entity,
37799
37852
  completeEvent = "PUZZLE_COMPLETE",
37853
+ toggleFlagEvent,
37854
+ checkEvent,
37855
+ playAgainEvent,
37800
37856
  className
37801
37857
  }) {
37802
37858
  const { emit } = useEventBus();
37803
37859
  const { t } = hooks.useTranslate();
37804
37860
  const resolved = boardEntity(entity);
37805
- const [flaggedLines, setFlaggedLines] = React82.useState(/* @__PURE__ */ new Set());
37806
37861
  const [headerError, setHeaderError] = React82.useState(false);
37807
- const [submitted, setSubmitted] = React82.useState(false);
37808
- const [attempts, setAttempts] = React82.useState(0);
37809
37862
  const [showHint, setShowHint] = React82.useState(false);
37863
+ const lines = Array.isArray(resolved?.lines) ? resolved.lines : [];
37864
+ const result = resolved?.result ?? null;
37865
+ const attempts = num(resolved?.attempts);
37866
+ const submitted = result != null;
37867
+ const bugLines = lines.filter((l) => l.isBug);
37868
+ const flaggedLines = lines.filter((l) => l.isFlagged);
37869
+ const correctFlags = lines.filter((l) => l.isBug && l.isFlagged);
37870
+ const falseFlags = lines.filter((l) => !l.isBug && l.isFlagged);
37871
+ const allCorrect = result === "win";
37810
37872
  const toggleLine = (lineId) => {
37811
37873
  if (submitted) return;
37812
- setFlaggedLines((prev) => {
37813
- const next = new Set(prev);
37814
- if (next.has(lineId)) {
37815
- next.delete(lineId);
37816
- } else {
37817
- next.add(lineId);
37818
- }
37819
- return next;
37820
- });
37874
+ if (toggleFlagEvent) {
37875
+ emit(`UI:${toggleFlagEvent}`, { lineId });
37876
+ }
37821
37877
  };
37822
- const lines = Array.isArray(resolved?.lines) ? resolved.lines : [];
37823
- const bugLines = lines.filter((l) => l.isBug);
37824
- const correctFlags = lines.filter((l) => l.isBug && flaggedLines.has(l.id));
37825
- const falseFlags = lines.filter((l) => !l.isBug && flaggedLines.has(l.id));
37826
- const allCorrect = submitted && correctFlags.length === bugLines.length && falseFlags.length === 0;
37827
37878
  const handleSubmit = React82.useCallback(() => {
37828
- setSubmitted(true);
37829
- setAttempts((a) => a + 1);
37879
+ if (checkEvent) {
37880
+ emit(`UI:${checkEvent}`, {});
37881
+ }
37830
37882
  const correct = correctFlags.length === bugLines.length && falseFlags.length === 0;
37831
37883
  if (correct) {
37832
37884
  emit(`UI:${completeEvent}`, { success: true, attempts: attempts + 1 });
37833
37885
  }
37834
- }, [correctFlags.length, bugLines.length, falseFlags.length, attempts, completeEvent, emit]);
37886
+ }, [checkEvent, correctFlags.length, bugLines.length, falseFlags.length, attempts, completeEvent, emit]);
37835
37887
  const handleReset = () => {
37836
- setSubmitted(false);
37888
+ if (playAgainEvent) {
37889
+ emit(`UI:${playAgainEvent}`, {});
37890
+ }
37837
37891
  if (attempts >= 2 && str(resolved?.hint)) {
37838
37892
  setShowHint(true);
37839
37893
  }
37840
37894
  };
37841
37895
  const handleFullReset = () => {
37842
- setFlaggedLines(/* @__PURE__ */ new Set());
37843
- setSubmitted(false);
37844
- setAttempts(0);
37896
+ if (playAgainEvent) {
37897
+ emit(`UI:${playAgainEvent}`, {});
37898
+ }
37845
37899
  setShowHint(false);
37846
37900
  };
37847
37901
  if (!resolved) return null;
@@ -37869,7 +37923,7 @@ function DebuggerBoard({
37869
37923
  /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", className: "text-muted-foreground", children: t("debugger.findBugs", { count: String(num(resolved.bugCount)) }) })
37870
37924
  ] }) }),
37871
37925
  /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "p-0 overflow-hidden", children: /* @__PURE__ */ jsxRuntime.jsx(VStack, { gap: "none", children: lines.map((line, i) => {
37872
- const isFlagged = flaggedLines.has(line.id);
37926
+ const isFlagged = !!line.isFlagged;
37873
37927
  let lineStyle = "";
37874
37928
  if (submitted) {
37875
37929
  if (line.isBug && isFlagged) lineStyle = "bg-success/10";
@@ -37903,9 +37957,9 @@ function DebuggerBoard({
37903
37957
  /* @__PURE__ */ jsxRuntime.jsx(
37904
37958
  Icon,
37905
37959
  {
37906
- icon: flaggedLines.has(line.id) ? LucideIcons2.CheckCircle : LucideIcons2.XCircle,
37960
+ icon: line.isFlagged ? LucideIcons2.CheckCircle : LucideIcons2.XCircle,
37907
37961
  size: "xs",
37908
- className: flaggedLines.has(line.id) ? "text-success mt-0.5" : "text-warning mt-0.5"
37962
+ className: line.isFlagged ? "text-success mt-0.5" : "text-warning mt-0.5"
37909
37963
  }
37910
37964
  ),
37911
37965
  /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "none", children: [
@@ -37916,7 +37970,7 @@ function DebuggerBoard({
37916
37970
  ] }) }),
37917
37971
  showHint && hint && /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", children: hint }) }),
37918
37972
  /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "sm", justify: "center", children: [
37919
- !submitted ? /* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "primary", onClick: handleSubmit, disabled: flaggedLines.size === 0, children: [
37973
+ !submitted ? /* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "primary", onClick: handleSubmit, disabled: flaggedLines.length === 0, children: [
37920
37974
  /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: LucideIcons2.Send, size: "sm" }),
37921
37975
  t("debugger.submit")
37922
37976
  ] }) : !allCorrect ? /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "primary", onClick: handleReset, children: t("debugger.tryAgain") }) : null,
@@ -41680,6 +41734,9 @@ function getOpponentAction(strategy, actions, history) {
41680
41734
  function NegotiatorBoard({
41681
41735
  entity,
41682
41736
  completeEvent = "PUZZLE_COMPLETE",
41737
+ playRoundEvent,
41738
+ finishEvent,
41739
+ playAgainEvent,
41683
41740
  className
41684
41741
  }) {
41685
41742
  const { emit } = useEventBus();
@@ -41688,13 +41745,14 @@ function NegotiatorBoard({
41688
41745
  const [history, setHistory] = React82.useState([]);
41689
41746
  const [headerError, setHeaderError] = React82.useState(false);
41690
41747
  const [showHint, setShowHint] = React82.useState(false);
41691
- const totalRounds = num(resolved?.totalRounds);
41748
+ const totalRounds = num(resolved?.maxRounds);
41692
41749
  const targetScore = num(resolved?.targetScore);
41693
- const currentRound = history.length;
41694
- const isComplete = currentRound >= totalRounds;
41695
- const playerTotal = history.reduce((s, r) => s + r.playerPayoff, 0);
41750
+ const currentRound = num(resolved?.round);
41751
+ const result = str(resolved?.result) || "none";
41752
+ const playerTotal = num(resolved?.score);
41753
+ const isComplete = result !== "none" || totalRounds > 0 && currentRound >= totalRounds;
41754
+ const won = result === "win";
41696
41755
  const opponentTotal = history.reduce((s, r) => s + r.opponentPayoff, 0);
41697
- const won = isComplete && playerTotal >= targetScore;
41698
41756
  const actions = Array.isArray(resolved?.actions) ? resolved.actions : [];
41699
41757
  const payoffMatrix = Array.isArray(resolved?.payoffMatrix) ? resolved.payoffMatrix : [];
41700
41758
  const handleAction = React82.useCallback((actionId) => {
@@ -41703,29 +41761,45 @@ function NegotiatorBoard({
41703
41761
  const payoff = payoffMatrix.find(
41704
41762
  (p2) => p2.playerAction === actionId && p2.opponentAction === opponentAction
41705
41763
  );
41706
- const result = {
41707
- round: currentRound + 1,
41708
- playerAction: actionId,
41709
- opponentAction,
41710
- playerPayoff: payoff?.playerPayoff ?? 0,
41711
- opponentPayoff: payoff?.opponentPayoff ?? 0
41712
- };
41713
- const newHistory = [...history, result];
41714
- setHistory(newHistory);
41715
- if (newHistory.length >= totalRounds) {
41716
- const total = newHistory.reduce((s, r) => s + r.playerPayoff, 0);
41717
- if (total >= targetScore) {
41718
- emit(`UI:${completeEvent}`, { success: true, score: total });
41719
- }
41720
- if (newHistory.length >= 3 && str(resolved?.hint)) {
41764
+ const playerPayoff = payoff?.playerPayoff ?? 0;
41765
+ setHistory((prev) => [
41766
+ ...prev,
41767
+ {
41768
+ round: prev.length + 1,
41769
+ playerAction: actionId,
41770
+ opponentAction,
41771
+ playerPayoff,
41772
+ opponentPayoff: payoff?.opponentPayoff ?? 0
41773
+ }
41774
+ ]);
41775
+ if (playRoundEvent) {
41776
+ emit(`UI:${playRoundEvent}`, { playerAction: actionId, payoff: playerPayoff });
41777
+ }
41778
+ if (totalRounds > 0 && currentRound + 1 >= totalRounds) {
41779
+ if (finishEvent) {
41780
+ emit(`UI:${finishEvent}`, {});
41781
+ }
41782
+ if (str(resolved?.hint)) {
41721
41783
  setShowHint(true);
41722
41784
  }
41723
41785
  }
41724
- }, [isComplete, resolved, totalRounds, targetScore, actions, payoffMatrix, history, currentRound, completeEvent, emit]);
41725
- const handleReset = () => {
41786
+ }, [isComplete, resolved, totalRounds, currentRound, actions, payoffMatrix, history, playRoundEvent, finishEvent, emit]);
41787
+ const handleReset = React82.useCallback(() => {
41726
41788
  setHistory([]);
41727
41789
  setShowHint(false);
41728
- };
41790
+ if (playAgainEvent) {
41791
+ emit(`UI:${playAgainEvent}`, {});
41792
+ }
41793
+ }, [playAgainEvent, emit]);
41794
+ const completedRef = React82.useRef(false);
41795
+ React82.useEffect(() => {
41796
+ if (result === "win" && !completedRef.current) {
41797
+ completedRef.current = true;
41798
+ emit(`UI:${completeEvent}`, { success: true, score: playerTotal });
41799
+ } else if (result === "none") {
41800
+ completedRef.current = false;
41801
+ }
41802
+ }, [result, playerTotal, completeEvent, emit]);
41729
41803
  const getActionLabel = (id) => actions.find((a) => a.id === id)?.label ?? id;
41730
41804
  if (!resolved) return null;
41731
41805
  const theme = resolved.theme ?? void 0;
@@ -44691,67 +44765,47 @@ var init_SimulationGraph = __esm({
44691
44765
  function SimulatorBoard({
44692
44766
  entity,
44693
44767
  completeEvent = "PUZZLE_COMPLETE",
44768
+ setAEvent,
44769
+ setBEvent,
44770
+ checkEvent,
44771
+ playAgainEvent,
44694
44772
  className
44695
44773
  }) {
44696
44774
  const { emit } = useEventBus();
44697
44775
  const { t } = hooks.useTranslate();
44698
44776
  const resolved = boardEntity(entity);
44699
44777
  const parameters = Array.isArray(resolved?.parameters) ? resolved.parameters : [];
44700
- const [values, setValues] = React82.useState(() => {
44701
- const init = {};
44702
- for (const p2 of parameters) {
44703
- init[p2.id] = p2.initial;
44704
- }
44705
- return init;
44706
- });
44707
44778
  const [headerError, setHeaderError] = React82.useState(false);
44708
- const [submitted, setSubmitted] = React82.useState(false);
44709
- const [attempts, setAttempts] = React82.useState(0);
44710
- const [showHint, setShowHint] = React82.useState(false);
44711
- const computeOutput = React82.useCallback((params) => {
44712
- try {
44713
- const fn = new Function("params", `return (${str(resolved?.computeExpression)})`);
44714
- return fn(params);
44715
- } catch {
44716
- return 0;
44717
- }
44718
- }, [resolved?.computeExpression]);
44719
- const output = React82.useMemo(() => computeOutput(values) ?? 0, [computeOutput, values]);
44720
- const targetValue = num(resolved?.targetValue);
44721
- const targetTolerance = num(resolved?.targetTolerance);
44722
- const isCorrect = Math.abs(output - targetValue) <= targetTolerance;
44723
- const handleParameterChange = (id, value) => {
44724
- if (submitted) return;
44725
- setValues((prev) => ({ ...prev, [id]: value }));
44726
- };
44727
- const handleSubmit = () => {
44728
- setSubmitted(true);
44729
- setAttempts((a) => a + 1);
44730
- if (isCorrect) {
44731
- emit(`UI:${completeEvent}`, { success: true, attempts: attempts + 1 });
44732
- }
44779
+ if (!resolved) return null;
44780
+ const paramA = num(resolved.paramA);
44781
+ const paramB = num(resolved.paramB);
44782
+ const output = num(resolved.output);
44783
+ const targetValue = num(resolved.target);
44784
+ const targetTolerance = num(resolved.tolerance);
44785
+ const attempts = num(resolved.attempts);
44786
+ const result = str(resolved.result);
44787
+ const isWin = result === "win";
44788
+ const isComplete = result !== "none" && result !== "";
44789
+ const paramAValue = parameters[0];
44790
+ const paramBValue = parameters[1];
44791
+ const sliderValues = [paramA, paramB];
44792
+ const sliderEvents = [setAEvent, setBEvent];
44793
+ const handleParameterChange = (index, value) => {
44794
+ if (isComplete) return;
44795
+ const ev = sliderEvents[index];
44796
+ if (ev) emit(`UI:${ev}`, { value });
44733
44797
  };
44734
- const handleReset = () => {
44735
- setSubmitted(false);
44736
- if (attempts >= 2 && str(resolved?.hint)) {
44737
- setShowHint(true);
44738
- }
44798
+ const handleCheck = () => {
44799
+ if (checkEvent) emit(`UI:${checkEvent}`, {});
44739
44800
  };
44740
- const handleFullReset = () => {
44741
- const init = {};
44742
- for (const p2 of parameters) {
44743
- init[p2.id] = p2.initial;
44744
- }
44745
- setValues(init);
44746
- setSubmitted(false);
44747
- setAttempts(0);
44748
- setShowHint(false);
44801
+ const handlePlayAgain = () => {
44802
+ if (playAgainEvent) emit(`UI:${playAgainEvent}`, {});
44749
44803
  };
44750
- if (!resolved) return null;
44751
44804
  const theme = resolved.theme ?? void 0;
44752
44805
  const themeBackground = theme?.background;
44753
44806
  const headerImage = str(resolved.headerImage);
44754
44807
  const hint = str(resolved.hint);
44808
+ const showHint = isComplete && !isWin && attempts >= 2 && Boolean(hint);
44755
44809
  const outputLabel = str(resolved.outputLabel);
44756
44810
  const outputUnit = str(resolved.outputUnit);
44757
44811
  return /* @__PURE__ */ jsxRuntime.jsx(
@@ -44771,41 +44825,43 @@ function SimulatorBoard({
44771
44825
  ] }) }),
44772
44826
  /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "md", children: [
44773
44827
  /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", weight: "bold", className: "uppercase tracking-wider text-muted-foreground", children: t("simulator.parameters") }),
44774
- parameters.map((param) => /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "xs", children: [
44775
- /* @__PURE__ */ jsxRuntime.jsxs(HStack, { justify: "between", align: "center", children: [
44776
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", weight: "medium", children: param.label }),
44777
- /* @__PURE__ */ jsxRuntime.jsxs(Badge, { size: "sm", children: [
44778
- values[param.id],
44779
- " ",
44780
- param.unit
44781
- ] })
44782
- ] }),
44783
- /* @__PURE__ */ jsxRuntime.jsx(
44784
- "input",
44785
- {
44786
- type: "range",
44787
- min: param.min,
44788
- max: param.max,
44789
- step: param.step,
44790
- value: values[param.id],
44791
- onChange: (e) => handleParameterChange(param.id, Number(e.target.value)),
44792
- disabled: submitted,
44793
- className: "w-full accent-foreground"
44794
- }
44795
- ),
44796
- /* @__PURE__ */ jsxRuntime.jsxs(HStack, { justify: "between", children: [
44797
- /* @__PURE__ */ jsxRuntime.jsxs(Typography, { variant: "caption", className: "text-muted-foreground", children: [
44798
- param.min,
44799
- " ",
44800
- param.unit
44828
+ [paramAValue, paramBValue].map(
44829
+ (param, index) => param ? /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "xs", children: [
44830
+ /* @__PURE__ */ jsxRuntime.jsxs(HStack, { justify: "between", align: "center", children: [
44831
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", weight: "medium", children: param.label }),
44832
+ /* @__PURE__ */ jsxRuntime.jsxs(Badge, { size: "sm", children: [
44833
+ sliderValues[index],
44834
+ " ",
44835
+ param.unit
44836
+ ] })
44801
44837
  ] }),
44802
- /* @__PURE__ */ jsxRuntime.jsxs(Typography, { variant: "caption", className: "text-muted-foreground", children: [
44803
- param.max,
44804
- " ",
44805
- param.unit
44838
+ /* @__PURE__ */ jsxRuntime.jsx(
44839
+ "input",
44840
+ {
44841
+ type: "range",
44842
+ min: param.min,
44843
+ max: param.max,
44844
+ step: param.step,
44845
+ value: sliderValues[index],
44846
+ onChange: (e) => handleParameterChange(index, Number(e.target.value)),
44847
+ disabled: isComplete,
44848
+ className: "w-full accent-foreground"
44849
+ }
44850
+ ),
44851
+ /* @__PURE__ */ jsxRuntime.jsxs(HStack, { justify: "between", children: [
44852
+ /* @__PURE__ */ jsxRuntime.jsxs(Typography, { variant: "caption", className: "text-muted-foreground", children: [
44853
+ param.min,
44854
+ " ",
44855
+ param.unit
44856
+ ] }),
44857
+ /* @__PURE__ */ jsxRuntime.jsxs(Typography, { variant: "caption", className: "text-muted-foreground", children: [
44858
+ param.max,
44859
+ " ",
44860
+ param.unit
44861
+ ] })
44806
44862
  ] })
44807
- ] })
44808
- ] }, param.id))
44863
+ ] }, param.id ?? index) : null
44864
+ )
44809
44865
  ] }) }),
44810
44866
  /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "sm", align: "center", children: [
44811
44867
  /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", weight: "bold", className: "uppercase tracking-wider text-muted-foreground", children: outputLabel }),
@@ -44814,9 +44870,9 @@ function SimulatorBoard({
44814
44870
  " ",
44815
44871
  outputUnit
44816
44872
  ] }),
44817
- submitted && /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "xs", align: "center", children: [
44818
- /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: isCorrect ? LucideIcons2.CheckCircle : LucideIcons2.XCircle, size: "sm", className: isCorrect ? "text-success" : "text-error" }),
44819
- /* @__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") })
44873
+ isComplete && /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "xs", align: "center", children: [
44874
+ /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: isWin ? LucideIcons2.CheckCircle : LucideIcons2.XCircle, size: "sm", className: isWin ? "text-success" : "text-error" }),
44875
+ /* @__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") })
44820
44876
  ] }),
44821
44877
  /* @__PURE__ */ jsxRuntime.jsxs(Typography, { variant: "caption", className: "text-muted-foreground", children: [
44822
44878
  t("simulator.target"),
@@ -44831,11 +44887,11 @@ function SimulatorBoard({
44831
44887
  ] }) }),
44832
44888
  showHint && hint && /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", children: hint }) }),
44833
44889
  /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "sm", justify: "center", children: [
44834
- !submitted ? /* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "primary", onClick: handleSubmit, children: [
44890
+ !isComplete ? /* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "primary", onClick: handleCheck, children: [
44835
44891
  /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: LucideIcons2.Play, size: "sm" }),
44836
44892
  t("simulator.simulate")
44837
- ] }) : !isCorrect ? /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "primary", onClick: handleReset, children: t("simulator.tryAgain") }) : null,
44838
- /* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "secondary", onClick: handleFullReset, children: [
44893
+ ] }) : null,
44894
+ /* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "secondary", onClick: handlePlayAgain, children: [
44839
44895
  /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: LucideIcons2.RotateCcw, size: "sm" }),
44840
44896
  t("simulator.reset")
44841
44897
  ] })