@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.
@@ -9490,7 +9490,7 @@ var init_AboutPageTemplate = __esm({
9490
9490
  const resolved = entity && typeof entity === "object" && !Array.isArray(entity) ? entity : void 0;
9491
9491
  if (!resolved) return null;
9492
9492
  return /* @__PURE__ */ jsxs(VStack, { gap: "none", className: cn("w-full", className), children: [
9493
- /* @__PURE__ */ jsx(
9493
+ resolved.hero && /* @__PURE__ */ jsx(
9494
9494
  HeroSection,
9495
9495
  {
9496
9496
  tag: resolved.hero.tag,
@@ -17372,61 +17372,53 @@ var init_Breadcrumb = __esm({
17372
17372
  function BuilderBoard({
17373
17373
  entity,
17374
17374
  completeEvent = "PUZZLE_COMPLETE",
17375
+ placeEvent,
17376
+ checkEvent,
17377
+ playAgainEvent,
17375
17378
  className
17376
17379
  }) {
17377
17380
  const { emit } = useEventBus();
17378
17381
  const { t } = useTranslate();
17379
17382
  const resolved = boardEntity(entity);
17380
- const [placements, setPlacements] = useState({});
17381
17383
  const [headerError, setHeaderError] = useState(false);
17382
- const [submitted, setSubmitted] = useState(false);
17383
- const [attempts, setAttempts] = useState(0);
17384
- const [showHint, setShowHint] = useState(false);
17384
+ const [selectedComponent, setSelectedComponent] = useState(null);
17385
17385
  const components = Array.isArray(resolved?.components) ? resolved.components : [];
17386
17386
  const slots = Array.isArray(resolved?.slots) ? resolved.slots : [];
17387
+ const placements = {};
17388
+ for (const slot of slots) {
17389
+ if (slot.placedComponentId) placements[slot.id] = slot.placedComponentId;
17390
+ }
17391
+ const attempts = num(resolved?.attempts);
17392
+ const result = str(resolved?.result) || "none";
17393
+ const submitted = result === "win";
17387
17394
  const usedComponentIds = new Set(Object.values(placements));
17388
17395
  const availableComponents = components.filter((c) => !usedComponentIds.has(c.id));
17389
- const [selectedComponent, setSelectedComponent] = useState(null);
17390
- const allPlaced = Object.keys(placements).length === slots.length;
17396
+ const allPlaced = slots.length > 0 && slots.every((s) => Boolean(placements[s.id]));
17391
17397
  const results = submitted ? slots.map((slot) => ({
17392
17398
  slot,
17393
17399
  placed: placements[slot.id],
17394
- correct: placements[slot.id] === slot.acceptsComponentId
17400
+ correct: placements[slot.id] === slot.requiredComponentId
17395
17401
  })) : [];
17396
- const allCorrect = results.length > 0 && results.every((r) => r.correct);
17402
+ const showHint = attempts >= 2 && Boolean(str(resolved?.hint));
17397
17403
  const handlePlaceComponent = (slotId) => {
17398
17404
  if (submitted || !selectedComponent) return;
17399
- setPlacements((prev) => ({ ...prev, [slotId]: selectedComponent }));
17405
+ if (placeEvent) emit(`UI:${placeEvent}`, { slotId, componentId: selectedComponent });
17400
17406
  setSelectedComponent(null);
17401
17407
  };
17402
17408
  const handleRemoveFromSlot = (slotId) => {
17403
17409
  if (submitted) return;
17404
- setPlacements((prev) => {
17405
- const next = { ...prev };
17406
- delete next[slotId];
17407
- return next;
17408
- });
17410
+ if (placeEvent) emit(`UI:${placeEvent}`, { slotId, componentId: "" });
17409
17411
  };
17410
- const handleSubmit = useCallback(() => {
17411
- setSubmitted(true);
17412
- setAttempts((a) => a + 1);
17413
- const correct = slots.every((slot) => placements[slot.id] === slot.acceptsComponentId);
17414
- if (correct) {
17412
+ const handleSubmit = () => {
17413
+ if (checkEvent) emit(`UI:${checkEvent}`, {});
17414
+ const solved = slots.length > 0 && slots.every((s) => placements[s.id] === s.requiredComponentId);
17415
+ if (solved && completeEvent) {
17415
17416
  emit(`UI:${completeEvent}`, { success: true, attempts: attempts + 1 });
17416
17417
  }
17417
- }, [slots, placements, attempts, completeEvent, emit]);
17418
- const handleReset = () => {
17419
- setSubmitted(false);
17420
- if (attempts >= 2 && str(resolved?.hint)) {
17421
- setShowHint(true);
17422
- }
17423
17418
  };
17424
- const handleFullReset = () => {
17425
- setPlacements({});
17426
- setSubmitted(false);
17419
+ const handlePlayAgain = () => {
17427
17420
  setSelectedComponent(null);
17428
- setAttempts(0);
17429
- setShowHint(false);
17421
+ if (playAgainEvent) emit(`UI:${playAgainEvent}`, {});
17430
17422
  };
17431
17423
  const getComponentById = (id) => components.find((c) => c.id === id);
17432
17424
  if (!resolved) return null;
@@ -17476,13 +17468,13 @@ function BuilderBoard({
17476
17468
  /* @__PURE__ */ jsx(Typography, { variant: "small", weight: "bold", className: "uppercase tracking-wider text-muted-foreground", children: t("builder.blueprint") }),
17477
17469
  /* @__PURE__ */ jsx(VStack, { gap: "sm", children: slots.map((slot) => {
17478
17470
  const placedComp = placements[slot.id] ? getComponentById(placements[slot.id]) : null;
17479
- const result = results.find((r) => r.slot.id === slot.id);
17471
+ const result2 = results.find((r) => r.slot.id === slot.id);
17480
17472
  return /* @__PURE__ */ jsxs(
17481
17473
  HStack,
17482
17474
  {
17483
17475
  gap: "sm",
17484
17476
  align: "center",
17485
- className: `p-3 border-2 rounded ${result ? result.correct ? "border-success" : "border-error" : selectedComponent ? "border-dashed border-foreground cursor-pointer" : "border-border"}`,
17477
+ className: `p-3 border-2 rounded ${result2 ? result2.correct ? "border-success" : "border-error" : selectedComponent ? "border-dashed border-foreground cursor-pointer" : "border-border"}`,
17486
17478
  onClick: () => handlePlaceComponent(slot.id),
17487
17479
  children: [
17488
17480
  /* @__PURE__ */ jsxs(VStack, { gap: "none", className: "flex-1", children: [
@@ -17497,7 +17489,7 @@ function BuilderBoard({
17497
17489
  ] }) : null,
17498
17490
  placedComp.label
17499
17491
  ] }),
17500
- result && /* @__PURE__ */ jsx(Icon, { icon: result.correct ? CheckCircle : XCircle, size: "sm", className: result.correct ? "text-success" : "text-error" })
17492
+ result2 && /* @__PURE__ */ jsx(Icon, { icon: result2.correct ? CheckCircle : XCircle, size: "sm", className: result2.correct ? "text-success" : "text-error" })
17501
17493
  ] }) : /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-muted-foreground", children: t("builder.empty") })
17502
17494
  ]
17503
17495
  },
@@ -17506,16 +17498,16 @@ function BuilderBoard({
17506
17498
  }) })
17507
17499
  ] }) }),
17508
17500
  submitted && /* @__PURE__ */ jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", align: "center", children: [
17509
- /* @__PURE__ */ jsx(Icon, { icon: allCorrect ? CheckCircle : XCircle, size: "lg", className: allCorrect ? "text-success" : "text-error" }),
17510
- /* @__PURE__ */ jsx(Typography, { variant: "body", weight: "bold", children: allCorrect ? str(resolved.successMessage) || t("builder.success") : str(resolved.failMessage) || t("builder.incorrect") })
17501
+ /* @__PURE__ */ jsx(Icon, { icon: CheckCircle, size: "lg", className: "text-success" }),
17502
+ /* @__PURE__ */ jsx(Typography, { variant: "body", weight: "bold", children: str(resolved.successMessage) || t("builder.success") })
17511
17503
  ] }) }),
17512
17504
  showHint && hint && /* @__PURE__ */ jsx(Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsx(Typography, { variant: "body", children: hint }) }),
17513
17505
  /* @__PURE__ */ jsxs(HStack, { gap: "sm", justify: "center", children: [
17514
- !submitted ? /* @__PURE__ */ jsxs(Button, { variant: "primary", onClick: handleSubmit, disabled: !allPlaced, children: [
17506
+ !submitted && /* @__PURE__ */ jsxs(Button, { variant: "primary", onClick: handleSubmit, disabled: !allPlaced, children: [
17515
17507
  /* @__PURE__ */ jsx(Icon, { icon: Wrench, size: "sm" }),
17516
17508
  t("builder.build")
17517
- ] }) : !allCorrect ? /* @__PURE__ */ jsx(Button, { variant: "primary", onClick: handleReset, children: t("builder.tryAgain") }) : null,
17518
- /* @__PURE__ */ jsxs(Button, { variant: "secondary", onClick: handleFullReset, children: [
17509
+ ] }),
17510
+ /* @__PURE__ */ jsxs(Button, { variant: "secondary", onClick: handlePlayAgain, children: [
17519
17511
  /* @__PURE__ */ jsx(Icon, { icon: RotateCcw, size: "sm" }),
17520
17512
  t("builder.reset")
17521
17513
  ] })
@@ -20971,57 +20963,84 @@ var init_ChartLegend = __esm({
20971
20963
  function ClassifierBoard({
20972
20964
  entity,
20973
20965
  completeEvent = "PUZZLE_COMPLETE",
20966
+ assignEvent,
20967
+ checkEvent,
20968
+ playAgainEvent,
20974
20969
  className
20975
20970
  }) {
20976
20971
  const { emit } = useEventBus();
20977
20972
  const { t } = useTranslate();
20978
20973
  const resolved = boardEntity(entity);
20979
- const [assignments, setAssignments] = useState({});
20974
+ const [localAssignments, setLocalAssignments] = useState({});
20980
20975
  const [headerError, setHeaderError] = useState(false);
20981
- const [submitted, setSubmitted] = useState(false);
20982
- const [attempts, setAttempts] = useState(0);
20976
+ const [localSubmitted, setLocalSubmitted] = useState(false);
20977
+ const [localAttempts, setLocalAttempts] = useState(0);
20983
20978
  const [showHint, setShowHint] = useState(false);
20984
20979
  const items = Array.isArray(resolved?.items) ? resolved.items : [];
20985
20980
  const categories = Array.isArray(resolved?.categories) ? resolved.categories : [];
20981
+ const entityResult = str(resolved?.result);
20982
+ const entityDrivesResult = entityResult.length > 0;
20983
+ const entityHasAssignments = items.some((item) => item.assignedCategory != null);
20984
+ const assignments = entityHasAssignments ? items.reduce((acc, item) => {
20985
+ if (item.assignedCategory != null) acc[item.id] = item.assignedCategory;
20986
+ return acc;
20987
+ }, {}) : localAssignments;
20988
+ const attempts = entityDrivesResult ? num(resolved?.attempts) : localAttempts;
20989
+ const submitted = entityDrivesResult || localSubmitted;
20986
20990
  const unassignedItems = items.filter((item) => !assignments[item.id]);
20987
- const allAssigned = Object.keys(assignments).length === items.length;
20991
+ const allAssigned = items.length > 0 && Object.keys(assignments).length === items.length;
20988
20992
  const results = submitted ? items.map((item) => ({
20989
20993
  item,
20990
20994
  assigned: assignments[item.id],
20991
20995
  correct: assignments[item.id] === item.correctCategory
20992
20996
  })) : [];
20993
- const allCorrect = results.length > 0 && results.every((r) => r.correct);
20997
+ const allCorrect = entityDrivesResult ? entityResult === "success" : results.length > 0 && results.every((r) => r.correct);
20994
20998
  const correctCount = results.filter((r) => r.correct).length;
20995
20999
  const handleAssign = (itemId, categoryId) => {
20996
21000
  if (submitted) return;
20997
- setAssignments((prev) => ({ ...prev, [itemId]: categoryId }));
21001
+ if (assignEvent) {
21002
+ emit(`UI:${assignEvent}`, { itemId, categoryId });
21003
+ }
21004
+ if (!entityHasAssignments) {
21005
+ setLocalAssignments((prev) => ({ ...prev, [itemId]: categoryId }));
21006
+ }
20998
21007
  };
20999
21008
  const handleUnassign = (itemId) => {
21000
21009
  if (submitted) return;
21001
- setAssignments((prev) => {
21002
- const next = { ...prev };
21003
- delete next[itemId];
21004
- return next;
21005
- });
21010
+ if (!entityHasAssignments) {
21011
+ setLocalAssignments((prev) => {
21012
+ const next = { ...prev };
21013
+ delete next[itemId];
21014
+ return next;
21015
+ });
21016
+ }
21006
21017
  };
21007
21018
  const handleSubmit = useCallback(() => {
21008
- setSubmitted(true);
21009
- setAttempts((a) => a + 1);
21010
- const correct = items.every((item) => assignments[item.id] === item.correctCategory);
21011
- if (correct) {
21012
- emit(`UI:${completeEvent}`, { success: true, attempts: attempts + 1 });
21019
+ if (checkEvent) {
21020
+ emit(`UI:${checkEvent}`, {});
21021
+ }
21022
+ if (!entityDrivesResult) {
21023
+ setLocalSubmitted(true);
21024
+ setLocalAttempts((a) => a + 1);
21025
+ const correct = items.every((item) => assignments[item.id] === item.correctCategory);
21026
+ if (correct) {
21027
+ emit(`UI:${completeEvent}`, { success: true, attempts: attempts + 1 });
21028
+ }
21013
21029
  }
21014
- }, [items, assignments, attempts, completeEvent, emit]);
21030
+ }, [checkEvent, entityDrivesResult, items, assignments, attempts, completeEvent, emit]);
21015
21031
  const handleReset = () => {
21016
- setSubmitted(false);
21032
+ if (!entityDrivesResult) setLocalSubmitted(false);
21017
21033
  if (attempts >= 2 && str(resolved?.hint)) {
21018
21034
  setShowHint(true);
21019
21035
  }
21020
21036
  };
21021
21037
  const handleFullReset = () => {
21022
- setAssignments({});
21023
- setSubmitted(false);
21024
- setAttempts(0);
21038
+ if (playAgainEvent) {
21039
+ emit(`UI:${playAgainEvent}`, {});
21040
+ }
21041
+ setLocalAssignments({});
21042
+ setLocalSubmitted(false);
21043
+ setLocalAttempts(0);
21025
21044
  setShowHint(false);
21026
21045
  };
21027
21046
  if (!resolved) return null;
@@ -28904,13 +28923,13 @@ var init_MapView = __esm({
28904
28923
  shadowSize: [41, 41]
28905
28924
  });
28906
28925
  L.Marker.prototype.options.icon = defaultIcon;
28907
- const { useEffect: useEffect72, useRef: useRef67, useCallback: useCallback113, useState: useState104 } = React83__default;
28926
+ const { useEffect: useEffect73, useRef: useRef68, useCallback: useCallback111, useState: useState104 } = React83__default;
28908
28927
  const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
28909
28928
  const { useEventBus: useEventBus2 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
28910
28929
  function MapUpdater({ centerLat, centerLng, zoom }) {
28911
28930
  const map = useMap();
28912
- const prevRef = useRef67({ centerLat, centerLng, zoom });
28913
- useEffect72(() => {
28931
+ const prevRef = useRef68({ centerLat, centerLng, zoom });
28932
+ useEffect73(() => {
28914
28933
  const prev = prevRef.current;
28915
28934
  if (prev.centerLat !== centerLat || prev.centerLng !== centerLng || prev.zoom !== zoom) {
28916
28935
  map.setView([centerLat, centerLng], zoom);
@@ -28921,7 +28940,7 @@ var init_MapView = __esm({
28921
28940
  }
28922
28941
  function MapClickHandler({ onMapClick }) {
28923
28942
  const map = useMap();
28924
- useEffect72(() => {
28943
+ useEffect73(() => {
28925
28944
  if (!onMapClick) return;
28926
28945
  const handler = (e) => {
28927
28946
  onMapClick(e.latlng.lat, e.latlng.lng);
@@ -28950,7 +28969,7 @@ var init_MapView = __esm({
28950
28969
  }) {
28951
28970
  const eventBus = useEventBus2();
28952
28971
  const [clickedPosition, setClickedPosition] = useState104(null);
28953
- const handleMapClick = useCallback113((lat, lng) => {
28972
+ const handleMapClick = useCallback111((lat, lng) => {
28954
28973
  if (showClickedPin) {
28955
28974
  setClickedPosition({ lat, lng });
28956
28975
  }
@@ -28959,7 +28978,7 @@ var init_MapView = __esm({
28959
28978
  eventBus.emit(`UI:${mapClickEvent}`, { latitude: lat, longitude: lng });
28960
28979
  }
28961
28980
  }, [onMapClick, mapClickEvent, eventBus, showClickedPin]);
28962
- const handleMarkerClick = useCallback113((marker) => {
28981
+ const handleMarkerClick = useCallback111((marker) => {
28963
28982
  onMarkerClick?.(marker);
28964
28983
  if (markerClickEvent) {
28965
28984
  eventBus.emit(`UI:${markerClickEvent}`, { ...marker });
@@ -38181,51 +38200,52 @@ var init_DataTable = __esm({
38181
38200
  function DebuggerBoard({
38182
38201
  entity,
38183
38202
  completeEvent = "PUZZLE_COMPLETE",
38203
+ toggleFlagEvent,
38204
+ checkEvent,
38205
+ playAgainEvent,
38184
38206
  className
38185
38207
  }) {
38186
38208
  const { emit } = useEventBus();
38187
38209
  const { t } = useTranslate();
38188
38210
  const resolved = boardEntity(entity);
38189
- const [flaggedLines, setFlaggedLines] = useState(/* @__PURE__ */ new Set());
38190
38211
  const [headerError, setHeaderError] = useState(false);
38191
- const [submitted, setSubmitted] = useState(false);
38192
- const [attempts, setAttempts] = useState(0);
38193
38212
  const [showHint, setShowHint] = useState(false);
38213
+ const lines = Array.isArray(resolved?.lines) ? resolved.lines : [];
38214
+ const result = resolved?.result ?? null;
38215
+ const attempts = num(resolved?.attempts);
38216
+ const submitted = result != null;
38217
+ const bugLines = lines.filter((l) => l.isBug);
38218
+ const flaggedLines = lines.filter((l) => l.isFlagged);
38219
+ const correctFlags = lines.filter((l) => l.isBug && l.isFlagged);
38220
+ const falseFlags = lines.filter((l) => !l.isBug && l.isFlagged);
38221
+ const allCorrect = result === "win";
38194
38222
  const toggleLine = (lineId) => {
38195
38223
  if (submitted) return;
38196
- setFlaggedLines((prev) => {
38197
- const next = new Set(prev);
38198
- if (next.has(lineId)) {
38199
- next.delete(lineId);
38200
- } else {
38201
- next.add(lineId);
38202
- }
38203
- return next;
38204
- });
38224
+ if (toggleFlagEvent) {
38225
+ emit(`UI:${toggleFlagEvent}`, { lineId });
38226
+ }
38205
38227
  };
38206
- const lines = Array.isArray(resolved?.lines) ? resolved.lines : [];
38207
- const bugLines = lines.filter((l) => l.isBug);
38208
- const correctFlags = lines.filter((l) => l.isBug && flaggedLines.has(l.id));
38209
- const falseFlags = lines.filter((l) => !l.isBug && flaggedLines.has(l.id));
38210
- const allCorrect = submitted && correctFlags.length === bugLines.length && falseFlags.length === 0;
38211
38228
  const handleSubmit = useCallback(() => {
38212
- setSubmitted(true);
38213
- setAttempts((a) => a + 1);
38229
+ if (checkEvent) {
38230
+ emit(`UI:${checkEvent}`, {});
38231
+ }
38214
38232
  const correct = correctFlags.length === bugLines.length && falseFlags.length === 0;
38215
38233
  if (correct) {
38216
38234
  emit(`UI:${completeEvent}`, { success: true, attempts: attempts + 1 });
38217
38235
  }
38218
- }, [correctFlags.length, bugLines.length, falseFlags.length, attempts, completeEvent, emit]);
38236
+ }, [checkEvent, correctFlags.length, bugLines.length, falseFlags.length, attempts, completeEvent, emit]);
38219
38237
  const handleReset = () => {
38220
- setSubmitted(false);
38238
+ if (playAgainEvent) {
38239
+ emit(`UI:${playAgainEvent}`, {});
38240
+ }
38221
38241
  if (attempts >= 2 && str(resolved?.hint)) {
38222
38242
  setShowHint(true);
38223
38243
  }
38224
38244
  };
38225
38245
  const handleFullReset = () => {
38226
- setFlaggedLines(/* @__PURE__ */ new Set());
38227
- setSubmitted(false);
38228
- setAttempts(0);
38246
+ if (playAgainEvent) {
38247
+ emit(`UI:${playAgainEvent}`, {});
38248
+ }
38229
38249
  setShowHint(false);
38230
38250
  };
38231
38251
  if (!resolved) return null;
@@ -38253,7 +38273,7 @@ function DebuggerBoard({
38253
38273
  /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-muted-foreground", children: t("debugger.findBugs", { count: String(num(resolved.bugCount)) }) })
38254
38274
  ] }) }),
38255
38275
  /* @__PURE__ */ jsx(Card, { className: "p-0 overflow-hidden", children: /* @__PURE__ */ jsx(VStack, { gap: "none", children: lines.map((line, i) => {
38256
- const isFlagged = flaggedLines.has(line.id);
38276
+ const isFlagged = !!line.isFlagged;
38257
38277
  let lineStyle = "";
38258
38278
  if (submitted) {
38259
38279
  if (line.isBug && isFlagged) lineStyle = "bg-success/10";
@@ -38287,9 +38307,9 @@ function DebuggerBoard({
38287
38307
  /* @__PURE__ */ jsx(
38288
38308
  Icon,
38289
38309
  {
38290
- icon: flaggedLines.has(line.id) ? CheckCircle : XCircle,
38310
+ icon: line.isFlagged ? CheckCircle : XCircle,
38291
38311
  size: "xs",
38292
- className: flaggedLines.has(line.id) ? "text-success mt-0.5" : "text-warning mt-0.5"
38312
+ className: line.isFlagged ? "text-success mt-0.5" : "text-warning mt-0.5"
38293
38313
  }
38294
38314
  ),
38295
38315
  /* @__PURE__ */ jsxs(VStack, { gap: "none", children: [
@@ -38300,7 +38320,7 @@ function DebuggerBoard({
38300
38320
  ] }) }),
38301
38321
  showHint && hint && /* @__PURE__ */ jsx(Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsx(Typography, { variant: "body", children: hint }) }),
38302
38322
  /* @__PURE__ */ jsxs(HStack, { gap: "sm", justify: "center", children: [
38303
- !submitted ? /* @__PURE__ */ jsxs(Button, { variant: "primary", onClick: handleSubmit, disabled: flaggedLines.size === 0, children: [
38323
+ !submitted ? /* @__PURE__ */ jsxs(Button, { variant: "primary", onClick: handleSubmit, disabled: flaggedLines.length === 0, children: [
38304
38324
  /* @__PURE__ */ jsx(Icon, { icon: Send, size: "sm" }),
38305
38325
  t("debugger.submit")
38306
38326
  ] }) : !allCorrect ? /* @__PURE__ */ jsx(Button, { variant: "primary", onClick: handleReset, children: t("debugger.tryAgain") }) : null,
@@ -39537,7 +39557,7 @@ var init_FeatureDetailPageTemplate = __esm({
39537
39557
  const resolved = entity && typeof entity === "object" && !Array.isArray(entity) ? entity : void 0;
39538
39558
  if (!resolved) return null;
39539
39559
  return /* @__PURE__ */ jsxs(VStack, { gap: "none", className: cn("w-full", className), children: [
39540
- /* @__PURE__ */ jsx(
39560
+ resolved.hero && /* @__PURE__ */ jsx(
39541
39561
  HeroSection,
39542
39562
  {
39543
39563
  tag: resolved.hero.tag,
@@ -41057,7 +41077,7 @@ var init_LandingPageTemplate = __esm({
41057
41077
  const resolved = entity && typeof entity === "object" && !Array.isArray(entity) ? entity : void 0;
41058
41078
  if (!resolved) return null;
41059
41079
  return /* @__PURE__ */ jsxs(VStack, { gap: "none", className: cn("w-full", className), children: [
41060
- /* @__PURE__ */ jsx(
41080
+ resolved.hero && /* @__PURE__ */ jsx(
41061
41081
  HeroSection,
41062
41082
  {
41063
41083
  tag: resolved.hero.tag,
@@ -42064,6 +42084,9 @@ function getOpponentAction(strategy, actions, history) {
42064
42084
  function NegotiatorBoard({
42065
42085
  entity,
42066
42086
  completeEvent = "PUZZLE_COMPLETE",
42087
+ playRoundEvent,
42088
+ finishEvent,
42089
+ playAgainEvent,
42067
42090
  className
42068
42091
  }) {
42069
42092
  const { emit } = useEventBus();
@@ -42072,13 +42095,14 @@ function NegotiatorBoard({
42072
42095
  const [history, setHistory] = useState([]);
42073
42096
  const [headerError, setHeaderError] = useState(false);
42074
42097
  const [showHint, setShowHint] = useState(false);
42075
- const totalRounds = num(resolved?.totalRounds);
42098
+ const totalRounds = num(resolved?.maxRounds);
42076
42099
  const targetScore = num(resolved?.targetScore);
42077
- const currentRound = history.length;
42078
- const isComplete = currentRound >= totalRounds;
42079
- const playerTotal = history.reduce((s, r) => s + r.playerPayoff, 0);
42100
+ const currentRound = num(resolved?.round);
42101
+ const result = str(resolved?.result) || "none";
42102
+ const playerTotal = num(resolved?.score);
42103
+ const isComplete = result !== "none" || totalRounds > 0 && currentRound >= totalRounds;
42104
+ const won = result === "win";
42080
42105
  const opponentTotal = history.reduce((s, r) => s + r.opponentPayoff, 0);
42081
- const won = isComplete && playerTotal >= targetScore;
42082
42106
  const actions = Array.isArray(resolved?.actions) ? resolved.actions : [];
42083
42107
  const payoffMatrix = Array.isArray(resolved?.payoffMatrix) ? resolved.payoffMatrix : [];
42084
42108
  const handleAction = useCallback((actionId) => {
@@ -42087,29 +42111,45 @@ function NegotiatorBoard({
42087
42111
  const payoff = payoffMatrix.find(
42088
42112
  (p2) => p2.playerAction === actionId && p2.opponentAction === opponentAction
42089
42113
  );
42090
- const result = {
42091
- round: currentRound + 1,
42092
- playerAction: actionId,
42093
- opponentAction,
42094
- playerPayoff: payoff?.playerPayoff ?? 0,
42095
- opponentPayoff: payoff?.opponentPayoff ?? 0
42096
- };
42097
- const newHistory = [...history, result];
42098
- setHistory(newHistory);
42099
- if (newHistory.length >= totalRounds) {
42100
- const total = newHistory.reduce((s, r) => s + r.playerPayoff, 0);
42101
- if (total >= targetScore) {
42102
- emit(`UI:${completeEvent}`, { success: true, score: total });
42103
- }
42104
- if (newHistory.length >= 3 && str(resolved?.hint)) {
42114
+ const playerPayoff = payoff?.playerPayoff ?? 0;
42115
+ setHistory((prev) => [
42116
+ ...prev,
42117
+ {
42118
+ round: prev.length + 1,
42119
+ playerAction: actionId,
42120
+ opponentAction,
42121
+ playerPayoff,
42122
+ opponentPayoff: payoff?.opponentPayoff ?? 0
42123
+ }
42124
+ ]);
42125
+ if (playRoundEvent) {
42126
+ emit(`UI:${playRoundEvent}`, { playerAction: actionId, payoff: playerPayoff });
42127
+ }
42128
+ if (totalRounds > 0 && currentRound + 1 >= totalRounds) {
42129
+ if (finishEvent) {
42130
+ emit(`UI:${finishEvent}`, {});
42131
+ }
42132
+ if (str(resolved?.hint)) {
42105
42133
  setShowHint(true);
42106
42134
  }
42107
42135
  }
42108
- }, [isComplete, resolved, totalRounds, targetScore, actions, payoffMatrix, history, currentRound, completeEvent, emit]);
42109
- const handleReset = () => {
42136
+ }, [isComplete, resolved, totalRounds, currentRound, actions, payoffMatrix, history, playRoundEvent, finishEvent, emit]);
42137
+ const handleReset = useCallback(() => {
42110
42138
  setHistory([]);
42111
42139
  setShowHint(false);
42112
- };
42140
+ if (playAgainEvent) {
42141
+ emit(`UI:${playAgainEvent}`, {});
42142
+ }
42143
+ }, [playAgainEvent, emit]);
42144
+ const completedRef = useRef(false);
42145
+ useEffect(() => {
42146
+ if (result === "win" && !completedRef.current) {
42147
+ completedRef.current = true;
42148
+ emit(`UI:${completeEvent}`, { success: true, score: playerTotal });
42149
+ } else if (result === "none") {
42150
+ completedRef.current = false;
42151
+ }
42152
+ }, [result, playerTotal, completeEvent, emit]);
42113
42153
  const getActionLabel = (id) => actions.find((a) => a.id === id)?.label ?? id;
42114
42154
  if (!resolved) return null;
42115
42155
  const theme = resolved.theme ?? void 0;
@@ -42278,7 +42318,7 @@ var init_PricingPageTemplate = __esm({
42278
42318
  const resolved = entity && typeof entity === "object" && !Array.isArray(entity) ? entity : void 0;
42279
42319
  if (!resolved) return null;
42280
42320
  return /* @__PURE__ */ jsxs(VStack, { gap: "none", className: cn("w-full", className), children: [
42281
- /* @__PURE__ */ jsx(
42321
+ resolved.hero && /* @__PURE__ */ jsx(
42282
42322
  HeroSection,
42283
42323
  {
42284
42324
  tag: resolved.hero.tag,
@@ -45056,67 +45096,47 @@ var init_SimulationGraph = __esm({
45056
45096
  function SimulatorBoard({
45057
45097
  entity,
45058
45098
  completeEvent = "PUZZLE_COMPLETE",
45099
+ setAEvent,
45100
+ setBEvent,
45101
+ checkEvent,
45102
+ playAgainEvent,
45059
45103
  className
45060
45104
  }) {
45061
45105
  const { emit } = useEventBus();
45062
45106
  const { t } = useTranslate();
45063
45107
  const resolved = boardEntity(entity);
45064
45108
  const parameters = Array.isArray(resolved?.parameters) ? resolved.parameters : [];
45065
- const [values, setValues] = useState(() => {
45066
- const init = {};
45067
- for (const p2 of parameters) {
45068
- init[p2.id] = p2.initial;
45069
- }
45070
- return init;
45071
- });
45072
45109
  const [headerError, setHeaderError] = useState(false);
45073
- const [submitted, setSubmitted] = useState(false);
45074
- const [attempts, setAttempts] = useState(0);
45075
- const [showHint, setShowHint] = useState(false);
45076
- const computeOutput = useCallback((params) => {
45077
- try {
45078
- const fn = new Function("params", `return (${str(resolved?.computeExpression)})`);
45079
- return fn(params);
45080
- } catch {
45081
- return 0;
45082
- }
45083
- }, [resolved?.computeExpression]);
45084
- const output = useMemo(() => computeOutput(values) ?? 0, [computeOutput, values]);
45085
- const targetValue = num(resolved?.targetValue);
45086
- const targetTolerance = num(resolved?.targetTolerance);
45087
- const isCorrect = Math.abs(output - targetValue) <= targetTolerance;
45088
- const handleParameterChange = (id, value) => {
45089
- if (submitted) return;
45090
- setValues((prev) => ({ ...prev, [id]: value }));
45110
+ if (!resolved) return null;
45111
+ const paramA = num(resolved.paramA);
45112
+ const paramB = num(resolved.paramB);
45113
+ const output = num(resolved.output);
45114
+ const targetValue = num(resolved.target);
45115
+ const targetTolerance = num(resolved.tolerance);
45116
+ const attempts = num(resolved.attempts);
45117
+ const result = str(resolved.result);
45118
+ const isWin = result === "win";
45119
+ const isComplete = result !== "none" && result !== "";
45120
+ const paramAValue = parameters[0];
45121
+ const paramBValue = parameters[1];
45122
+ const sliderValues = [paramA, paramB];
45123
+ const sliderEvents = [setAEvent, setBEvent];
45124
+ const handleParameterChange = (index, value) => {
45125
+ if (isComplete) return;
45126
+ const ev = sliderEvents[index];
45127
+ if (ev) emit(`UI:${ev}`, { value });
45091
45128
  };
45092
- const handleSubmit = () => {
45093
- setSubmitted(true);
45094
- setAttempts((a) => a + 1);
45095
- if (isCorrect) {
45096
- emit(`UI:${completeEvent}`, { success: true, attempts: attempts + 1 });
45097
- }
45129
+ const handleCheck = () => {
45130
+ if (checkEvent) emit(`UI:${checkEvent}`, {});
45098
45131
  };
45099
- const handleReset = () => {
45100
- setSubmitted(false);
45101
- if (attempts >= 2 && str(resolved?.hint)) {
45102
- setShowHint(true);
45103
- }
45132
+ const handlePlayAgain = () => {
45133
+ if (playAgainEvent) emit(`UI:${playAgainEvent}`, {});
45104
45134
  };
45105
- const handleFullReset = () => {
45106
- const init = {};
45107
- for (const p2 of parameters) {
45108
- init[p2.id] = p2.initial;
45109
- }
45110
- setValues(init);
45111
- setSubmitted(false);
45112
- setAttempts(0);
45113
- setShowHint(false);
45114
- };
45115
- if (!resolved) return null;
45116
45135
  const theme = resolved.theme ?? void 0;
45117
45136
  const themeBackground = theme?.background;
45118
45137
  const headerImage = str(resolved.headerImage);
45119
45138
  const hint = str(resolved.hint);
45139
+ const showHint = isComplete && !isWin && attempts >= 2 && Boolean(hint);
45120
45140
  const outputLabel = str(resolved.outputLabel);
45121
45141
  const outputUnit = str(resolved.outputUnit);
45122
45142
  return /* @__PURE__ */ jsx(
@@ -45136,41 +45156,43 @@ function SimulatorBoard({
45136
45156
  ] }) }),
45137
45157
  /* @__PURE__ */ jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxs(VStack, { gap: "md", children: [
45138
45158
  /* @__PURE__ */ jsx(Typography, { variant: "small", weight: "bold", className: "uppercase tracking-wider text-muted-foreground", children: t("simulator.parameters") }),
45139
- parameters.map((param) => /* @__PURE__ */ jsxs(VStack, { gap: "xs", children: [
45140
- /* @__PURE__ */ jsxs(HStack, { justify: "between", align: "center", children: [
45141
- /* @__PURE__ */ jsx(Typography, { variant: "body", weight: "medium", children: param.label }),
45142
- /* @__PURE__ */ jsxs(Badge, { size: "sm", children: [
45143
- values[param.id],
45144
- " ",
45145
- param.unit
45146
- ] })
45147
- ] }),
45148
- /* @__PURE__ */ jsx(
45149
- "input",
45150
- {
45151
- type: "range",
45152
- min: param.min,
45153
- max: param.max,
45154
- step: param.step,
45155
- value: values[param.id],
45156
- onChange: (e) => handleParameterChange(param.id, Number(e.target.value)),
45157
- disabled: submitted,
45158
- className: "w-full accent-foreground"
45159
- }
45160
- ),
45161
- /* @__PURE__ */ jsxs(HStack, { justify: "between", children: [
45162
- /* @__PURE__ */ jsxs(Typography, { variant: "caption", className: "text-muted-foreground", children: [
45163
- param.min,
45164
- " ",
45165
- param.unit
45159
+ [paramAValue, paramBValue].map(
45160
+ (param, index) => param ? /* @__PURE__ */ jsxs(VStack, { gap: "xs", children: [
45161
+ /* @__PURE__ */ jsxs(HStack, { justify: "between", align: "center", children: [
45162
+ /* @__PURE__ */ jsx(Typography, { variant: "body", weight: "medium", children: param.label }),
45163
+ /* @__PURE__ */ jsxs(Badge, { size: "sm", children: [
45164
+ sliderValues[index],
45165
+ " ",
45166
+ param.unit
45167
+ ] })
45166
45168
  ] }),
45167
- /* @__PURE__ */ jsxs(Typography, { variant: "caption", className: "text-muted-foreground", children: [
45168
- param.max,
45169
- " ",
45170
- param.unit
45169
+ /* @__PURE__ */ jsx(
45170
+ "input",
45171
+ {
45172
+ type: "range",
45173
+ min: param.min,
45174
+ max: param.max,
45175
+ step: param.step,
45176
+ value: sliderValues[index],
45177
+ onChange: (e) => handleParameterChange(index, Number(e.target.value)),
45178
+ disabled: isComplete,
45179
+ className: "w-full accent-foreground"
45180
+ }
45181
+ ),
45182
+ /* @__PURE__ */ jsxs(HStack, { justify: "between", children: [
45183
+ /* @__PURE__ */ jsxs(Typography, { variant: "caption", className: "text-muted-foreground", children: [
45184
+ param.min,
45185
+ " ",
45186
+ param.unit
45187
+ ] }),
45188
+ /* @__PURE__ */ jsxs(Typography, { variant: "caption", className: "text-muted-foreground", children: [
45189
+ param.max,
45190
+ " ",
45191
+ param.unit
45192
+ ] })
45171
45193
  ] })
45172
- ] })
45173
- ] }, param.id))
45194
+ ] }, param.id ?? index) : null
45195
+ )
45174
45196
  ] }) }),
45175
45197
  /* @__PURE__ */ jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", align: "center", children: [
45176
45198
  /* @__PURE__ */ jsx(Typography, { variant: "small", weight: "bold", className: "uppercase tracking-wider text-muted-foreground", children: outputLabel }),
@@ -45179,9 +45201,9 @@ function SimulatorBoard({
45179
45201
  " ",
45180
45202
  outputUnit
45181
45203
  ] }),
45182
- submitted && /* @__PURE__ */ jsxs(HStack, { gap: "xs", align: "center", children: [
45183
- /* @__PURE__ */ jsx(Icon, { icon: isCorrect ? CheckCircle : XCircle, size: "sm", className: isCorrect ? "text-success" : "text-error" }),
45184
- /* @__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") })
45204
+ isComplete && /* @__PURE__ */ jsxs(HStack, { gap: "xs", align: "center", children: [
45205
+ /* @__PURE__ */ jsx(Icon, { icon: isWin ? CheckCircle : XCircle, size: "sm", className: isWin ? "text-success" : "text-error" }),
45206
+ /* @__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") })
45185
45207
  ] }),
45186
45208
  /* @__PURE__ */ jsxs(Typography, { variant: "caption", className: "text-muted-foreground", children: [
45187
45209
  t("simulator.target"),
@@ -45196,11 +45218,11 @@ function SimulatorBoard({
45196
45218
  ] }) }),
45197
45219
  showHint && hint && /* @__PURE__ */ jsx(Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsx(Typography, { variant: "body", children: hint }) }),
45198
45220
  /* @__PURE__ */ jsxs(HStack, { gap: "sm", justify: "center", children: [
45199
- !submitted ? /* @__PURE__ */ jsxs(Button, { variant: "primary", onClick: handleSubmit, children: [
45221
+ !isComplete ? /* @__PURE__ */ jsxs(Button, { variant: "primary", onClick: handleCheck, children: [
45200
45222
  /* @__PURE__ */ jsx(Icon, { icon: Play, size: "sm" }),
45201
45223
  t("simulator.simulate")
45202
- ] }) : !isCorrect ? /* @__PURE__ */ jsx(Button, { variant: "primary", onClick: handleReset, children: t("simulator.tryAgain") }) : null,
45203
- /* @__PURE__ */ jsxs(Button, { variant: "secondary", onClick: handleFullReset, children: [
45224
+ ] }) : null,
45225
+ /* @__PURE__ */ jsxs(Button, { variant: "secondary", onClick: handlePlayAgain, children: [
45204
45226
  /* @__PURE__ */ jsx(Icon, { icon: RotateCcw, size: "sm" }),
45205
45227
  t("simulator.reset")
45206
45228
  ] })