@almadar/ui 5.34.0 → 5.36.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.
@@ -7997,7 +7997,7 @@ var init_AboutPageTemplate = __esm({
7997
7997
  const resolved = entity && typeof entity === "object" && !Array.isArray(entity) ? entity : void 0;
7998
7998
  if (!resolved) return null;
7999
7999
  return /* @__PURE__ */ jsxRuntime.jsxs(exports.VStack, { gap: "none", className: cn("w-full", className), children: [
8000
- /* @__PURE__ */ jsxRuntime.jsx(
8000
+ resolved.hero && /* @__PURE__ */ jsxRuntime.jsx(
8001
8001
  exports.HeroSection,
8002
8002
  {
8003
8003
  tag: resolved.hero.tag,
@@ -15977,61 +15977,53 @@ var init_Breadcrumb = __esm({
15977
15977
  function BuilderBoard({
15978
15978
  entity,
15979
15979
  completeEvent = "PUZZLE_COMPLETE",
15980
+ placeEvent,
15981
+ checkEvent,
15982
+ playAgainEvent,
15980
15983
  className
15981
15984
  }) {
15982
15985
  const { emit } = useEventBus();
15983
15986
  const { t } = hooks.useTranslate();
15984
15987
  const resolved = boardEntity(entity);
15985
- const [placements, setPlacements] = React77.useState({});
15986
15988
  const [headerError, setHeaderError] = React77.useState(false);
15987
- const [submitted, setSubmitted] = React77.useState(false);
15988
- const [attempts, setAttempts] = React77.useState(0);
15989
- const [showHint, setShowHint] = React77.useState(false);
15989
+ const [selectedComponent, setSelectedComponent] = React77.useState(null);
15990
15990
  const components = Array.isArray(resolved?.components) ? resolved.components : [];
15991
15991
  const slots = Array.isArray(resolved?.slots) ? resolved.slots : [];
15992
+ const placements = {};
15993
+ for (const slot of slots) {
15994
+ if (slot.placedComponentId) placements[slot.id] = slot.placedComponentId;
15995
+ }
15996
+ const attempts = num(resolved?.attempts);
15997
+ const result = str(resolved?.result) || "none";
15998
+ const submitted = result === "win";
15992
15999
  const usedComponentIds = new Set(Object.values(placements));
15993
16000
  const availableComponents = components.filter((c) => !usedComponentIds.has(c.id));
15994
- const [selectedComponent, setSelectedComponent] = React77.useState(null);
15995
- const allPlaced = Object.keys(placements).length === slots.length;
16001
+ const allPlaced = slots.length > 0 && slots.every((s) => Boolean(placements[s.id]));
15996
16002
  const results = submitted ? slots.map((slot) => ({
15997
16003
  slot,
15998
16004
  placed: placements[slot.id],
15999
- correct: placements[slot.id] === slot.acceptsComponentId
16005
+ correct: placements[slot.id] === slot.requiredComponentId
16000
16006
  })) : [];
16001
- const allCorrect = results.length > 0 && results.every((r) => r.correct);
16007
+ const showHint = attempts >= 2 && Boolean(str(resolved?.hint));
16002
16008
  const handlePlaceComponent = (slotId) => {
16003
16009
  if (submitted || !selectedComponent) return;
16004
- setPlacements((prev) => ({ ...prev, [slotId]: selectedComponent }));
16010
+ if (placeEvent) emit(`UI:${placeEvent}`, { slotId, componentId: selectedComponent });
16005
16011
  setSelectedComponent(null);
16006
16012
  };
16007
16013
  const handleRemoveFromSlot = (slotId) => {
16008
16014
  if (submitted) return;
16009
- setPlacements((prev) => {
16010
- const next = { ...prev };
16011
- delete next[slotId];
16012
- return next;
16013
- });
16015
+ if (placeEvent) emit(`UI:${placeEvent}`, { slotId, componentId: "" });
16014
16016
  };
16015
- const handleSubmit = React77.useCallback(() => {
16016
- setSubmitted(true);
16017
- setAttempts((a) => a + 1);
16018
- const correct = slots.every((slot) => placements[slot.id] === slot.acceptsComponentId);
16019
- if (correct) {
16017
+ const handleSubmit = () => {
16018
+ if (checkEvent) emit(`UI:${checkEvent}`, {});
16019
+ const solved = slots.length > 0 && slots.every((s) => placements[s.id] === s.requiredComponentId);
16020
+ if (solved && completeEvent) {
16020
16021
  emit(`UI:${completeEvent}`, { success: true, attempts: attempts + 1 });
16021
16022
  }
16022
- }, [slots, placements, attempts, completeEvent, emit]);
16023
- const handleReset = () => {
16024
- setSubmitted(false);
16025
- if (attempts >= 2 && str(resolved?.hint)) {
16026
- setShowHint(true);
16027
- }
16028
16023
  };
16029
- const handleFullReset = () => {
16030
- setPlacements({});
16031
- setSubmitted(false);
16024
+ const handlePlayAgain = () => {
16032
16025
  setSelectedComponent(null);
16033
- setAttempts(0);
16034
- setShowHint(false);
16026
+ if (playAgainEvent) emit(`UI:${playAgainEvent}`, {});
16035
16027
  };
16036
16028
  const getComponentById = (id) => components.find((c) => c.id === id);
16037
16029
  if (!resolved) return null;
@@ -16081,13 +16073,13 @@ function BuilderBoard({
16081
16073
  /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "small", weight: "bold", className: "uppercase tracking-wider text-muted-foreground", children: t("builder.blueprint") }),
16082
16074
  /* @__PURE__ */ jsxRuntime.jsx(exports.VStack, { gap: "sm", children: slots.map((slot) => {
16083
16075
  const placedComp = placements[slot.id] ? getComponentById(placements[slot.id]) : null;
16084
- const result = results.find((r) => r.slot.id === slot.id);
16076
+ const result2 = results.find((r) => r.slot.id === slot.id);
16085
16077
  return /* @__PURE__ */ jsxRuntime.jsxs(
16086
16078
  exports.HStack,
16087
16079
  {
16088
16080
  gap: "sm",
16089
16081
  align: "center",
16090
- className: `p-3 border-2 rounded ${result ? result.correct ? "border-success" : "border-error" : selectedComponent ? "border-dashed border-foreground cursor-pointer" : "border-border"}`,
16082
+ className: `p-3 border-2 rounded ${result2 ? result2.correct ? "border-success" : "border-error" : selectedComponent ? "border-dashed border-foreground cursor-pointer" : "border-border"}`,
16091
16083
  onClick: () => handlePlaceComponent(slot.id),
16092
16084
  children: [
16093
16085
  /* @__PURE__ */ jsxRuntime.jsxs(exports.VStack, { gap: "none", className: "flex-1", children: [
@@ -16102,7 +16094,7 @@ function BuilderBoard({
16102
16094
  ] }) : null,
16103
16095
  placedComp.label
16104
16096
  ] }),
16105
- result && /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { icon: result.correct ? LucideIcons2.CheckCircle : LucideIcons2.XCircle, size: "sm", className: result.correct ? "text-success" : "text-error" })
16097
+ result2 && /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { icon: result2.correct ? LucideIcons2.CheckCircle : LucideIcons2.XCircle, size: "sm", className: result2.correct ? "text-success" : "text-error" })
16106
16098
  ] }) : /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "caption", className: "text-muted-foreground", children: t("builder.empty") })
16107
16099
  ]
16108
16100
  },
@@ -16111,16 +16103,16 @@ function BuilderBoard({
16111
16103
  }) })
16112
16104
  ] }) }),
16113
16105
  submitted && /* @__PURE__ */ jsxRuntime.jsx(exports.Card, { className: "p-4", children: /* @__PURE__ */ jsxRuntime.jsxs(exports.VStack, { gap: "sm", align: "center", children: [
16114
- /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { icon: allCorrect ? LucideIcons2.CheckCircle : LucideIcons2.XCircle, size: "lg", className: allCorrect ? "text-success" : "text-error" }),
16115
- /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "body", weight: "bold", children: allCorrect ? str(resolved.successMessage) || t("builder.success") : str(resolved.failMessage) || t("builder.incorrect") })
16106
+ /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { icon: LucideIcons2.CheckCircle, size: "lg", className: "text-success" }),
16107
+ /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "body", weight: "bold", children: str(resolved.successMessage) || t("builder.success") })
16116
16108
  ] }) }),
