@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 providers = require('@almadar/ui/providers');
7
7
  var clsx = require('clsx');
8
8
  var tailwindMerge = require('tailwind-merge');
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');
@@ -68,9 +65,6 @@ function _interopNamespace(e) {
68
65
 
69
66
  var React83__namespace = /*#__PURE__*/_interopNamespace(React83);
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);
@@ -842,6 +836,41 @@ function kebabToPascal(name) {
842
836
  return part.charAt(0).toUpperCase() + part.slice(1);
843
837
  }).join("");
844
838
  }
839
+ function loadLib(key, importer) {
840
+ let p2 = libPromises.get(key);
841
+ if (!p2) {
842
+ p2 = importer().then((m) => m);
843
+ libPromises.set(key, p2);
844
+ }
845
+ return p2;
846
+ }
847
+ function lazyFamilyIcon(libKey, importer, pick, fallbackName, family) {
848
+ const Lazy = React83__namespace.default.lazy(async () => {
849
+ const lib = await loadLib(libKey, importer);
850
+ const Comp = pick(lib);
851
+ if (!Comp) {
852
+ warnFallback(fallbackName, family);
853
+ return { default: makeLucideAdapter(fallbackName, true) };
854
+ }
855
+ return { default: Comp };
856
+ });
857
+ const Wrapped = (props) => /* @__PURE__ */ jsxRuntime.jsx(
858
+ React83__namespace.default.Suspense,
859
+ {
860
+ fallback: /* @__PURE__ */ jsxRuntime.jsx(
861
+ "span",
862
+ {
863
+ "aria-hidden": true,
864
+ className: props.className,
865
+ style: { display: "inline-block", ...props.style }
866
+ }
867
+ ),
868
+ children: /* @__PURE__ */ jsxRuntime.jsx(Lazy, { ...props })
869
+ }
870
+ );
871
+ Wrapped.displayName = `Lazy.${libKey}.${fallbackName}`;
872
+ return Wrapped;
873
+ }
845
874
  function resolveLucide(name) {
846
875
  if (lucideAliases[name]) return lucideAliases[name];
847
876
  const pascal = kebabToPascal(name);
@@ -852,60 +881,81 @@ function resolveLucide(name) {
852
881
  if (asIs && typeof asIs === "object") return asIs;
853
882
  return LucideIcons2__namespace.HelpCircle;
854
883
  }
855
- function resolvePhosphor(name, weight) {
884
+ function resolvePhosphor(name, weight, family) {
856
885
  const target = phosphorAliases[name] ?? kebabToPascal(name);
857
- const map = PhosphorIcons__namespace;
858
- const PhosphorComp = map[target];
859
- if (!PhosphorComp || typeof PhosphorComp !== "object") return null;
860
- const Component = PhosphorComp;
861
- const Adapter = (props) => /* @__PURE__ */ jsxRuntime.jsx(
862
- Component,
863
- {
864
- weight,
865
- className: props.className,
866
- style: props.style,
867
- size: props.size ?? "1em"
868
- }
886
+ return lazyFamilyIcon(
887
+ "phosphor",
888
+ () => import('@phosphor-icons/react'),
889
+ (lib) => {
890
+ const PhosphorComp = lib[target];
891
+ if (!PhosphorComp || typeof PhosphorComp !== "object") return null;
892
+ const Component = PhosphorComp;
893
+ const Adapter = (props) => /* @__PURE__ */ jsxRuntime.jsx(
894
+ Component,
895
+ {
896
+ weight,
897
+ className: props.className,
898
+ style: props.style,
899
+ size: props.size ?? "1em"
900
+ }
901
+ );
902
+ Adapter.displayName = `Phosphor.${target}.${weight}`;
903
+ return Adapter;
904
+ },
905
+ name,
906
+ family
869
907
  );
870
- Adapter.displayName = `Phosphor.${target}.${weight}`;
871
- return Adapter;
872
908
  }
873
- function resolveTabler(name) {
909
+ function resolveTabler(name, family) {
874
910
  const suffix = tablerAliases[name] ?? kebabToPascal(name);
875
911
  const target = `Icon${suffix}`;
876
- const map = TablerIcons__namespace;
877
- const TablerComp = map[target];
878
- if (!TablerComp || typeof TablerComp !== "object") return null;
879
- const Component = TablerComp;
880
- const Adapter = (props) => /* @__PURE__ */ jsxRuntime.jsx(
881
- Component,
882
- {
883
- stroke: props.strokeWidth ?? 1.5,
884
- className: props.className,
885
- style: props.style,
886
- size: props.size ?? 24
887
- }
912
+ return lazyFamilyIcon(
913
+ "tabler",
914
+ () => import('@tabler/icons-react'),
915
+ (lib) => {
916
+ const TablerComp = lib[target];
917
+ if (!TablerComp || typeof TablerComp !== "object") return null;
918
+ const Component = TablerComp;
919
+ const Adapter = (props) => /* @__PURE__ */ jsxRuntime.jsx(
920
+ Component,
921
+ {
922
+ stroke: props.strokeWidth ?? 1.5,
923
+ className: props.className,
924
+ style: props.style,
925
+ size: props.size ?? 24
926
+ }
927
+ );
928
+ Adapter.displayName = `Tabler.${target}`;
929
+ return Adapter;
930
+ },
931
+ name,
932
+ family
888
933
  );
889
- Adapter.displayName = `Tabler.${target}`;
890
- return Adapter;
891
934
  }
892
- function resolveFa(name) {
935
+ function resolveFa(name, family) {
893
936
  const suffix = faAliases[name] ?? kebabToPascal(name);
894
937
  const target = `Fa${suffix}`;
895
- const map = FaIcons__namespace;
896
- const FaComp = map[target];
897
- if (!FaComp || typeof FaComp !== "function") return null;
898
- const Component = FaComp;
899
- const Adapter = (props) => /* @__PURE__ */ jsxRuntime.jsx(
900
- Component,
901
- {
902
- className: props.className,
903
- style: props.style,
904
- size: props.size ?? "1em"
905
- }
938
+ return lazyFamilyIcon(
939
+ "fa",
940
+ () => import('react-icons/fa'),
941
+ (lib) => {
942
+ const FaComp = lib[target];
943
+ if (!FaComp || typeof FaComp !== "function") return null;
944
+ const Component = FaComp;
945
+ const Adapter = (props) => /* @__PURE__ */ jsxRuntime.jsx(
946
+ Component,
947
+ {
948
+ className: props.className,
949
+ style: props.style,
950
+ size: props.size ?? "1em"
951
+ }
952
+ );
953
+ Adapter.displayName = `Fa.${target}`;
954
+ return Adapter;
955
+ },
956
+ name,
957
+ family
906
958
  );
907
- Adapter.displayName = `Fa.${target}`;
908
- return Adapter;
909
959
  }
910
960
  function warnFallback(name, family) {
911
961
  const key = `${family}::${name}`;
@@ -939,39 +989,22 @@ function resolveIconForFamily(name, family) {
939
989
  switch (family) {
940
990
  case "lucide":
941
991
  return makeLucideAdapter(name, false);
942
- case "phosphor-outline": {
943
- const p2 = resolvePhosphor(name, "regular");
944
- if (p2) return p2;
945
- warnFallback(name, family);
946
- return makeLucideAdapter(name, true);
947
- }
948
- case "phosphor-fill": {
949
- const p2 = resolvePhosphor(name, "fill");
950
- if (p2) return p2;
951
- warnFallback(name, family);
952
- return makeLucideAdapter(name, true);
953
- }
954
- case "phosphor-duotone": {
955
- const p2 = resolvePhosphor(name, "duotone");
956
- if (p2) return p2;
957
- warnFallback(name, family);
958
- return makeLucideAdapter(name, true);
959
- }
960
- case "tabler": {
961
- const t = resolveTabler(name);
962
- if (t) return t;
963
- warnFallback(name, family);
964
- return makeLucideAdapter(name, true);
965
- }
966
- case "fa-solid": {
967
- const f3 = resolveFa(name);
968
- if (f3) return f3;
969
- warnFallback(name, family);
970
- return makeLucideAdapter(name, true);
971
- }
972
- }
973
- }
974
- var DEFAULT_FAMILY, VALID_FAMILIES, cachedFamily, listeners, observer, lucideAliases, phosphorAliases, tablerAliases, faAliases, warned;
992
+ // Non-lucide families resolve to a lazy, Suspense-wrapped component that
993
+ // dynamic-imports the library on first render and falls back to lucide
994
+ // internally when the family lacks the icon (see lazyFamilyIcon).
995
+ case "phosphor-outline":
996
+ return resolvePhosphor(name, "regular", family);
997
+ case "phosphor-fill":
998
+ return resolvePhosphor(name, "fill", family);
999
+ case "phosphor-duotone":
1000
+ return resolvePhosphor(name, "duotone", family);
1001
+ case "tabler":
1002
+ return resolveTabler(name, family);
1003
+ case "fa-solid":
1004
+ return resolveFa(name, family);
1005
+ }
1006
+ }
1007
+ var DEFAULT_FAMILY, VALID_FAMILIES, cachedFamily, listeners, observer, libPromises, lucideAliases, phosphorAliases, tablerAliases, faAliases, warned;
975
1008
  var init_iconFamily = __esm({
976
1009
  "lib/iconFamily.tsx"() {
977
1010
  "use client";
@@ -987,6 +1020,7 @@ var init_iconFamily = __esm({
987
1020
  cachedFamily = null;
988
1021
  listeners = /* @__PURE__ */ new Set();
989
1022
  observer = null;
1023
+ libPromises = /* @__PURE__ */ new Map();
990
1024
  lucideAliases = {
991
1025
  close: LucideIcons2__namespace.X,
992
1026
  trash: LucideIcons2__namespace.Trash2,
@@ -17421,61 +17455,53 @@ var init_Breadcrumb = __esm({
17421
17455
  function BuilderBoard({
17422
17456
  entity,
17423
17457
  completeEvent = "PUZZLE_COMPLETE",
17458
+ placeEvent,
17459
+ checkEvent,
17460
+ playAgainEvent,
17424
17461
  className
17425
17462
  }) {
17426
17463
  const { emit } = useEventBus();
17427
17464
  const { t } = hooks.useTranslate();
17428
17465
  const resolved = boardEntity(entity);
17429
- const [placements, setPlacements] = React83.useState({});
17430
17466
  const [headerError, setHeaderError] = React83.useState(false);
17431
- const [submitted, setSubmitted] = React83.useState(false);
17432
- const [attempts, setAttempts] = React83.useState(0);
17433
- const [showHint, setShowHint] = React83.useState(false);
17467
+ const [selectedComponent, setSelectedComponent] = React83.useState(null);
17434
17468
  const components = Array.isArray(resolved?.components) ? resolved.components : [];
17435
17469
  const slots = Array.isArray(resolved?.slots) ? resolved.slots : [];
17470
+ const placements = {};
17471
+ for (const slot of slots) {
17472
+ if (slot.placedComponentId) placements[slot.id] = slot.placedComponentId;
17473
+ }
17474
+ const attempts = num(resolved?.attempts);
17475
+ const result = str(resolved?.result) || "none";
17476
+ const submitted = result === "win";
17436
17477
  const usedComponentIds = new Set(Object.values(placements));
17437
17478
  const availableComponents = components.filter((c) => !usedComponentIds.has(c.id));
17438
- const [selectedComponent, setSelectedComponent] = React83.useState(null);
17439
- const allPlaced = Object.keys(placements).length === slots.length;
17479
+ const allPlaced = slots.length > 0 && slots.every((s) => Boolean(placements[s.id]));
17440
17480
  const results = submitted ? slots.map((slot) => ({
17441
17481
  slot,
17442
17482
  placed: placements[slot.id],
17443
- correct: placements[slot.id] === slot.acceptsComponentId
17483
+ correct: placements[slot.id] === slot.requiredComponentId
17444
17484
  })) : [];
17445
- const allCorrect = results.length > 0 && results.every((r) => r.correct);
17485
+ const showHint = attempts >= 2 && Boolean(str(resolved?.hint));
17446
17486
  const handlePlaceComponent = (slotId) => {
17447
17487
  if (submitted || !selectedComponent) return;
17448
- setPlacements((prev) => ({ ...prev, [slotId]: selectedComponent }));
17488
+ if (placeEvent) emit(`UI:${placeEvent}`, { slotId, componentId: selectedComponent });
17449
17489
  setSelectedComponent(null);
17450
17490
  };
17451
17491
  const handleRemoveFromSlot = (slotId) => {
17452
17492
  if (submitted) return;
17453
- setPlacements((prev) => {
17454
- const next = { ...prev };
17455
- delete next[slotId];
17456
- return next;
17457
- });
17493
+ if (placeEvent) emit(`UI:${placeEvent}`, { slotId, componentId: "" });
17458
17494
  };
17459
- const handleSubmit = React83.useCallback(() => {
17460
- setSubmitted(true);
17461
- setAttempts((a) => a + 1);
17462
- const correct = slots.every((slot) => placements[slot.id] === slot.acceptsComponentId);
17463
- if (correct) {
17495
+ const handleSubmit = () => {
17496
+ if (checkEvent) emit(`UI:${checkEvent}`, {});
17497
+ const solved = slots.length > 0 && slots.every((s) => placements[s.id] === s.requiredComponentId);
17498
+ if (solved && completeEvent) {
17464
17499
  emit(`UI:${completeEvent}`, { success: true, attempts: attempts + 1 });
17465
17500
  }
17466
- }, [slots, placements, attempts, completeEvent, emit]);
17467
- const handleReset = () => {
17468
- setSubmitted(false);
17469
- if (attempts >= 2 && str(resolved?.hint)) {
17470
- setShowHint(true);
17471
- }
17472
17501
  };
17473
- const handleFullReset = () => {
17474
- setPlacements({});
17475
- setSubmitted(false);
17502
+ const handlePlayAgain = () => {
17476
17503
  setSelectedComponent(null);
17477
- setAttempts(0);
17478
- setShowHint(false);
17504
+ if (playAgainEvent) emit(`UI:${playAgainEvent}`, {});
17479
17505
  };
17480
17506
  const getComponentById = (id) => components.find((c) => c.id === id);
17481
17507
  if (!resolved) return null;
@@ -17525,13 +17551,13 @@ function BuilderBoard({
17525
17551
  /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", weight: "bold", className: "uppercase tracking-wider text-muted-foreground", children: t("builder.blueprint") }),
17526
17552
  /* @__PURE__ */ jsxRuntime.jsx(VStack, { gap: "sm", children: slots.map((slot) => {
17527
17553
  const placedComp = placements[slot.id] ? getComponentById(placements[slot.id]) : null;
17528
- const result = results.find((r) => r.slot.id === slot.id);
17554
+ const result2 = results.find((r) => r.slot.id === slot.id);
17529
17555
  return /* @__PURE__ */ jsxRuntime.jsxs(
17530
17556
  HStack,
17531
17557
  {
17532
17558
  gap: "sm",
17533
17559
  align: "center",
17534
- className: `p-3 border-2 rounded ${result ? result.correct ? "border-success" : "border-error" : selectedComponent ? "border-dashed border-foreground cursor-pointer" : "border-border"}`,
17560
+ className: `p-3 border-2 rounded ${result2 ? result2.correct ? "border-success" : "border-error" : selectedComponent ? "border-dashed border-foreground cursor-pointer" : "border-border"}`,
17535
17561
  onClick: () => handlePlaceComponent(slot.id),
17536
17562
  children: [
17537
17563
  /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "none", className: "flex-1", children: [
@@ -17546,7 +17572,7 @@ function BuilderBoard({
17546
17572
  ] }) : null,
17547
17573
  placedComp.label
17548
17574
  ] }),
17549
- result && /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: result.correct ? LucideIcons2.CheckCircle : LucideIcons2.XCircle, size: "sm", className: result.correct ? "text-success" : "text-error" })
17575
+ result2 && /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: result2.correct ? LucideIcons2.CheckCircle : LucideIcons2.XCircle, size: "sm", className: result2.correct ? "text-success" : "text-error" })
17550
17576
  ] }) : /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", className: "text-muted-foreground", children: t("builder.empty") })
17551
17577
  ]
17552
17578
  },
@@ -17555,16 +17581,16 @@ function BuilderBoard({
17555
17581
  }) })
17556
17582
  ] }) }),
17557
17583
  submitted && /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "sm", align: "center", children: [
17558
- /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: allCorrect ? LucideIcons2.CheckCircle : LucideIcons2.XCircle, size: "lg", className: allCorrect ? "text-success" : "text-error" }),
17559
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", weight: "bold", children: allCorrect ? str(resolved.successMessage) || t("builder.success") : str(resolved.failMessage) || t("builder.incorrect") })
17584
+ /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: LucideIcons2.CheckCircle, size: "lg", className: "text-success" }),
17585
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", weight: "bold", children: str(resolved.successMessage) || t("builder.success") })
17560
17586
  ] }) }),
