@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.
@@ -9094,7 +9094,7 @@ var init_AboutPageTemplate = __esm({
9094
9094
  const resolved = entity && typeof entity === "object" && !Array.isArray(entity) ? entity : void 0;
9095
9095
  if (!resolved) return null;
9096
9096
  return /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "none", className: cn("w-full", className), children: [
9097
- /* @__PURE__ */ jsxRuntime.jsx(
9097
+ resolved.hero && /* @__PURE__ */ jsxRuntime.jsx(
9098
9098
  HeroSection,
9099
9099
  {
9100
9100
  tag: resolved.hero.tag,
@@ -16988,61 +16988,53 @@ var init_Breadcrumb = __esm({
16988
16988
  function BuilderBoard({
16989
16989
  entity,
16990
16990
  completeEvent = "PUZZLE_COMPLETE",
16991
+ placeEvent,
16992
+ checkEvent,
16993
+ playAgainEvent,
16991
16994
  className
16992
16995
  }) {
16993
16996
  const { emit } = useEventBus();
16994
16997
  const { t } = hooks.useTranslate();
16995
16998
  const resolved = boardEntity(entity);
16996
- const [placements, setPlacements] = React82.useState({});
16997
16999
  const [headerError, setHeaderError] = React82.useState(false);
16998
- const [submitted, setSubmitted] = React82.useState(false);
16999
- const [attempts, setAttempts] = React82.useState(0);
17000
- const [showHint, setShowHint] = React82.useState(false);
17000
+ const [selectedComponent, setSelectedComponent] = React82.useState(null);
17001
17001
  const components = Array.isArray(resolved?.components) ? resolved.components : [];
17002
17002
  const slots = Array.isArray(resolved?.slots) ? resolved.slots : [];
17003
+ const placements = {};
17004
+ for (const slot of slots) {
17005
+ if (slot.placedComponentId) placements[slot.id] = slot.placedComponentId;
17006
+ }
17007
+ const attempts = num(resolved?.attempts);
17008
+ const result = str(resolved?.result) || "none";
17009
+ const submitted = result === "win";
17003
17010
  const usedComponentIds = new Set(Object.values(placements));
17004
17011
  const availableComponents = components.filter((c) => !usedComponentIds.has(c.id));
17005
- const [selectedComponent, setSelectedComponent] = React82.useState(null);
17006
- const allPlaced = Object.keys(placements).length === slots.length;
17012
+ const allPlaced = slots.length > 0 && slots.every((s) => Boolean(placements[s.id]));
17007
17013
  const results = submitted ? slots.map((slot) => ({
17008
17014
  slot,
17009
17015
  placed: placements[slot.id],
17010
- correct: placements[slot.id] === slot.acceptsComponentId
17016
+ correct: placements[slot.id] === slot.requiredComponentId
17011
17017
  })) : [];
17012
- const allCorrect = results.length > 0 && results.every((r) => r.correct);
17018
+ const showHint = attempts >= 2 && Boolean(str(resolved?.hint));
17013
17019
  const handlePlaceComponent = (slotId) => {
17014
17020
  if (submitted || !selectedComponent) return;
17015
- setPlacements((prev) => ({ ...prev, [slotId]: selectedComponent }));
17021
+ if (placeEvent) emit(`UI:${placeEvent}`, { slotId, componentId: selectedComponent });
17016
17022
  setSelectedComponent(null);
17017
17023
  };
17018
17024
  const handleRemoveFromSlot = (slotId) => {
17019
17025
  if (submitted) return;
17020
- setPlacements((prev) => {
17021
- const next = { ...prev };
17022
- delete next[slotId];
17023
- return next;
17024
- });
17026
+ if (placeEvent) emit(`UI:${placeEvent}`, { slotId, componentId: "" });
17025
17027
  };
17026
- const handleSubmit = React82.useCallback(() => {
17027
- setSubmitted(true);
17028
- setAttempts((a) => a + 1);
17029
- const correct = slots.every((slot) => placements[slot.id] === slot.acceptsComponentId);
17030
- if (correct) {
17028
+ const handleSubmit = () => {
17029
+ if (checkEvent) emit(`UI:${checkEvent}`, {});
17030
+ const solved = slots.length > 0 && slots.every((s) => placements[s.id] === s.requiredComponentId);
17031
+ if (solved && completeEvent) {
17031
17032
  emit(`UI:${completeEvent}`, { success: true, attempts: attempts + 1 });
17032
17033
  }
17033
- }, [slots, placements, attempts, completeEvent, emit]);
17034
- const handleReset = () => {
17035
- setSubmitted(false);
17036
- if (attempts >= 2 && str(resolved?.hint)) {
17037
- setShowHint(true);
17038
- }
17039
17034
  };
17040
- const handleFullReset = () => {
17041
- setPlacements({});
17042
- setSubmitted(false);
17035
+ const handlePlayAgain = () => {
17043
17036
  setSelectedComponent(null);
17044
- setAttempts(0);
17045
- setShowHint(false);
17037
+ if (playAgainEvent) emit(`UI:${playAgainEvent}`, {});
17046
17038
  };
17047
17039
  const getComponentById = (id) => components.find((c) => c.id === id);
17048
17040
  if (!resolved) return null;
@@ -17092,13 +17084,13 @@ function BuilderBoard({
17092
17084
  /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", weight: "bold", className: "uppercase tracking-wider text-muted-foreground", children: t("builder.blueprint") }),
17093
17085
  /* @__PURE__ */ jsxRuntime.jsx(VStack, { gap: "sm", children: slots.map((slot) => {
17094
17086
  const placedComp = placements[slot.id] ? getComponentById(placements[slot.id]) : null;
17095
- const result = results.find((r) => r.slot.id === slot.id);
17087
+ const result2 = results.find((r) => r.slot.id === slot.id);
17096
17088
  return /* @__PURE__ */ jsxRuntime.jsxs(
17097
17089
  HStack,
17098
17090
  {
17099
17091
  gap: "sm",
17100
17092
  align: "center",
17101
- className: `p-3 border-2 rounded ${result ? result.correct ? "border-success" : "border-error" : selectedComponent ? "border-dashed border-foreground cursor-pointer" : "border-border"}`,
17093
+ className: `p-3 border-2 rounded ${result2 ? result2.correct ? "border-success" : "border-error" : selectedComponent ? "border-dashed border-foreground cursor-pointer" : "border-border"}`,
17102
17094
  onClick: () => handlePlaceComponent(slot.id),
17103
17095
  children: [
17104
17096
  /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "none", className: "flex-1", children: [
@@ -17113,7 +17105,7 @@ function BuilderBoard({
17113
17105
  ] }) : null,
17114
17106
  placedComp.label
17115
17107
  ] }),
17116
- result && /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: result.correct ? LucideIcons2.CheckCircle : LucideIcons2.XCircle, size: "sm", className: result.correct ? "text-success" : "text-error" })
17108
+ result2 && /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: result2.correct ? LucideIcons2.CheckCircle : LucideIcons2.XCircle, size: "sm", className: result2.correct ? "text-success" : "text-error" })
17117
17109
  ] }) : /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", className: "text-muted-foreground", children: t("builder.empty") })
17118
17110
  ]
17119
17111
  },
@@ -17122,16 +17114,16 @@ function BuilderBoard({
17122
17114
  }) })
17123
17115
  ] }) }),
17124
17116
  submitted && /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "sm", align: "center", children: [
17125
- /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: allCorrect ? LucideIcons2.CheckCircle : LucideIcons2.XCircle, size: "lg", className: allCorrect ? "text-success" : "text-error" }),
17126
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", weight: "bold", children: allCorrect ? str(resolved.successMessage) || t("builder.success") : str(resolved.failMessage) || t("builder.incorrect") })
17117
+ /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: LucideIcons2.CheckCircle, size: "lg", className: "text-success" }),
17118
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", weight: "bold", children: str(resolved.successMessage) || t("builder.success") })
17127
17119
  ] }) }),
