@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.
@@ -9539,7 +9539,7 @@ var init_AboutPageTemplate = __esm({
9539
9539
  const resolved = entity && typeof entity === "object" && !Array.isArray(entity) ? entity : void 0;
9540
9540
  if (!resolved) return null;
9541
9541
  return /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "none", className: cn("w-full", className), children: [
9542
- /* @__PURE__ */ jsxRuntime.jsx(
9542
+ resolved.hero && /* @__PURE__ */ jsxRuntime.jsx(
9543
9543
  HeroSection,
9544
9544
  {
9545
9545
  tag: resolved.hero.tag,
@@ -17421,61 +17421,53 @@ var init_Breadcrumb = __esm({
17421
17421
  function BuilderBoard({
17422
17422
  entity,
17423
17423
  completeEvent = "PUZZLE_COMPLETE",
17424
+ placeEvent,
17425
+ checkEvent,
17426
+ playAgainEvent,
17424
17427
  className
17425
17428
  }) {
17426
17429
  const { emit } = useEventBus();
17427
17430
  const { t } = hooks.useTranslate();
17428
17431
  const resolved = boardEntity(entity);
17429
- const [placements, setPlacements] = React83.useState({});
17430
17432
  const [headerError, setHeaderError] = React83.useState(false);
17431
- const [submitted, setSubmitted] = React83.useState(false);
17432
- const [attempts, setAttempts] = React83.useState(0);
17433
- const [showHint, setShowHint] = React83.useState(false);
17433
+ const [selectedComponent, setSelectedComponent] = React83.useState(null);
17434
17434
  const components = Array.isArray(resolved?.components) ? resolved.components : [];
17435
17435
  const slots = Array.isArray(resolved?.slots) ? resolved.slots : [];
17436
+ const placements = {};
17437
+ for (const slot of slots) {
17438
+ if (slot.placedComponentId) placements[slot.id] = slot.placedComponentId;
17439
+ }
17440
+ const attempts = num(resolved?.attempts);
17441
+ const result = str(resolved?.result) || "none";
17442
+ const submitted = result === "win";
17436
17443
  const usedComponentIds = new Set(Object.values(placements));
17437
17444
  const availableComponents = components.filter((c) => !usedComponentIds.has(c.id));
17438
- const [selectedComponent, setSelectedComponent] = React83.useState(null);
17439
- const allPlaced = Object.keys(placements).length === slots.length;
17445
+ const allPlaced = slots.length > 0 && slots.every((s) => Boolean(placements[s.id]));
17440
17446
  const results = submitted ? slots.map((slot) => ({
17441
17447
  slot,
17442
17448
  placed: placements[slot.id],
17443
- correct: placements[slot.id] === slot.acceptsComponentId
17449
+ correct: placements[slot.id] === slot.requiredComponentId
17444
17450
  })) : [];
17445
- const allCorrect = results.length > 0 && results.every((r) => r.correct);
17451
+ const showHint = attempts >= 2 && Boolean(str(resolved?.hint));
17446
17452
  const handlePlaceComponent = (slotId) => {
17447
17453
  if (submitted || !selectedComponent) return;
17448
- setPlacements((prev) => ({ ...prev, [slotId]: selectedComponent }));
17454
+ if (placeEvent) emit(`UI:${placeEvent}`, { slotId, componentId: selectedComponent });
17449
17455
  setSelectedComponent(null);
17450
17456
  };
17451
17457
  const handleRemoveFromSlot = (slotId) => {
17452
17458
  if (submitted) return;
17453
- setPlacements((prev) => {
17454
- const next = { ...prev };
17455
- delete next[slotId];
17456
- return next;
17457
- });
17459
+ if (placeEvent) emit(`UI:${placeEvent}`, { slotId, componentId: "" });
17458
17460
  };
17459
- const handleSubmit = React83.useCallback(() => {
17460
- setSubmitted(true);
17461
- setAttempts((a) => a + 1);
17462
- const correct = slots.every((slot) => placements[slot.id] === slot.acceptsComponentId);
17463
- if (correct) {
17461
+ const handleSubmit = () => {
17462
+ if (checkEvent) emit(`UI:${checkEvent}`, {});
17463
+ const solved = slots.length > 0 && slots.every((s) => placements[s.id] === s.requiredComponentId);
17464
+ if (solved && completeEvent) {
17464
17465
  emit(`UI:${completeEvent}`, { success: true, attempts: attempts + 1 });
17465
17466
  }
17466
- }, [slots, placements, attempts, completeEvent, emit]);
17467
- const handleReset = () => {
17468
- setSubmitted(false);
17469
- if (attempts >= 2 && str(resolved?.hint)) {
17470
- setShowHint(true);
17471
- }
17472
17467
  };
17473
- const handleFullReset = () => {
17474
- setPlacements({});
17475
- setSubmitted(false);
17468
+ const handlePlayAgain = () => {
17476
17469
  setSelectedComponent(null);
17477
- setAttempts(0);
17478
- setShowHint(false);
17470
+ if (playAgainEvent) emit(`UI:${playAgainEvent}`, {});
17479
17471
  };
17480
17472
  const getComponentById = (id) => components.find((c) => c.id === id);
17481
17473
  if (!resolved) return null;
@@ -17525,13 +17517,13 @@ function BuilderBoard({
17525
17517
  /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", weight: "bold", className: "uppercase tracking-wider text-muted-foreground", children: t("builder.blueprint") }),
17526
17518
  /* @__PURE__ */ jsxRuntime.jsx(VStack, { gap: "sm", children: slots.map((slot) => {
17527
17519
  const placedComp = placements[slot.id] ? getComponentById(placements[slot.id]) : null;
17528
- const result = results.find((r) => r.slot.id === slot.id);
17520
+ const result2 = results.find((r) => r.slot.id === slot.id);
17529
17521
  return /* @__PURE__ */ jsxRuntime.jsxs(
17530
17522
  HStack,
17531
17523
  {
17532
17524
  gap: "sm",
17533
17525
  align: "center",
17534
- className: `p-3 border-2 rounded ${result ? result.correct ? "border-success" : "border-error" : selectedComponent ? "border-dashed border-foreground cursor-pointer" : "border-border"}`,
17526
+ className: `p-3 border-2 rounded ${result2 ? result2.correct ? "border-success" : "border-error" : selectedComponent ? "border-dashed border-foreground cursor-pointer" : "border-border"}`,
17535
17527
  onClick: () => handlePlaceComponent(slot.id),
17536
17528
  children: [
17537
17529
  /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "none", className: "flex-1", children: [
@@ -17546,7 +17538,7 @@ function BuilderBoard({
17546
17538
  ] }) : null,
17547
17539
  placedComp.label
17548
17540
  ] }),
17549
- result && /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: result.correct ? LucideIcons2.CheckCircle : LucideIcons2.XCircle, size: "sm", className: result.correct ? "text-success" : "text-error" })
17541
+ result2 && /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: result2.correct ? LucideIcons2.CheckCircle : LucideIcons2.XCircle, size: "sm", className: result2.correct ? "text-success" : "text-error" })
17550
17542
  ] }) : /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", className: "text-muted-foreground", children: t("builder.empty") })
17551
17543
  ]
17552
17544
  },
@@ -17555,16 +17547,16 @@ function BuilderBoard({
17555
17547
  }) })
17556
17548
  ] }) }),
17557
17549
  submitted && /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "sm", align: "center", children: [
17558
- /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: allCorrect ? LucideIcons2.CheckCircle : LucideIcons2.XCircle, size: "lg", className: allCorrect ? "text-success" : "text-error" }),
17559
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", weight: "bold", children: allCorrect ? str(resolved.successMessage) || t("builder.success") : str(resolved.failMessage) || t("builder.incorrect") })
17550
+ /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: LucideIcons2.CheckCircle, size: "lg", className: "text-success" }),
17551
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", weight: "bold", children: str(resolved.successMessage) || t("builder.success") })
17560
17552
  ] }) }),