17561
17587
  showHint && hint && /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", children: hint }) }),
17562
17588
  /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "sm", justify: "center", children: [
17563
- !submitted ? /* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "primary", onClick: handleSubmit, disabled: !allPlaced, children: [
17589
+ !submitted && /* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "primary", onClick: handleSubmit, disabled: !allPlaced, children: [
17564
17590
  /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: LucideIcons2.Wrench, size: "sm" }),
17565
17591
  t("builder.build")
17566
- ] }) : !allCorrect ? /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "primary", onClick: handleReset, children: t("builder.tryAgain") }) : null,
17567
- /* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "secondary", onClick: handleFullReset, children: [
17592
+ ] }),
17593
+ /* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "secondary", onClick: handlePlayAgain, children: [
17568
17594
  /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: LucideIcons2.RotateCcw, size: "sm" }),
17569
17595
  t("builder.reset")
17570
17596
  ] })
@@ -21020,57 +21046,84 @@ var init_ChartLegend = __esm({
21020
21046
  function ClassifierBoard({
21021
21047
  entity,
21022
21048
  completeEvent = "PUZZLE_COMPLETE",
21049
+ assignEvent,
21050
+ checkEvent,
21051
+ playAgainEvent,
21023
21052
  className
21024
21053
  }) {
21025
21054
  const { emit } = useEventBus();
21026
21055
  const { t } = hooks.useTranslate();
21027
21056
  const resolved = boardEntity(entity);
21028
- const [assignments, setAssignments] = React83.useState({});
21057
+ const [localAssignments, setLocalAssignments] = React83.useState({});
21029
21058
  const [headerError, setHeaderError] = React83.useState(false);
21030
- const [submitted, setSubmitted] = React83.useState(false);
21031
- const [attempts, setAttempts] = React83.useState(0);
21059
+ const [localSubmitted, setLocalSubmitted] = React83.useState(false);
21060
+ const [localAttempts, setLocalAttempts] = React83.useState(0);
21032
21061
  const [showHint, setShowHint] = React83.useState(false);
21033
21062
  const items = Array.isArray(resolved?.items) ? resolved.items : [];
21034
21063
  const categories = Array.isArray(resolved?.categories) ? resolved.categories : [];
21064
+ const entityResult = str(resolved?.result);
21065
+ const entityDrivesResult = entityResult.length > 0;
21066
+ const entityHasAssignments = items.some((item) => item.assignedCategory != null);
21067
+ const assignments = entityHasAssignments ? items.reduce((acc, item) => {
21068
+ if (item.assignedCategory != null) acc[item.id] = item.assignedCategory;
21069
+ return acc;
21070
+ }, {}) : localAssignments;
21071
+ const attempts = entityDrivesResult ? num(resolved?.attempts) : localAttempts;
21072
+ const submitted = entityDrivesResult || localSubmitted;
21035
21073
  const unassignedItems = items.filter((item) => !assignments[item.id]);
21036
- const allAssigned = Object.keys(assignments).length === items.length;
21074
+ const allAssigned = items.length > 0 && Object.keys(assignments).length === items.length;
21037
21075
  const results = submitted ? items.map((item) => ({
21038
21076
  item,
21039
21077
  assigned: assignments[item.id],
21040
21078
  correct: assignments[item.id] === item.correctCategory
21041
21079
  })) : [];
21042
- const allCorrect = results.length > 0 && results.every((r) => r.correct);
21080
+ const allCorrect = entityDrivesResult ? entityResult === "success" : results.length > 0 && results.every((r) => r.correct);
21043
21081
  const correctCount = results.filter((r) => r.correct).length;
21044
21082
  const handleAssign = (itemId, categoryId) => {
21045
21083
  if (submitted) return;
21046
- setAssignments((prev) => ({ ...prev, [itemId]: categoryId }));
21084
+ if (assignEvent) {
21085
+ emit(`UI:${assignEvent}`, { itemId, categoryId });
21086
+ }
21087
+ if (!entityHasAssignments) {
21088
+ setLocalAssignments((prev) => ({ ...prev, [itemId]: categoryId }));
21089
+ }
21047
21090
  };
21048
21091
  const handleUnassign = (itemId) => {
21049
21092
  if (submitted) return;
21050
- setAssignments((prev) => {
21051
- const next = { ...prev };
21052
- delete next[itemId];
21053
- return next;
21054
- });
21093
+ if (!entityHasAssignments) {
21094
+ setLocalAssignments((prev) => {
21095
+ const next = { ...prev };
21096
+ delete next[itemId];
21097
+ return next;
21098
+ });
21099
+ }
21055
21100
  };
21056
21101
  const handleSubmit = React83.useCallback(() => {
21057
- setSubmitted(true);
21058
- setAttempts((a) => a + 1);
21059
- const correct = items.every((item) => assignments[item.id] === item.correctCategory);
21060
- if (correct) {
21061
- emit(`UI:${completeEvent}`, { success: true, attempts: attempts + 1 });
21102
+ if (checkEvent) {
21103
+ emit(`UI:${checkEvent}`, {});
21104
+ }
21105
+ if (!entityDrivesResult) {
21106
+ setLocalSubmitted(true);
21107
+ setLocalAttempts((a) => a + 1);
21108
+ const correct = items.every((item) => assignments[item.id] === item.correctCategory);
21109
+ if (correct) {
21110
+ emit(`UI:${completeEvent}`, { success: true, attempts: attempts + 1 });
21111
+ }
21062
21112
  }
21063
- }, [items, assignments, attempts, completeEvent, emit]);
21113
+ }, [checkEvent, entityDrivesResult, items, assignments, attempts, completeEvent, emit]);
21064
21114
  const handleReset = () => {
21065
- setSubmitted(false);
21115
+ if (!entityDrivesResult) setLocalSubmitted(false);
21066
21116
  if (attempts >= 2 && str(resolved?.hint)) {
21067
21117
  setShowHint(true);
21068
21118
  }
21069
21119
  };
21070
21120
  const handleFullReset = () => {
21071
- setAssignments({});
21072
- setSubmitted(false);
21073
- setAttempts(0);
21121
+ if (playAgainEvent) {
21122
+ emit(`UI:${playAgainEvent}`, {});
21123
+ }
21124
+ setLocalAssignments({});
21125
+ setLocalSubmitted(false);
21126
+ setLocalAttempts(0);
21074
21127
  setShowHint(false);
21075
21128
  };
21076
21129
  if (!resolved) return null;
@@ -28953,13 +29006,13 @@ var init_MapView = __esm({
28953
29006
  shadowSize: [41, 41]
28954
29007
  });
28955
29008
  L.Marker.prototype.options.icon = defaultIcon;
28956
- const { useEffect: useEffect72, useRef: useRef67, useCallback: useCallback113, useState: useState104 } = React83__namespace.default;
29009
+ const { useEffect: useEffect73, useRef: useRef68, useCallback: useCallback111, useState: useState104 } = React83__namespace.default;
28957
29010
  const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
28958
29011
  const { useEventBus: useEventBus2 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
28959
29012
  function MapUpdater({ centerLat, centerLng, zoom }) {
28960
29013
  const map = useMap();
28961
- const prevRef = useRef67({ centerLat, centerLng, zoom });
28962
- useEffect72(() => {
29014
+ const prevRef = useRef68({ centerLat, centerLng, zoom });
29015
+ useEffect73(() => {
28963
29016
  const prev = prevRef.current;
28964
29017
  if (prev.centerLat !== centerLat || prev.centerLng !== centerLng || prev.zoom !== zoom) {
28965
29018
  map.setView([centerLat, centerLng], zoom);
@@ -28970,7 +29023,7 @@ var init_MapView = __esm({
28970
29023
  }
28971
29024
  function MapClickHandler({ onMapClick }) {
28972
29025
  const map = useMap();
28973
- useEffect72(() => {
29026
+ useEffect73(() => {
28974
29027
  if (!onMapClick) return;
28975
29028
  const handler = (e) => {
28976
29029
  onMapClick(e.latlng.lat, e.latlng.lng);
@@ -28999,7 +29052,7 @@ var init_MapView = __esm({
28999
29052
  }) {
29000
29053
  const eventBus = useEventBus2();
29001
29054
  const [clickedPosition, setClickedPosition] = useState104(null);
29002
- const handleMapClick = useCallback113((lat, lng) => {
29055
+ const handleMapClick = useCallback111((lat, lng) => {
29003
29056
  if (showClickedPin) {
29004
29057
  setClickedPosition({ lat, lng });
29005
29058
  }
@@ -29008,7 +29061,7 @@ var init_MapView = __esm({
29008
29061
  eventBus.emit(`UI:${mapClickEvent}`, { latitude: lat, longitude: lng });
29009
29062
  }
29010
29063
  }, [onMapClick, mapClickEvent, eventBus, showClickedPin]);
29011
- const handleMarkerClick = useCallback113((marker) => {
29064
+ const handleMarkerClick = useCallback111((marker) => {
29012
29065
  onMarkerClick?.(marker);
29013
29066
  if (markerClickEvent) {
29014
29067
  eventBus.emit(`UI:${markerClickEvent}`, { ...marker });
@@ -38230,51 +38283,52 @@ var init_DataTable = __esm({
38230
38283
  function DebuggerBoard({
38231
38284
  entity,
38232
38285
  completeEvent = "PUZZLE_COMPLETE",
38286
+ toggleFlagEvent,
38287
+ checkEvent,
38288
+ playAgainEvent,
38233
38289
  className
38234
38290
  }) {
38235
38291
  const { emit } = useEventBus();
38236
38292
  const { t } = hooks.useTranslate();
38237
38293
  const resolved = boardEntity(entity);
38238
- const [flaggedLines, setFlaggedLines] = React83.useState(/* @__PURE__ */ new Set());
38239
38294
  const [headerError, setHeaderError] = React83.useState(false);