17128
17120
  showHint && hint && /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", children: hint }) }),
17129
17121
  /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "sm", justify: "center", children: [
17130
- !submitted ? /* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "primary", onClick: handleSubmit, disabled: !allPlaced, children: [
17122
+ !submitted && /* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "primary", onClick: handleSubmit, disabled: !allPlaced, children: [
17131
17123
  /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: LucideIcons2.Wrench, size: "sm" }),
17132
17124
  t("builder.build")
17133
- ] }) : !allCorrect ? /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "primary", onClick: handleReset, children: t("builder.tryAgain") }) : null,
17134
- /* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "secondary", onClick: handleFullReset, children: [
17125
+ ] }),
17126
+ /* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "secondary", onClick: handlePlayAgain, children: [
17135
17127
  /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: LucideIcons2.RotateCcw, size: "sm" }),
17136
17128
  t("builder.reset")
17137
17129
  ] })
@@ -20587,57 +20579,84 @@ var init_ChartLegend = __esm({
20587
20579
  function ClassifierBoard({
20588
20580
  entity,
20589
20581
  completeEvent = "PUZZLE_COMPLETE",
20582
+ assignEvent,
20583
+ checkEvent,
20584
+ playAgainEvent,
20590
20585
  className
20591
20586
  }) {
20592
20587
  const { emit } = useEventBus();
20593
20588
  const { t } = hooks.useTranslate();
20594
20589
  const resolved = boardEntity(entity);
20595
- const [assignments, setAssignments] = React82.useState({});
20590
+ const [localAssignments, setLocalAssignments] = React82.useState({});
20596
20591
  const [headerError, setHeaderError] = React82.useState(false);
20597
- const [submitted, setSubmitted] = React82.useState(false);
20598
- const [attempts, setAttempts] = React82.useState(0);
20592
+ const [localSubmitted, setLocalSubmitted] = React82.useState(false);
20593
+ const [localAttempts, setLocalAttempts] = React82.useState(0);
20599
20594
  const [showHint, setShowHint] = React82.useState(false);
20600
20595
  const items = Array.isArray(resolved?.items) ? resolved.items : [];
20601
20596
  const categories = Array.isArray(resolved?.categories) ? resolved.categories : [];
20597
+ const entityResult = str(resolved?.result);
20598
+ const entityDrivesResult = entityResult.length > 0;
20599
+ const entityHasAssignments = items.some((item) => item.assignedCategory != null);
20600
+ const assignments = entityHasAssignments ? items.reduce((acc, item) => {
20601
+ if (item.assignedCategory != null) acc[item.id] = item.assignedCategory;
20602
+ return acc;
20603
+ }, {}) : localAssignments;
20604
+ const attempts = entityDrivesResult ? num(resolved?.attempts) : localAttempts;
20605
+ const submitted = entityDrivesResult || localSubmitted;
20602
20606
  const unassignedItems = items.filter((item) => !assignments[item.id]);
20603
- const allAssigned = Object.keys(assignments).length === items.length;
20607
+ const allAssigned = items.length > 0 && Object.keys(assignments).length === items.length;
20604
20608
  const results = submitted ? items.map((item) => ({
20605
20609
  item,
20606
20610
  assigned: assignments[item.id],
20607
20611
  correct: assignments[item.id] === item.correctCategory
20608
20612
  })) : [];
20609
- const allCorrect = results.length > 0 && results.every((r) => r.correct);
20613
+ const allCorrect = entityDrivesResult ? entityResult === "success" : results.length > 0 && results.every((r) => r.correct);
20610
20614
  const correctCount = results.filter((r) => r.correct).length;
20611
20615
  const handleAssign = (itemId, categoryId) => {
20612
20616
  if (submitted) return;
20613
- setAssignments((prev) => ({ ...prev, [itemId]: categoryId }));
20617
+ if (assignEvent) {
20618
+ emit(`UI:${assignEvent}`, { itemId, categoryId });
20619
+ }
20620
+ if (!entityHasAssignments) {
20621
+ setLocalAssignments((prev) => ({ ...prev, [itemId]: categoryId }));
20622
+ }
20614
20623
  };
20615
20624
  const handleUnassign = (itemId) => {
20616
20625
  if (submitted) return;
20617
- setAssignments((prev) => {
20618
- const next = { ...prev };
20619
- delete next[itemId];
20620
- return next;
20621
- });
20626
+ if (!entityHasAssignments) {
20627
+ setLocalAssignments((prev) => {
20628
+ const next = { ...prev };
20629
+ delete next[itemId];
20630
+ return next;
20631
+ });
20632
+ }
20622
20633
  };
20623
20634
  const handleSubmit = React82.useCallback(() => {
20624
- setSubmitted(true);
20625
- setAttempts((a) => a + 1);
20626
- const correct = items.every((item) => assignments[item.id] === item.correctCategory);
20627
- if (correct) {
20628
- emit(`UI:${completeEvent}`, { success: true, attempts: attempts + 1 });
20635
+ if (checkEvent) {
20636
+ emit(`UI:${checkEvent}`, {});
20637
+ }
20638
+ if (!entityDrivesResult) {
20639
+ setLocalSubmitted(true);
20640
+ setLocalAttempts((a) => a + 1);
20641
+ const correct = items.every((item) => assignments[item.id] === item.correctCategory);
20642
+ if (correct) {
20643
+ emit(`UI:${completeEvent}`, { success: true, attempts: attempts + 1 });
20644
+ }
20629
20645
  }
20630
- }, [items, assignments, attempts, completeEvent, emit]);
20646
+ }, [checkEvent, entityDrivesResult, items, assignments, attempts, completeEvent, emit]);
20631
20647
  const handleReset = () => {
20632
- setSubmitted(false);
20648
+ if (!entityDrivesResult) setLocalSubmitted(false);
20633
20649
  if (attempts >= 2 && str(resolved?.hint)) {
20634
20650
  setShowHint(true);
20635
20651
  }
20636
20652
  };
20637
20653
  const handleFullReset = () => {
20638
- setAssignments({});
20639
- setSubmitted(false);
20640
- setAttempts(0);
20654
+ if (playAgainEvent) {
20655
+ emit(`UI:${playAgainEvent}`, {});
20656
+ }
20657
+ setLocalAssignments({});
20658
+ setLocalSubmitted(false);
20659
+ setLocalAttempts(0);
20641
20660
  setShowHint(false);
20642
20661
  };
20643
20662
  if (!resolved) return null;
@@ -28520,13 +28539,13 @@ var init_MapView = __esm({
28520
28539
  shadowSize: [41, 41]
28521
28540
  });
28522
28541
  L.Marker.prototype.options.icon = defaultIcon;
28523
- const { useEffect: useEffect73, useRef: useRef67, useCallback: useCallback113, useState: useState107 } = React82__namespace.default;
28542
+ const { useEffect: useEffect74, useRef: useRef68, useCallback: useCallback111, useState: useState107 } = React82__namespace.default;
28524
28543
  const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
28525
28544
  const { useEventBus: useEventBus3 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
28526
28545
  function MapUpdater({ centerLat, centerLng, zoom }) {
28527
28546
  const map = useMap();
28528
- const prevRef = useRef67({ centerLat, centerLng, zoom });
28529
- useEffect73(() => {
28547
+ const prevRef = useRef68({ centerLat, centerLng, zoom });
28548
+ useEffect74(() => {
28530
28549
  const prev = prevRef.current;
28531
28550
  if (prev.centerLat !== centerLat || prev.centerLng !== centerLng || prev.zoom !== zoom) {
28532
28551
  map.setView([centerLat, centerLng], zoom);
@@ -28537,7 +28556,7 @@ var init_MapView = __esm({
28537
28556
  }
28538
28557
  function MapClickHandler({ onMapClick }) {
28539
28558
  const map = useMap();
28540
- useEffect73(() => {
28559
+ useEffect74(() => {
28541
28560
  if (!onMapClick) return;
28542
28561
  const handler = (e) => {
28543
28562
  onMapClick(e.latlng.lat, e.latlng.lng);
@@ -28566,7 +28585,7 @@ var init_MapView = __esm({
28566
28585
  }) {
28567
28586
  const eventBus = useEventBus3();
28568
28587
  const [clickedPosition, setClickedPosition] = useState107(null);
28569
- const handleMapClick = useCallback113((lat, lng) => {
28588
+ const handleMapClick = useCallback111((lat, lng) => {
28570
28589
  if (showClickedPin) {
28571
28590
  setClickedPosition({ lat, lng });
28572
28591
  }
@@ -28575,7 +28594,7 @@ var init_MapView = __esm({
28575
28594
  eventBus.emit(`UI:${mapClickEvent}`, { latitude: lat, longitude: lng });
28576
28595
  }
28577
28596
  }, [onMapClick, mapClickEvent, eventBus, showClickedPin]);
28578
- const handleMarkerClick = useCallback113((marker) => {
28597
+ const handleMarkerClick = useCallback111((marker) => {
28579
28598
  onMarkerClick?.(marker);
28580
28599
  if (markerClickEvent) {
28581
28600
  eventBus.emit(`UI:${markerClickEvent}`, { ...marker });
@@ -37797,51 +37816,52 @@ var init_DataTable = __esm({
37797
37816
  function DebuggerBoard({
37798
37817
  entity,
37799
37818
  completeEvent = "PUZZLE_COMPLETE",
37819
+ toggleFlagEvent,
37820
+ checkEvent,
37821
+ playAgainEvent,
37800
37822
  className
37801
37823
  }) {
37802
37824
  const { emit } = useEventBus();
37803
37825
  const { t } = hooks.useTranslate();
37804
37826
  const resolved = boardEntity(entity);
37805
- const [flaggedLines, setFlaggedLines] = React82.useState(/* @__PURE__ */ new Set());
37806
37827
  const [headerError, setHeaderError] = React82.useState(false);