17561
17553
  showHint && hint && /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", children: hint }) }),
17562
17554
  /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "sm", justify: "center", children: [
17563
- !submitted ? /* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "primary", onClick: handleSubmit, disabled: !allPlaced, children: [
17555
+ !submitted && /* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "primary", onClick: handleSubmit, disabled: !allPlaced, children: [
17564
17556
  /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: LucideIcons2.Wrench, size: "sm" }),
17565
17557
  t("builder.build")
17566
- ] }) : !allCorrect ? /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "primary", onClick: handleReset, children: t("builder.tryAgain") }) : null,
17567
- /* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "secondary", onClick: handleFullReset, children: [
17558
+ ] }),
17559
+ /* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "secondary", onClick: handlePlayAgain, children: [
17568
17560
  /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: LucideIcons2.RotateCcw, size: "sm" }),
17569
17561
  t("builder.reset")
17570
17562
  ] })
@@ -21020,57 +21012,84 @@ var init_ChartLegend = __esm({
21020
21012
  function ClassifierBoard({
21021
21013
  entity,
21022
21014
  completeEvent = "PUZZLE_COMPLETE",
21015
+ assignEvent,
21016
+ checkEvent,
21017
+ playAgainEvent,
21023
21018
  className
21024
21019
  }) {
21025
21020
  const { emit } = useEventBus();
21026
21021
  const { t } = hooks.useTranslate();
21027
21022
  const resolved = boardEntity(entity);
21028
- const [assignments, setAssignments] = React83.useState({});
21023
+ const [localAssignments, setLocalAssignments] = React83.useState({});
21029
21024
  const [headerError, setHeaderError] = React83.useState(false);
21030
- const [submitted, setSubmitted] = React83.useState(false);
21031
- const [attempts, setAttempts] = React83.useState(0);
21025
+ const [localSubmitted, setLocalSubmitted] = React83.useState(false);
21026
+ const [localAttempts, setLocalAttempts] = React83.useState(0);
21032
21027
  const [showHint, setShowHint] = React83.useState(false);
21033
21028
  const items = Array.isArray(resolved?.items) ? resolved.items : [];
21034
21029
  const categories = Array.isArray(resolved?.categories) ? resolved.categories : [];
21030
+ const entityResult = str(resolved?.result);
21031
+ const entityDrivesResult = entityResult.length > 0;
21032
+ const entityHasAssignments = items.some((item) => item.assignedCategory != null);
21033
+ const assignments = entityHasAssignments ? items.reduce((acc, item) => {
21034
+ if (item.assignedCategory != null) acc[item.id] = item.assignedCategory;
21035
+ return acc;
21036
+ }, {}) : localAssignments;
21037
+ const attempts = entityDrivesResult ? num(resolved?.attempts) : localAttempts;
21038
+ const submitted = entityDrivesResult || localSubmitted;
21035
21039
  const unassignedItems = items.filter((item) => !assignments[item.id]);
21036
- const allAssigned = Object.keys(assignments).length === items.length;
21040
+ const allAssigned = items.length > 0 && Object.keys(assignments).length === items.length;
21037
21041
  const results = submitted ? items.map((item) => ({
21038
21042
  item,
21039
21043
  assigned: assignments[item.id],
21040
21044
  correct: assignments[item.id] === item.correctCategory
21041
21045
  })) : [];
21042
- const allCorrect = results.length > 0 && results.every((r) => r.correct);
21046
+ const allCorrect = entityDrivesResult ? entityResult === "success" : results.length > 0 && results.every((r) => r.correct);
21043
21047
  const correctCount = results.filter((r) => r.correct).length;
21044
21048
  const handleAssign = (itemId, categoryId) => {
21045
21049
  if (submitted) return;
21046
- setAssignments((prev) => ({ ...prev, [itemId]: categoryId }));
21050
+ if (assignEvent) {
21051
+ emit(`UI:${assignEvent}`, { itemId, categoryId });
21052
+ }
21053
+ if (!entityHasAssignments) {
21054
+ setLocalAssignments((prev) => ({ ...prev, [itemId]: categoryId }));
21055
+ }
21047
21056
  };
21048
21057
  const handleUnassign = (itemId) => {
21049
21058
  if (submitted) return;
21050
- setAssignments((prev) => {
21051
- const next = { ...prev };
21052
- delete next[itemId];
21053
- return next;
21054
- });
21059
+ if (!entityHasAssignments) {
21060
+ setLocalAssignments((prev) => {
21061
+ const next = { ...prev };
21062
+ delete next[itemId];
21063
+ return next;
21064
+ });
21065
+ }
21055
21066
  };
21056
21067
  const handleSubmit = React83.useCallback(() => {
21057
- setSubmitted(true);
21058
- setAttempts((a) => a + 1);
21059
- const correct = items.every((item) => assignments[item.id] === item.correctCategory);
21060
- if (correct) {
21061
- emit(`UI:${completeEvent}`, { success: true, attempts: attempts + 1 });
21068
+ if (checkEvent) {
21069
+ emit(`UI:${checkEvent}`, {});
21070
+ }
21071
+ if (!entityDrivesResult) {
21072
+ setLocalSubmitted(true);
21073
+ setLocalAttempts((a) => a + 1);
21074
+ const correct = items.every((item) => assignments[item.id] === item.correctCategory);
21075
+ if (correct) {
21076
+ emit(`UI:${completeEvent}`, { success: true, attempts: attempts + 1 });
21077
+ }
21062
21078
  }
21063
- }, [items, assignments, attempts, completeEvent, emit]);
21079
+ }, [checkEvent, entityDrivesResult, items, assignments, attempts, completeEvent, emit]);
21064
21080
  const handleReset = () => {
21065
- setSubmitted(false);
21081
+ if (!entityDrivesResult) setLocalSubmitted(false);
21066
21082
  if (attempts >= 2 && str(resolved?.hint)) {
21067
21083
  setShowHint(true);
21068
21084
  }
21069
21085
  };
21070
21086
  const handleFullReset = () => {
21071
- setAssignments({});
21072
- setSubmitted(false);
21073
- setAttempts(0);
21087
+ if (playAgainEvent) {
21088
+ emit(`UI:${playAgainEvent}`, {});
21089
+ }
21090
+ setLocalAssignments({});
21091
+ setLocalSubmitted(false);
21092
+ setLocalAttempts(0);
21074
21093
  setShowHint(false);
21075
21094
  };
21076
21095
  if (!resolved) return null;
@@ -28953,13 +28972,13 @@ var init_MapView = __esm({
28953
28972
  shadowSize: [41, 41]
28954
28973
  });
28955
28974
  L.Marker.prototype.options.icon = defaultIcon;
28956
- const { useEffect: useEffect72, useRef: useRef67, useCallback: useCallback113, useState: useState104 } = React83__namespace.default;
28975
+ const { useEffect: useEffect73, useRef: useRef68, useCallback: useCallback111, useState: useState104 } = React83__namespace.default;
28957
28976
  const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
28958
28977
  const { useEventBus: useEventBus2 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
28959
28978
  function MapUpdater({ centerLat, centerLng, zoom }) {
28960
28979
  const map = useMap();
28961
- const prevRef = useRef67({ centerLat, centerLng, zoom });
28962
- useEffect72(() => {
28980
+ const prevRef = useRef68({ centerLat, centerLng, zoom });
28981
+ useEffect73(() => {
28963
28982
  const prev = prevRef.current;
28964
28983
  if (prev.centerLat !== centerLat || prev.centerLng !== centerLng || prev.zoom !== zoom) {
28965
28984
  map.setView([centerLat, centerLng], zoom);
@@ -28970,7 +28989,7 @@ var init_MapView = __esm({
28970
28989
  }
28971
28990
  function MapClickHandler({ onMapClick }) {
28972
28991
  const map = useMap();
28973
- useEffect72(() => {
28992
+ useEffect73(() => {
28974
28993
  if (!onMapClick) return;
28975
28994
  const handler = (e) => {
28976
28995
  onMapClick(e.latlng.lat, e.latlng.lng);
@@ -28999,7 +29018,7 @@ var init_MapView = __esm({
28999
29018
  }) {
29000
29019
  const eventBus = useEventBus2();
29001
29020
  const [clickedPosition, setClickedPosition] = useState104(null);
29002
- const handleMapClick = useCallback113((lat, lng) => {
29021
+ const handleMapClick = useCallback111((lat, lng) => {
29003
29022
  if (showClickedPin) {
29004
29023
  setClickedPosition({ lat, lng });
29005
29024
  }
@@ -29008,7 +29027,7 @@ var init_MapView = __esm({
29008
29027
  eventBus.emit(`UI:${mapClickEvent}`, { latitude: lat, longitude: lng });
29009
29028
  }
29010
29029
  }, [onMapClick, mapClickEvent, eventBus, showClickedPin]);
29011
- const handleMarkerClick = useCallback113((marker) => {
29030
+ const handleMarkerClick = useCallback111((marker) => {
29012
29031
  onMarkerClick?.(marker);
29013
29032
  if (markerClickEvent) {
29014
29033
  eventBus.emit(`UI:${markerClickEvent}`, { ...marker });
@@ -38230,51 +38249,52 @@ var init_DataTable = __esm({
38230
38249
  function DebuggerBoard({
38231
38250
  entity,
38232
38251
  completeEvent = "PUZZLE_COMPLETE",
38252
+ toggleFlagEvent,
38253
+ checkEvent,
38254
+ playAgainEvent,
38233
38255
  className
38234
38256
  }) {
38235
38257
  const { emit } = useEventBus();
38236
38258
  const { t } = hooks.useTranslate();
38237
38259
  const resolved = boardEntity(entity);
38238
- const [flaggedLines, setFlaggedLines] = React83.useState(/* @__PURE__ */ new Set());
38239
38260
  const [headerError, setHeaderError] = React83.useState(false);
