@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.
@@ -7949,7 +7949,7 @@ var init_AboutPageTemplate = __esm({
7949
7949
  const resolved = entity && typeof entity === "object" && !Array.isArray(entity) ? entity : void 0;
7950
7950
  if (!resolved) return null;
7951
7951
  return /* @__PURE__ */ jsxs(VStack, { gap: "none", className: cn("w-full", className), children: [
7952
- /* @__PURE__ */ jsx(
7952
+ resolved.hero && /* @__PURE__ */ jsx(
7953
7953
  HeroSection,
7954
7954
  {
7955
7955
  tag: resolved.hero.tag,
@@ -15929,61 +15929,53 @@ var init_Breadcrumb = __esm({
15929
15929
  function BuilderBoard({
15930
15930
  entity,
15931
15931
  completeEvent = "PUZZLE_COMPLETE",
15932
+ placeEvent,
15933
+ checkEvent,
15934
+ playAgainEvent,
15932
15935
  className
15933
15936
  }) {
15934
15937
  const { emit } = useEventBus();
15935
15938
  const { t } = useTranslate();
15936
15939
  const resolved = boardEntity(entity);
15937
- const [placements, setPlacements] = useState({});
15938
15940
  const [headerError, setHeaderError] = useState(false);
15939
- const [submitted, setSubmitted] = useState(false);
15940
- const [attempts, setAttempts] = useState(0);
15941
- const [showHint, setShowHint] = useState(false);
15941
+ const [selectedComponent, setSelectedComponent] = useState(null);
15942
15942
  const components = Array.isArray(resolved?.components) ? resolved.components : [];
15943
15943
  const slots = Array.isArray(resolved?.slots) ? resolved.slots : [];
15944
+ const placements = {};
15945
+ for (const slot of slots) {
15946
+ if (slot.placedComponentId) placements[slot.id] = slot.placedComponentId;
15947
+ }
15948
+ const attempts = num(resolved?.attempts);
15949
+ const result = str(resolved?.result) || "none";
15950
+ const submitted = result === "win";
15944
15951
  const usedComponentIds = new Set(Object.values(placements));
15945
15952
  const availableComponents = components.filter((c) => !usedComponentIds.has(c.id));
15946
- const [selectedComponent, setSelectedComponent] = useState(null);
15947
- const allPlaced = Object.keys(placements).length === slots.length;
15953
+ const allPlaced = slots.length > 0 && slots.every((s) => Boolean(placements[s.id]));
15948
15954
  const results = submitted ? slots.map((slot) => ({
15949
15955
  slot,
15950
15956
  placed: placements[slot.id],
15951
- correct: placements[slot.id] === slot.acceptsComponentId
15957
+ correct: placements[slot.id] === slot.requiredComponentId
15952
15958
  })) : [];
15953
- const allCorrect = results.length > 0 && results.every((r) => r.correct);
15959
+ const showHint = attempts >= 2 && Boolean(str(resolved?.hint));
15954
15960
  const handlePlaceComponent = (slotId) => {
15955
15961
  if (submitted || !selectedComponent) return;
15956
- setPlacements((prev) => ({ ...prev, [slotId]: selectedComponent }));
15962
+ if (placeEvent) emit(`UI:${placeEvent}`, { slotId, componentId: selectedComponent });
15957
15963
  setSelectedComponent(null);
15958
15964
  };
15959
15965
  const handleRemoveFromSlot = (slotId) => {
15960
15966
  if (submitted) return;
15961
- setPlacements((prev) => {
15962
- const next = { ...prev };
15963
- delete next[slotId];
15964
- return next;
15965
- });
15967
+ if (placeEvent) emit(`UI:${placeEvent}`, { slotId, componentId: "" });
15966
15968
  };
15967
- const handleSubmit = useCallback(() => {
15968
- setSubmitted(true);
15969
- setAttempts((a) => a + 1);
15970
- const correct = slots.every((slot) => placements[slot.id] === slot.acceptsComponentId);
15971
- if (correct) {
15969
+ const handleSubmit = () => {
15970
+ if (checkEvent) emit(`UI:${checkEvent}`, {});
15971
+ const solved = slots.length > 0 && slots.every((s) => placements[s.id] === s.requiredComponentId);
15972
+ if (solved && completeEvent) {
15972
15973
  emit(`UI:${completeEvent}`, { success: true, attempts: attempts + 1 });
15973
15974
  }
15974
- }, [slots, placements, attempts, completeEvent, emit]);
15975
- const handleReset = () => {
15976
- setSubmitted(false);
15977
- if (attempts >= 2 && str(resolved?.hint)) {
15978
- setShowHint(true);
15979
- }
15980
15975
  };
15981
- const handleFullReset = () => {
15982
- setPlacements({});
15983
- setSubmitted(false);
15976
+ const handlePlayAgain = () => {
15984
15977
  setSelectedComponent(null);
15985
- setAttempts(0);
15986
- setShowHint(false);
15978
+ if (playAgainEvent) emit(`UI:${playAgainEvent}`, {});
15987
15979
  };
15988
15980
  const getComponentById = (id) => components.find((c) => c.id === id);
15989
15981
  if (!resolved) return null;
@@ -16033,13 +16025,13 @@ function BuilderBoard({
16033
16025
  /* @__PURE__ */ jsx(Typography, { variant: "small", weight: "bold", className: "uppercase tracking-wider text-muted-foreground", children: t("builder.blueprint") }),
16034
16026
  /* @__PURE__ */ jsx(VStack, { gap: "sm", children: slots.map((slot) => {
16035
16027
  const placedComp = placements[slot.id] ? getComponentById(placements[slot.id]) : null;
16036
- const result = results.find((r) => r.slot.id === slot.id);
16028
+ const result2 = results.find((r) => r.slot.id === slot.id);
16037
16029
  return /* @__PURE__ */ jsxs(
16038
16030
  HStack,
16039
16031
  {
16040
16032
  gap: "sm",
16041
16033
  align: "center",
16042
- className: `p-3 border-2 rounded ${result ? result.correct ? "border-success" : "border-error" : selectedComponent ? "border-dashed border-foreground cursor-pointer" : "border-border"}`,
16034
+ className: `p-3 border-2 rounded ${result2 ? result2.correct ? "border-success" : "border-error" : selectedComponent ? "border-dashed border-foreground cursor-pointer" : "border-border"}`,
16043
16035
  onClick: () => handlePlaceComponent(slot.id),
16044
16036
  children: [
16045
16037
  /* @__PURE__ */ jsxs(VStack, { gap: "none", className: "flex-1", children: [
@@ -16054,7 +16046,7 @@ function BuilderBoard({
16054
16046
  ] }) : null,
16055
16047
  placedComp.label
16056
16048
  ] }),
16057
- result && /* @__PURE__ */ jsx(Icon, { icon: result.correct ? CheckCircle : XCircle, size: "sm", className: result.correct ? "text-success" : "text-error" })
16049
+ result2 && /* @__PURE__ */ jsx(Icon, { icon: result2.correct ? CheckCircle : XCircle, size: "sm", className: result2.correct ? "text-success" : "text-error" })
16058
16050
  ] }) : /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-muted-foreground", children: t("builder.empty") })
16059
16051
  ]
16060
16052
  },
@@ -16063,16 +16055,16 @@ function BuilderBoard({
16063
16055
  }) })
16064
16056
  ] }) }),
16065
16057
  submitted && /* @__PURE__ */ jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", align: "center", children: [
16066
- /* @__PURE__ */ jsx(Icon, { icon: allCorrect ? CheckCircle : XCircle, size: "lg", className: allCorrect ? "text-success" : "text-error" }),
16067
- /* @__PURE__ */ jsx(Typography, { variant: "body", weight: "bold", children: allCorrect ? str(resolved.successMessage) || t("builder.success") : str(resolved.failMessage) || t("builder.incorrect") })
16058
+ /* @__PURE__ */ jsx(Icon, { icon: CheckCircle, size: "lg", className: "text-success" }),
16059
+ /* @__PURE__ */ jsx(Typography, { variant: "body", weight: "bold", children: str(resolved.successMessage) || t("builder.success") })
16068
16060
  ] }) }),
16069
16061
  showHint && hint && /* @__PURE__ */ jsx(Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsx(Typography, { variant: "body", children: hint }) }),
16070
16062
  /* @__PURE__ */ jsxs(HStack, { gap: "sm", justify: "center", children: [
16071
- !submitted ? /* @__PURE__ */ jsxs(Button, { variant: "primary", onClick: handleSubmit, disabled: !allPlaced, children: [
16063
+ !submitted && /* @__PURE__ */ jsxs(Button, { variant: "primary", onClick: handleSubmit, disabled: !allPlaced, children: [
16072
16064
  /* @__PURE__ */ jsx(Icon, { icon: Wrench, size: "sm" }),
16073
16065
  t("builder.build")
16074
- ] }) : !allCorrect ? /* @__PURE__ */ jsx(Button, { variant: "primary", onClick: handleReset, children: t("builder.tryAgain") }) : null,
16075
- /* @__PURE__ */ jsxs(Button, { variant: "secondary", onClick: handleFullReset, children: [
16066
+ ] }),
16067
+ /* @__PURE__ */ jsxs(Button, { variant: "secondary", onClick: handlePlayAgain, children: [
16076
16068
  /* @__PURE__ */ jsx(Icon, { icon: RotateCcw, size: "sm" }),
16077
16069
  t("builder.reset")
16078
16070
  ] })
@@ -19574,57 +19566,84 @@ var init_ChoiceButton = __esm({
19574
19566
  function ClassifierBoard({
19575
19567
  entity,
19576
19568
  completeEvent = "PUZZLE_COMPLETE",
19569
+ assignEvent,
19570
+ checkEvent,
19571
+ playAgainEvent,
19577
19572
  className
19578
19573
  }) {
19579
19574
  const { emit } = useEventBus();
19580
19575
  const { t } = useTranslate();
19581
19576
  const resolved = boardEntity(entity);
19582
- const [assignments, setAssignments] = useState({});
19577
+ const [localAssignments, setLocalAssignments] = useState({});
19583
19578
  const [headerError, setHeaderError] = useState(false);
19584
- const [submitted, setSubmitted] = useState(false);
19585
- const [attempts, setAttempts] = useState(0);
19579
+ const [localSubmitted, setLocalSubmitted] = useState(false);
19580
+ const [localAttempts, setLocalAttempts] = useState(0);
19586
19581
  const [showHint, setShowHint] = useState(false);
19587
19582
  const items = Array.isArray(resolved?.items) ? resolved.items : [];
19588
19583
  const categories = Array.isArray(resolved?.categories) ? resolved.categories : [];
19584
+ const entityResult = str(resolved?.result);
19585
+ const entityDrivesResult = entityResult.length > 0;
19586
+ const entityHasAssignments = items.some((item) => item.assignedCategory != null);
19587
+ const assignments = entityHasAssignments ? items.reduce((acc, item) => {
19588
+ if (item.assignedCategory != null) acc[item.id] = item.assignedCategory;
19589
+ return acc;
19590
+ }, {}) : localAssignments;
19591
+ const attempts = entityDrivesResult ? num(resolved?.attempts) : localAttempts;
19592
+ const submitted = entityDrivesResult || localSubmitted;
19589
19593
  const unassignedItems = items.filter((item) => !assignments[item.id]);
19590
- const allAssigned = Object.keys(assignments).length === items.length;
19594
+ const allAssigned = items.length > 0 && Object.keys(assignments).length === items.length;
19591
19595
  const results = submitted ? items.map((item) => ({
19592
19596
  item,
19593
19597
  assigned: assignments[item.id],
19594
19598
  correct: assignments[item.id] === item.correctCategory
19595
19599
  })) : [];
19596
- const allCorrect = results.length > 0 && results.every((r) => r.correct);
19600
+ const allCorrect = entityDrivesResult ? entityResult === "success" : results.length > 0 && results.every((r) => r.correct);
19597
19601
  const correctCount = results.filter((r) => r.correct).length;
19598
19602
  const handleAssign = (itemId, categoryId) => {
19599
19603
  if (submitted) return;
19600
- setAssignments((prev) => ({ ...prev, [itemId]: categoryId }));
19604
+ if (assignEvent) {
19605
+ emit(`UI:${assignEvent}`, { itemId, categoryId });
19606
+ }
19607
+ if (!entityHasAssignments) {
19608
+ setLocalAssignments((prev) => ({ ...prev, [itemId]: categoryId }));
19609
+ }
19601
19610
  };
19602
19611
  const handleUnassign = (itemId) => {
19603
19612
  if (submitted) return;
19604
- setAssignments((prev) => {
19605
- const next = { ...prev };
19606
- delete next[itemId];
19607
- return next;
19608
- });
19613
+ if (!entityHasAssignments) {
19614
+ setLocalAssignments((prev) => {
19615
+ const next = { ...prev };
19616
+ delete next[itemId];
19617
+ return next;
19618
+ });
19619
+ }
19609
19620
  };
19610
19621
  const handleSubmit = useCallback(() => {
19611
- setSubmitted(true);
19612
- setAttempts((a) => a + 1);
19613
- const correct = items.every((item) => assignments[item.id] === item.correctCategory);
19614
- if (correct) {
19615
- emit(`UI:${completeEvent}`, { success: true, attempts: attempts + 1 });
19622
+ if (checkEvent) {
19623
+ emit(`UI:${checkEvent}`, {});
19624
+ }
19625
+ if (!entityDrivesResult) {
19626
+ setLocalSubmitted(true);
19627
+ setLocalAttempts((a) => a + 1);
19628
+ const correct = items.every((item) => assignments[item.id] === item.correctCategory);
19629
+ if (correct) {
19630
+ emit(`UI:${completeEvent}`, { success: true, attempts: attempts + 1 });
19631
+ }
19616
19632
  }
19617
- }, [items, assignments, attempts, completeEvent, emit]);
19633
+ }, [checkEvent, entityDrivesResult, items, assignments, attempts, completeEvent, emit]);
19618
19634
  const handleReset = () => {
19619
- setSubmitted(false);
19635
+ if (!entityDrivesResult) setLocalSubmitted(false);
19620
19636
  if (attempts >= 2 && str(resolved?.hint)) {
19621
19637
  setShowHint(true);
19622
19638
  }
19623
19639
  };
19624
19640
  const handleFullReset = () => {
19625
- setAssignments({});
19626
- setSubmitted(false);
19627
- setAttempts(0);
19641
+ if (playAgainEvent) {
19642
+ emit(`UI:${playAgainEvent}`, {});
19643
+ }
19644
+ setLocalAssignments({});
19645
+ setLocalSubmitted(false);
19646
+ setLocalAttempts(0);
19628
19647
  setShowHint(false);
19629
19648
  };
19630
19649
  if (!resolved) return null;
@@ -29163,13 +29182,13 @@ var init_MapView = __esm({
29163
29182
  shadowSize: [41, 41]
29164
29183
  });
29165
29184
  L.Marker.prototype.options.icon = defaultIcon;
29166
- const { useEffect: useEffect72, useRef: useRef69, useCallback: useCallback115, useState: useState105 } = React77__default;
29185
+ const { useEffect: useEffect73, useRef: useRef70, useCallback: useCallback113, useState: useState105 } = React77__default;
29167
29186
  const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
29168
29187
  const { useEventBus: useEventBus2 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
29169
29188
  function MapUpdater({ centerLat, centerLng, zoom }) {
29170
29189
  const map = useMap();
29171
- const prevRef = useRef69({ centerLat, centerLng, zoom });
29172
- useEffect72(() => {
29190
+ const prevRef = useRef70({ centerLat, centerLng, zoom });
29191
+ useEffect73(() => {
29173
29192
  const prev = prevRef.current;
29174
29193
  if (prev.centerLat !== centerLat || prev.centerLng !== centerLng || prev.zoom !== zoom) {
29175
29194
  map.setView([centerLat, centerLng], zoom);
@@ -29180,7 +29199,7 @@ var init_MapView = __esm({
29180
29199
  }
29181
29200
  function MapClickHandler({ onMapClick }) {
29182
29201
  const map = useMap();
29183
- useEffect72(() => {
29202
+ useEffect73(() => {
29184
29203
  if (!onMapClick) return;
29185
29204
  const handler = (e) => {
29186
29205
  onMapClick(e.latlng.lat, e.latlng.lng);
@@ -29209,7 +29228,7 @@ var init_MapView = __esm({
29209
29228
  }) {
29210
29229
  const eventBus = useEventBus2();
29211
29230
  const [clickedPosition, setClickedPosition] = useState105(null);
29212
- const handleMapClick = useCallback115((lat, lng) => {
29231
+ const handleMapClick = useCallback113((lat, lng) => {
29213
29232
  if (showClickedPin) {
29214
29233
  setClickedPosition({ lat, lng });
29215
29234
  }
@@ -29218,7 +29237,7 @@ var init_MapView = __esm({
29218
29237
  eventBus.emit(`UI:${mapClickEvent}`, { latitude: lat, longitude: lng });
29219
29238
  }
29220
29239
  }, [onMapClick, mapClickEvent, eventBus, showClickedPin]);
29221
- const handleMarkerClick = useCallback115((marker) => {
29240
+ const handleMarkerClick = useCallback113((marker) => {
29222
29241
  onMarkerClick?.(marker);
29223
29242
  if (markerClickEvent) {
29224
29243
  eventBus.emit(`UI:${markerClickEvent}`, { ...marker });
@@ -38652,51 +38671,52 @@ var init_DataTable = __esm({
38652
38671
  function DebuggerBoard({
38653
38672
  entity,
38654
38673
  completeEvent = "PUZZLE_COMPLETE",
38674
+ toggleFlagEvent,
38675
+ checkEvent,
38676
+ playAgainEvent,
38655
38677
  className
38656
38678
  }) {
38657
38679
  const { emit } = useEventBus();
38658
38680
  const { t } = useTranslate();
38659
38681
  const resolved = boardEntity(entity);
38660
- const [flaggedLines, setFlaggedLines] = useState(/* @__PURE__ */ new Set());
38661
38682
  const [headerError, setHeaderError] = useState(false);
38662
- const [submitted, setSubmitted] = useState(false);
38663
- const [attempts, setAttempts] = useState(0);
38664
38683
  const [showHint, setShowHint] = useState(false);
38684
+ const lines = Array.isArray(resolved?.lines) ? resolved.lines : [];
38685
+ const result = resolved?.result ?? null;
38686
+ const attempts = num(resolved?.attempts);
38687
+ const submitted = result != null;
38688
+ const bugLines = lines.filter((l) => l.isBug);
38689
+ const flaggedLines = lines.filter((l) => l.isFlagged);
38690
+ const correctFlags = lines.filter((l) => l.isBug && l.isFlagged);
38691
+ const falseFlags = lines.filter((l) => !l.isBug && l.isFlagged);
38692
+ const allCorrect = result === "win";
38665
38693
  const toggleLine = (lineId) => {
38666
38694
  if (submitted) return;
38667
- setFlaggedLines((prev) => {
38668
- const next = new Set(prev);
38669
- if (next.has(lineId)) {
38670
- next.delete(lineId);
38671
- } else {
38672
- next.add(lineId);
38673
- }
38674
- return next;
38675
- });
38695
+ if (toggleFlagEvent) {
38696
+ emit(`UI:${toggleFlagEvent}`, { lineId });
38697
+ }
38676
38698
  };
38677
- const lines = Array.isArray(resolved?.lines) ? resolved.lines : [];
38678
- const bugLines = lines.filter((l) => l.isBug);
38679
- const correctFlags = lines.filter((l) => l.isBug && flaggedLines.has(l.id));
38680
- const falseFlags = lines.filter((l) => !l.isBug && flaggedLines.has(l.id));
38681
- const allCorrect = submitted && correctFlags.length === bugLines.length && falseFlags.length === 0;
38682
38699
  const handleSubmit = useCallback(() => {
38683
- setSubmitted(true);
38684
- setAttempts((a) => a + 1);
38700
+ if (checkEvent) {
38701
+ emit(`UI:${checkEvent}`, {});
38702
+ }
38685
38703
  const correct = correctFlags.length === bugLines.length && falseFlags.length === 0;
38686
38704
  if (correct) {
38687
38705
  emit(`UI:${completeEvent}`, { success: true, attempts: attempts + 1 });
38688
38706
  }
38689
- }, [correctFlags.length, bugLines.length, falseFlags.length, attempts, completeEvent, emit]);
38707
+ }, [checkEvent, correctFlags.length, bugLines.length, falseFlags.length, attempts, completeEvent, emit]);
38690
38708
  const handleReset = () => {
38691
- setSubmitted(false);
38709
+ if (playAgainEvent) {
38710
+ emit(`UI:${playAgainEvent}`, {});
38711
+ }
38692
38712
  if (attempts >= 2 && str(resolved?.hint)) {
38693
38713
  setShowHint(true);
38694
38714
  }
38695
38715
  };
38696
38716
  const handleFullReset = () => {
38697
- setFlaggedLines(/* @__PURE__ */ new Set());
38698
- setSubmitted(false);
38699
- setAttempts(0);
38717
+ if (playAgainEvent) {
38718
+ emit(`UI:${playAgainEvent}`, {});
38719
+ }
38700
38720
  setShowHint(false);
38701
38721
  };
38702
38722
  if (!resolved) return null;
@@ -38724,7 +38744,7 @@ function DebuggerBoard({
38724
38744
  /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-muted-foreground", children: t("debugger.findBugs", { count: String(num(resolved.bugCount)) }) })
38725
38745
  ] }) }),
38726
38746
  /* @__PURE__ */ jsx(Card, { className: "p-0 overflow-hidden", children: /* @__PURE__ */ jsx(VStack, { gap: "none", children: lines.map((line, i) => {
38727
- const isFlagged = flaggedLines.has(line.id);
38747
+ const isFlagged = !!line.isFlagged;
38728
38748
  let lineStyle = "";
38729
38749
  if (submitted) {
38730
38750
  if (line.isBug && isFlagged) lineStyle = "bg-success/10";
@@ -38758,9 +38778,9 @@ function DebuggerBoard({
38758
38778
  /* @__PURE__ */ jsx(
38759
38779
  Icon,
38760
38780
  {
38761
- icon: flaggedLines.has(line.id) ? CheckCircle : XCircle,
38781
+ icon: line.isFlagged ? CheckCircle : XCircle,
38762
38782
  size: "xs",
38763
- className: flaggedLines.has(line.id) ? "text-success mt-0.5" : "text-warning mt-0.5"
38783
+ className: line.isFlagged ? "text-success mt-0.5" : "text-warning mt-0.5"
38764
38784
  }
38765
38785
  ),
38766
38786
  /* @__PURE__ */ jsxs(VStack, { gap: "none", children: [
@@ -38771,7 +38791,7 @@ function DebuggerBoard({
38771
38791
  ] }) }),
38772
38792
  showHint && hint && /* @__PURE__ */ jsx(Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsx(Typography, { variant: "body", children: hint }) }),
38773
38793
  /* @__PURE__ */ jsxs(HStack, { gap: "sm", justify: "center", children: [
38774
- !submitted ? /* @__PURE__ */ jsxs(Button, { variant: "primary", onClick: handleSubmit, disabled: flaggedLines.size === 0, children: [
38794
+ !submitted ? /* @__PURE__ */ jsxs(Button, { variant: "primary", onClick: handleSubmit, disabled: flaggedLines.length === 0, children: [
38775
38795
  /* @__PURE__ */ jsx(Icon, { icon: Send, size: "sm" }),
38776
38796
  t("debugger.submit")
38777
38797
  ] }) : !allCorrect ? /* @__PURE__ */ jsx(Button, { variant: "primary", onClick: handleReset, children: t("debugger.tryAgain") }) : null,
@@ -40104,7 +40124,7 @@ var init_FeatureDetailPageTemplate = __esm({
40104
40124
  const resolved = entity && typeof entity === "object" && !Array.isArray(entity) ? entity : void 0;
40105
40125
  if (!resolved) return null;
40106
40126
  return /* @__PURE__ */ jsxs(VStack, { gap: "none", className: cn("w-full", className), children: [
40107
- /* @__PURE__ */ jsx(
40127
+ resolved.hero && /* @__PURE__ */ jsx(
40108
40128
  HeroSection,
40109
40129
  {
40110
40130
  tag: resolved.hero.tag,
@@ -41631,7 +41651,7 @@ var init_LandingPageTemplate = __esm({
41631
41651
  const resolved = entity && typeof entity === "object" && !Array.isArray(entity) ? entity : void 0;
41632
41652
  if (!resolved) return null;
41633
41653
  return /* @__PURE__ */ jsxs(VStack, { gap: "none", className: cn("w-full", className), children: [
41634
- /* @__PURE__ */ jsx(
41654
+ resolved.hero && /* @__PURE__ */ jsx(
41635
41655
  HeroSection,
41636
41656
  {
41637
41657
  tag: resolved.hero.tag,
@@ -42731,6 +42751,9 @@ function getOpponentAction(strategy, actions, history) {
42731
42751
  function NegotiatorBoard({
42732
42752
  entity,
42733
42753
  completeEvent = "PUZZLE_COMPLETE",
42754
+ playRoundEvent,
42755
+ finishEvent,
42756
+ playAgainEvent,
42734
42757
  className
42735
42758
  }) {
42736
42759
  const { emit } = useEventBus();
@@ -42739,13 +42762,14 @@ function NegotiatorBoard({
42739
42762
  const [history, setHistory] = useState([]);
42740
42763
  const [headerError, setHeaderError] = useState(false);
42741
42764
  const [showHint, setShowHint] = useState(false);
42742
- const totalRounds = num(resolved?.totalRounds);
42765
+ const totalRounds = num(resolved?.maxRounds);
42743
42766
  const targetScore = num(resolved?.targetScore);
42744
- const currentRound = history.length;
42745
- const isComplete = currentRound >= totalRounds;
42746
- const playerTotal = history.reduce((s, r) => s + r.playerPayoff, 0);
42767
+ const currentRound = num(resolved?.round);
42768
+ const result = str(resolved?.result) || "none";
42769
+ const playerTotal = num(resolved?.score);
42770
+ const isComplete = result !== "none" || totalRounds > 0 && currentRound >= totalRounds;
42771
+ const won = result === "win";
42747
42772
  const opponentTotal = history.reduce((s, r) => s + r.opponentPayoff, 0);
42748
- const won = isComplete && playerTotal >= targetScore;
42749
42773
  const actions = Array.isArray(resolved?.actions) ? resolved.actions : [];
42750
42774
  const payoffMatrix = Array.isArray(resolved?.payoffMatrix) ? resolved.payoffMatrix : [];
42751
42775
  const handleAction = useCallback((actionId) => {
@@ -42754,29 +42778,45 @@ function NegotiatorBoard({
42754
42778
  const payoff = payoffMatrix.find(
42755
42779
  (p2) => p2.playerAction === actionId && p2.opponentAction === opponentAction
42756
42780
  );
42757
- const result = {
42758
- round: currentRound + 1,
42759
- playerAction: actionId,
42760
- opponentAction,
42761
- playerPayoff: payoff?.playerPayoff ?? 0,
42762
- opponentPayoff: payoff?.opponentPayoff ?? 0
42763
- };
42764
- const newHistory = [...history, result];
42765
- setHistory(newHistory);
42766
- if (newHistory.length >= totalRounds) {
42767
- const total = newHistory.reduce((s, r) => s + r.playerPayoff, 0);
42768
- if (total >= targetScore) {
42769
- emit(`UI:${completeEvent}`, { success: true, score: total });
42770
- }
42771
- if (newHistory.length >= 3 && str(resolved?.hint)) {
42781
+ const playerPayoff = payoff?.playerPayoff ?? 0;
42782
+ setHistory((prev) => [
42783
+ ...prev,
42784
+ {
42785
+ round: prev.length + 1,
42786
+ playerAction: actionId,
42787
+ opponentAction,
42788
+ playerPayoff,
42789
+ opponentPayoff: payoff?.opponentPayoff ?? 0
42790
+ }
42791
+ ]);
42792
+ if (playRoundEvent) {
42793
+ emit(`UI:${playRoundEvent}`, { playerAction: actionId, payoff: playerPayoff });
42794
+ }
42795
+ if (totalRounds > 0 && currentRound + 1 >= totalRounds) {
42796
+ if (finishEvent) {
42797
+ emit(`UI:${finishEvent}`, {});
42798
+ }
42799
+ if (str(resolved?.hint)) {
42772
42800
  setShowHint(true);
42773
42801
  }
42774
42802
  }
42775
- }, [isComplete, resolved, totalRounds, targetScore, actions, payoffMatrix, history, currentRound, completeEvent, emit]);
42776
- const handleReset = () => {
42803
+ }, [isComplete, resolved, totalRounds, currentRound, actions, payoffMatrix, history, playRoundEvent, finishEvent, emit]);
42804
+ const handleReset = useCallback(() => {
42777
42805
  setHistory([]);
42778
42806
  setShowHint(false);
42779
- };
42807
+ if (playAgainEvent) {
42808
+ emit(`UI:${playAgainEvent}`, {});
42809
+ }
42810
+ }, [playAgainEvent, emit]);
42811
+ const completedRef = useRef(false);
42812
+ useEffect(() => {
42813
+ if (result === "win" && !completedRef.current) {
42814
+ completedRef.current = true;
42815
+ emit(`UI:${completeEvent}`, { success: true, score: playerTotal });
42816
+ } else if (result === "none") {
42817
+ completedRef.current = false;
42818
+ }
42819
+ }, [result, playerTotal, completeEvent, emit]);
42780
42820
  const getActionLabel = (id) => actions.find((a) => a.id === id)?.label ?? id;
42781
42821
  if (!resolved) return null;
42782
42822
  const theme = resolved.theme ?? void 0;
@@ -42945,7 +42985,7 @@ var init_PricingPageTemplate = __esm({
42945
42985
  const resolved = entity && typeof entity === "object" && !Array.isArray(entity) ? entity : void 0;
42946
42986
  if (!resolved) return null;
42947
42987
  return /* @__PURE__ */ jsxs(VStack, { gap: "none", className: cn("w-full", className), children: [
42948
- /* @__PURE__ */ jsx(
42988
+ resolved.hero && /* @__PURE__ */ jsx(
42949
42989
  HeroSection,
42950
42990
  {
42951
42991
  tag: resolved.hero.tag,
@@ -45777,67 +45817,47 @@ var init_SimulationGraph = __esm({
45777
45817
  function SimulatorBoard({
45778
45818
  entity,
45779
45819
  completeEvent = "PUZZLE_COMPLETE",
45820
+ setAEvent,
45821
+ setBEvent,
45822
+ checkEvent,
45823
+ playAgainEvent,
45780
45824
  className
45781
45825
  }) {
45782
45826
  const { emit } = useEventBus();
45783
45827
  const { t } = useTranslate();
45784
45828
  const resolved = boardEntity(entity);
45785
45829
  const parameters = Array.isArray(resolved?.parameters) ? resolved.parameters : [];
45786
- const [values, setValues] = useState(() => {
45787
- const init = {};
45788
- for (const p2 of parameters) {
45789
- init[p2.id] = p2.initial;
45790
- }
45791
- return init;
45792
- });
45793
45830
  const [headerError, setHeaderError] = useState(false);
45794
- const [submitted, setSubmitted] = useState(false);
45795
- const [attempts, setAttempts] = useState(0);
45796
- const [showHint, setShowHint] = useState(false);
45797
- const computeOutput = useCallback((params) => {
45798
- try {
45799
- const fn = new Function("params", `return (${str(resolved?.computeExpression)})`);
45800
- return fn(params);
45801
- } catch {
45802
- return 0;
45803
- }
45804
- }, [resolved?.computeExpression]);
45805
- const output = useMemo(() => computeOutput(values) ?? 0, [computeOutput, values]);
45806
- const targetValue = num(resolved?.targetValue);
45807
- const targetTolerance = num(resolved?.targetTolerance);
45808
- const isCorrect = Math.abs(output - targetValue) <= targetTolerance;
45809
- const handleParameterChange = (id, value) => {
45810
- if (submitted) return;
45811
- setValues((prev) => ({ ...prev, [id]: value }));
45812
- };
45813
- const handleSubmit = () => {
45814
- setSubmitted(true);
45815
- setAttempts((a) => a + 1);
45816
- if (isCorrect) {
45817
- emit(`UI:${completeEvent}`, { success: true, attempts: attempts + 1 });
45818
- }
45831
+ if (!resolved) return null;
45832
+ const paramA = num(resolved.paramA);
45833
+ const paramB = num(resolved.paramB);
45834
+ const output = num(resolved.output);
45835
+ const targetValue = num(resolved.target);
45836
+ const targetTolerance = num(resolved.tolerance);
45837
+ const attempts = num(resolved.attempts);
45838
+ const result = str(resolved.result);
45839
+ const isWin = result === "win";
45840
+ const isComplete = result !== "none" && result !== "";
45841
+ const paramAValue = parameters[0];
45842
+ const paramBValue = parameters[1];
45843
+ const sliderValues = [paramA, paramB];
45844
+ const sliderEvents = [setAEvent, setBEvent];
45845
+ const handleParameterChange = (index, value) => {
45846
+ if (isComplete) return;
45847
+ const ev = sliderEvents[index];
45848
+ if (ev) emit(`UI:${ev}`, { value });
45819
45849
  };
45820
- const handleReset = () => {
45821
- setSubmitted(false);
45822
- if (attempts >= 2 && str(resolved?.hint)) {
45823
- setShowHint(true);
45824
- }
45850
+ const handleCheck = () => {
45851
+ if (checkEvent) emit(`UI:${checkEvent}`, {});
45825
45852
  };
45826
- const handleFullReset = () => {
45827
- const init = {};
45828
- for (const p2 of parameters) {
45829
- init[p2.id] = p2.initial;
45830
- }
45831
- setValues(init);
45832
- setSubmitted(false);
45833
- setAttempts(0);
45834
- setShowHint(false);
45853
+ const handlePlayAgain = () => {
45854
+ if (playAgainEvent) emit(`UI:${playAgainEvent}`, {});
45835
45855
  };
45836
- if (!resolved) return null;
45837
45856
  const theme = resolved.theme ?? void 0;
45838
45857
  const themeBackground = theme?.background;
45839
45858
  const headerImage = str(resolved.headerImage);
45840
45859
  const hint = str(resolved.hint);
45860
+ const showHint = isComplete && !isWin && attempts >= 2 && Boolean(hint);
45841
45861
  const outputLabel = str(resolved.outputLabel);
45842
45862
  const outputUnit = str(resolved.outputUnit);
45843
45863
  return /* @__PURE__ */ jsx(
@@ -45857,41 +45877,43 @@ function SimulatorBoard({
45857
45877
  ] }) }),
45858
45878
  /* @__PURE__ */ jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxs(VStack, { gap: "md", children: [
45859
45879
  /* @__PURE__ */ jsx(Typography, { variant: "small", weight: "bold", className: "uppercase tracking-wider text-muted-foreground", children: t("simulator.parameters") }),
45860
- parameters.map((param) => /* @__PURE__ */ jsxs(VStack, { gap: "xs", children: [
45861
- /* @__PURE__ */ jsxs(HStack, { justify: "between", align: "center", children: [
45862
- /* @__PURE__ */ jsx(Typography, { variant: "body", weight: "medium", children: param.label }),
45863
- /* @__PURE__ */ jsxs(Badge, { size: "sm", children: [
45864
- values[param.id],
45865
- " ",
45866
- param.unit
45867
- ] })
45868
- ] }),
45869
- /* @__PURE__ */ jsx(
45870
- "input",
45871
- {
45872
- type: "range",
45873
- min: param.min,
45874
- max: param.max,
45875
- step: param.step,
45876
- value: values[param.id],
45877
- onChange: (e) => handleParameterChange(param.id, Number(e.target.value)),
45878
- disabled: submitted,
45879
- className: "w-full accent-foreground"
45880
- }
45881
- ),
45882
- /* @__PURE__ */ jsxs(HStack, { justify: "between", children: [
45883
- /* @__PURE__ */ jsxs(Typography, { variant: "caption", className: "text-muted-foreground", children: [
45884
- param.min,
45885
- " ",
45886
- param.unit
45880
+ [paramAValue, paramBValue].map(
45881
+ (param, index) => param ? /* @__PURE__ */ jsxs(VStack, { gap: "xs", children: [
45882
+ /* @__PURE__ */ jsxs(HStack, { justify: "between", align: "center", children: [
45883
+ /* @__PURE__ */ jsx(Typography, { variant: "body", weight: "medium", children: param.label }),
45884
+ /* @__PURE__ */ jsxs(Badge, { size: "sm", children: [
45885
+ sliderValues[index],
45886
+ " ",
45887
+ param.unit
45888
+ ] })
45887
45889
  ] }),
45888
- /* @__PURE__ */ jsxs(Typography, { variant: "caption", className: "text-muted-foreground", children: [
45889
- param.max,
45890
- " ",
45891
- param.unit
45890
+ /* @__PURE__ */ jsx(
45891
+ "input",
45892
+ {
45893
+ type: "range",
45894
+ min: param.min,
45895
+ max: param.max,
45896
+ step: param.step,
45897
+ value: sliderValues[index],
45898
+ onChange: (e) => handleParameterChange(index, Number(e.target.value)),
45899
+ disabled: isComplete,
45900
+ className: "w-full accent-foreground"
45901
+ }
45902
+ ),
45903
+ /* @__PURE__ */ jsxs(HStack, { justify: "between", children: [
45904
+ /* @__PURE__ */ jsxs(Typography, { variant: "caption", className: "text-muted-foreground", children: [
45905
+ param.min,
45906
+ " ",
45907
+ param.unit
45908
+ ] }),
45909
+ /* @__PURE__ */ jsxs(Typography, { variant: "caption", className: "text-muted-foreground", children: [
45910
+ param.max,
45911
+ " ",
45912
+ param.unit
45913
+ ] })
45892
45914
  ] })
45893
- ] })
45894
- ] }, param.id))
45915
+ ] }, param.id ?? index) : null
45916
+ )
45895
45917
  ] }) }),
45896
45918
  /* @__PURE__ */ jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", align: "center", children: [
45897
45919
  /* @__PURE__ */ jsx(Typography, { variant: "small", weight: "bold", className: "uppercase tracking-wider text-muted-foreground", children: outputLabel }),
@@ -45900,9 +45922,9 @@ function SimulatorBoard({
45900
45922
  " ",
45901
45923
  outputUnit
45902
45924
  ] }),
45903
- submitted && /* @__PURE__ */ jsxs(HStack, { gap: "xs", align: "center", children: [
45904
- /* @__PURE__ */ jsx(Icon, { icon: isCorrect ? CheckCircle : XCircle, size: "sm", className: isCorrect ? "text-success" : "text-error" }),
45905
- /* @__PURE__ */ jsx(Typography, { variant: "body", className: isCorrect ? "text-success" : "text-error", children: isCorrect ? str(resolved.successMessage) || t("simulator.correct") : str(resolved.failMessage) || t("simulator.incorrect") })
45925
+ isComplete && /* @__PURE__ */ jsxs(HStack, { gap: "xs", align: "center", children: [
45926
+ /* @__PURE__ */ jsx(Icon, { icon: isWin ? CheckCircle : XCircle, size: "sm", className: isWin ? "text-success" : "text-error" }),
45927
+ /* @__PURE__ */ jsx(Typography, { variant: "body", className: isWin ? "text-success" : "text-error", children: isWin ? str(resolved.successMessage) || t("simulator.correct") : str(resolved.failMessage) || t("simulator.incorrect") })
45906
45928
  ] }),
45907
45929
  /* @__PURE__ */ jsxs(Typography, { variant: "caption", className: "text-muted-foreground", children: [
45908
45930
  t("simulator.target"),
@@ -45917,11 +45939,11 @@ function SimulatorBoard({
45917
45939
  ] }) }),
45918
45940
  showHint && hint && /* @__PURE__ */ jsx(Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsx(Typography, { variant: "body", children: hint }) }),
45919
45941
  /* @__PURE__ */ jsxs(HStack, { gap: "sm", justify: "center", children: [
45920
- !submitted ? /* @__PURE__ */ jsxs(Button, { variant: "primary", onClick: handleSubmit, children: [
45942
+ !isComplete ? /* @__PURE__ */ jsxs(Button, { variant: "primary", onClick: handleCheck, children: [
45921
45943
  /* @__PURE__ */ jsx(Icon, { icon: Play, size: "sm" }),
45922
45944
  t("simulator.simulate")
45923
- ] }) : !isCorrect ? /* @__PURE__ */ jsx(Button, { variant: "primary", onClick: handleReset, children: t("simulator.tryAgain") }) : null,
45924
- /* @__PURE__ */ jsxs(Button, { variant: "secondary", onClick: handleFullReset, children: [
45945
+ ] }) : null,
45946
+ /* @__PURE__ */ jsxs(Button, { variant: "secondary", onClick: handlePlayAgain, children: [
45925
45947
  /* @__PURE__ */ jsx(Icon, { icon: RotateCcw, size: "sm" }),
45926
45948
  t("simulator.reset")
45927
45949
  ] })