37807
- const [submitted, setSubmitted] = React82.useState(false);
37808
- const [attempts, setAttempts] = React82.useState(0);
37809
37828
  const [showHint, setShowHint] = React82.useState(false);
37829
+ const lines = Array.isArray(resolved?.lines) ? resolved.lines : [];
37830
+ const result = resolved?.result ?? null;
37831
+ const attempts = num(resolved?.attempts);
37832
+ const submitted = result != null;
37833
+ const bugLines = lines.filter((l) => l.isBug);
37834
+ const flaggedLines = lines.filter((l) => l.isFlagged);
37835
+ const correctFlags = lines.filter((l) => l.isBug && l.isFlagged);
37836
+ const falseFlags = lines.filter((l) => !l.isBug && l.isFlagged);
37837
+ const allCorrect = result === "win";
37810
37838
  const toggleLine = (lineId) => {
37811
37839
  if (submitted) return;
37812
- setFlaggedLines((prev) => {
37813
- const next = new Set(prev);
37814
- if (next.has(lineId)) {
37815
- next.delete(lineId);
37816
- } else {
37817
- next.add(lineId);
37818
- }
37819
- return next;
37820
- });
37840
+ if (toggleFlagEvent) {
37841
+ emit(`UI:${toggleFlagEvent}`, { lineId });
37842
+ }
37821
37843
  };