38240
- const [submitted, setSubmitted] = React83.useState(false);
38241
- const [attempts, setAttempts] = React83.useState(0);
38242
38261
  const [showHint, setShowHint] = React83.useState(false);
38262
+ const lines = Array.isArray(resolved?.lines) ? resolved.lines : [];
38263
+ const result = resolved?.result ?? null;
38264
+ const attempts = num(resolved?.attempts);
38265
+ const submitted = result != null;
38266
+ const bugLines = lines.filter((l) => l.isBug);
38267
+ const flaggedLines = lines.filter((l) => l.isFlagged);
38268
+ const correctFlags = lines.filter((l) => l.isBug && l.isFlagged);
38269
+ const falseFlags = lines.filter((l) => !l.isBug && l.isFlagged);
38270
+ const allCorrect = result === "win";
38243
38271
  const toggleLine = (lineId) => {
38244
38272
  if (submitted) return;
38245
- setFlaggedLines((prev) => {
38246
- const next = new Set(prev);
38247
- if (next.has(lineId)) {
38248
- next.delete(lineId);
38249
- } else {
38250
- next.add(lineId);
38251
- }
38252
- return next;
38253
- });
38273
+ if (toggleFlagEvent) {
38274
+ emit(`UI:${toggleFlagEvent}`, { lineId });
38275
+ }
38254
38276
  };