38240
- const [submitted, setSubmitted] = React83.useState(false);
38241
- const [attempts, setAttempts] = React83.useState(0);
38242
38295
  const [showHint, setShowHint] = React83.useState(false);
38296
+ const lines = Array.isArray(resolved?.lines) ? resolved.lines : [];
38297
+ const result = resolved?.result ?? null;
38298
+ const attempts = num(resolved?.attempts);
38299
+ const submitted = result != null;
38300
+ const bugLines = lines.filter((l) => l.isBug);
38301
+ const flaggedLines = lines.filter((l) => l.isFlagged);
38302
+ const correctFlags = lines.filter((l) => l.isBug && l.isFlagged);
38303
+ const falseFlags = lines.filter((l) => !l.isBug && l.isFlagged);
38304
+ const allCorrect = result === "win";
38243
38305
  const toggleLine = (lineId) => {
38244
38306
  if (submitted) return;
38245
- setFlaggedLines((prev) => {
38246
- const next = new Set(prev);
38247
- if (next.has(lineId)) {
38248
- next.delete(lineId);
38249
- } else {
38250
- next.add(lineId);
38251
- }
38252
- return next;
38253
- });
38307
+ if (toggleFlagEvent) {
38308
+ emit(`UI:${toggleFlagEvent}`, { lineId });
38309
+ }
38254
38310
  };