37822
- const lines = Array.isArray(resolved?.lines) ? resolved.lines : [];
37823
- const bugLines = lines.filter((l) => l.isBug);
37824
- const correctFlags = lines.filter((l) => l.isBug && flaggedLines.has(l.id));
37825
- const falseFlags = lines.filter((l) => !l.isBug && flaggedLines.has(l.id));
37826
- const allCorrect = submitted && correctFlags.length === bugLines.length && falseFlags.length === 0;
37827
37844
  const handleSubmit = React82.useCallback(() => {
37828
- setSubmitted(true);
37829
- setAttempts((a) => a + 1);
37845
+ if (checkEvent) {
37846
+ emit(`UI:${checkEvent}`, {});
37847
+ }
37830
37848
  const correct = correctFlags.length === bugLines.length && falseFlags.length === 0;
37831
37849
  if (correct) {
37832
37850
  emit(`UI:${completeEvent}`, { success: true, attempts: attempts + 1 });
37833
37851
  }
37834
- }, [correctFlags.length, bugLines.length, falseFlags.length, attempts, completeEvent, emit]);
37852
+ }, [checkEvent, correctFlags.length, bugLines.length, falseFlags.length, attempts, completeEvent, emit]);
37835
37853
  const handleReset = () => {
37836
- setSubmitted(false);
37854
+ if (playAgainEvent) {
37855
+ emit(`UI:${playAgainEvent}`, {});
37856
+ }
37837
37857
  if (attempts >= 2 && str(resolved?.hint)) {
37838
37858
  setShowHint(true);
37839
37859
  }
37840
37860
  };