38255
- const lines = Array.isArray(resolved?.lines) ? resolved.lines : [];
38256
- const bugLines = lines.filter((l) => l.isBug);
38257
- const correctFlags = lines.filter((l) => l.isBug && flaggedLines.has(l.id));
38258
- const falseFlags = lines.filter((l) => !l.isBug && flaggedLines.has(l.id));
38259
- const allCorrect = submitted && correctFlags.length === bugLines.length && falseFlags.length === 0;
38260
38277
  const handleSubmit = React83.useCallback(() => {
38261
- setSubmitted(true);
38262
- setAttempts((a) => a + 1);
38278
+ if (checkEvent) {
38279
+ emit(`UI:${checkEvent}`, {});
38280
+ }
38263
38281
  const correct = correctFlags.length === bugLines.length && falseFlags.length === 0;
38264
38282
  if (correct) {
38265
38283
  emit(`UI:${completeEvent}`, { success: true, attempts: attempts + 1 });
38266
38284
  }
38267
- }, [correctFlags.length, bugLines.length, falseFlags.length, attempts, completeEvent, emit]);
38285
+ }, [checkEvent, correctFlags.length, bugLines.length, falseFlags.length, attempts, completeEvent, emit]);
38268
38286
  const handleReset = () => {
38269
- setSubmitted(false);
38287
+ if (playAgainEvent) {
38288
+ emit(`UI:${playAgainEvent}`, {});
38289
+ }
38270
38290
  if (attempts >= 2 && str(resolved?.hint)) {
38271
38291
  setShowHint(true);
38272
38292
  }
38273
38293
  };