38255
- const lines = Array.isArray(resolved?.lines) ? resolved.lines : [];
38256
- const bugLines = lines.filter((l) => l.isBug);
38257
- const correctFlags = lines.filter((l) => l.isBug && flaggedLines.has(l.id));
38258
- const falseFlags = lines.filter((l) => !l.isBug && flaggedLines.has(l.id));
38259
- const allCorrect = submitted && correctFlags.length === bugLines.length && falseFlags.length === 0;
38260
38311
  const handleSubmit = React83.useCallback(() => {
38261
- setSubmitted(true);
38262
- setAttempts((a) => a + 1);
38312
+ if (checkEvent) {
38313
+ emit(`UI:${checkEvent}`, {});
38314
+ }
38263
38315
  const correct = correctFlags.length === bugLines.length && falseFlags.length === 0;
38264
38316
  if (correct) {
38265
38317
  emit(`UI:${completeEvent}`, { success: true, attempts: attempts + 1 });
38266
38318
  }
38267
- }, [correctFlags.length, bugLines.length, falseFlags.length, attempts, completeEvent, emit]);
38319
+ }, [checkEvent, correctFlags.length, bugLines.length, falseFlags.length, attempts, completeEvent, emit]);
38268
38320
  const handleReset = () => {
38269
- setSubmitted(false);
38321
+ if (playAgainEvent) {
38322
+ emit(`UI:${playAgainEvent}`, {});
38323
+ }
38270
38324
  if (attempts >= 2 && str(resolved?.hint)) {
38271
38325
  setShowHint(true);
38272
38326
  }
38273
38327
  };