37841
37861
  const handleFullReset = () => {
37842
- setFlaggedLines(/* @__PURE__ */ new Set());
37843
- setSubmitted(false);
37844
- setAttempts(0);
37862
+ if (playAgainEvent) {
37863
+ emit(`UI:${playAgainEvent}`, {});
37864
+ }
37845
37865
  setShowHint(false);
37846
37866
  };
37847
37867
  if (!resolved) return null;
@@ -37869,7 +37889,7 @@ function DebuggerBoard({
37869
37889
  /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", className: "text-muted-foreground", children: t("debugger.findBugs", { count: String(num(resolved.bugCount)) }) })
37870
37890
  ] }) }),
37871
37891
  /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "p-0 overflow-hidden", children: /* @__PURE__ */ jsxRuntime.jsx(VStack, { gap: "none", children: lines.map((line, i) => {
37872
- const isFlagged = flaggedLines.has(line.id);
37892
+ const isFlagged = !!line.isFlagged;
37873
37893
  let lineStyle = "";
37874
37894
  if (submitted) {
37875
37895
  if (line.isBug && isFlagged) lineStyle = "bg-success/10";
@@ -37903,9 +37923,9 @@ function DebuggerBoard({
37903
37923
  /* @__PURE__ */ jsxRuntime.jsx(
37904
37924
  Icon,
37905
37925
  {
37906
- icon: flaggedLines.has(line.id) ? LucideIcons2.CheckCircle : LucideIcons2.XCircle,
37926
+ icon: line.isFlagged ? LucideIcons2.CheckCircle : LucideIcons2.XCircle,
37907
37927
  size: "xs",
37908
- className: flaggedLines.has(line.id) ? "text-success mt-0.5" : "text-warning mt-0.5"
37928
+ className: line.isFlagged ? "text-success mt-0.5" : "text-warning mt-0.5"
37909
37929
  }
37910
37930
  ),
37911
37931
  /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "none", children: [
@@ -37916,7 +37936,7 @@ function DebuggerBoard({
37916
37936
  ] }) }),
37917
37937
  showHint && hint && /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", children: hint }) }),
37918
37938
  /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "sm", justify: "center", children: [
37919
- !submitted ? /* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "primary", onClick: handleSubmit, disabled: flaggedLines.size === 0, children: [
37939
+ !submitted ? /* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "primary", onClick: handleSubmit, disabled: flaggedLines.length === 0, children: [
37920
37940
  /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: LucideIcons2.Send, size: "sm" }),
37921
37941
  t("debugger.submit")
37922
37942
  ] }) : !allCorrect ? /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "primary", onClick: handleReset, children: t("debugger.tryAgain") }) : null,
@@ -39153,7 +39173,7 @@ var init_FeatureDetailPageTemplate = __esm({
39153
39173
  const resolved = entity && typeof entity === "object" && !Array.isArray(entity) ? entity : void 0;
39154
39174
  if (!resolved) return null;
39155
39175
  return /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "none", className: cn("w-full", className), children: [
39156
- /* @__PURE__ */ jsxRuntime.jsx(
39176
+ resolved.hero && /* @__PURE__ */ jsxRuntime.jsx(
39157
39177
  HeroSection,
39158
39178
  {
39159
39179
  tag: resolved.hero.tag,
@@ -40673,7 +40693,7 @@ var init_LandingPageTemplate = __esm({
40673
40693
  const resolved = entity && typeof entity === "object" && !Array.isArray(entity) ? entity : void 0;
40674
40694
  if (!resolved) return null;
40675
40695
  return /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "none", className: cn("w-full", className), children: [
40676
- /* @__PURE__ */ jsxRuntime.jsx(
40696
+ resolved.hero && /* @__PURE__ */ jsxRuntime.jsx(
40677
40697
  HeroSection,
40678
40698
  {
40679
40699
  tag: resolved.hero.tag,
@@ -41680,6 +41700,9 @@ function getOpponentAction(strategy, actions, history) {
41680
41700
  function NegotiatorBoard({
41681
41701
  entity,
41682
41702
  completeEvent = "PUZZLE_COMPLETE",
41703
+ playRoundEvent,
41704
+ finishEvent,
41705
+ playAgainEvent,
41683
41706
  className
41684
41707
  }) {
41685
41708
  const { emit } = useEventBus();
@@ -41688,13 +41711,14 @@ function NegotiatorBoard({
41688
41711
  const [history, setHistory] = React82.useState([]);
41689
41712
  const [headerError, setHeaderError] = React82.useState(false);
41690
41713
  const [showHint, setShowHint] = React82.useState(false);
41691
- const totalRounds = num(resolved?.totalRounds);
41714
+ const totalRounds = num(resolved?.maxRounds);
41692
41715
  const targetScore = num(resolved?.targetScore);
41693
- const currentRound = history.length;
41694
- const isComplete = currentRound >= totalRounds;
41695
- const playerTotal = history.reduce((s, r) => s + r.playerPayoff, 0);
41716
+ const currentRound = num(resolved?.round);
41717
+ const result = str(resolved?.result) || "none";
41718
+ const playerTotal = num(resolved?.score);
41719
+ const isComplete = result !== "none" || totalRounds > 0 && currentRound >= totalRounds;
41720
+ const won = result === "win";
41696
41721
  const opponentTotal = history.reduce((s, r) => s + r.opponentPayoff, 0);
41697
- const won = isComplete && playerTotal >= targetScore;
41698
41722
  const actions = Array.isArray(resolved?.actions) ? resolved.actions : [];
41699
41723
  const payoffMatrix = Array.isArray(resolved?.payoffMatrix) ? resolved.payoffMatrix : [];
41700
41724
  const handleAction = React82.useCallback((actionId) => {
@@ -41703,29 +41727,45 @@ function NegotiatorBoard({
41703
41727
  const payoff = payoffMatrix.find(
41704
41728
  (p2) => p2.playerAction === actionId && p2.opponentAction === opponentAction
41705
41729
  );
41706
- const result = {
41707
- round: currentRound + 1,
41708
- playerAction: actionId,
41709
- opponentAction,
41710
- playerPayoff: payoff?.playerPayoff ?? 0,
41711
- opponentPayoff: payoff?.opponentPayoff ?? 0
41712
- };
41713
- const newHistory = [...history, result];
41714
- setHistory(newHistory);
41715
- if (newHistory.length >= totalRounds) {
41716
- const total = newHistory.reduce((s, r) => s + r.playerPayoff, 0);
41717
- if (total >= targetScore) {
41718
- emit(`UI:${completeEvent}`, { success: true, score: total });
41719
- }
41720
- if (newHistory.length >= 3 && str(resolved?.hint)) {
41730
+ const playerPayoff = payoff?.playerPayoff ?? 0;
41731
+ setHistory((prev) => [
41732
+ ...prev,
41733
+ {
41734
+ round: prev.length + 1,
41735
+ playerAction: actionId,
41736
+ opponentAction,
41737
+ playerPayoff,
41738
+ opponentPayoff: payoff?.opponentPayoff ?? 0
41739
+ }
41740
+ ]);
41741
+ if (playRoundEvent) {
41742
+ emit(`UI:${playRoundEvent}`, { playerAction: actionId, payoff: playerPayoff });
41743
+ }
41744
+ if (totalRounds > 0 && currentRound + 1 >= totalRounds) {
41745
+ if (finishEvent) {
41746
+ emit(`UI:${finishEvent}`, {});
41747
+ }
41748
+ if (str(resolved?.hint)) {
41721
41749
  setShowHint(true);
41722
41750
  }
41723
41751
  }
41724
- }, [isComplete, resolved, totalRounds, targetScore, actions, payoffMatrix, history, currentRound, completeEvent, emit]);
41725
- const handleReset = () => {
41752
+ }, [isComplete, resolved, totalRounds, currentRound, actions, payoffMatrix, history, playRoundEvent, finishEvent, emit]);
41753
+ const handleReset = React82.useCallback(() => {
41726
41754
  setHistory([]);
41727
41755
  setShowHint(false);
41728
- };
41756
+ if (playAgainEvent) {
41757
+ emit(`UI:${playAgainEvent}`, {});
41758
+ }
41759
+ }, [playAgainEvent, emit]);
41760
+ const completedRef = React82.useRef(false);
41761
+ React82.useEffect(() => {
41762
+ if (result === "win" && !completedRef.current) {
41763
+ completedRef.current = true;
41764
+ emit(`UI:${completeEvent}`, { success: true, score: playerTotal });
41765
+ } else if (result === "none") {
41766
+ completedRef.current = false;
41767
+ }
41768
+ }, [result, playerTotal, completeEvent, emit]);
41729
41769
  const getActionLabel = (id) => actions.find((a) => a.id === id)?.label ?? id;
41730
41770
  if (!resolved) return null;
41731
41771
  const theme = resolved.theme ?? void 0;
@@ -41894,7 +41934,7 @@ var init_PricingPageTemplate = __esm({
41894
41934
  const resolved = entity && typeof entity === "object" && !Array.isArray(entity) ? entity : void 0;
41895
41935
  if (!resolved) return null;
41896
41936
  return /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "none", className: cn("w-full", className), children: [
41897
- /* @__PURE__ */ jsxRuntime.jsx(
41937
+ resolved.hero && /* @__PURE__ */ jsxRuntime.jsx(
41898
41938
  HeroSection,
41899
41939
  {
41900
41940
  tag: resolved.hero.tag,
@@ -44691,67 +44731,47 @@ var init_SimulationGraph = __esm({
44691
44731
  function SimulatorBoard({
44692
44732
  entity,
44693
44733
  completeEvent = "PUZZLE_COMPLETE",
44734
+ setAEvent,
44735
+ setBEvent,
44736
+ checkEvent,
44737
+ playAgainEvent,
44694
44738
  className
44695
44739
  }) {
44696
44740
  const { emit } = useEventBus();
44697
44741
  const { t } = hooks.useTranslate();
44698
44742
  const resolved = boardEntity(entity);
44699
44743
  const parameters = Array.isArray(resolved?.parameters) ? resolved.parameters : [];
44700
- const [values, setValues] = React82.useState(() => {
44701
- const init = {};
44702
- for (const p2 of parameters) {
44703
- init[p2.id] = p2.initial;
44704
- }
44705
- return init;
44706
- });
44707
44744
  const [headerError, setHeaderError] = React82.useState(false);
44708
- const [submitted, setSubmitted] = React82.useState(false);
44709
- const [attempts, setAttempts] = React82.useState(0);
44710
- const [showHint, setShowHint] = React82.useState(false);
44711
- const computeOutput = React82.useCallback((params) => {
44712
- try {
44713
- const fn = new Function("params", `return (${str(resolved?.computeExpression)})`);
44714
- return fn(params);
44715
- } catch {
44716
- return 0;
44717
- }
44718
- }, [resolved?.computeExpression]);
44719
- const output = React82.useMemo(() => computeOutput(values) ?? 0, [computeOutput, values]);
44720
- const targetValue = num(resolved?.targetValue);
44721
- const targetTolerance = num(resolved?.targetTolerance);
44722
- const isCorrect = Math.abs(output - targetValue) <= targetTolerance;
44723
- const handleParameterChange = (id, value) => {
44724
- if (submitted) return;
44725
- setValues((prev) => ({ ...prev, [id]: value }));
44745
+ if (!resolved) return null;
44746
+ const paramA = num(resolved.paramA);
44747
+ const paramB = num(resolved.paramB);
44748
+ const output = num(resolved.output);
44749
+ const targetValue = num(resolved.target);
44750
+ const targetTolerance = num(resolved.tolerance);
44751
+ const attempts = num(resolved.attempts);
44752
+ const result = str(resolved.result);
44753
+ const isWin = result === "win";
44754
+ const isComplete = result !== "none" && result !== "";
44755
+ const paramAValue = parameters[0];
44756
+ const paramBValue = parameters[1];
44757
+ const sliderValues = [paramA, paramB];
44758
+ const sliderEvents = [setAEvent, setBEvent];
44759
+ const handleParameterChange = (index, value) => {
44760
+ if (isComplete) return;
44761
+ const ev = sliderEvents[index];
44762
+ if (ev) emit(`UI:${ev}`, { value });
44726
44763
  };
44727
- const handleSubmit = () => {
44728
- setSubmitted(true);
44729
- setAttempts((a) => a + 1);
44730
- if (isCorrect) {
44731
- emit(`UI:${completeEvent}`, { success: true, attempts: attempts + 1 });
44732
- }
44764
+ const handleCheck = () => {
44765
+ if (checkEvent) emit(`UI:${checkEvent}`, {});
44733
44766
  };
44734
- const handleReset = () => {
44735
- setSubmitted(false);
44736
- if (attempts >= 2 && str(resolved?.hint)) {
44737
- setShowHint(true);
44738
- }
44767
+ const handlePlayAgain = () => {
44768
+ if (playAgainEvent) emit(`UI:${playAgainEvent}`, {});
44739
44769
  };
44740
- const handleFullReset = () => {
44741
- const init = {};
44742
- for (const p2 of parameters) {
44743
- init[p2.id] = p2.initial;
44744
- }
44745
- setValues(init);
44746
- setSubmitted(false);
44747
- setAttempts(0);
44748
- setShowHint(false);
44749
- };
44750
- if (!resolved) return null;
44751
44770
  const theme = resolved.theme ?? void 0;
44752
44771
  const themeBackground = theme?.background;
44753
44772
  const headerImage = str(resolved.headerImage);
44754
44773
  const hint = str(resolved.hint);
44774
+ const showHint = isComplete && !isWin && attempts >= 2 && Boolean(hint);
44755
44775
  const outputLabel = str(resolved.outputLabel);
44756
44776
  const outputUnit = str(resolved.outputUnit);
44757
44777
  return /* @__PURE__ */ jsxRuntime.jsx(
@@ -44771,41 +44791,43 @@ function SimulatorBoard({
44771
44791
  ] }) }),
44772
44792
  /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "md", children: [
44773
44793
  /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", weight: "bold", className: "uppercase tracking-wider text-muted-foreground", children: t("simulator.parameters") }),
44774
- parameters.map((param) => /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "xs", children: [
44775
- /* @__PURE__ */ jsxRuntime.jsxs(HStack, { justify: "between", align: "center", children: [
44776
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", weight: "medium", children: param.label }),
44777
- /* @__PURE__ */ jsxRuntime.jsxs(Badge, { size: "sm", children: [
44778
- values[param.id],
44779
- " ",
44780
- param.unit
44781
- ] })
44782
- ] }),
44783
- /* @__PURE__ */ jsxRuntime.jsx(
44784
- "input",
44785
- {
44786
- type: "range",
44787
- min: param.min,
44788
- max: param.max,
44789
- step: param.step,
44790
- value: values[param.id],
44791
- onChange: (e) => handleParameterChange(param.id, Number(e.target.value)),
44792
- disabled: submitted,
44793
- className: "w-full accent-foreground"
44794
- }
44795
- ),
44796
- /* @__PURE__ */ jsxRuntime.jsxs(HStack, { justify: "between", children: [
44797
- /* @__PURE__ */ jsxRuntime.jsxs(Typography, { variant: "caption", className: "text-muted-foreground", children: [
44798
- param.min,
44799
- " ",
44800
- param.unit
44794
+ [paramAValue, paramBValue].map(
44795
+ (param, index) => param ? /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "xs", children: [
44796
+ /* @__PURE__ */ jsxRuntime.jsxs(HStack, { justify: "between", align: "center", children: [
44797
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", weight: "medium", children: param.label }),
44798
+ /* @__PURE__ */ jsxRuntime.jsxs(Badge, { size: "sm", children: [
44799
+ sliderValues[index],
44800
+ " ",
44801
+ param.unit
44802
+ ] })
44801
44803
  ] }),
44802
- /* @__PURE__ */ jsxRuntime.jsxs(Typography, { variant: "caption", className: "text-muted-foreground", children: [
44803
- param.max,
44804
- " ",
44805
- param.unit
44804
+ /* @__PURE__ */ jsxRuntime.jsx(
44805
+ "input",
44806
+ {
44807
+ type: "range",
44808
+ min: param.min,
44809
+ max: param.max,
44810
+ step: param.step,
44811
+ value: sliderValues[index],
44812
+ onChange: (e) => handleParameterChange(index, Number(e.target.value)),
44813
+ disabled: isComplete,
44814
+ className: "w-full accent-foreground"
44815
+ }
44816
+ ),
44817
+ /* @__PURE__ */ jsxRuntime.jsxs(HStack, { justify: "between", children: [
44818
+ /* @__PURE__ */ jsxRuntime.jsxs(Typography, { variant: "caption", className: "text-muted-foreground", children: [
44819
+ param.min,
44820
+ " ",
44821
+ param.unit
44822
+ ] }),
44823
+ /* @__PURE__ */ jsxRuntime.jsxs(Typography, { variant: "caption", className: "text-muted-foreground", children: [
44824
+ param.max,
44825
+ " ",
44826
+ param.unit
44827
+ ] })
44806
44828
  ] })
44807
- ] })
44808
- ] }, param.id))
44829
+ ] }, param.id ?? index) : null
44830
+ )
44809
44831
  ] }) }),
44810
44832
  /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "sm", align: "center", children: [
44811
44833
  /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", weight: "bold", className: "uppercase tracking-wider text-muted-foreground", children: outputLabel }),
@@ -44814,9 +44836,9 @@ function SimulatorBoard({
44814
44836
  " ",
44815
44837
  outputUnit
44816
44838
  ] }),
44817
- submitted && /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "xs", align: "center", children: [
44818
- /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: isCorrect ? LucideIcons2.CheckCircle : LucideIcons2.XCircle, size: "sm", className: isCorrect ? "text-success" : "text-error" }),
44819
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", className: isCorrect ? "text-success" : "text-error", children: isCorrect ? str(resolved.successMessage) || t("simulator.correct") : str(resolved.failMessage) || t("simulator.incorrect") })
44839
+ isComplete && /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "xs", align: "center", children: [
44840
+ /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: isWin ? LucideIcons2.CheckCircle : LucideIcons2.XCircle, size: "sm", className: isWin ? "text-success" : "text-error" }),
44841
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", className: isWin ? "text-success" : "text-error", children: isWin ? str(resolved.successMessage) || t("simulator.correct") : str(resolved.failMessage) || t("simulator.incorrect") })
44820
44842
  ] }),
44821
44843
  /* @__PURE__ */ jsxRuntime.jsxs(Typography, { variant: "caption", className: "text-muted-foreground", children: [
44822
44844
  t("simulator.target"),
@@ -44831,11 +44853,11 @@ function SimulatorBoard({
44831
44853
  ] }) }),
44832
44854
  showHint && hint && /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", children: hint }) }),
44833
44855
  /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "sm", justify: "center", children: [
44834
- !submitted ? /* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "primary", onClick: handleSubmit, children: [
44856
+ !isComplete ? /* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "primary", onClick: handleCheck, children: [
44835
44857
  /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: LucideIcons2.Play, size: "sm" }),
44836
44858
  t("simulator.simulate")
44837
- ] }) : !isCorrect ? /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "primary", onClick: handleReset, children: t("simulator.tryAgain") }) : null,
44838
- /* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "secondary", onClick: handleFullReset, children: [
44859
+ ] }) : null,
44860
+ /* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "secondary", onClick: handlePlayAgain, children: [
44839
44861
  /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: LucideIcons2.RotateCcw, size: "sm" }),
44840
44862
  t("simulator.reset")
44841
44863
  ] })