38274
38294
  const handleFullReset = () => {
38275
- setFlaggedLines(/* @__PURE__ */ new Set());
38276
- setSubmitted(false);
38277
- setAttempts(0);
38295
+ if (playAgainEvent) {
38296
+ emit(`UI:${playAgainEvent}`, {});
38297
+ }
38278
38298
  setShowHint(false);
38279
38299
  };
38280
38300
  if (!resolved) return null;
@@ -38302,7 +38322,7 @@ function DebuggerBoard({
38302
38322
  /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", className: "text-muted-foreground", children: t("debugger.findBugs", { count: String(num(resolved.bugCount)) }) })
38303
38323
  ] }) }),
38304
38324
  /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "p-0 overflow-hidden", children: /* @__PURE__ */ jsxRuntime.jsx(VStack, { gap: "none", children: lines.map((line, i) => {
38305
- const isFlagged = flaggedLines.has(line.id);
38325
+ const isFlagged = !!line.isFlagged;
38306
38326
  let lineStyle = "";
38307
38327
  if (submitted) {
38308
38328
  if (line.isBug && isFlagged) lineStyle = "bg-success/10";
@@ -38336,9 +38356,9 @@ function DebuggerBoard({
38336
38356
  /* @__PURE__ */ jsxRuntime.jsx(
38337
38357
  Icon,
38338
38358
  {
38339
- icon: flaggedLines.has(line.id) ? LucideIcons2.CheckCircle : LucideIcons2.XCircle,
38359
+ icon: line.isFlagged ? LucideIcons2.CheckCircle : LucideIcons2.XCircle,
38340
38360
  size: "xs",
38341
- className: flaggedLines.has(line.id) ? "text-success mt-0.5" : "text-warning mt-0.5"
38361
+ className: line.isFlagged ? "text-success mt-0.5" : "text-warning mt-0.5"
38342
38362
  }
38343
38363
  ),
38344
38364
  /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "none", children: [
@@ -38349,7 +38369,7 @@ function DebuggerBoard({
38349
38369
  ] }) }),
38350
38370
  showHint && hint && /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", children: hint }) }),
38351
38371
  /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "sm", justify: "center", children: [
38352
- !submitted ? /* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "primary", onClick: handleSubmit, disabled: flaggedLines.size === 0, children: [
38372
+ !submitted ? /* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "primary", onClick: handleSubmit, disabled: flaggedLines.length === 0, children: [
38353
38373
  /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: LucideIcons2.Send, size: "sm" }),
38354
38374
  t("debugger.submit")
38355
38375
  ] }) : !allCorrect ? /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "primary", onClick: handleReset, children: t("debugger.tryAgain") }) : null,
@@ -39586,7 +39606,7 @@ var init_FeatureDetailPageTemplate = __esm({
39586
39606
  const resolved = entity && typeof entity === "object" && !Array.isArray(entity) ? entity : void 0;
39587
39607
  if (!resolved) return null;
39588
39608
  return /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "none", className: cn("w-full", className), children: [
39589
- /* @__PURE__ */ jsxRuntime.jsx(
39609
+ resolved.hero && /* @__PURE__ */ jsxRuntime.jsx(
39590
39610
  HeroSection,
39591
39611
  {
39592
39612
  tag: resolved.hero.tag,
@@ -41106,7 +41126,7 @@ var init_LandingPageTemplate = __esm({
41106
41126
  const resolved = entity && typeof entity === "object" && !Array.isArray(entity) ? entity : void 0;
41107
41127
  if (!resolved) return null;
41108
41128
  return /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "none", className: cn("w-full", className), children: [
41109
- /* @__PURE__ */ jsxRuntime.jsx(
41129
+ resolved.hero && /* @__PURE__ */ jsxRuntime.jsx(
41110
41130
  HeroSection,
41111
41131
  {
41112
41132
  tag: resolved.hero.tag,
@@ -42113,6 +42133,9 @@ function getOpponentAction(strategy, actions, history) {
42113
42133
  function NegotiatorBoard({
42114
42134
  entity,
42115
42135
  completeEvent = "PUZZLE_COMPLETE",
42136
+ playRoundEvent,
42137
+ finishEvent,
42138
+ playAgainEvent,
42116
42139
  className
42117
42140
  }) {
42118
42141
  const { emit } = useEventBus();
@@ -42121,13 +42144,14 @@ function NegotiatorBoard({
42121
42144
  const [history, setHistory] = React83.useState([]);
42122
42145
  const [headerError, setHeaderError] = React83.useState(false);
42123
42146
  const [showHint, setShowHint] = React83.useState(false);
42124
- const totalRounds = num(resolved?.totalRounds);
42147
+ const totalRounds = num(resolved?.maxRounds);
42125
42148
  const targetScore = num(resolved?.targetScore);
42126
- const currentRound = history.length;
42127
- const isComplete = currentRound >= totalRounds;
42128
- const playerTotal = history.reduce((s, r) => s + r.playerPayoff, 0);
42149
+ const currentRound = num(resolved?.round);
42150
+ const result = str(resolved?.result) || "none";
42151
+ const playerTotal = num(resolved?.score);
42152
+ const isComplete = result !== "none" || totalRounds > 0 && currentRound >= totalRounds;
42153
+ const won = result === "win";
42129
42154
  const opponentTotal = history.reduce((s, r) => s + r.opponentPayoff, 0);
42130
- const won = isComplete && playerTotal >= targetScore;
42131
42155
  const actions = Array.isArray(resolved?.actions) ? resolved.actions : [];
42132
42156
  const payoffMatrix = Array.isArray(resolved?.payoffMatrix) ? resolved.payoffMatrix : [];
42133
42157
  const handleAction = React83.useCallback((actionId) => {
@@ -42136,29 +42160,45 @@ function NegotiatorBoard({
42136
42160
  const payoff = payoffMatrix.find(
42137
42161
  (p2) => p2.playerAction === actionId && p2.opponentAction === opponentAction
42138
42162
  );
42139
- const result = {
42140
- round: currentRound + 1,
42141
- playerAction: actionId,
42142
- opponentAction,
42143
- playerPayoff: payoff?.playerPayoff ?? 0,
42144
- opponentPayoff: payoff?.opponentPayoff ?? 0
42145
- };
42146
- const newHistory = [...history, result];
42147
- setHistory(newHistory);
42148
- if (newHistory.length >= totalRounds) {
42149
- const total = newHistory.reduce((s, r) => s + r.playerPayoff, 0);
42150
- if (total >= targetScore) {
42151
- emit(`UI:${completeEvent}`, { success: true, score: total });
42152
- }
42153
- if (newHistory.length >= 3 && str(resolved?.hint)) {
42163
+ const playerPayoff = payoff?.playerPayoff ?? 0;
42164
+ setHistory((prev) => [
42165
+ ...prev,
42166
+ {
42167
+ round: prev.length + 1,
42168
+ playerAction: actionId,
42169
+ opponentAction,
42170
+ playerPayoff,
42171
+ opponentPayoff: payoff?.opponentPayoff ?? 0
42172
+ }
42173
+ ]);
42174
+ if (playRoundEvent) {
42175
+ emit(`UI:${playRoundEvent}`, { playerAction: actionId, payoff: playerPayoff });
42176
+ }
42177
+ if (totalRounds > 0 && currentRound + 1 >= totalRounds) {
42178
+ if (finishEvent) {
42179
+ emit(`UI:${finishEvent}`, {});
42180
+ }
42181
+ if (str(resolved?.hint)) {
42154
42182
  setShowHint(true);
42155
42183
  }
42156
42184
  }
42157
- }, [isComplete, resolved, totalRounds, targetScore, actions, payoffMatrix, history, currentRound, completeEvent, emit]);
42158
- const handleReset = () => {
42185
+ }, [isComplete, resolved, totalRounds, currentRound, actions, payoffMatrix, history, playRoundEvent, finishEvent, emit]);
42186
+ const handleReset = React83.useCallback(() => {
42159
42187
  setHistory([]);
42160
42188
  setShowHint(false);
42161
- };
42189
+ if (playAgainEvent) {
42190
+ emit(`UI:${playAgainEvent}`, {});
42191
+ }
42192
+ }, [playAgainEvent, emit]);
42193
+ const completedRef = React83.useRef(false);
42194
+ React83.useEffect(() => {
42195
+ if (result === "win" && !completedRef.current) {
42196
+ completedRef.current = true;
42197
+ emit(`UI:${completeEvent}`, { success: true, score: playerTotal });
42198
+ } else if (result === "none") {
42199
+ completedRef.current = false;
42200
+ }
42201
+ }, [result, playerTotal, completeEvent, emit]);
42162
42202
  const getActionLabel = (id) => actions.find((a) => a.id === id)?.label ?? id;
42163
42203
  if (!resolved) return null;
42164
42204
  const theme = resolved.theme ?? void 0;
@@ -42327,7 +42367,7 @@ var init_PricingPageTemplate = __esm({
42327
42367
  const resolved = entity && typeof entity === "object" && !Array.isArray(entity) ? entity : void 0;
42328
42368
  if (!resolved) return null;
42329
42369
  return /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "none", className: cn("w-full", className), children: [
42330
- /* @__PURE__ */ jsxRuntime.jsx(
42370
+ resolved.hero && /* @__PURE__ */ jsxRuntime.jsx(
42331
42371
  HeroSection,
42332
42372
  {
42333
42373
  tag: resolved.hero.tag,
@@ -45105,67 +45145,47 @@ var init_SimulationGraph = __esm({
45105
45145
  function SimulatorBoard({
45106
45146
  entity,
45107
45147
  completeEvent = "PUZZLE_COMPLETE",
45148
+ setAEvent,
45149
+ setBEvent,
45150
+ checkEvent,
45151
+ playAgainEvent,
45108
45152
  className
45109
45153
  }) {
45110
45154
  const { emit } = useEventBus();
45111
45155
  const { t } = hooks.useTranslate();
45112
45156
  const resolved = boardEntity(entity);
45113
45157
  const parameters = Array.isArray(resolved?.parameters) ? resolved.parameters : [];
45114
- const [values, setValues] = React83.useState(() => {
45115
- const init = {};
45116
- for (const p2 of parameters) {
45117
- init[p2.id] = p2.initial;
45118
- }
45119
- return init;
45120
- });
45121
45158
  const [headerError, setHeaderError] = React83.useState(false);
45122
- const [submitted, setSubmitted] = React83.useState(false);
45123
- const [attempts, setAttempts] = React83.useState(0);
45124
- const [showHint, setShowHint] = React83.useState(false);
45125
- const computeOutput = React83.useCallback((params) => {
45126
- try {
45127
- const fn = new Function("params", `return (${str(resolved?.computeExpression)})`);
45128
- return fn(params);
45129
- } catch {
45130
- return 0;
45131
- }
45132
- }, [resolved?.computeExpression]);
45133
- const output = React83.useMemo(() => computeOutput(values) ?? 0, [computeOutput, values]);
45134
- const targetValue = num(resolved?.targetValue);
45135
- const targetTolerance = num(resolved?.targetTolerance);
45136
- const isCorrect = Math.abs(output - targetValue) <= targetTolerance;
45137
- const handleParameterChange = (id, value) => {
45138
- if (submitted) return;
45139
- setValues((prev) => ({ ...prev, [id]: value }));
45159
+ if (!resolved) return null;
45160
+ const paramA = num(resolved.paramA);
45161
+ const paramB = num(resolved.paramB);
45162
+ const output = num(resolved.output);
45163
+ const targetValue = num(resolved.target);
45164
+ const targetTolerance = num(resolved.tolerance);
45165
+ const attempts = num(resolved.attempts);
45166
+ const result = str(resolved.result);
45167
+ const isWin = result === "win";
45168
+ const isComplete = result !== "none" && result !== "";
45169
+ const paramAValue = parameters[0];
45170
+ const paramBValue = parameters[1];
45171
+ const sliderValues = [paramA, paramB];
45172
+ const sliderEvents = [setAEvent, setBEvent];
45173
+ const handleParameterChange = (index, value) => {
45174
+ if (isComplete) return;
45175
+ const ev = sliderEvents[index];
45176
+ if (ev) emit(`UI:${ev}`, { value });
45140
45177
  };
45141
- const handleSubmit = () => {
45142
- setSubmitted(true);
45143
- setAttempts((a) => a + 1);
45144
- if (isCorrect) {
45145
- emit(`UI:${completeEvent}`, { success: true, attempts: attempts + 1 });
45146
- }
45178
+ const handleCheck = () => {
45179
+ if (checkEvent) emit(`UI:${checkEvent}`, {});
45147
45180
  };
45148
- const handleReset = () => {
45149
- setSubmitted(false);
45150
- if (attempts >= 2 && str(resolved?.hint)) {
45151
- setShowHint(true);
45152
- }
45181
+ const handlePlayAgain = () => {
45182
+ if (playAgainEvent) emit(`UI:${playAgainEvent}`, {});
45153
45183
  };
45154
- const handleFullReset = () => {
45155
- const init = {};
45156
- for (const p2 of parameters) {
45157
- init[p2.id] = p2.initial;
45158
- }
45159
- setValues(init);
45160
- setSubmitted(false);
45161
- setAttempts(0);
45162
- setShowHint(false);
45163
- };
45164
- if (!resolved) return null;
45165
45184
  const theme = resolved.theme ?? void 0;
45166
45185
  const themeBackground = theme?.background;
45167
45186
  const headerImage = str(resolved.headerImage);
45168
45187
  const hint = str(resolved.hint);
45188
+ const showHint = isComplete && !isWin && attempts >= 2 && Boolean(hint);
45169
45189
  const outputLabel = str(resolved.outputLabel);
45170
45190
  const outputUnit = str(resolved.outputUnit);
45171
45191
  return /* @__PURE__ */ jsxRuntime.jsx(
@@ -45185,41 +45205,43 @@ function SimulatorBoard({
45185
45205
  ] }) }),
45186
45206
  /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "md", children: [
45187
45207
  /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", weight: "bold", className: "uppercase tracking-wider text-muted-foreground", children: t("simulator.parameters") }),
45188
- parameters.map((param) => /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "xs", children: [
45189
- /* @__PURE__ */ jsxRuntime.jsxs(HStack, { justify: "between", align: "center", children: [
45190
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", weight: "medium", children: param.label }),
45191
- /* @__PURE__ */ jsxRuntime.jsxs(Badge, { size: "sm", children: [
45192
- values[param.id],
45193
- " ",
45194
- param.unit
45195
- ] })
45196
- ] }),
45197
- /* @__PURE__ */ jsxRuntime.jsx(
45198
- "input",
45199
- {
45200
- type: "range",
45201
- min: param.min,
45202
- max: param.max,
45203
- step: param.step,
45204
- value: values[param.id],
45205
- onChange: (e) => handleParameterChange(param.id, Number(e.target.value)),
45206
- disabled: submitted,
45207
- className: "w-full accent-foreground"
45208
- }
45209
- ),
45210
- /* @__PURE__ */ jsxRuntime.jsxs(HStack, { justify: "between", children: [
45211
- /* @__PURE__ */ jsxRuntime.jsxs(Typography, { variant: "caption", className: "text-muted-foreground", children: [
45212
- param.min,
45213
- " ",
45214
- param.unit
45208
+ [paramAValue, paramBValue].map(
45209
+ (param, index) => param ? /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "xs", children: [
45210
+ /* @__PURE__ */ jsxRuntime.jsxs(HStack, { justify: "between", align: "center", children: [
45211
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", weight: "medium", children: param.label }),
45212
+ /* @__PURE__ */ jsxRuntime.jsxs(Badge, { size: "sm", children: [
45213
+ sliderValues[index],
45214
+ " ",
45215
+ param.unit
45216
+ ] })
45215
45217
  ] }),
45216
- /* @__PURE__ */ jsxRuntime.jsxs(Typography, { variant: "caption", className: "text-muted-foreground", children: [
45217
- param.max,
45218
- " ",
45219
- param.unit
45218
+ /* @__PURE__ */ jsxRuntime.jsx(
45219
+ "input",
45220
+ {
45221
+ type: "range",
45222
+ min: param.min,
45223
+ max: param.max,
45224
+ step: param.step,
45225
+ value: sliderValues[index],
45226
+ onChange: (e) => handleParameterChange(index, Number(e.target.value)),
45227
+ disabled: isComplete,
45228
+ className: "w-full accent-foreground"
45229
+ }
45230
+ ),
45231
+ /* @__PURE__ */ jsxRuntime.jsxs(HStack, { justify: "between", children: [
45232
+ /* @__PURE__ */ jsxRuntime.jsxs(Typography, { variant: "caption", className: "text-muted-foreground", children: [
45233
+ param.min,
45234
+ " ",
45235
+ param.unit
45236
+ ] }),
45237
+ /* @__PURE__ */ jsxRuntime.jsxs(Typography, { variant: "caption", className: "text-muted-foreground", children: [
45238
+ param.max,
45239
+ " ",
45240
+ param.unit
45241
+ ] })
45220
45242
  ] })
45221
- ] })
45222
- ] }, param.id))
45243
+ ] }, param.id ?? index) : null
45244
+ )
45223
45245
  ] }) }),
45224
45246
  /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "sm", align: "center", children: [
45225
45247
  /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", weight: "bold", className: "uppercase tracking-wider text-muted-foreground", children: outputLabel }),
@@ -45228,9 +45250,9 @@ function SimulatorBoard({
45228
45250
  " ",
45229
45251
  outputUnit
45230
45252
  ] }),
45231
- submitted && /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "xs", align: "center", children: [
45232
- /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: isCorrect ? LucideIcons2.CheckCircle : LucideIcons2.XCircle, size: "sm", className: isCorrect ? "text-success" : "text-error" }),
45233
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", className: isCorrect ? "text-success" : "text-error", children: isCorrect ? str(resolved.successMessage) || t("simulator.correct") : str(resolved.failMessage) || t("simulator.incorrect") })
45253
+ isComplete && /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "xs", align: "center", children: [
45254
+ /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: isWin ? LucideIcons2.CheckCircle : LucideIcons2.XCircle, size: "sm", className: isWin ? "text-success" : "text-error" }),
45255
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", className: isWin ? "text-success" : "text-error", children: isWin ? str(resolved.successMessage) || t("simulator.correct") : str(resolved.failMessage) || t("simulator.incorrect") })
45234
45256
  ] }),
45235
45257
  /* @__PURE__ */ jsxRuntime.jsxs(Typography, { variant: "caption", className: "text-muted-foreground", children: [
45236
45258
  t("simulator.target"),
@@ -45245,11 +45267,11 @@ function SimulatorBoard({
45245
45267
  ] }) }),
45246
45268
  showHint && hint && /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", children: hint }) }),
45247
45269
  /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "sm", justify: "center", children: [
45248
- !submitted ? /* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "primary", onClick: handleSubmit, children: [
45270
+ !isComplete ? /* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "primary", onClick: handleCheck, children: [
45249
45271
  /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: LucideIcons2.Play, size: "sm" }),
45250
45272
  t("simulator.simulate")
45251
- ] }) : !isCorrect ? /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "primary", onClick: handleReset, children: t("simulator.tryAgain") }) : null,
45252
- /* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "secondary", onClick: handleFullReset, children: [
45273
+ ] }) : null,
45274
+ /* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "secondary", onClick: handlePlayAgain, children: [
45253
45275
  /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: LucideIcons2.RotateCcw, size: "sm" }),
45254
45276
  t("simulator.reset")
45255
45277
  ] })