38274
38328
  const handleFullReset = () => {
38275
- setFlaggedLines(/* @__PURE__ */ new Set());
38276
- setSubmitted(false);
38277
- setAttempts(0);
38329
+ if (playAgainEvent) {
38330
+ emit(`UI:${playAgainEvent}`, {});
38331
+ }
38278
38332
  setShowHint(false);
38279
38333
  };
38280
38334
  if (!resolved) return null;
@@ -38302,7 +38356,7 @@ function DebuggerBoard({
38302
38356
  /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", className: "text-muted-foreground", children: t("debugger.findBugs", { count: String(num(resolved.bugCount)) }) })
38303
38357
  ] }) }),
38304
38358
  /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "p-0 overflow-hidden", children: /* @__PURE__ */ jsxRuntime.jsx(VStack, { gap: "none", children: lines.map((line, i) => {
38305
- const isFlagged = flaggedLines.has(line.id);
38359
+ const isFlagged = !!line.isFlagged;
38306
38360
  let lineStyle = "";
38307
38361
  if (submitted) {
38308
38362
  if (line.isBug && isFlagged) lineStyle = "bg-success/10";
@@ -38336,9 +38390,9 @@ function DebuggerBoard({
38336
38390
  /* @__PURE__ */ jsxRuntime.jsx(
38337
38391
  Icon,
38338
38392
  {
38339
- icon: flaggedLines.has(line.id) ? LucideIcons2.CheckCircle : LucideIcons2.XCircle,
38393
+ icon: line.isFlagged ? LucideIcons2.CheckCircle : LucideIcons2.XCircle,
38340
38394
  size: "xs",
38341
- className: flaggedLines.has(line.id) ? "text-success mt-0.5" : "text-warning mt-0.5"
38395
+ className: line.isFlagged ? "text-success mt-0.5" : "text-warning mt-0.5"
38342
38396
  }
38343
38397
  ),
38344
38398
  /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "none", children: [
@@ -38349,7 +38403,7 @@ function DebuggerBoard({
38349
38403
  ] }) }),
38350
38404
  showHint && hint && /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", children: hint }) }),
38351
38405
  /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "sm", justify: "center", children: [
38352
- !submitted ? /* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "primary", onClick: handleSubmit, disabled: flaggedLines.size === 0, children: [
38406
+ !submitted ? /* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "primary", onClick: handleSubmit, disabled: flaggedLines.length === 0, children: [
38353
38407
  /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: LucideIcons2.Send, size: "sm" }),
38354
38408
  t("debugger.submit")
38355
38409
  ] }) : !allCorrect ? /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "primary", onClick: handleReset, children: t("debugger.tryAgain") }) : null,
@@ -42113,6 +42167,9 @@ function getOpponentAction(strategy, actions, history) {
42113
42167
  function NegotiatorBoard({
42114
42168
  entity,
42115
42169
  completeEvent = "PUZZLE_COMPLETE",
42170
+ playRoundEvent,
42171
+ finishEvent,
42172
+ playAgainEvent,
42116
42173
  className
42117
42174
  }) {
42118
42175
  const { emit } = useEventBus();
@@ -42121,13 +42178,14 @@ function NegotiatorBoard({
42121
42178
  const [history, setHistory] = React83.useState([]);
42122
42179
  const [headerError, setHeaderError] = React83.useState(false);
42123
42180
  const [showHint, setShowHint] = React83.useState(false);
42124
- const totalRounds = num(resolved?.totalRounds);
42181
+ const totalRounds = num(resolved?.maxRounds);
42125
42182
  const targetScore = num(resolved?.targetScore);
42126
- const currentRound = history.length;
42127
- const isComplete = currentRound >= totalRounds;
42128
- const playerTotal = history.reduce((s, r) => s + r.playerPayoff, 0);
42183
+ const currentRound = num(resolved?.round);
42184
+ const result = str(resolved?.result) || "none";
42185
+ const playerTotal = num(resolved?.score);
42186
+ const isComplete = result !== "none" || totalRounds > 0 && currentRound >= totalRounds;
42187
+ const won = result === "win";
42129
42188
  const opponentTotal = history.reduce((s, r) => s + r.opponentPayoff, 0);
42130
- const won = isComplete && playerTotal >= targetScore;
42131
42189
  const actions = Array.isArray(resolved?.actions) ? resolved.actions : [];
42132
42190
  const payoffMatrix = Array.isArray(resolved?.payoffMatrix) ? resolved.payoffMatrix : [];
42133
42191
  const handleAction = React83.useCallback((actionId) => {
@@ -42136,29 +42194,45 @@ function NegotiatorBoard({
42136
42194
  const payoff = payoffMatrix.find(
42137
42195
  (p2) => p2.playerAction === actionId && p2.opponentAction === opponentAction
42138
42196
  );
42139
- const result = {
42140
- round: currentRound + 1,
42141
- playerAction: actionId,
42142
- opponentAction,
42143
- playerPayoff: payoff?.playerPayoff ?? 0,
42144
- opponentPayoff: payoff?.opponentPayoff ?? 0
42145
- };
42146
- const newHistory = [...history, result];
42147
- setHistory(newHistory);
42148
- if (newHistory.length >= totalRounds) {
42149
- const total = newHistory.reduce((s, r) => s + r.playerPayoff, 0);
42150
- if (total >= targetScore) {
42151
- emit(`UI:${completeEvent}`, { success: true, score: total });
42152
- }
42153
- if (newHistory.length >= 3 && str(resolved?.hint)) {
42197
+ const playerPayoff = payoff?.playerPayoff ?? 0;
42198
+ setHistory((prev) => [
42199
+ ...prev,
42200
+ {
42201
+ round: prev.length + 1,
42202
+ playerAction: actionId,
42203
+ opponentAction,
42204
+ playerPayoff,
42205
+ opponentPayoff: payoff?.opponentPayoff ?? 0
42206
+ }
42207
+ ]);
42208
+ if (playRoundEvent) {
42209
+ emit(`UI:${playRoundEvent}`, { playerAction: actionId, payoff: playerPayoff });
42210
+ }
42211
+ if (totalRounds > 0 && currentRound + 1 >= totalRounds) {
42212
+ if (finishEvent) {
42213
+ emit(`UI:${finishEvent}`, {});
42214
+ }
42215
+ if (str(resolved?.hint)) {
42154
42216
  setShowHint(true);
42155
42217
  }
42156
42218
  }
42157
- }, [isComplete, resolved, totalRounds, targetScore, actions, payoffMatrix, history, currentRound, completeEvent, emit]);
42158
- const handleReset = () => {
42219
+ }, [isComplete, resolved, totalRounds, currentRound, actions, payoffMatrix, history, playRoundEvent, finishEvent, emit]);
42220
+ const handleReset = React83.useCallback(() => {
42159
42221
  setHistory([]);
42160
42222
  setShowHint(false);
42161
- };
42223
+ if (playAgainEvent) {
42224
+ emit(`UI:${playAgainEvent}`, {});
42225
+ }
42226
+ }, [playAgainEvent, emit]);
42227
+ const completedRef = React83.useRef(false);
42228
+ React83.useEffect(() => {
42229
+ if (result === "win" && !completedRef.current) {
42230
+ completedRef.current = true;
42231
+ emit(`UI:${completeEvent}`, { success: true, score: playerTotal });
42232
+ } else if (result === "none") {
42233
+ completedRef.current = false;
42234
+ }
42235
+ }, [result, playerTotal, completeEvent, emit]);
42162
42236
  const getActionLabel = (id) => actions.find((a) => a.id === id)?.label ?? id;
42163
42237
  if (!resolved) return null;
42164
42238
  const theme = resolved.theme ?? void 0;
@@ -45105,67 +45179,47 @@ var init_SimulationGraph = __esm({
45105
45179
  function SimulatorBoard({
45106
45180
  entity,
45107
45181
  completeEvent = "PUZZLE_COMPLETE",
45182
+ setAEvent,
45183
+ setBEvent,
45184
+ checkEvent,
45185
+ playAgainEvent,
45108
45186
  className
45109
45187
  }) {
45110
45188
  const { emit } = useEventBus();
45111
45189
  const { t } = hooks.useTranslate();
45112
45190
  const resolved = boardEntity(entity);
45113
45191
  const parameters = Array.isArray(resolved?.parameters) ? resolved.parameters : [];
45114
- const [values, setValues] = React83.useState(() => {
45115
- const init = {};
45116
- for (const p2 of parameters) {
45117
- init[p2.id] = p2.initial;
45118
- }
45119
- return init;
45120
- });
45121
45192
  const [headerError, setHeaderError] = React83.useState(false);
45122
- const [submitted, setSubmitted] = React83.useState(false);
45123
- const [attempts, setAttempts] = React83.useState(0);
45124
- const [showHint, setShowHint] = React83.useState(false);
45125
- const computeOutput = React83.useCallback((params) => {
45126
- try {
45127
- const fn = new Function("params", `return (${str(resolved?.computeExpression)})`);
45128
- return fn(params);
45129
- } catch {
45130
- return 0;
45131
- }
45132
- }, [resolved?.computeExpression]);
45133
- const output = React83.useMemo(() => computeOutput(values) ?? 0, [computeOutput, values]);
45134
- const targetValue = num(resolved?.targetValue);
45135
- const targetTolerance = num(resolved?.targetTolerance);
45136
- const isCorrect = Math.abs(output - targetValue) <= targetTolerance;
45137
- const handleParameterChange = (id, value) => {
45138
- if (submitted) return;
45139
- setValues((prev) => ({ ...prev, [id]: value }));
45140
- };
45141
- const handleSubmit = () => {
45142
- setSubmitted(true);
45143
- setAttempts((a) => a + 1);
45144
- if (isCorrect) {
45145
- emit(`UI:${completeEvent}`, { success: true, attempts: attempts + 1 });
45146
- }
45193
+ if (!resolved) return null;
45194
+ const paramA = num(resolved.paramA);
45195
+ const paramB = num(resolved.paramB);
45196
+ const output = num(resolved.output);
45197
+ const targetValue = num(resolved.target);
45198
+ const targetTolerance = num(resolved.tolerance);
45199
+ const attempts = num(resolved.attempts);
45200
+ const result = str(resolved.result);
45201
+ const isWin = result === "win";
45202
+ const isComplete = result !== "none" && result !== "";
45203
+ const paramAValue = parameters[0];
45204
+ const paramBValue = parameters[1];
45205
+ const sliderValues = [paramA, paramB];
45206
+ const sliderEvents = [setAEvent, setBEvent];
45207
+ const handleParameterChange = (index, value) => {
45208
+ if (isComplete) return;
45209
+ const ev = sliderEvents[index];
45210
+ if (ev) emit(`UI:${ev}`, { value });
45147
45211
  };
45148
- const handleReset = () => {
45149
- setSubmitted(false);
45150
- if (attempts >= 2 && str(resolved?.hint)) {
45151
- setShowHint(true);
45152
- }
45212
+ const handleCheck = () => {
45213
+ if (checkEvent) emit(`UI:${checkEvent}`, {});
45153
45214
  };
45154
- const handleFullReset = () => {
45155
- const init = {};
45156
- for (const p2 of parameters) {
45157
- init[p2.id] = p2.initial;
45158
- }
45159
- setValues(init);
45160
- setSubmitted(false);
45161
- setAttempts(0);
45162
- setShowHint(false);
45215
+ const handlePlayAgain = () => {
45216
+ if (playAgainEvent) emit(`UI:${playAgainEvent}`, {});
45163
45217
  };
45164
- if (!resolved) return null;
45165
45218
  const theme = resolved.theme ?? void 0;
45166
45219
  const themeBackground = theme?.background;
45167
45220
  const headerImage = str(resolved.headerImage);
45168
45221
  const hint = str(resolved.hint);
45222
+ const showHint = isComplete && !isWin && attempts >= 2 && Boolean(hint);
45169
45223
  const outputLabel = str(resolved.outputLabel);
45170
45224
  const outputUnit = str(resolved.outputUnit);
45171
45225
  return /* @__PURE__ */ jsxRuntime.jsx(
@@ -45185,41 +45239,43 @@ function SimulatorBoard({
45185
45239
  ] }) }),
45186
45240
  /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "md", children: [
45187
45241
  /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", weight: "bold", className: "uppercase tracking-wider text-muted-foreground", children: t("simulator.parameters") }),
45188
- parameters.map((param) => /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "xs", children: [
45189
- /* @__PURE__ */ jsxRuntime.jsxs(HStack, { justify: "between", align: "center", children: [
45190
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", weight: "medium", children: param.label }),
45191
- /* @__PURE__ */ jsxRuntime.jsxs(Badge, { size: "sm", children: [
45192
- values[param.id],
45193
- " ",
45194
- param.unit
45195
- ] })
45196
- ] }),
45197
- /* @__PURE__ */ jsxRuntime.jsx(
45198
- "input",
45199
- {
45200
- type: "range",
45201
- min: param.min,
45202
- max: param.max,
45203
- step: param.step,
45204
- value: values[param.id],
45205
- onChange: (e) => handleParameterChange(param.id, Number(e.target.value)),
45206
- disabled: submitted,
45207
- className: "w-full accent-foreground"
45208
- }
45209
- ),
45210
- /* @__PURE__ */ jsxRuntime.jsxs(HStack, { justify: "between", children: [
45211
- /* @__PURE__ */ jsxRuntime.jsxs(Typography, { variant: "caption", className: "text-muted-foreground", children: [
45212
- param.min,
45213
- " ",
45214
- param.unit
45242
+ [paramAValue, paramBValue].map(
45243
+ (param, index) => param ? /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "xs", children: [
45244
+ /* @__PURE__ */ jsxRuntime.jsxs(HStack, { justify: "between", align: "center", children: [
45245
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", weight: "medium", children: param.label }),
45246
+ /* @__PURE__ */ jsxRuntime.jsxs(Badge, { size: "sm", children: [
45247
+ sliderValues[index],
45248
+ " ",
45249
+ param.unit
45250
+ ] })
45215
45251
  ] }),
45216
- /* @__PURE__ */ jsxRuntime.jsxs(Typography, { variant: "caption", className: "text-muted-foreground", children: [
45217
- param.max,
45218
- " ",
45219
- param.unit
45252
+ /* @__PURE__ */ jsxRuntime.jsx(
45253
+ "input",
45254
+ {
45255
+ type: "range",
45256
+ min: param.min,
45257
+ max: param.max,
45258
+ step: param.step,
45259
+ value: sliderValues[index],
45260
+ onChange: (e) => handleParameterChange(index, Number(e.target.value)),
45261
+ disabled: isComplete,
45262
+ className: "w-full accent-foreground"
45263
+ }
45264
+ ),
45265
+ /* @__PURE__ */ jsxRuntime.jsxs(HStack, { justify: "between", children: [
45266
+ /* @__PURE__ */ jsxRuntime.jsxs(Typography, { variant: "caption", className: "text-muted-foreground", children: [
45267
+ param.min,
45268
+ " ",
45269
+ param.unit
45270
+ ] }),
45271
+ /* @__PURE__ */ jsxRuntime.jsxs(Typography, { variant: "caption", className: "text-muted-foreground", children: [
45272
+ param.max,
45273
+ " ",
45274
+ param.unit
45275
+ ] })
45220
45276
  ] })
45221
- ] })
45222
- ] }, param.id))
45277
+ ] }, param.id ?? index) : null
45278
+ )
45223
45279
  ] }) }),
45224
45280
  /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "sm", align: "center", children: [
45225
45281
  /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", weight: "bold", className: "uppercase tracking-wider text-muted-foreground", children: outputLabel }),
@@ -45228,9 +45284,9 @@ function SimulatorBoard({
45228
45284
  " ",
45229
45285
  outputUnit
45230
45286
  ] }),
45231
- submitted && /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "xs", align: "center", children: [
45232
- /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: isCorrect ? LucideIcons2.CheckCircle : LucideIcons2.XCircle, size: "sm", className: isCorrect ? "text-success" : "text-error" }),
45233
- /* @__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") })
45287
+ isComplete && /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "xs", align: "center", children: [
45288
+ /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: isWin ? LucideIcons2.CheckCircle : LucideIcons2.XCircle, size: "sm", className: isWin ? "text-success" : "text-error" }),
45289
+ /* @__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") })
45234
45290
  ] }),
45235
45291
  /* @__PURE__ */ jsxRuntime.jsxs(Typography, { variant: "caption", className: "text-muted-foreground", children: [
45236
45292
  t("simulator.target"),
@@ -45245,11 +45301,11 @@ function SimulatorBoard({
45245
45301
  ] }) }),
45246
45302
  showHint && hint && /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", children: hint }) }),
45247
45303
  /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "sm", justify: "center", children: [
45248
- !submitted ? /* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "primary", onClick: handleSubmit, children: [
45304
+ !isComplete ? /* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "primary", onClick: handleCheck, children: [
45249
45305
  /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: LucideIcons2.Play, size: "sm" }),
45250
45306
  t("simulator.simulate")
45251
- ] }) : !isCorrect ? /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "primary", onClick: handleReset, children: t("simulator.tryAgain") }) : null,
45252
- /* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "secondary", onClick: handleFullReset, children: [
45307
+ ] }) : null,
45308
+ /* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "secondary", onClick: handlePlayAgain, children: [
45253
45309
  /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: LucideIcons2.RotateCcw, size: "sm" }),
45254
45310
  t("simulator.reset")
45255
45311
  ] })