16117
16109
  showHint && hint && /* @__PURE__ */ jsxRuntime.jsx(exports.Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "body", children: hint }) }),
16118
16110
  /* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { gap: "sm", justify: "center", children: [
16119
- !submitted ? /* @__PURE__ */ jsxRuntime.jsxs(exports.Button, { variant: "primary", onClick: handleSubmit, disabled: !allPlaced, children: [
16111
+ !submitted && /* @__PURE__ */ jsxRuntime.jsxs(exports.Button, { variant: "primary", onClick: handleSubmit, disabled: !allPlaced, children: [
16120
16112
  /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { icon: LucideIcons2.Wrench, size: "sm" }),
16121
16113
  t("builder.build")
16122
- ] }) : !allCorrect ? /* @__PURE__ */ jsxRuntime.jsx(exports.Button, { variant: "primary", onClick: handleReset, children: t("builder.tryAgain") }) : null,
16123
- /* @__PURE__ */ jsxRuntime.jsxs(exports.Button, { variant: "secondary", onClick: handleFullReset, children: [
16114
+ ] }),
16115
+ /* @__PURE__ */ jsxRuntime.jsxs(exports.Button, { variant: "secondary", onClick: handlePlayAgain, children: [
16124
16116
  /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { icon: LucideIcons2.RotateCcw, size: "sm" }),
16125
16117
  t("builder.reset")
16126
16118
  ] })
@@ -19622,57 +19614,84 @@ var init_ChoiceButton = __esm({
19622
19614
  function ClassifierBoard({
19623
19615
  entity,
19624
19616
  completeEvent = "PUZZLE_COMPLETE",
19617
+ assignEvent,
19618
+ checkEvent,
19619
+ playAgainEvent,
19625
19620
  className
19626
19621
  }) {
19627
19622
  const { emit } = useEventBus();
19628
19623
  const { t } = hooks.useTranslate();
19629
19624
  const resolved = boardEntity(entity);
19630
- const [assignments, setAssignments] = React77.useState({});
19625
+ const [localAssignments, setLocalAssignments] = React77.useState({});
19631
19626
  const [headerError, setHeaderError] = React77.useState(false);
19632
- const [submitted, setSubmitted] = React77.useState(false);
19633
- const [attempts, setAttempts] = React77.useState(0);
19627
+ const [localSubmitted, setLocalSubmitted] = React77.useState(false);
19628
+ const [localAttempts, setLocalAttempts] = React77.useState(0);
19634
19629
  const [showHint, setShowHint] = React77.useState(false);
19635
19630
  const items = Array.isArray(resolved?.items) ? resolved.items : [];
19636
19631
  const categories = Array.isArray(resolved?.categories) ? resolved.categories : [];
19632
+ const entityResult = str(resolved?.result);
19633
+ const entityDrivesResult = entityResult.length > 0;
19634
+ const entityHasAssignments = items.some((item) => item.assignedCategory != null);
19635
+ const assignments = entityHasAssignments ? items.reduce((acc, item) => {
19636
+ if (item.assignedCategory != null) acc[item.id] = item.assignedCategory;
19637
+ return acc;
19638
+ }, {}) : localAssignments;
19639
+ const attempts = entityDrivesResult ? num(resolved?.attempts) : localAttempts;
19640
+ const submitted = entityDrivesResult || localSubmitted;
19637
19641
  const unassignedItems = items.filter((item) => !assignments[item.id]);
19638
- const allAssigned = Object.keys(assignments).length === items.length;
19642
+ const allAssigned = items.length > 0 && Object.keys(assignments).length === items.length;
19639
19643
  const results = submitted ? items.map((item) => ({
19640
19644
  item,
19641
19645
  assigned: assignments[item.id],
19642
19646
  correct: assignments[item.id] === item.correctCategory
19643
19647
  })) : [];
19644
- const allCorrect = results.length > 0 && results.every((r) => r.correct);
19648
+ const allCorrect = entityDrivesResult ? entityResult === "success" : results.length > 0 && results.every((r) => r.correct);
19645
19649
  const correctCount = results.filter((r) => r.correct).length;
19646
19650
  const handleAssign = (itemId, categoryId) => {
19647
19651
  if (submitted) return;
19648
- setAssignments((prev) => ({ ...prev, [itemId]: categoryId }));
19652
+ if (assignEvent) {
19653
+ emit(`UI:${assignEvent}`, { itemId, categoryId });
19654
+ }
19655
+ if (!entityHasAssignments) {
19656
+ setLocalAssignments((prev) => ({ ...prev, [itemId]: categoryId }));
19657
+ }
19649
19658
  };
19650
19659
  const handleUnassign = (itemId) => {
19651
19660
  if (submitted) return;
19652
- setAssignments((prev) => {
19653
- const next = { ...prev };
19654
- delete next[itemId];
19655
- return next;
19656
- });
19661
+ if (!entityHasAssignments) {
19662
+ setLocalAssignments((prev) => {
19663
+ const next = { ...prev };
19664
+ delete next[itemId];
19665
+ return next;
19666
+ });
19667
+ }
19657
19668
  };
19658
19669
  const handleSubmit = React77.useCallback(() => {
19659
- setSubmitted(true);
19660
- setAttempts((a) => a + 1);
19661
- const correct = items.every((item) => assignments[item.id] === item.correctCategory);
19662
- if (correct) {
19663
- emit(`UI:${completeEvent}`, { success: true, attempts: attempts + 1 });
19670
+ if (checkEvent) {
19671
+ emit(`UI:${checkEvent}`, {});
19672
+ }
19673
+ if (!entityDrivesResult) {
19674
+ setLocalSubmitted(true);
19675
+ setLocalAttempts((a) => a + 1);
19676
+ const correct = items.every((item) => assignments[item.id] === item.correctCategory);
19677
+ if (correct) {
19678
+ emit(`UI:${completeEvent}`, { success: true, attempts: attempts + 1 });
19679
+ }
19664
19680
  }
19665
- }, [items, assignments, attempts, completeEvent, emit]);
19681
+ }, [checkEvent, entityDrivesResult, items, assignments, attempts, completeEvent, emit]);
19666
19682
  const handleReset = () => {
19667
- setSubmitted(false);
19683
+ if (!entityDrivesResult) setLocalSubmitted(false);
19668
19684
  if (attempts >= 2 && str(resolved?.hint)) {
19669
19685
  setShowHint(true);
19670
19686
  }
19671
19687
  };
19672
19688
  const handleFullReset = () => {
19673
- setAssignments({});
19674
- setSubmitted(false);
19675
- setAttempts(0);
19689
+ if (playAgainEvent) {
19690
+ emit(`UI:${playAgainEvent}`, {});
19691
+ }
19692
+ setLocalAssignments({});
19693
+ setLocalSubmitted(false);
19694
+ setLocalAttempts(0);
19676
19695
  setShowHint(false);
19677
19696
  };
19678
19697
  if (!resolved) return null;
@@ -29211,13 +29230,13 @@ var init_MapView = __esm({
29211
29230
  shadowSize: [41, 41]
29212
29231
  });
29213
29232
  L.Marker.prototype.options.icon = defaultIcon;
29214
- const { useEffect: useEffect72, useRef: useRef69, useCallback: useCallback115, useState: useState105 } = React77__namespace.default;
29233
+ const { useEffect: useEffect73, useRef: useRef70, useCallback: useCallback113, useState: useState105 } = React77__namespace.default;
29215
29234
  const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
29216
29235
  const { useEventBus: useEventBus2 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
29217
29236
  function MapUpdater({ centerLat, centerLng, zoom }) {
29218
29237
  const map = useMap();
29219
- const prevRef = useRef69({ centerLat, centerLng, zoom });
29220
- useEffect72(() => {
29238
+ const prevRef = useRef70({ centerLat, centerLng, zoom });
29239
+ useEffect73(() => {
29221
29240
  const prev = prevRef.current;
29222
29241
  if (prev.centerLat !== centerLat || prev.centerLng !== centerLng || prev.zoom !== zoom) {
29223
29242
  map.setView([centerLat, centerLng], zoom);
@@ -29228,7 +29247,7 @@ var init_MapView = __esm({
29228
29247
  }
29229
29248
  function MapClickHandler({ onMapClick }) {
29230
29249
  const map = useMap();
29231
- useEffect72(() => {
29250
+ useEffect73(() => {
29232
29251
  if (!onMapClick) return;
29233
29252
  const handler = (e) => {
29234
29253
  onMapClick(e.latlng.lat, e.latlng.lng);
@@ -29257,7 +29276,7 @@ var init_MapView = __esm({
29257
29276
  }) {
29258
29277
  const eventBus = useEventBus2();
29259
29278
  const [clickedPosition, setClickedPosition] = useState105(null);
29260
- const handleMapClick = useCallback115((lat, lng) => {
29279
+ const handleMapClick = useCallback113((lat, lng) => {
29261
29280
  if (showClickedPin) {
29262
29281
  setClickedPosition({ lat, lng });
29263
29282
  }
@@ -29266,7 +29285,7 @@ var init_MapView = __esm({
29266
29285
  eventBus.emit(`UI:${mapClickEvent}`, { latitude: lat, longitude: lng });
29267
29286
  }
29268
29287
  }, [onMapClick, mapClickEvent, eventBus, showClickedPin]);
29269
- const handleMarkerClick = useCallback115((marker) => {
29288
+ const handleMarkerClick = useCallback113((marker) => {
29270
29289
  onMarkerClick?.(marker);
29271
29290
  if (markerClickEvent) {
29272
29291
  eventBus.emit(`UI:${markerClickEvent}`, { ...marker });
@@ -38700,51 +38719,52 @@ var init_DataTable = __esm({
38700
38719
  function DebuggerBoard({
38701
38720
  entity,
38702
38721
  completeEvent = "PUZZLE_COMPLETE",
38722
+ toggleFlagEvent,
38723
+ checkEvent,
38724
+ playAgainEvent,
38703
38725
  className
38704
38726
  }) {
38705
38727
  const { emit } = useEventBus();
38706
38728
  const { t } = hooks.useTranslate();
38707
38729
  const resolved = boardEntity(entity);
38708
- const [flaggedLines, setFlaggedLines] = React77.useState(/* @__PURE__ */ new Set());
38709
38730
  const [headerError, setHeaderError] = React77.useState(false);
38710
- const [submitted, setSubmitted] = React77.useState(false);
38711
- const [attempts, setAttempts] = React77.useState(0);
38712
38731
  const [showHint, setShowHint] = React77.useState(false);
38732
+ const lines = Array.isArray(resolved?.lines) ? resolved.lines : [];
38733
+ const result = resolved?.result ?? null;
38734
+ const attempts = num(resolved?.attempts);
38735
+ const submitted = result != null;
38736
+ const bugLines = lines.filter((l) => l.isBug);
38737
+ const flaggedLines = lines.filter((l) => l.isFlagged);
38738
+ const correctFlags = lines.filter((l) => l.isBug && l.isFlagged);
38739
+ const falseFlags = lines.filter((l) => !l.isBug && l.isFlagged);
38740
+ const allCorrect = result === "win";
38713
38741
  const toggleLine = (lineId) => {
38714
38742
  if (submitted) return;
38715
- setFlaggedLines((prev) => {
38716
- const next = new Set(prev);
38717
- if (next.has(lineId)) {
38718
- next.delete(lineId);
38719
- } else {
38720
- next.add(lineId);
38721
- }
38722
- return next;
38723
- });
38743
+ if (toggleFlagEvent) {
38744
+ emit(`UI:${toggleFlagEvent}`, { lineId });
38745
+ }
38724
38746
  };
38725
- const lines = Array.isArray(resolved?.lines) ? resolved.lines : [];
38726
- const bugLines = lines.filter((l) => l.isBug);
38727
- const correctFlags = lines.filter((l) => l.isBug && flaggedLines.has(l.id));
38728
- const falseFlags = lines.filter((l) => !l.isBug && flaggedLines.has(l.id));
38729
- const allCorrect = submitted && correctFlags.length === bugLines.length && falseFlags.length === 0;
38730
38747
  const handleSubmit = React77.useCallback(() => {
38731
- setSubmitted(true);
38732
- setAttempts((a) => a + 1);
38748
+ if (checkEvent) {
38749
+ emit(`UI:${checkEvent}`, {});
38750
+ }
38733
38751
  const correct = correctFlags.length === bugLines.length && falseFlags.length === 0;
38734
38752
  if (correct) {
38735
38753
  emit(`UI:${completeEvent}`, { success: true, attempts: attempts + 1 });
38736
38754
  }
38737
- }, [correctFlags.length, bugLines.length, falseFlags.length, attempts, completeEvent, emit]);
38755
+ }, [checkEvent, correctFlags.length, bugLines.length, falseFlags.length, attempts, completeEvent, emit]);
38738
38756
  const handleReset = () => {
38739
- setSubmitted(false);
38757
+ if (playAgainEvent) {
38758
+ emit(`UI:${playAgainEvent}`, {});
38759
+ }
38740
38760
  if (attempts >= 2 && str(resolved?.hint)) {
38741
38761
  setShowHint(true);
38742
38762
  }
38743
38763
  };
38744
38764
  const handleFullReset = () => {
38745
- setFlaggedLines(/* @__PURE__ */ new Set());
38746
- setSubmitted(false);
38747
- setAttempts(0);
38765
+ if (playAgainEvent) {
38766
+ emit(`UI:${playAgainEvent}`, {});
38767
+ }
38748
38768
  setShowHint(false);
38749
38769
  };
38750
38770
  if (!resolved) return null;
@@ -38772,7 +38792,7 @@ function DebuggerBoard({
38772
38792
  /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "caption", className: "text-muted-foreground", children: t("debugger.findBugs", { count: String(num(resolved.bugCount)) }) })
38773
38793
  ] }) }),
38774
38794
  /* @__PURE__ */ jsxRuntime.jsx(exports.Card, { className: "p-0 overflow-hidden", children: /* @__PURE__ */ jsxRuntime.jsx(exports.VStack, { gap: "none", children: lines.map((line, i) => {
38775
- const isFlagged = flaggedLines.has(line.id);
38795
+ const isFlagged = !!line.isFlagged;
38776
38796
  let lineStyle = "";
38777
38797
  if (submitted) {
38778
38798
  if (line.isBug && isFlagged) lineStyle = "bg-success/10";
@@ -38806,9 +38826,9 @@ function DebuggerBoard({
38806
38826
  /* @__PURE__ */ jsxRuntime.jsx(
38807
38827
  exports.Icon,
38808
38828
  {
38809
- icon: flaggedLines.has(line.id) ? LucideIcons2.CheckCircle : LucideIcons2.XCircle,
38829
+ icon: line.isFlagged ? LucideIcons2.CheckCircle : LucideIcons2.XCircle,
38810
38830
  size: "xs",
38811
- className: flaggedLines.has(line.id) ? "text-success mt-0.5" : "text-warning mt-0.5"
38831
+ className: line.isFlagged ? "text-success mt-0.5" : "text-warning mt-0.5"
38812
38832
  }
38813
38833
  ),
38814
38834
  /* @__PURE__ */ jsxRuntime.jsxs(exports.VStack, { gap: "none", children: [
@@ -38819,7 +38839,7 @@ function DebuggerBoard({
38819
38839
  ] }) }),
38820
38840
  showHint && hint && /* @__PURE__ */ jsxRuntime.jsx(exports.Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "body", children: hint }) }),
38821
38841
  /* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { gap: "sm", justify: "center", children: [
38822
- !submitted ? /* @__PURE__ */ jsxRuntime.jsxs(exports.Button, { variant: "primary", onClick: handleSubmit, disabled: flaggedLines.size === 0, children: [
38842
+ !submitted ? /* @__PURE__ */ jsxRuntime.jsxs(exports.Button, { variant: "primary", onClick: handleSubmit, disabled: flaggedLines.length === 0, children: [
38823
38843
  /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { icon: LucideIcons2.Send, size: "sm" }),
38824
38844
  t("debugger.submit")
38825
38845
  ] }) : !allCorrect ? /* @__PURE__ */ jsxRuntime.jsx(exports.Button, { variant: "primary", onClick: handleReset, children: t("debugger.tryAgain") }) : null,
@@ -40152,7 +40172,7 @@ var init_FeatureDetailPageTemplate = __esm({
40152
40172
  const resolved = entity && typeof entity === "object" && !Array.isArray(entity) ? entity : void 0;
40153
40173
  if (!resolved) return null;
40154
40174
  return /* @__PURE__ */ jsxRuntime.jsxs(exports.VStack, { gap: "none", className: cn("w-full", className), children: [
40155
- /* @__PURE__ */ jsxRuntime.jsx(
40175
+ resolved.hero && /* @__PURE__ */ jsxRuntime.jsx(
40156
40176
  exports.HeroSection,
40157
40177
  {
40158
40178
  tag: resolved.hero.tag,
@@ -41679,7 +41699,7 @@ var init_LandingPageTemplate = __esm({
41679
41699
  const resolved = entity && typeof entity === "object" && !Array.isArray(entity) ? entity : void 0;
41680
41700
  if (!resolved) return null;
41681
41701
  return /* @__PURE__ */ jsxRuntime.jsxs(exports.VStack, { gap: "none", className: cn("w-full", className), children: [
41682
- /* @__PURE__ */ jsxRuntime.jsx(
41702
+ resolved.hero && /* @__PURE__ */ jsxRuntime.jsx(
41683
41703
  exports.HeroSection,
41684
41704
  {
41685
41705
  tag: resolved.hero.tag,
@@ -42779,6 +42799,9 @@ function getOpponentAction(strategy, actions, history) {
42779
42799
  function NegotiatorBoard({
42780
42800
  entity,
42781
42801
  completeEvent = "PUZZLE_COMPLETE",
42802
+ playRoundEvent,
42803
+ finishEvent,
42804
+ playAgainEvent,
42782
42805
  className
42783
42806
  }) {
42784
42807
  const { emit } = useEventBus();
@@ -42787,13 +42810,14 @@ function NegotiatorBoard({
42787
42810
  const [history, setHistory] = React77.useState([]);
42788
42811
  const [headerError, setHeaderError] = React77.useState(false);
42789
42812
  const [showHint, setShowHint] = React77.useState(false);
42790
- const totalRounds = num(resolved?.totalRounds);
42813
+ const totalRounds = num(resolved?.maxRounds);
42791
42814
  const targetScore = num(resolved?.targetScore);
42792
- const currentRound = history.length;
42793
- const isComplete = currentRound >= totalRounds;
42794
- const playerTotal = history.reduce((s, r) => s + r.playerPayoff, 0);
42815
+ const currentRound = num(resolved?.round);
42816
+ const result = str(resolved?.result) || "none";
42817
+ const playerTotal = num(resolved?.score);
42818
+ const isComplete = result !== "none" || totalRounds > 0 && currentRound >= totalRounds;
42819
+ const won = result === "win";
42795
42820
  const opponentTotal = history.reduce((s, r) => s + r.opponentPayoff, 0);
42796
- const won = isComplete && playerTotal >= targetScore;
42797
42821
  const actions = Array.isArray(resolved?.actions) ? resolved.actions : [];
42798
42822
  const payoffMatrix = Array.isArray(resolved?.payoffMatrix) ? resolved.payoffMatrix : [];
42799
42823
  const handleAction = React77.useCallback((actionId) => {
@@ -42802,29 +42826,45 @@ function NegotiatorBoard({
42802
42826
  const payoff = payoffMatrix.find(
42803
42827
  (p2) => p2.playerAction === actionId && p2.opponentAction === opponentAction
42804
42828
  );
42805
- const result = {
42806
- round: currentRound + 1,
42807
- playerAction: actionId,
42808
- opponentAction,
42809
- playerPayoff: payoff?.playerPayoff ?? 0,
42810
- opponentPayoff: payoff?.opponentPayoff ?? 0
42811
- };
42812
- const newHistory = [...history, result];
42813
- setHistory(newHistory);
42814
- if (newHistory.length >= totalRounds) {
42815
- const total = newHistory.reduce((s, r) => s + r.playerPayoff, 0);
42816
- if (total >= targetScore) {
42817
- emit(`UI:${completeEvent}`, { success: true, score: total });
42818
- }
42819
- if (newHistory.length >= 3 && str(resolved?.hint)) {
42829
+ const playerPayoff = payoff?.playerPayoff ?? 0;
42830
+ setHistory((prev) => [
42831
+ ...prev,
42832
+ {
42833
+ round: prev.length + 1,
42834
+ playerAction: actionId,
42835
+ opponentAction,
42836
+ playerPayoff,
42837
+ opponentPayoff: payoff?.opponentPayoff ?? 0
42838
+ }
42839
+ ]);
42840
+ if (playRoundEvent) {
42841
+ emit(`UI:${playRoundEvent}`, { playerAction: actionId, payoff: playerPayoff });
42842
+ }
42843
+ if (totalRounds > 0 && currentRound + 1 >= totalRounds) {
42844
+ if (finishEvent) {
42845
+ emit(`UI:${finishEvent}`, {});
42846
+ }
42847
+ if (str(resolved?.hint)) {
42820
42848
  setShowHint(true);
42821
42849
  }
42822
42850
  }
42823
- }, [isComplete, resolved, totalRounds, targetScore, actions, payoffMatrix, history, currentRound, completeEvent, emit]);
42824
- const handleReset = () => {
42851
+ }, [isComplete, resolved, totalRounds, currentRound, actions, payoffMatrix, history, playRoundEvent, finishEvent, emit]);
42852
+ const handleReset = React77.useCallback(() => {
42825
42853
  setHistory([]);
42826
42854
  setShowHint(false);
42827
- };
42855
+ if (playAgainEvent) {
42856
+ emit(`UI:${playAgainEvent}`, {});
42857
+ }
42858
+ }, [playAgainEvent, emit]);
42859
+ const completedRef = React77.useRef(false);
42860
+ React77.useEffect(() => {
42861
+ if (result === "win" && !completedRef.current) {
42862
+ completedRef.current = true;
42863
+ emit(`UI:${completeEvent}`, { success: true, score: playerTotal });
42864
+ } else if (result === "none") {
42865
+ completedRef.current = false;
42866
+ }
42867
+ }, [result, playerTotal, completeEvent, emit]);
42828
42868
  const getActionLabel = (id) => actions.find((a) => a.id === id)?.label ?? id;
42829
42869
  if (!resolved) return null;
42830
42870
  const theme = resolved.theme ?? void 0;
@@ -42993,7 +43033,7 @@ var init_PricingPageTemplate = __esm({
42993
43033
  const resolved = entity && typeof entity === "object" && !Array.isArray(entity) ? entity : void 0;
42994
43034
  if (!resolved) return null;
42995
43035
  return /* @__PURE__ */ jsxRuntime.jsxs(exports.VStack, { gap: "none", className: cn("w-full", className), children: [
42996
- /* @__PURE__ */ jsxRuntime.jsx(
43036
+ resolved.hero && /* @__PURE__ */ jsxRuntime.jsx(
42997
43037
  exports.HeroSection,
42998
43038
  {
42999
43039
  tag: resolved.hero.tag,
@@ -45825,67 +45865,47 @@ var init_SimulationGraph = __esm({
45825
45865
  function SimulatorBoard({
45826
45866
  entity,
45827
45867
  completeEvent = "PUZZLE_COMPLETE",
45868
+ setAEvent,
45869
+ setBEvent,
45870
+ checkEvent,
45871
+ playAgainEvent,
45828
45872
  className
45829
45873
  }) {
45830
45874
  const { emit } = useEventBus();
45831
45875
  const { t } = hooks.useTranslate();
45832
45876
  const resolved = boardEntity(entity);
45833
45877
  const parameters = Array.isArray(resolved?.parameters) ? resolved.parameters : [];
45834
- const [values, setValues] = React77.useState(() => {
45835
- const init = {};
45836
- for (const p2 of parameters) {
45837
- init[p2.id] = p2.initial;
45838
- }
45839
- return init;
45840
- });
45841
45878
  const [headerError, setHeaderError] = React77.useState(false);
45842
- const [submitted, setSubmitted] = React77.useState(false);
45843
- const [attempts, setAttempts] = React77.useState(0);
45844
- const [showHint, setShowHint] = React77.useState(false);
45845
- const computeOutput = React77.useCallback((params) => {
45846
- try {
45847
- const fn = new Function("params", `return (${str(resolved?.computeExpression)})`);
45848
- return fn(params);
45849
- } catch {
45850
- return 0;
45851
- }
45852
- }, [resolved?.computeExpression]);
45853
- const output = React77.useMemo(() => computeOutput(values) ?? 0, [computeOutput, values]);
45854
- const targetValue = num(resolved?.targetValue);
45855
- const targetTolerance = num(resolved?.targetTolerance);
45856
- const isCorrect = Math.abs(output - targetValue) <= targetTolerance;
45857
- const handleParameterChange = (id, value) => {
45858
- if (submitted) return;
45859
- setValues((prev) => ({ ...prev, [id]: value }));
45860
- };
45861
- const handleSubmit = () => {
45862
- setSubmitted(true);
45863
- setAttempts((a) => a + 1);
45864
- if (isCorrect) {
45865
- emit(`UI:${completeEvent}`, { success: true, attempts: attempts + 1 });
45866
- }
45879
+ if (!resolved) return null;
45880
+ const paramA = num(resolved.paramA);
45881
+ const paramB = num(resolved.paramB);
45882
+ const output = num(resolved.output);
45883
+ const targetValue = num(resolved.target);
45884
+ const targetTolerance = num(resolved.tolerance);
45885
+ const attempts = num(resolved.attempts);
45886
+ const result = str(resolved.result);
45887
+ const isWin = result === "win";
45888
+ const isComplete = result !== "none" && result !== "";
45889
+ const paramAValue = parameters[0];
45890
+ const paramBValue = parameters[1];
45891
+ const sliderValues = [paramA, paramB];
45892
+ const sliderEvents = [setAEvent, setBEvent];
45893
+ const handleParameterChange = (index, value) => {
45894
+ if (isComplete) return;
45895
+ const ev = sliderEvents[index];
45896
+ if (ev) emit(`UI:${ev}`, { value });
45867
45897
  };
45868
- const handleReset = () => {
45869
- setSubmitted(false);
45870
- if (attempts >= 2 && str(resolved?.hint)) {
45871
- setShowHint(true);
45872
- }
45898
+ const handleCheck = () => {
45899
+ if (checkEvent) emit(`UI:${checkEvent}`, {});
45873
45900
  };
45874
- const handleFullReset = () => {
45875
- const init = {};
45876
- for (const p2 of parameters) {
45877
- init[p2.id] = p2.initial;
45878
- }
45879
- setValues(init);
45880
- setSubmitted(false);
45881
- setAttempts(0);
45882
- setShowHint(false);
45901
+ const handlePlayAgain = () => {
45902
+ if (playAgainEvent) emit(`UI:${playAgainEvent}`, {});
45883
45903
  };
45884
- if (!resolved) return null;
45885
45904
  const theme = resolved.theme ?? void 0;
45886
45905
  const themeBackground = theme?.background;
45887
45906
  const headerImage = str(resolved.headerImage);
45888
45907
  const hint = str(resolved.hint);
45908
+ const showHint = isComplete && !isWin && attempts >= 2 && Boolean(hint);
45889
45909
  const outputLabel = str(resolved.outputLabel);
45890
45910
  const outputUnit = str(resolved.outputUnit);
45891
45911
  return /* @__PURE__ */ jsxRuntime.jsx(
@@ -45905,41 +45925,43 @@ function SimulatorBoard({
45905
45925
  ] }) }),
45906
45926
  /* @__PURE__ */ jsxRuntime.jsx(exports.Card, { className: "p-4", children: /* @__PURE__ */ jsxRuntime.jsxs(exports.VStack, { gap: "md", children: [
45907
45927
  /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "small", weight: "bold", className: "uppercase tracking-wider text-muted-foreground", children: t("simulator.parameters") }),
45908
- parameters.map((param) => /* @__PURE__ */ jsxRuntime.jsxs(exports.VStack, { gap: "xs", children: [
45909
- /* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { justify: "between", align: "center", children: [
45910
- /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "body", weight: "medium", children: param.label }),
45911
- /* @__PURE__ */ jsxRuntime.jsxs(exports.Badge, { size: "sm", children: [
45912
- values[param.id],
45913
- " ",
45914
- param.unit
45915
- ] })
45916
- ] }),
45917
- /* @__PURE__ */ jsxRuntime.jsx(
45918
- "input",
45919
- {
45920
- type: "range",
45921
- min: param.min,
45922
- max: param.max,
45923
- step: param.step,
45924
- value: values[param.id],
45925
- onChange: (e) => handleParameterChange(param.id, Number(e.target.value)),
45926
- disabled: submitted,
45927
- className: "w-full accent-foreground"
45928
- }
45929
- ),
45930
- /* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { justify: "between", children: [
45931
- /* @__PURE__ */ jsxRuntime.jsxs(exports.Typography, { variant: "caption", className: "text-muted-foreground", children: [
45932
- param.min,
45933
- " ",
45934
- param.unit
45928
+ [paramAValue, paramBValue].map(
45929
+ (param, index) => param ? /* @__PURE__ */ jsxRuntime.jsxs(exports.VStack, { gap: "xs", children: [
45930
+ /* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { justify: "between", align: "center", children: [
45931
+ /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "body", weight: "medium", children: param.label }),
45932
+ /* @__PURE__ */ jsxRuntime.jsxs(exports.Badge, { size: "sm", children: [
45933
+ sliderValues[index],
45934
+ " ",
45935
+ param.unit
45936
+ ] })
45935
45937
  ] }),
45936
- /* @__PURE__ */ jsxRuntime.jsxs(exports.Typography, { variant: "caption", className: "text-muted-foreground", children: [
45937
- param.max,
45938
- " ",
45939
- param.unit
45938
+ /* @__PURE__ */ jsxRuntime.jsx(
45939
+ "input",
45940
+ {
45941
+ type: "range",
45942
+ min: param.min,
45943
+ max: param.max,
45944
+ step: param.step,
45945
+ value: sliderValues[index],
45946
+ onChange: (e) => handleParameterChange(index, Number(e.target.value)),
45947
+ disabled: isComplete,
45948
+ className: "w-full accent-foreground"
45949
+ }
45950
+ ),
45951
+ /* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { justify: "between", children: [
45952
+ /* @__PURE__ */ jsxRuntime.jsxs(exports.Typography, { variant: "caption", className: "text-muted-foreground", children: [
45953
+ param.min,
45954
+ " ",
45955
+ param.unit
45956
+ ] }),
45957
+ /* @__PURE__ */ jsxRuntime.jsxs(exports.Typography, { variant: "caption", className: "text-muted-foreground", children: [
45958
+ param.max,
45959
+ " ",
45960
+ param.unit
45961
+ ] })
45940
45962
  ] })
45941
- ] })
45942
- ] }, param.id))
45963
+ ] }, param.id ?? index) : null
45964
+ )
45943
45965
  ] }) }),
45944
45966
  /* @__PURE__ */ jsxRuntime.jsx(exports.Card, { className: "p-4", children: /* @__PURE__ */ jsxRuntime.jsxs(exports.VStack, { gap: "sm", align: "center", children: [
45945
45967
  /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "small", weight: "bold", className: "uppercase tracking-wider text-muted-foreground", children: outputLabel }),
@@ -45948,9 +45970,9 @@ function SimulatorBoard({
45948
45970
  " ",
45949
45971
  outputUnit
45950
45972
  ] }),
45951
- submitted && /* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { gap: "xs", align: "center", children: [
45952
- /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { icon: isCorrect ? LucideIcons2.CheckCircle : LucideIcons2.XCircle, size: "sm", className: isCorrect ? "text-success" : "text-error" }),
45953
- /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "body", className: isCorrect ? "text-success" : "text-error", children: isCorrect ? str(resolved.successMessage) || t("simulator.correct") : str(resolved.failMessage) || t("simulator.incorrect") })
45973
+ isComplete && /* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { gap: "xs", align: "center", children: [
45974
+ /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { icon: isWin ? LucideIcons2.CheckCircle : LucideIcons2.XCircle, size: "sm", className: isWin ? "text-success" : "text-error" }),
45975
+ /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "body", className: isWin ? "text-success" : "text-error", children: isWin ? str(resolved.successMessage) || t("simulator.correct") : str(resolved.failMessage) || t("simulator.incorrect") })
45954
45976
  ] }),
45955
45977
  /* @__PURE__ */ jsxRuntime.jsxs(exports.Typography, { variant: "caption", className: "text-muted-foreground", children: [
45956
45978
  t("simulator.target"),
@@ -45965,11 +45987,11 @@ function SimulatorBoard({
45965
45987
  ] }) }),
45966
45988
  showHint && hint && /* @__PURE__ */ jsxRuntime.jsx(exports.Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "body", children: hint }) }),
45967
45989
  /* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { gap: "sm", justify: "center", children: [
45968
- !submitted ? /* @__PURE__ */ jsxRuntime.jsxs(exports.Button, { variant: "primary", onClick: handleSubmit, children: [
45990
+ !isComplete ? /* @__PURE__ */ jsxRuntime.jsxs(exports.Button, { variant: "primary", onClick: handleCheck, children: [
45969
45991
  /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { icon: LucideIcons2.Play, size: "sm" }),
45970
45992
  t("simulator.simulate")
45971
- ] }) : !isCorrect ? /* @__PURE__ */ jsxRuntime.jsx(exports.Button, { variant: "primary", onClick: handleReset, children: t("simulator.tryAgain") }) : null,
45972
- /* @__PURE__ */ jsxRuntime.jsxs(exports.Button, { variant: "secondary", onClick: handleFullReset, children: [
45993
+ ] }) : null,
45994
+ /* @__PURE__ */ jsxRuntime.jsxs(exports.Button, { variant: "secondary", onClick: handlePlayAgain, children: [
45973
45995
  /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { icon: LucideIcons2.RotateCcw, size: "sm" }),
45974
45996
  t("simulator.reset")
45975
45997
  ] })