@almadar/ui 2.1.1 → 2.1.3

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.
@@ -1,12 +1,12 @@
1
1
  import { DEFAULT_CONFIG, renderStateMachineToDomData, parseContentSegments } from '../chunk-N6DJVKZ6.js';
2
- import { VStack, HStack, Typography, Button, Icon, Box, Card, Avatar, Badge, SearchInput, Checkbox, Menu as Menu$1, Pagination, LoadingState, EmptyState, Modal, ErrorState, QuizBlock, CodeBlock, ScaledDiagram, MarkdownContent, Divider, ProgressBar, Stack, Drawer, Toast, Tabs, Input, ThemeToggle, HealthBar, ScoreDisplay, StateIndicator, Container, EntityDisplayEvents } from '../chunk-6YV5YRGT.js';
3
- export { Accordion, Card2 as ActionCard, Alert, Avatar, Badge, Box, Breadcrumb, Button, ButtonGroup, Card, CardBody, CardContent, CardFooter, CardGrid, CardHeader, CardTitle, Center, Checkbox, CodeBlock, ConditionalWrapper, Container, ControlButton, DataTable, DetailPanel, Divider, Drawer, EmptyState, EntityDisplayEvents, ErrorBoundary, ErrorState, FilterGroup, Flex, FloatingActionButton, Form, FormField, FormSectionHeader, Grid, HStack, Heading, HealthBar, Icon, Input, InputGroup, Label, LawReferenceTooltip, LoadingState, MarkdownContent, MasterDetail, Menu, Modal, Overlay, PageHeader, Pagination, Popover, ProgressBar, QuizBlock, Radio, RelationSelect, RepeatableFormSection, ScaledDiagram, ScoreDisplay, SearchInput, Select, SidePanel, SimpleGrid, Skeleton, SlotContentRenderer, Spacer, Spinner, Sprite, Stack, StatCard, StateIndicator, Switch, Tabs, Text, TextHighlight, Textarea, ThemeSelector, ThemeToggle, Toast, Tooltip, Typography, UISlotComponent, UISlotRenderer, VStack, ViolationAlert, WizardNavigation, WizardProgress, drawSprite } from '../chunk-6YV5YRGT.js';
4
- import '../chunk-BTXQJGFB.js';
5
- import { cn, getNestedValue } from '../chunk-KKCVDUK7.js';
6
- export { cn } from '../chunk-KKCVDUK7.js';
7
2
  import { useAuthContext } from '../chunk-BKC4XU44.js';
8
3
  export { ENTITY_EVENTS, useAgentChat, useAuthContext, useCompile, useConnectGitHub, useCreateEntity, useDeepAgentGeneration, useDeleteEntity, useDisconnectGitHub, useEntities, useEntitiesByType, useEntity as useEntityById, useEntityMutations, useExtensions, useFileEditor, useFileSystem, useGitHubBranches, useGitHubRepo, useGitHubRepos, useGitHubStatus, useInput, useOrbitalHistory, useOrbitalMutations, usePhysics, usePlayer, usePreview, useResolvedEntity, useSelectedEntity, useSendOrbitalEvent, useSingletonEntity, useUIEvents, useUpdateEntity, useValidation } from '../chunk-BKC4XU44.js';
9
4
  import '../chunk-XSEDIUM6.js';
5
+ import { VStack, HStack, Typography, Button, Icon, Box, Card, Avatar, Badge, SearchInput, Checkbox, Menu as Menu$1, Pagination, LoadingState, EmptyState, Modal, ErrorState, QuizBlock, CodeBlock, ScaledDiagram, MarkdownContent, Divider, ProgressBar, Stack, Select, Drawer, Toast, Tabs, Input, ThemeToggle, HealthBar, ScoreDisplay, StateIndicator, Container, EntityDisplayEvents } from '../chunk-LX4G4SVJ.js';
6
+ export { Accordion, Card2 as ActionCard, Alert, Avatar, Badge, Box, Breadcrumb, Button, ButtonGroup, Card, CardBody, CardContent, CardFooter, CardGrid, CardHeader, CardTitle, Center, Checkbox, CodeBlock, ConditionalWrapper, Container, ControlButton, DataTable, DetailPanel, Divider, Drawer, EmptyState, EntityDisplayEvents, ErrorBoundary, ErrorState, FilterGroup, Flex, FloatingActionButton, Form, FormField, FormSectionHeader, Grid, HStack, Heading, HealthBar, Icon, Input, InputGroup, Label, LawReferenceTooltip, LoadingState, MarkdownContent, MasterDetail, Menu, Modal, Overlay, PageHeader, Pagination, Popover, ProgressBar, QuizBlock, Radio, RelationSelect, RepeatableFormSection, ScaledDiagram, ScoreDisplay, SearchInput, Select, SidePanel, SimpleGrid, Skeleton, SlotContentRenderer, Spacer, Spinner, Sprite, Stack, StatCard, StateIndicator, Switch, Tabs, Text, TextHighlight, Textarea, ThemeSelector, ThemeToggle, Toast, Tooltip, Typography, UISlotComponent, UISlotRenderer, VStack, ViolationAlert, WizardNavigation, WizardProgress, drawSprite } from '../chunk-LX4G4SVJ.js';
7
+ import '../chunk-QU4JHKVC.js';
8
+ import { cn, getNestedValue } from '../chunk-KKCVDUK7.js';
9
+ export { cn } from '../chunk-KKCVDUK7.js';
10
10
  import { useTranslate } from '../chunk-PE2H3NAW.js';
11
11
  export { EntityDataProvider, I18nProvider, createTranslate, entityDataKeys, parseQueryBinding, useEntity, useEntityDataAdapter, useEntityDetail, useEntityList, useEntityListSuspense, useEntitySuspense, useQuerySingleton, useTranslate } from '../chunk-PE2H3NAW.js';
12
12
  import { useEventBus, useEventListener } from '../chunk-YXZM3WCF.js';
@@ -14,12 +14,11 @@ export { useEmitEvent, useEventBus, useEventListener } from '../chunk-YXZM3WCF.j
14
14
  export { DEFAULT_SLOTS, useUISlotManager } from '../chunk-7NEWMNNU.js';
15
15
  export { clearEntities, getAllEntities, getByType, getEntity, getSingleton, removeEntity, spawnEntity, updateEntity, updateSingleton } from '../chunk-N7MVUW4R.js';
16
16
  import { __publicField } from '../chunk-PKBMQBKP.js';
17
- import * as React25 from 'react';
18
- import React25__default, { createContext, useState, useMemo, useCallback, useEffect, useRef, useContext } from 'react';
19
- import { ChevronDown, X, Menu, ChevronRight, ChevronLeft, ArrowUp, ArrowDown, MoreVertical, Package, Check, AlertTriangle, Trash2, List as List$1, Printer, AlertCircle, Circle, Clock, CheckCircle2, Image as Image$1, Upload, ZoomIn, Eraser, FileText, ZoomOut, Download, RotateCcw, Code, WrapText, Copy, Settings, Search, Bell, LogOut, Pause, Play, Calendar, Pencil, Eye, MoreHorizontal, Minus, Plus } from 'lucide-react';
17
+ import * as React42 from 'react';
18
+ import React42__default, { createContext, useState, useMemo, useCallback, useEffect, useRef, useContext } from 'react';
19
+ import { ChevronDown, X, Menu, ChevronRight, ChevronLeft, ArrowUp, ArrowDown, MoreVertical, Package, Check, AlertTriangle, Trash2, List as List$1, Printer, CheckCircle, XCircle, Play, RotateCcw, Send, Wrench, Bug, ArrowRight, Pause, SkipForward, Zap, Sword, Move, Heart, Shield, AlertCircle, Circle, Clock, CheckCircle2, Image as Image$1, Upload, ZoomIn, Eraser, FileText, ZoomOut, Download, Code, WrapText, Copy, Settings, Search, Bell, LogOut, Calendar, Pencil, Eye, MoreHorizontal, Minus, Plus } from 'lucide-react';
20
20
  import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
21
21
  import { createPortal } from 'react-dom';
22
- import { Button as Button$1, cn as cn$1 } from '@almadar/ui';
23
22
  import { useLocation, Link, Outlet } from 'react-router-dom';
24
23
 
25
24
  var FormSection = ({
@@ -32,7 +31,7 @@ var FormSection = ({
32
31
  columns = 1,
33
32
  className
34
33
  }) => {
35
- const [collapsed, setCollapsed] = React25__default.useState(defaultCollapsed);
34
+ const [collapsed, setCollapsed] = React42__default.useState(defaultCollapsed);
36
35
  const gridClass = {
37
36
  1: "grid-cols-1",
38
37
  2: "grid-cols-1 md:grid-cols-2",
@@ -407,7 +406,7 @@ var Section = ({
407
406
  };
408
407
  Section.displayName = "Section";
409
408
  var SidebarNavItem = ({ item, collapsed }) => {
410
- const Icon2 = item.icon;
409
+ const Icon3 = item.icon;
411
410
  const isActive = item.active ?? item.isActive;
412
411
  return /* @__PURE__ */ jsxs(
413
412
  Button,
@@ -429,8 +428,8 @@ var SidebarNavItem = ({ item, collapsed }) => {
429
428
  ),
430
429
  title: collapsed ? item.label : void 0,
431
430
  children: [
432
- Icon2 && /* @__PURE__ */ jsx(
433
- Icon2,
431
+ Icon3 && /* @__PURE__ */ jsx(
432
+ Icon3,
434
433
  {
435
434
  size: 20,
436
435
  className: cn(
@@ -1019,7 +1018,7 @@ var List = ({
1019
1018
  if (d && typeof d === "object" && "id" in d) return [d];
1020
1019
  return [];
1021
1020
  }, [data]);
1022
- const getItemActions = React25__default.useCallback(
1021
+ const getItemActions = React42__default.useCallback(
1023
1022
  (item) => {
1024
1023
  if (!itemActions) return [];
1025
1024
  if (typeof itemActions === "function") {
@@ -1522,7 +1521,7 @@ var WizardContainer = ({
1522
1521
  const isCompleted = index < currentStep;
1523
1522
  const stepKey = step.id ?? step.tabId ?? `step-${index}`;
1524
1523
  const stepTitle = step.title ?? step.name ?? `Step ${index + 1}`;
1525
- return /* @__PURE__ */ jsxs(React25__default.Fragment, { children: [
1524
+ return /* @__PURE__ */ jsxs(React42__default.Fragment, { children: [
1526
1525
  /* @__PURE__ */ jsx(
1527
1526
  Button,
1528
1527
  {
@@ -2746,7 +2745,7 @@ var StateMachineView = ({
2746
2745
  style: { top: title ? 30 : 0 },
2747
2746
  children: [
2748
2747
  entity && /* @__PURE__ */ jsx(EntityBox, { entity, config }),
2749
- states.map((state) => renderStateNode ? /* @__PURE__ */ jsx(React25__default.Fragment, { children: renderStateNode(state, config) }, state.id) : /* @__PURE__ */ jsx(
2748
+ states.map((state) => renderStateNode ? /* @__PURE__ */ jsx(React42__default.Fragment, { children: renderStateNode(state, config) }, state.id) : /* @__PURE__ */ jsx(
2750
2749
  StateNode,
2751
2750
  {
2752
2751
  state,
@@ -6129,12 +6128,12 @@ function GameAudioToggle({
6129
6128
  setMuted(!muted);
6130
6129
  }, [muted, setMuted]);
6131
6130
  return /* @__PURE__ */ jsx(
6132
- Button$1,
6131
+ Button,
6133
6132
  {
6134
6133
  variant: "ghost",
6135
6134
  size,
6136
6135
  onClick: handleToggle,
6137
- className: cn$1("text-lg leading-none px-2", className),
6136
+ className: cn("text-lg leading-none px-2", className),
6138
6137
  "aria-pressed": muted,
6139
6138
  children: muted ? "\u{1F507}" : "\u{1F50A}"
6140
6139
  }
@@ -6760,7 +6759,7 @@ function GameMenu({
6760
6759
  } catch {
6761
6760
  }
6762
6761
  const eventBus = eventBusProp || eventBusFromHook;
6763
- const handleOptionClick = React25.useCallback(
6762
+ const handleOptionClick = React42.useCallback(
6764
6763
  (option) => {
6765
6764
  if (option.event && eventBus) {
6766
6765
  eventBus.emit(`UI:${option.event}`, { option });
@@ -6883,7 +6882,7 @@ function GameOverScreen({
6883
6882
  } catch {
6884
6883
  }
6885
6884
  const eventBus = eventBusProp || eventBusFromHook;
6886
- const handleActionClick = React25.useCallback(
6885
+ const handleActionClick = React42.useCallback(
6887
6886
  (action) => {
6888
6887
  if (action.event && eventBus) {
6889
6888
  eventBus.emit(`UI:${action.event}`, { action });
@@ -7308,7 +7307,7 @@ function BattleBoard({
7308
7307
  onAttack,
7309
7308
  onGameEnd,
7310
7309
  onUnitMove,
7311
- calculateDamage,
7310
+ calculateDamage: calculateDamage2,
7312
7311
  onDrawEffects,
7313
7312
  hasActiveEffects: hasActiveEffects2 = false,
7314
7313
  effectSpriteUrls = [],
@@ -7334,6 +7333,7 @@ function BattleBoard({
7334
7333
  const currentTurn = entity.turn;
7335
7334
  const gameResult = entity.gameResult;
7336
7335
  const eventBus = useEventBus();
7336
+ const { t } = useTranslate();
7337
7337
  const [hoveredTile, setHoveredTile] = useState(null);
7338
7338
  const [isShaking, setIsShaking] = useState(false);
7339
7339
  const selectedUnit = useMemo(
@@ -7387,11 +7387,11 @@ function BattleBoard({
7387
7387
  const anim2 = movementAnimRef.current;
7388
7388
  if (!anim2) return;
7389
7389
  anim2.elapsed += 16;
7390
- const t = Math.min(anim2.elapsed / anim2.duration, 1);
7391
- const eased = 1 - (1 - t) * (1 - t);
7390
+ const t2 = Math.min(anim2.elapsed / anim2.duration, 1);
7391
+ const eased = 1 - (1 - t2) * (1 - t2);
7392
7392
  const cx = anim2.from.x + (anim2.to.x - anim2.from.x) * eased;
7393
7393
  const cy = anim2.from.y + (anim2.to.y - anim2.from.y) * eased;
7394
- if (t >= 1) {
7394
+ if (t2 >= 1) {
7395
7395
  movementAnimRef.current = null;
7396
7396
  setMovingPositions((prev) => {
7397
7397
  const next = new Map(prev);
@@ -7422,16 +7422,16 @@ function BattleBoard({
7422
7422
  unitType: unit.unitType,
7423
7423
  heroId: unit.heroId,
7424
7424
  sprite: unit.sprite,
7425
- traits: unit.traits?.map((t) => ({
7426
- name: t.name,
7427
- currentState: t.currentState,
7428
- states: t.states,
7429
- cooldown: t.cooldown ?? 0
7425
+ traits: unit.traits?.map((t2) => ({
7426
+ name: t2.name,
7427
+ currentState: t2.currentState,
7428
+ states: t2.states,
7429
+ cooldown: t2.cooldown ?? 0
7430
7430
  }))
7431
7431
  };
7432
7432
  });
7433
7433
  }, [units, movingPositions]);
7434
- const maxY = Math.max(...tiles.map((t) => t.y), 0);
7434
+ const maxY = Math.max(...tiles.map((t2) => t2.y), 0);
7435
7435
  const baseOffsetX = (maxY + 1) * (TILE_WIDTH * scale / 2);
7436
7436
  const tileToScreen = useCallback(
7437
7437
  (tx, ty) => isoToScreen(tx, ty, scale, baseOffsetX),
@@ -7459,8 +7459,8 @@ function BattleBoard({
7459
7459
  eventBus.emit(`UI:${unitClickEvent}`, { unitId });
7460
7460
  }
7461
7461
  if (currentPhase === "action" && selectedUnit) {
7462
- if (unit.team === "enemy" && attackTargets.some((t) => t.x === unit.position.x && t.y === unit.position.y)) {
7463
- const damage = calculateDamage ? calculateDamage(selectedUnit, unit) : Math.max(1, selectedUnit.attack - unit.defense);
7462
+ if (unit.team === "enemy" && attackTargets.some((t2) => t2.x === unit.position.x && t2.y === unit.position.y)) {
7463
+ const damage = calculateDamage2 ? calculateDamage2(selectedUnit, unit) : Math.max(1, selectedUnit.attack - unit.defense);
7464
7464
  setIsShaking(true);
7465
7465
  setTimeout(() => setIsShaking(false), 300);
7466
7466
  onAttack?.(selectedUnit, unit, damage);
@@ -7474,7 +7474,7 @@ function BattleBoard({
7474
7474
  setTimeout(checkGameEnd, 100);
7475
7475
  }
7476
7476
  }
7477
- }, [currentPhase, selectedUnit, attackTargets, units, checkGameEnd, onAttack, calculateDamage, unitClickEvent, attackEvent, eventBus]);
7477
+ }, [currentPhase, selectedUnit, attackTargets, units, checkGameEnd, onAttack, calculateDamage2, unitClickEvent, attackEvent, eventBus]);
7478
7478
  const handleTileClick = useCallback((x, y) => {
7479
7479
  if (tileClickEvent) {
7480
7480
  eventBus.emit(`UI:${tileClickEvent}`, { x, y });
@@ -7536,7 +7536,7 @@ function BattleBoard({
7536
7536
  ]
7537
7537
  );
7538
7538
  const shakeStyle = isShaking ? { animation: "battle-shake 0.3s ease-in-out" } : {};
7539
- return /* @__PURE__ */ jsxs("div", { className: cn("battle-board relative flex flex-col min-h-[600px] bg-[var(--color-background)]", className), children: [
7539
+ return /* @__PURE__ */ jsxs(VStack, { className: cn("battle-board relative min-h-[600px] bg-[var(--color-background)]", className), gap: "none", children: [
7540
7540
  /* @__PURE__ */ jsx("style", { children: `
7541
7541
  @keyframes battle-shake {
7542
7542
  0%, 100% { transform: translate(0, 0); }
@@ -7551,9 +7551,9 @@ function BattleBoard({
7551
7551
  90% { transform: translate(-2px, 1px); }
7552
7552
  }
7553
7553
  ` }),
7554
- header && /* @__PURE__ */ jsx("div", { className: "p-4", children: header(ctx) }),
7555
- /* @__PURE__ */ jsxs("div", { className: "flex flex-1 gap-4 p-4 pt-0", children: [
7556
- /* @__PURE__ */ jsxs("div", { className: "relative flex-1", style: shakeStyle, children: [
7554
+ header && /* @__PURE__ */ jsx(Box, { className: "p-4", children: header(ctx) }),
7555
+ /* @__PURE__ */ jsxs(HStack, { className: "flex-1 gap-4 p-4 pt-0", gap: "none", children: [
7556
+ /* @__PURE__ */ jsxs(Box, { className: "relative flex-1", style: shakeStyle, children: [
7557
7557
  /* @__PURE__ */ jsx(
7558
7558
  IsometricCanvas_default,
7559
7559
  {
@@ -7581,47 +7581,52 @@ function BattleBoard({
7581
7581
  ),
7582
7582
  overlay && overlay(ctx)
7583
7583
  ] }),
7584
- sidebar && /* @__PURE__ */ jsx("div", { className: "w-80 shrink-0", children: sidebar(ctx) })
7584
+ sidebar && /* @__PURE__ */ jsx(Box, { className: "w-80 shrink-0", children: sidebar(ctx) })
7585
7585
  ] }),
7586
- actions ? actions(ctx) : currentPhase !== "game_over" && /* @__PURE__ */ jsxs("div", { className: "fixed bottom-6 right-6 z-50 flex gap-2", children: [
7586
+ actions ? actions(ctx) : currentPhase !== "game_over" && /* @__PURE__ */ jsxs(HStack, { className: "fixed bottom-6 right-6 z-50", gap: "sm", children: [
7587
7587
  (currentPhase === "movement" || currentPhase === "action") && /* @__PURE__ */ jsx(
7588
- "button",
7588
+ Button,
7589
7589
  {
7590
- className: "px-4 py-2 rounded-lg bg-[var(--color-surface)] text-[var(--color-foreground)] border border-[var(--color-border)] shadow-xl hover:opacity-90",
7590
+ variant: "secondary",
7591
+ className: "shadow-xl",
7591
7592
  onClick: handleCancel,
7592
- children: "Cancel"
7593
+ children: t("battle.cancel")
7593
7594
  }
7594
7595
  ),
7595
7596
  /* @__PURE__ */ jsx(
7596
- "button",
7597
+ Button,
7597
7598
  {
7598
- className: "px-4 py-2 rounded-lg bg-[var(--color-primary)] text-white shadow-xl hover:opacity-90",
7599
+ variant: "primary",
7600
+ className: "shadow-xl",
7599
7601
  onClick: handleEndTurn,
7600
- children: "End Turn"
7602
+ children: t("battle.endTurn")
7601
7603
  }
7602
7604
  )
7603
7605
  ] }),
7604
- gameResult && (gameOverOverlay ? gameOverOverlay(ctx) : /* @__PURE__ */ jsx("div", { className: "absolute inset-0 z-50 flex items-center justify-center bg-black/70 backdrop-blur-sm rounded-xl", children: /* @__PURE__ */ jsxs("div", { className: "text-center space-y-6 p-8", children: [
7606
+ gameResult && (gameOverOverlay ? gameOverOverlay(ctx) : /* @__PURE__ */ jsx(Box, { className: "absolute inset-0 z-50 flex items-center justify-center bg-black/70 backdrop-blur-sm rounded-xl", children: /* @__PURE__ */ jsxs(VStack, { className: "text-center p-8", gap: "lg", children: [
7605
7607
  /* @__PURE__ */ jsx(
7606
- "h2",
7608
+ Typography,
7607
7609
  {
7610
+ variant: "h2",
7608
7611
  className: cn(
7609
7612
  "text-4xl font-black tracking-widest uppercase",
7610
7613
  gameResult === "victory" ? "text-yellow-400" : "text-red-500"
7611
7614
  ),
7612
- children: gameResult === "victory" ? "Victory!" : "Defeat"
7615
+ children: gameResult === "victory" ? t("battle.victory") : t("battle.defeat")
7613
7616
  }
7614
7617
  ),
7615
- /* @__PURE__ */ jsxs("p", { className: "text-gray-300", children: [
7616
- "Turns played: ",
7618
+ /* @__PURE__ */ jsxs(Typography, { variant: "body1", className: "text-gray-300", children: [
7619
+ t("battle.turnsPlayed"),
7620
+ ": ",
7617
7621
  currentTurn
7618
7622
  ] }),
7619
7623
  /* @__PURE__ */ jsx(
7620
- "button",
7624
+ Button,
7621
7625
  {
7622
- className: "px-8 py-3 rounded-lg bg-[var(--color-primary)] text-white font-semibold hover:opacity-90",
7626
+ variant: "primary",
7627
+ className: "px-8 py-3 font-semibold",
7623
7628
  onClick: handleReset,
7624
- children: "Play Again"
7629
+ children: t("battle.playAgain")
7625
7630
  }
7626
7631
  )
7627
7632
  ] }) }))
@@ -7639,7 +7644,7 @@ function useBattleState(initialUnits, eventConfig = {}, callbacks = {}) {
7639
7644
  attackEvent,
7640
7645
  cancelEvent
7641
7646
  } = eventConfig;
7642
- const { onAttack, onGameEnd, onUnitMove, calculateDamage } = callbacks;
7647
+ const { onAttack, onGameEnd, onUnitMove, calculateDamage: calculateDamage2 } = callbacks;
7643
7648
  const [units, setUnits] = useState(initialUnits);
7644
7649
  const [selectedUnitId, setSelectedUnitId] = useState(null);
7645
7650
  const [phase, setPhase] = useState("observation");
@@ -7682,7 +7687,7 @@ function useBattleState(initialUnits, eventConfig = {}, callbacks = {}) {
7682
7687
  const dx = Math.abs(unit.position.x - selectedUnit.position.x);
7683
7688
  const dy = Math.abs(unit.position.y - selectedUnit.position.y);
7684
7689
  if (dx <= 1 && dy <= 1 && dx + dy > 0) {
7685
- const damage = calculateDamage ? calculateDamage(selectedUnit, unit) : Math.max(1, selectedUnit.attack - unit.defense);
7690
+ const damage = calculateDamage2 ? calculateDamage2(selectedUnit, unit) : Math.max(1, selectedUnit.attack - unit.defense);
7686
7691
  const newHealth = Math.max(0, unit.health - damage);
7687
7692
  const updatedUnits = units.map(
7688
7693
  (u) => u.id === unit.id ? { ...u, health: newHealth } : u
@@ -7703,7 +7708,7 @@ function useBattleState(initialUnits, eventConfig = {}, callbacks = {}) {
7703
7708
  }
7704
7709
  }
7705
7710
  }
7706
- }, [units, selectedUnitId, phase, checkGameEnd, onAttack, calculateDamage, unitClickEvent, attackEvent, eventBus]);
7711
+ }, [units, selectedUnitId, phase, checkGameEnd, onAttack, calculateDamage2, unitClickEvent, attackEvent, eventBus]);
7707
7712
  const handleTileClick = useCallback((x, y) => {
7708
7713
  if (tileClickEvent) {
7709
7714
  eventBus.emit(`UI:${tileClickEvent}`, { x, y });
@@ -8144,7 +8149,7 @@ function LinearView({
8144
8149
  /* @__PURE__ */ jsx(HStack, { className: "flex-wrap items-center", gap: "xs", children: trait.states.map((state, i) => {
8145
8150
  const isDone = i < currentIdx;
8146
8151
  const isCurrent = i === currentIdx;
8147
- return /* @__PURE__ */ jsxs(React25__default.Fragment, { children: [
8152
+ return /* @__PURE__ */ jsxs(React42__default.Fragment, { children: [
8148
8153
  i > 0 && /* @__PURE__ */ jsx(
8149
8154
  Typography,
8150
8155
  {
@@ -8562,7 +8567,7 @@ var FEATURE_TYPES = [
8562
8567
  "castle"
8563
8568
  ];
8564
8569
  function CollapsibleSection({ title, expanded, onToggle, children, className }) {
8565
- const Icon2 = expanded ? ChevronDown : ChevronRight;
8570
+ const Icon3 = expanded ? ChevronDown : ChevronRight;
8566
8571
  return /* @__PURE__ */ jsxs(VStack, { gap: "xs", className, children: [
8567
8572
  /* @__PURE__ */ jsx(
8568
8573
  Button,
@@ -8572,7 +8577,7 @@ function CollapsibleSection({ title, expanded, onToggle, children, className })
8572
8577
  onClick: onToggle,
8573
8578
  className: "w-full justify-start text-left",
8574
8579
  children: /* @__PURE__ */ jsxs(HStack, { gap: "xs", align: "center", children: [
8575
- /* @__PURE__ */ jsx(Icon2, { size: 14 }),
8580
+ /* @__PURE__ */ jsx(Icon3, { size: 14 }),
8576
8581
  /* @__PURE__ */ jsx(Typography, { variant: "label", weight: "semibold", children: title })
8577
8582
  ] })
8578
8583
  }
@@ -8704,128 +8709,2516 @@ function EditorToolbar({ mode, onModeChange, className }) {
8704
8709
  )) });
8705
8710
  }
8706
8711
  EditorToolbar.displayName = "EditorToolbar";
8707
- function extractTitle(children) {
8708
- if (!React25__default.isValidElement(children)) return void 0;
8709
- const props = children.props;
8710
- if (typeof props.title === "string") {
8711
- return props.title;
8712
- }
8713
- return void 0;
8714
- }
8715
- var ModalSlot = ({
8716
- children,
8717
- title: overrideTitle,
8712
+ var DRAG_MIME2 = "application/x-almadar-slot-item";
8713
+ var SIZE_CONFIG3 = {
8714
+ sm: { px: "px-2 py-1", icon: "text-lg", text: "text-xs" },
8715
+ md: { px: "px-3 py-2", icon: "text-2xl", text: "text-sm" },
8716
+ lg: { px: "px-4 py-3", icon: "text-3xl", text: "text-base" }
8717
+ };
8718
+ function ActionTile({
8719
+ action,
8718
8720
  size = "md",
8721
+ disabled = false,
8722
+ categoryColors,
8719
8723
  className
8720
- }) => {
8721
- const eventBus = useEventBus();
8722
- const isOpen = Boolean(children);
8723
- const title = overrideTitle || extractTitle(children);
8724
- const handleClose = () => {
8725
- eventBus.emit("UI:CLOSE");
8726
- eventBus.emit("UI:CANCEL");
8727
- };
8728
- if (!isOpen) return null;
8729
- return /* @__PURE__ */ jsx(
8730
- Modal,
8724
+ }) {
8725
+ useTranslate();
8726
+ const config = SIZE_CONFIG3[size];
8727
+ const catColor = categoryColors?.[action.category];
8728
+ const handleDragStart = useCallback((e) => {
8729
+ if (disabled) {
8730
+ e.preventDefault();
8731
+ return;
8732
+ }
8733
+ e.dataTransfer.setData(DRAG_MIME2, JSON.stringify(action));
8734
+ e.dataTransfer.effectAllowed = "copy";
8735
+ }, [action, disabled]);
8736
+ return /* @__PURE__ */ jsxs(
8737
+ Box,
8731
8738
  {
8732
- isOpen,
8733
- onClose: handleClose,
8734
- title,
8735
- size,
8736
- className,
8737
- children
8739
+ display: "flex",
8740
+ className: cn(
8741
+ "flex-col items-center gap-1 rounded-lg border-2 transition-all select-none",
8742
+ config.px,
8743
+ disabled ? "opacity-40 cursor-not-allowed border-border bg-muted" : "cursor-grab active:cursor-grabbing hover:scale-105 hover:shadow-md border-border bg-card",
8744
+ className
8745
+ ),
8746
+ style: {
8747
+ backgroundColor: !disabled && catColor ? catColor.bg : void 0,
8748
+ borderColor: !disabled && catColor ? catColor.border : void 0
8749
+ },
8750
+ draggable: !disabled,
8751
+ onDragStart: handleDragStart,
8752
+ children: [
8753
+ action.iconUrl ? /* @__PURE__ */ jsx("img", { src: action.iconUrl, alt: "", className: "w-8 h-8 object-contain" }) : /* @__PURE__ */ jsx(Typography, { variant: "body1", className: cn(config.icon, "leading-none"), children: action.iconEmoji || "\u2726" }),
8754
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", className: cn(config.text, "text-foreground font-medium whitespace-nowrap"), children: action.name })
8755
+ ]
8738
8756
  }
8739
8757
  );
8740
- };
8741
- ModalSlot.displayName = "ModalSlot";
8742
- function extractTitle2(children) {
8743
- if (!React25__default.isValidElement(children)) return void 0;
8744
- const props = children.props;
8745
- if (typeof props.title === "string") {
8746
- return props.title;
8747
- }
8748
- return void 0;
8749
8758
  }
8750
- var DrawerSlot = ({
8751
- children,
8752
- title: overrideTitle,
8753
- position = "right",
8759
+ ActionTile.displayName = "ActionTile";
8760
+ function ActionPalette({
8761
+ actions,
8762
+ usedActionIds = [],
8763
+ allowDuplicates = true,
8764
+ categoryColors,
8754
8765
  size = "md",
8766
+ label,
8755
8767
  className
8756
- }) => {
8757
- const eventBus = useEventBus();
8758
- const isOpen = Boolean(children);
8759
- const title = overrideTitle || extractTitle2(children);
8760
- const handleClose = () => {
8761
- eventBus.emit("UI:CLOSE");
8762
- eventBus.emit("UI:CANCEL");
8768
+ }) {
8769
+ const { t } = useTranslate();
8770
+ return /* @__PURE__ */ jsxs(VStack, { className: cn("p-3 rounded-lg bg-card border border-border", className), gap: "sm", children: [
8771
+ /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-muted-foreground font-medium", children: label ?? t("sequencer.actions") }),
8772
+ /* @__PURE__ */ jsx(HStack, { className: "flex-wrap", gap: "sm", children: actions.map((action) => /* @__PURE__ */ jsx(
8773
+ ActionTile,
8774
+ {
8775
+ action,
8776
+ size,
8777
+ categoryColors,
8778
+ disabled: !allowDuplicates && usedActionIds.includes(action.id)
8779
+ },
8780
+ action.id
8781
+ )) })
8782
+ ] });
8783
+ }
8784
+ ActionPalette.displayName = "ActionPalette";
8785
+ function SequenceBar({
8786
+ slots,
8787
+ maxSlots,
8788
+ onSlotDrop,
8789
+ onSlotRemove,
8790
+ playing = false,
8791
+ currentStep = -1,
8792
+ categoryColors,
8793
+ slotFeedback,
8794
+ size = "lg",
8795
+ className
8796
+ }) {
8797
+ const handleDrop = useCallback((index) => (item) => {
8798
+ if (playing) return;
8799
+ onSlotDrop(index, item);
8800
+ }, [onSlotDrop, playing]);
8801
+ const handleRemove = useCallback((index) => () => {
8802
+ if (playing) return;
8803
+ onSlotRemove(index);
8804
+ }, [onSlotRemove, playing]);
8805
+ const paddedSlots = Array.from({ length: maxSlots }, (_, i) => slots[i]);
8806
+ return /* @__PURE__ */ jsx(HStack, { className: cn("items-center", className), gap: "sm", children: paddedSlots.map((slot, i) => /* @__PURE__ */ jsxs(React42__default.Fragment, { children: [
8807
+ i > 0 && /* @__PURE__ */ jsx(
8808
+ Typography,
8809
+ {
8810
+ variant: "body1",
8811
+ className: cn(
8812
+ "text-lg",
8813
+ currentStep >= 0 && i <= currentStep ? "text-primary" : "text-muted-foreground"
8814
+ ),
8815
+ children: "\u2192"
8816
+ }
8817
+ ),
8818
+ /* @__PURE__ */ jsx(
8819
+ TraitSlot,
8820
+ {
8821
+ slotNumber: i + 1,
8822
+ equippedItem: slot,
8823
+ size,
8824
+ categoryColors,
8825
+ onItemDrop: handleDrop(i),
8826
+ onRemove: slot ? handleRemove(i) : void 0,
8827
+ draggable: !playing && !!slot,
8828
+ selected: currentStep === i,
8829
+ locked: playing,
8830
+ feedback: slotFeedback?.[i]
8831
+ }
8832
+ )
8833
+ ] }, i)) });
8834
+ }
8835
+ SequenceBar.displayName = "SequenceBar";
8836
+ var ENCOURAGEMENT_KEYS = [
8837
+ "puzzle.tryAgain1",
8838
+ "puzzle.tryAgain2",
8839
+ "puzzle.tryAgain3"
8840
+ ];
8841
+ var stepLabel = (slot, i) => slot ? `${i + 1}. ${slot.name}` : `Step ${i + 1}`;
8842
+ function computeSlotFeedback(playerSeq, solutions) {
8843
+ let bestSolution = solutions[0];
8844
+ let bestMatches = -1;
8845
+ for (const sol of solutions) {
8846
+ const matches = sol.filter((id, i) => id === playerSeq[i]).length;
8847
+ if (matches > bestMatches) {
8848
+ bestMatches = matches;
8849
+ bestSolution = sol;
8850
+ }
8851
+ }
8852
+ return playerSeq.map(
8853
+ (id, i) => id !== void 0 && id === bestSolution[i] ? "correct" : "wrong"
8854
+ );
8855
+ }
8856
+ function SequencerBoard({
8857
+ entity,
8858
+ categoryColors,
8859
+ stepDurationMs = 1e3,
8860
+ playEvent,
8861
+ completeEvent,
8862
+ className
8863
+ }) {
8864
+ const { emit } = useEventBus();
8865
+ const { t } = useTranslate();
8866
+ const [headerError, setHeaderError] = useState(false);
8867
+ const [slots, setSlots] = useState(
8868
+ () => Array.from({ length: entity.maxSlots }, () => void 0)
8869
+ );
8870
+ const [playState, setPlayState] = useState("idle");
8871
+ const [currentStep, setCurrentStep] = useState(-1);
8872
+ const [attempts, setAttempts] = useState(0);
8873
+ const [slotFeedback, setSlotFeedback] = useState(
8874
+ () => Array.from({ length: entity.maxSlots }, () => null)
8875
+ );
8876
+ const timerRef = useRef(null);
8877
+ useEffect(() => () => {
8878
+ if (timerRef.current) clearTimeout(timerRef.current);
8879
+ }, []);
8880
+ const handleSlotDrop = useCallback((index, item) => {
8881
+ setSlots((prev) => {
8882
+ const next = [...prev];
8883
+ next[index] = item;
8884
+ return next;
8885
+ });
8886
+ setSlotFeedback((prev) => {
8887
+ const next = [...prev];
8888
+ next[index] = null;
8889
+ return next;
8890
+ });
8891
+ emit("UI:PLAY_SOUND", { key: "drop_slot" });
8892
+ }, [emit]);
8893
+ const handleSlotRemove = useCallback((index) => {
8894
+ setSlots((prev) => {
8895
+ const next = [...prev];
8896
+ next[index] = void 0;
8897
+ return next;
8898
+ });
8899
+ setSlotFeedback((prev) => {
8900
+ const next = [...prev];
8901
+ next[index] = null;
8902
+ return next;
8903
+ });
8904
+ emit("UI:PLAY_SOUND", { key: "back" });
8905
+ }, [emit]);
8906
+ const handleReset = useCallback(() => {
8907
+ if (timerRef.current) clearTimeout(timerRef.current);
8908
+ setSlots(Array.from({ length: entity.maxSlots }, () => void 0));
8909
+ setPlayState("idle");
8910
+ setCurrentStep(-1);
8911
+ setAttempts(0);
8912
+ setSlotFeedback(Array.from({ length: entity.maxSlots }, () => null));
8913
+ }, [entity.maxSlots]);
8914
+ const filledSlots = slots.filter((s) => !!s);
8915
+ const canPlay = filledSlots.length > 0 && playState === "idle";
8916
+ const handlePlay = useCallback(() => {
8917
+ if (!canPlay) return;
8918
+ setSlotFeedback(Array.from({ length: entity.maxSlots }, () => null));
8919
+ emit("UI:PLAY_SOUND", { key: "confirm" });
8920
+ const sequence = slots.map((s) => s?.id || "");
8921
+ if (playEvent) {
8922
+ emit(`UI:${playEvent}`, { sequence });
8923
+ }
8924
+ setPlayState("playing");
8925
+ setCurrentStep(0);
8926
+ let step = 0;
8927
+ const advance = () => {
8928
+ step++;
8929
+ if (step >= entity.maxSlots) {
8930
+ const playerSeq = slots.map((s) => s?.id);
8931
+ const playerIds = slots.filter(Boolean).map((s) => s?.id || "");
8932
+ const success = entity.solutions.some(
8933
+ (sol) => sol.length === playerIds.length && sol.every((id, i) => id === playerIds[i])
8934
+ );
8935
+ if (success) {
8936
+ setPlayState("success");
8937
+ setCurrentStep(-1);
8938
+ emit("UI:PLAY_SOUND", { key: "levelComplete" });
8939
+ if (completeEvent) {
8940
+ emit(`UI:${completeEvent}`, { success: true, sequence: playerIds });
8941
+ }
8942
+ } else {
8943
+ setAttempts((prev) => prev + 1);
8944
+ const feedback = computeSlotFeedback(playerSeq, entity.solutions);
8945
+ setSlotFeedback(feedback);
8946
+ setPlayState("idle");
8947
+ setCurrentStep(-1);
8948
+ emit("UI:PLAY_SOUND", { key: "fail" });
8949
+ const correctCount2 = feedback.filter((f) => f === "correct").length;
8950
+ for (let ci = 0; ci < correctCount2; ci++) {
8951
+ setTimeout(() => {
8952
+ emit("UI:PLAY_SOUND", { key: "correctSlot" });
8953
+ }, 300 + ci * 150);
8954
+ }
8955
+ }
8956
+ } else {
8957
+ setCurrentStep(step);
8958
+ timerRef.current = setTimeout(advance, stepDurationMs);
8959
+ }
8960
+ };
8961
+ timerRef.current = setTimeout(advance, stepDurationMs);
8962
+ }, [canPlay, slots, entity.maxSlots, entity.solutions, stepDurationMs, playEvent, completeEvent, emit]);
8963
+ const machine = {
8964
+ name: entity.title,
8965
+ description: entity.description,
8966
+ states: slots.map((s, i) => stepLabel(s, i)),
8967
+ currentState: currentStep >= 0 ? stepLabel(slots[currentStep], currentStep) : "__idle__",
8968
+ transitions: slots.slice(0, -1).map((s, i) => ({
8969
+ from: stepLabel(s, i),
8970
+ to: stepLabel(slots[i + 1], i + 1),
8971
+ event: "NEXT"
8972
+ }))
8763
8973
  };
8764
- if (!isOpen) return null;
8765
- return /* @__PURE__ */ jsx(
8766
- Drawer,
8974
+ const usedIds = entity.allowDuplicates === false ? slots.filter(Boolean).map((s) => s?.id || "") : [];
8975
+ const showHint = attempts >= 3 && !!entity.hint;
8976
+ const hasFeedback = slotFeedback.some((f) => f !== null);
8977
+ const correctCount = slotFeedback.filter((f) => f === "correct").length;
8978
+ const encourageKey = ENCOURAGEMENT_KEYS[Math.min(attempts - 1, ENCOURAGEMENT_KEYS.length - 1)] ?? ENCOURAGEMENT_KEYS[0];
8979
+ return /* @__PURE__ */ jsxs(
8980
+ VStack,
8767
8981
  {
8768
- isOpen,
8769
- onClose: handleClose,
8770
- title,
8771
- position,
8772
- width: size,
8773
- className,
8774
- children
8982
+ className: cn("p-4 gap-6", className),
8983
+ style: {
8984
+ backgroundImage: entity.theme?.background ? `url(${entity.theme.background})` : void 0,
8985
+ backgroundSize: "cover",
8986
+ backgroundPosition: "center"
8987
+ },
8988
+ children: [
8989
+ entity.headerImage && !headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 overflow-hidden rounded-lg", children: /* @__PURE__ */ jsx("img", { src: entity.headerImage, alt: "", onError: () => setHeaderError(true), className: "w-full h-full object-cover" }) }) : entity.headerImage && headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 rounded-lg bg-gradient-to-br from-[var(--color-muted)] to-[var(--color-accent)] opacity-60" }) : null,
8990
+ /* @__PURE__ */ jsxs(VStack, { gap: "xs", children: [
8991
+ /* @__PURE__ */ jsx(Typography, { variant: "h4", className: "text-foreground", children: entity.title }),
8992
+ /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-muted-foreground", children: entity.description })
8993
+ ] }),
8994
+ showHint && /* @__PURE__ */ jsx(Box, { className: "p-3 rounded-lg bg-accent/10 border border-accent/30", children: /* @__PURE__ */ jsxs(HStack, { className: "items-start", gap: "xs", children: [
8995
+ /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-accent font-bold shrink-0", children: "\u{1F4A1} " + t("game.hint") + ":" }),
8996
+ /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-foreground", children: entity.hint })
8997
+ ] }) }),
8998
+ filledSlots.length > 0 && /* @__PURE__ */ jsx(TraitStateViewer, { trait: machine, variant: "linear", size: "md" }),
8999
+ /* @__PURE__ */ jsxs(VStack, { gap: "xs", children: [
9000
+ /* @__PURE__ */ jsxs(HStack, { className: "items-center justify-between", children: [
9001
+ /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-muted-foreground font-medium", children: t("sequencer.yourSequence") + ":" }),
9002
+ hasFeedback && playState === "idle" && /* @__PURE__ */ jsxs(Typography, { variant: "caption", className: "text-muted-foreground", children: [
9003
+ `${correctCount}/${entity.maxSlots} `,
9004
+ "\u2705"
9005
+ ] })
9006
+ ] }),
9007
+ /* @__PURE__ */ jsx(
9008
+ SequenceBar,
9009
+ {
9010
+ slots,
9011
+ maxSlots: entity.maxSlots,
9012
+ onSlotDrop: handleSlotDrop,
9013
+ onSlotRemove: handleSlotRemove,
9014
+ playing: playState === "playing",
9015
+ currentStep,
9016
+ categoryColors,
9017
+ slotFeedback,
9018
+ size: "lg"
9019
+ }
9020
+ )
9021
+ ] }),
9022
+ playState !== "playing" && /* @__PURE__ */ jsx(
9023
+ ActionPalette,
9024
+ {
9025
+ actions: entity.availableActions,
9026
+ usedActionIds: usedIds,
9027
+ allowDuplicates: entity.allowDuplicates !== false,
9028
+ categoryColors,
9029
+ label: t("sequencer.dragActions")
9030
+ }
9031
+ ),
9032
+ hasFeedback && playState === "idle" && attempts > 0 && /* @__PURE__ */ jsx(Box, { className: "p-3 rounded-lg bg-warning/10 border border-warning/30 text-center", children: /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-foreground", children: t(encourageKey) }) }),
9033
+ playState === "success" && /* @__PURE__ */ jsx(Box, { className: "p-4 rounded-lg bg-success/20 border border-success text-center", children: /* @__PURE__ */ jsx(Typography, { variant: "h5", className: "text-success", children: entity.successMessage || t("sequencer.levelComplete") }) }),
9034
+ /* @__PURE__ */ jsxs(HStack, { gap: "sm", children: [
9035
+ /* @__PURE__ */ jsx(
9036
+ Button,
9037
+ {
9038
+ variant: "primary",
9039
+ onClick: handlePlay,
9040
+ disabled: !canPlay,
9041
+ children: "\u25B6 " + t("game.play")
9042
+ }
9043
+ ),
9044
+ /* @__PURE__ */ jsx(Button, { variant: "ghost", onClick: handleReset, children: "\u21BA " + t("game.reset") })
9045
+ ] })
9046
+ ]
8775
9047
  }
8776
9048
  );
9049
+ }
9050
+ SequencerBoard.displayName = "SequencerBoard";
9051
+ function RuleEditor({
9052
+ rule,
9053
+ availableEvents,
9054
+ availableActions,
9055
+ onChange,
9056
+ onRemove,
9057
+ disabled = false,
9058
+ className
9059
+ }) {
9060
+ const { t } = useTranslate();
9061
+ const handleWhenChange = useCallback((e) => {
9062
+ onChange({ ...rule, whenEvent: e.target.value });
9063
+ }, [rule, onChange]);
9064
+ const handleThenChange = useCallback((e) => {
9065
+ onChange({ ...rule, thenAction: e.target.value });
9066
+ }, [rule, onChange]);
9067
+ return /* @__PURE__ */ jsxs(HStack, { className: cn("items-center p-2 rounded-lg bg-muted/50 border border-border", className), gap: "sm", children: [
9068
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-primary font-bold whitespace-nowrap", children: t("eventHandler.when") }),
9069
+ /* @__PURE__ */ jsx(
9070
+ Select,
9071
+ {
9072
+ value: rule.whenEvent,
9073
+ onChange: handleWhenChange,
9074
+ options: availableEvents,
9075
+ disabled,
9076
+ className: "flex-1 min-w-0"
9077
+ }
9078
+ ),
9079
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-accent font-bold whitespace-nowrap", children: "\u2192 " + t("eventHandler.then") }),
9080
+ /* @__PURE__ */ jsx(
9081
+ Select,
9082
+ {
9083
+ value: rule.thenAction,
9084
+ onChange: handleThenChange,
9085
+ options: availableActions,
9086
+ disabled,
9087
+ className: "flex-1 min-w-0"
9088
+ }
9089
+ ),
9090
+ onRemove && /* @__PURE__ */ jsx(Button, { variant: "ghost", onClick: onRemove, disabled, className: "shrink-0", children: "\xD7" })
9091
+ ] });
9092
+ }
9093
+ RuleEditor.displayName = "RuleEditor";
9094
+ var STATUS_STYLES2 = {
9095
+ pending: "text-muted-foreground",
9096
+ active: "text-primary animate-pulse",
9097
+ done: "text-success",
9098
+ error: "text-error"
8777
9099
  };
8778
- DrawerSlot.displayName = "DrawerSlot";
8779
- function extractToastProps(children) {
8780
- if (!React25__default.isValidElement(children)) {
8781
- if (typeof children === "string") {
8782
- return { message: children };
9100
+ var STATUS_DOTS = {
9101
+ pending: "\u25CB",
9102
+ active: "\u25CF",
9103
+ done: "\u2714",
9104
+ error: "\u2717"
9105
+ };
9106
+ function EventLog({
9107
+ entries,
9108
+ maxHeight = 200,
9109
+ label,
9110
+ className
9111
+ }) {
9112
+ const { t } = useTranslate();
9113
+ const scrollRef = useRef(null);
9114
+ useEffect(() => {
9115
+ if (scrollRef.current) {
9116
+ scrollRef.current.scrollTop = scrollRef.current.scrollHeight;
8783
9117
  }
8784
- return {};
8785
- }
8786
- const props = children.props;
8787
- return {
8788
- message: typeof props.message === "string" ? props.message : void 0,
8789
- variant: props.variant,
8790
- title: typeof props.title === "string" ? props.title : void 0
8791
- };
9118
+ }, [entries.length]);
9119
+ return /* @__PURE__ */ jsxs(VStack, { className: cn("p-3 rounded-lg bg-card border border-border", className), gap: "sm", children: [
9120
+ /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-muted-foreground font-medium", children: label ?? t("eventHandler.eventLog") }),
9121
+ /* @__PURE__ */ jsx(
9122
+ Box,
9123
+ {
9124
+ ref: scrollRef,
9125
+ className: "overflow-y-auto",
9126
+ style: { maxHeight },
9127
+ children: /* @__PURE__ */ jsxs(VStack, { gap: "xs", children: [
9128
+ entries.length === 0 && /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-muted-foreground italic", children: t("eventHandler.noEvents") }),
9129
+ entries.map((entry) => /* @__PURE__ */ jsxs(HStack, { className: "items-start", gap: "xs", children: [
9130
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", className: STATUS_STYLES2[entry.status], children: STATUS_DOTS[entry.status] }),
9131
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-foreground", children: entry.icon }),
9132
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", className: cn("flex-1", STATUS_STYLES2[entry.status]), children: entry.message })
9133
+ ] }, entry.id))
9134
+ ] })
9135
+ }
9136
+ )
9137
+ ] });
8792
9138
  }
8793
- var ToastSlot = ({
8794
- children,
8795
- variant: overrideVariant,
8796
- title: overrideTitle,
8797
- duration = 5e3,
9139
+ EventLog.displayName = "EventLog";
9140
+ var nextRuleId = 1;
9141
+ function ObjectRulePanel({
9142
+ object,
9143
+ onRulesChange,
9144
+ disabled = false,
8798
9145
  className
8799
- }) => {
8800
- const eventBus = useEventBus();
8801
- const isVisible = Boolean(children);
8802
- const extracted = extractToastProps(children);
8803
- const variant = overrideVariant || extracted.variant || "info";
8804
- const title = overrideTitle || extracted.title;
8805
- const message = extracted.message || (typeof children === "string" ? children : "");
8806
- const handleDismiss = () => {
8807
- eventBus.emit("UI:DISMISS");
8808
- eventBus.emit("UI:CLOSE");
9146
+ }) {
9147
+ const { t } = useTranslate();
9148
+ const maxRules = object.maxRules || 3;
9149
+ const canAdd = object.rules.length < maxRules;
9150
+ const handleRuleChange = useCallback((index, updatedRule) => {
9151
+ const newRules = [...object.rules];
9152
+ newRules[index] = updatedRule;
9153
+ onRulesChange(object.id, newRules);
9154
+ }, [object.id, object.rules, onRulesChange]);
9155
+ const handleRuleRemove = useCallback((index) => {
9156
+ const newRules = object.rules.filter((_, i) => i !== index);
9157
+ onRulesChange(object.id, newRules);
9158
+ }, [object.id, object.rules, onRulesChange]);
9159
+ const handleAddRule = useCallback(() => {
9160
+ if (!canAdd || disabled) return;
9161
+ const firstEvent = object.availableEvents[0]?.value || "";
9162
+ const firstAction = object.availableActions[0]?.value || "";
9163
+ const newRule = {
9164
+ id: `rule-${nextRuleId++}`,
9165
+ whenEvent: firstEvent,
9166
+ thenAction: firstAction
9167
+ };
9168
+ onRulesChange(object.id, [...object.rules, newRule]);
9169
+ }, [canAdd, disabled, object, onRulesChange]);
9170
+ const machine = {
9171
+ name: object.name,
9172
+ states: object.states,
9173
+ currentState: object.currentState,
9174
+ transitions: object.rules.map((r) => ({
9175
+ from: object.currentState,
9176
+ to: object.states.find((s) => s !== object.currentState) || object.currentState,
9177
+ event: r.whenEvent
9178
+ }))
8809
9179
  };
8810
- if (!isVisible) return null;
8811
- const isCustomContent = React25__default.isValidElement(children) && !message;
8812
- return /* @__PURE__ */ jsx(Box, { className: "fixed bottom-4 right-4 z-50", children: isCustomContent ? children : /* @__PURE__ */ jsx(
8813
- Toast,
8814
- {
8815
- variant,
8816
- title,
8817
- message: message || "Notification",
8818
- duration,
8819
- onDismiss: handleDismiss,
8820
- className
8821
- }
8822
- ) });
8823
- };
8824
- ToastSlot.displayName = "ToastSlot";
8825
- var CHART_COLORS = [
8826
- "var(--color-primary)",
8827
- "var(--color-success)",
8828
- "var(--color-warning)",
9180
+ return /* @__PURE__ */ jsxs(VStack, { className: cn("p-4 rounded-lg bg-card border border-border", className), gap: "sm", children: [
9181
+ /* @__PURE__ */ jsxs(HStack, { className: "items-center", gap: "sm", children: [
9182
+ /* @__PURE__ */ jsx(Typography, { variant: "h5", children: object.icon }),
9183
+ /* @__PURE__ */ jsxs(VStack, { gap: "none", children: [
9184
+ /* @__PURE__ */ jsx(Typography, { variant: "body1", className: "text-foreground font-bold", children: object.name }),
9185
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-muted-foreground", children: t("eventHandler.state") + ": " + object.currentState })
9186
+ ] })
9187
+ ] }),
9188
+ /* @__PURE__ */ jsx(TraitStateViewer, { trait: machine, variant: "compact", size: "sm" }),
9189
+ /* @__PURE__ */ jsxs(VStack, { gap: "xs", children: [
9190
+ /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-muted-foreground font-medium", children: t("eventHandler.rules", { count: object.rules.length, max: maxRules }) + ":" }),
9191
+ object.rules.map((rule, i) => /* @__PURE__ */ jsx(
9192
+ RuleEditor,
9193
+ {
9194
+ rule,
9195
+ availableEvents: object.availableEvents,
9196
+ availableActions: object.availableActions,
9197
+ onChange: (r) => handleRuleChange(i, r),
9198
+ onRemove: () => handleRuleRemove(i),
9199
+ disabled
9200
+ },
9201
+ rule.id
9202
+ )),
9203
+ canAdd && !disabled && /* @__PURE__ */ jsx(Button, { variant: "ghost", onClick: handleAddRule, className: "self-start", children: t("eventHandler.addRule") })
9204
+ ] })
9205
+ ] });
9206
+ }
9207
+ ObjectRulePanel.displayName = "ObjectRulePanel";
9208
+ var ENCOURAGEMENT_KEYS2 = [
9209
+ "puzzle.tryAgain1",
9210
+ "puzzle.tryAgain2",
9211
+ "puzzle.tryAgain3"
9212
+ ];
9213
+ function EventHandlerBoard({
9214
+ entity,
9215
+ stepDurationMs = 800,
9216
+ playEvent,
9217
+ completeEvent,
9218
+ className
9219
+ }) {
9220
+ const { emit } = useEventBus();
9221
+ const { t } = useTranslate();
9222
+ const [objects, setObjects] = useState(entity.objects);
9223
+ const [selectedObjectId, setSelectedObjectId] = useState(
9224
+ entity.objects[0]?.id || null
9225
+ );
9226
+ const [headerError, setHeaderError] = useState(false);
9227
+ const [playState, setPlayState] = useState("editing");
9228
+ const [eventLog, setEventLog] = useState([]);
9229
+ const [attempts, setAttempts] = useState(0);
9230
+ const timerRef = useRef(null);
9231
+ const logIdCounter = useRef(0);
9232
+ useEffect(() => () => {
9233
+ if (timerRef.current) clearTimeout(timerRef.current);
9234
+ }, []);
9235
+ const selectedObject = objects.find((o) => o.id === selectedObjectId) || null;
9236
+ const handleRulesChange = useCallback((objectId, rules) => {
9237
+ setObjects((prev) => prev.map(
9238
+ (o) => o.id === objectId ? { ...o, rules } : o
9239
+ ));
9240
+ }, []);
9241
+ const addLogEntry = useCallback((icon, message, status = "done") => {
9242
+ const id = `log-${logIdCounter.current++}`;
9243
+ setEventLog((prev) => [...prev, { id, timestamp: Date.now(), icon, message, status }]);
9244
+ }, []);
9245
+ const handlePlay = useCallback(() => {
9246
+ if (playState !== "editing") return;
9247
+ if (playEvent) emit(`UI:${playEvent}`, {});
9248
+ setPlayState("playing");
9249
+ setEventLog([]);
9250
+ const allRules = [];
9251
+ objects.forEach((obj) => {
9252
+ obj.rules.forEach((rule) => {
9253
+ allRules.push({ object: obj, rule });
9254
+ });
9255
+ });
9256
+ const triggers = entity.triggerEvents || [];
9257
+ const eventQueue = [...triggers];
9258
+ const firedEvents = /* @__PURE__ */ new Set();
9259
+ let stepIdx = 0;
9260
+ let goalReached = false;
9261
+ const processNext = () => {
9262
+ if (eventQueue.length === 0 || stepIdx > 20) {
9263
+ if (goalReached) {
9264
+ setPlayState("success");
9265
+ if (completeEvent) {
9266
+ emit(`UI:${completeEvent}`, { success: true });
9267
+ }
9268
+ } else {
9269
+ setAttempts((prev) => prev + 1);
9270
+ setPlayState("fail");
9271
+ }
9272
+ return;
9273
+ }
9274
+ const currentEvent = eventQueue.shift();
9275
+ if (firedEvents.has(currentEvent)) {
9276
+ timerRef.current = setTimeout(processNext, 100);
9277
+ return;
9278
+ }
9279
+ firedEvents.add(currentEvent);
9280
+ const matching = allRules.filter((r) => r.rule.whenEvent === currentEvent);
9281
+ if (matching.length === 0) {
9282
+ addLogEntry("\u26A1", t("eventHandler.noListeners", { event: currentEvent }), "done");
9283
+ } else {
9284
+ matching.forEach(({ object, rule }) => {
9285
+ addLogEntry(object.icon, t("eventHandler.heardEvent", { object: object.name, event: currentEvent, action: rule.thenAction }), "done");
9286
+ eventQueue.push(rule.thenAction);
9287
+ if (rule.thenAction === entity.goalEvent) {
9288
+ goalReached = true;
9289
+ }
9290
+ });
9291
+ }
9292
+ if (currentEvent === entity.goalEvent) {
9293
+ goalReached = true;
9294
+ }
9295
+ stepIdx++;
9296
+ timerRef.current = setTimeout(processNext, stepDurationMs);
9297
+ };
9298
+ if (triggers.length > 0) {
9299
+ addLogEntry("\u{1F3AC}", t("eventHandler.simulationStarted", { events: triggers.join(", ") }), "active");
9300
+ }
9301
+ timerRef.current = setTimeout(processNext, stepDurationMs);
9302
+ }, [playState, objects, entity, stepDurationMs, playEvent, completeEvent, emit, addLogEntry, t]);
9303
+ const handleTryAgain = useCallback(() => {
9304
+ if (timerRef.current) clearTimeout(timerRef.current);
9305
+ setPlayState("editing");
9306
+ setEventLog([]);
9307
+ }, []);
9308
+ const handleReset = useCallback(() => {
9309
+ if (timerRef.current) clearTimeout(timerRef.current);
9310
+ setObjects(entity.objects);
9311
+ setPlayState("editing");
9312
+ setEventLog([]);
9313
+ setSelectedObjectId(entity.objects[0]?.id || null);
9314
+ setAttempts(0);
9315
+ }, [entity.objects]);
9316
+ const objectViewers = objects.map((obj) => {
9317
+ const machine = {
9318
+ name: obj.name,
9319
+ states: obj.states,
9320
+ currentState: obj.currentState,
9321
+ transitions: obj.rules.map((r) => ({
9322
+ from: obj.currentState,
9323
+ to: obj.states.find((s) => s !== obj.currentState) || obj.currentState,
9324
+ event: r.whenEvent
9325
+ }))
9326
+ };
9327
+ return { obj, machine };
9328
+ });
9329
+ const showHint = attempts >= 3 && entity.hint;
9330
+ const encourageKey = ENCOURAGEMENT_KEYS2[Math.min(attempts - 1, ENCOURAGEMENT_KEYS2.length - 1)] ?? ENCOURAGEMENT_KEYS2[0];
9331
+ return /* @__PURE__ */ jsxs(
9332
+ VStack,
9333
+ {
9334
+ className: cn("p-4 gap-6", className),
9335
+ style: {
9336
+ backgroundImage: entity.theme?.background ? `url(${entity.theme.background})` : void 0,
9337
+ backgroundSize: "cover",
9338
+ backgroundPosition: "center"
9339
+ },
9340
+ children: [
9341
+ entity.headerImage && !headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 overflow-hidden rounded-lg", children: /* @__PURE__ */ jsx("img", { src: entity.headerImage, alt: "", onError: () => setHeaderError(true), className: "w-full h-full object-cover" }) }) : entity.headerImage && headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 rounded-lg bg-gradient-to-br from-[var(--color-muted)] to-[var(--color-accent)] opacity-60" }) : null,
9342
+ /* @__PURE__ */ jsxs(VStack, { gap: "xs", children: [
9343
+ /* @__PURE__ */ jsx(Typography, { variant: "h4", className: "text-foreground", children: entity.title }),
9344
+ /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-muted-foreground", children: entity.description }),
9345
+ /* @__PURE__ */ jsxs(HStack, { className: "items-center p-2 rounded bg-primary/10 border border-primary/30", gap: "xs", children: [
9346
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-primary font-bold", children: t("game.goal") + ":" }),
9347
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-foreground", children: entity.goalCondition })
9348
+ ] })
9349
+ ] }),
9350
+ /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
9351
+ /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-muted-foreground font-medium", children: t("eventHandler.clickObject") + ":" }),
9352
+ /* @__PURE__ */ jsx(HStack, { className: "flex-wrap", gap: "sm", children: objectViewers.map(({ obj, machine }) => /* @__PURE__ */ jsx(
9353
+ Box,
9354
+ {
9355
+ className: cn(
9356
+ "p-3 rounded-lg border-2 cursor-pointer transition-all hover:scale-105",
9357
+ selectedObjectId === obj.id ? "border-primary bg-primary/10" : "border-border bg-card hover:border-muted-foreground"
9358
+ ),
9359
+ onClick: () => setSelectedObjectId(obj.id),
9360
+ children: /* @__PURE__ */ jsxs(VStack, { gap: "xs", className: "items-center min-w-[120px]", children: [
9361
+ /* @__PURE__ */ jsx(Typography, { variant: "h5", children: obj.icon }),
9362
+ /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-foreground font-medium", children: obj.name }),
9363
+ /* @__PURE__ */ jsx(TraitStateViewer, { trait: machine, variant: "compact", size: "sm" })
9364
+ ] })
9365
+ },
9366
+ obj.id
9367
+ )) })
9368
+ ] }),
9369
+ selectedObject && /* @__PURE__ */ jsx(
9370
+ ObjectRulePanel,
9371
+ {
9372
+ object: selectedObject,
9373
+ onRulesChange: handleRulesChange,
9374
+ disabled: playState !== "editing"
9375
+ }
9376
+ ),
9377
+ eventLog.length > 0 && /* @__PURE__ */ jsx(EventLog, { entries: eventLog }),
9378
+ playState === "success" && /* @__PURE__ */ jsx(Box, { className: "p-4 rounded-lg bg-success/20 border border-success text-center", children: /* @__PURE__ */ jsx(Typography, { variant: "h5", className: "text-success", children: entity.successMessage || t("eventHandler.chainComplete") }) }),
9379
+ playState === "fail" && /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
9380
+ /* @__PURE__ */ jsx(Box, { className: "p-4 rounded-lg bg-warning/10 border border-warning/30 text-center", children: /* @__PURE__ */ jsx(Typography, { variant: "body1", className: "text-foreground font-medium", children: t(encourageKey) }) }),
9381
+ showHint && /* @__PURE__ */ jsx(Box, { className: "p-3 rounded-lg bg-accent/10 border border-accent/30", children: /* @__PURE__ */ jsxs(HStack, { className: "items-start", gap: "xs", children: [
9382
+ /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-accent font-bold shrink-0", children: "\u{1F4A1} " + t("game.hint") + ":" }),
9383
+ /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-foreground", children: entity.hint })
9384
+ ] }) })
9385
+ ] }),
9386
+ /* @__PURE__ */ jsxs(HStack, { gap: "sm", children: [
9387
+ playState === "fail" ? /* @__PURE__ */ jsx(Button, { variant: "primary", onClick: handleTryAgain, children: "\u{1F504} " + t("puzzle.tryAgainButton") }) : /* @__PURE__ */ jsx(
9388
+ Button,
9389
+ {
9390
+ variant: "primary",
9391
+ onClick: handlePlay,
9392
+ disabled: playState !== "editing",
9393
+ children: "\u25B6 " + t("game.play")
9394
+ }
9395
+ ),
9396
+ /* @__PURE__ */ jsx(Button, { variant: "ghost", onClick: handleReset, children: "\u21BA " + t("game.reset") })
9397
+ ] })
9398
+ ]
9399
+ }
9400
+ );
9401
+ }
9402
+ EventHandlerBoard.displayName = "EventHandlerBoard";
9403
+ function StateNode2({
9404
+ name,
9405
+ isCurrent = false,
9406
+ isSelected = false,
9407
+ isInitial = false,
9408
+ position,
9409
+ onClick,
9410
+ className
9411
+ }) {
9412
+ return /* @__PURE__ */ jsx(
9413
+ Box,
9414
+ {
9415
+ position: "absolute",
9416
+ display: "flex",
9417
+ className: cn(
9418
+ "items-center justify-center rounded-full border-3 transition-all cursor-pointer select-none",
9419
+ "min-w-[80px] h-[80px] px-3",
9420
+ isCurrent && "bg-primary/20 border-primary shadow-lg shadow-primary/30 scale-110",
9421
+ isSelected && !isCurrent && "bg-accent/20 border-accent ring-2 ring-accent/50",
9422
+ !isCurrent && !isSelected && "bg-card border-border hover:border-muted-foreground hover:scale-105",
9423
+ className
9424
+ ),
9425
+ style: {
9426
+ left: position.x,
9427
+ top: position.y,
9428
+ transform: "translate(-50%, -50%)"
9429
+ },
9430
+ onClick,
9431
+ children: /* @__PURE__ */ jsxs(Box, { className: "text-center", children: [
9432
+ isInitial && /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-muted-foreground text-xs block", children: "\u25B6 start" }),
9433
+ /* @__PURE__ */ jsx(
9434
+ Typography,
9435
+ {
9436
+ variant: "body2",
9437
+ className: cn(
9438
+ "font-bold whitespace-nowrap",
9439
+ isCurrent ? "text-primary" : "text-foreground"
9440
+ ),
9441
+ children: name
9442
+ }
9443
+ )
9444
+ ] })
9445
+ }
9446
+ );
9447
+ }
9448
+ StateNode2.displayName = "StateNode";
9449
+ var NODE_RADIUS = 40;
9450
+ function TransitionArrow({
9451
+ from,
9452
+ to,
9453
+ eventLabel,
9454
+ guardHint,
9455
+ isActive = false,
9456
+ onClick,
9457
+ className
9458
+ }) {
9459
+ const dx = to.x - from.x;
9460
+ const dy = to.y - from.y;
9461
+ const dist = Math.sqrt(dx * dx + dy * dy);
9462
+ if (dist === 0) return /* @__PURE__ */ jsx(Fragment, {});
9463
+ const nx = dx / dist;
9464
+ const ny = dy / dist;
9465
+ const startX = from.x + nx * NODE_RADIUS;
9466
+ const startY = from.y + ny * NODE_RADIUS;
9467
+ const endX = to.x - nx * NODE_RADIUS;
9468
+ const endY = to.y - ny * NODE_RADIUS;
9469
+ const midX = (startX + endX) / 2;
9470
+ const midY = (startY + endY) / 2;
9471
+ const perpX = -ny * 20;
9472
+ const perpY = nx * 20;
9473
+ const ctrlX = midX + perpX;
9474
+ const ctrlY = midY + perpY;
9475
+ const path = `M ${startX} ${startY} Q ${ctrlX} ${ctrlY} ${endX} ${endY}`;
9476
+ return /* @__PURE__ */ jsxs("g", { className: cn("cursor-pointer", className), onClick, children: [
9477
+ /* @__PURE__ */ jsx(
9478
+ "path",
9479
+ {
9480
+ d: path,
9481
+ fill: "none",
9482
+ stroke: isActive ? "var(--color-primary)" : "var(--color-border)",
9483
+ strokeWidth: isActive ? 3 : 2,
9484
+ markerEnd: isActive ? "url(#arrowhead-active)" : "url(#arrowhead)"
9485
+ }
9486
+ ),
9487
+ /* @__PURE__ */ jsx(
9488
+ "text",
9489
+ {
9490
+ x: ctrlX,
9491
+ y: ctrlY - 8,
9492
+ textAnchor: "middle",
9493
+ fill: isActive ? "var(--color-primary)" : "var(--color-foreground)",
9494
+ fontSize: 12,
9495
+ fontWeight: isActive ? "bold" : "normal",
9496
+ className: "select-none",
9497
+ children: eventLabel
9498
+ }
9499
+ ),
9500
+ guardHint && /* @__PURE__ */ jsx(
9501
+ "text",
9502
+ {
9503
+ x: ctrlX,
9504
+ y: ctrlY + 6,
9505
+ textAnchor: "middle",
9506
+ fill: "var(--color-warning)",
9507
+ fontSize: 10,
9508
+ className: "select-none",
9509
+ children: "\u26A0 " + guardHint
9510
+ }
9511
+ )
9512
+ ] });
9513
+ }
9514
+ TransitionArrow.displayName = "TransitionArrow";
9515
+ function VariablePanel({
9516
+ entityName,
9517
+ variables,
9518
+ className
9519
+ }) {
9520
+ const { t } = useTranslate();
9521
+ return /* @__PURE__ */ jsxs(VStack, { className: cn("p-3 rounded-lg bg-card border border-border", className), gap: "sm", children: [
9522
+ /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-muted-foreground font-medium", children: t("stateArchitect.variables", { name: entityName }) }),
9523
+ variables.map((v) => {
9524
+ const max = v.max ?? 100;
9525
+ const min = v.min ?? 0;
9526
+ const pct = Math.round((v.value - min) / (max - min) * 100);
9527
+ const isHigh = pct > 80;
9528
+ const isLow = pct < 20;
9529
+ return /* @__PURE__ */ jsxs(VStack, { gap: "none", children: [
9530
+ /* @__PURE__ */ jsxs(HStack, { className: "items-center justify-between", children: [
9531
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-foreground font-medium", children: v.name }),
9532
+ /* @__PURE__ */ jsxs(Typography, { variant: "caption", className: cn(
9533
+ isHigh ? "text-error" : isLow ? "text-warning" : "text-foreground"
9534
+ ), children: [
9535
+ v.value,
9536
+ v.unit || "",
9537
+ " / ",
9538
+ max,
9539
+ v.unit || ""
9540
+ ] })
9541
+ ] }),
9542
+ /* @__PURE__ */ jsx(
9543
+ ProgressBar,
9544
+ {
9545
+ value: pct,
9546
+ color: isHigh ? "danger" : isLow ? "warning" : "primary",
9547
+ size: "sm"
9548
+ }
9549
+ )
9550
+ ] }, v.name);
9551
+ })
9552
+ ] });
9553
+ }
9554
+ VariablePanel.displayName = "VariablePanel";
9555
+ function CodeView({
9556
+ data,
9557
+ label,
9558
+ defaultExpanded = false,
9559
+ className
9560
+ }) {
9561
+ const { t } = useTranslate();
9562
+ const [expanded, setExpanded] = useState(defaultExpanded);
9563
+ const jsonString = JSON.stringify(data, null, 2);
9564
+ return /* @__PURE__ */ jsxs(VStack, { className: cn("rounded-lg border border-border overflow-hidden", className), gap: "none", children: [
9565
+ /* @__PURE__ */ jsxs(HStack, { className: "items-center justify-between p-2 bg-muted", gap: "sm", children: [
9566
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-muted-foreground font-medium", children: label ?? t("stateArchitect.viewCode") }),
9567
+ /* @__PURE__ */ jsx(Button, { variant: "ghost", onClick: () => setExpanded(!expanded), className: "text-xs", children: expanded ? t("stateArchitect.hideJson") : t("stateArchitect.showJson") })
9568
+ ] }),
9569
+ expanded && /* @__PURE__ */ jsx(Box, { className: "p-3 bg-background overflow-x-auto", children: /* @__PURE__ */ jsx(
9570
+ Typography,
9571
+ {
9572
+ variant: "caption",
9573
+ className: "text-foreground font-mono whitespace-pre text-xs leading-relaxed block",
9574
+ children: jsonString
9575
+ }
9576
+ ) })
9577
+ ] });
9578
+ }
9579
+ CodeView.displayName = "CodeView";
9580
+ var ENCOURAGEMENT_KEYS3 = [
9581
+ "puzzle.tryAgain1",
9582
+ "puzzle.tryAgain2",
9583
+ "puzzle.tryAgain3"
9584
+ ];
9585
+ function layoutStates(states, width, height) {
9586
+ const cx = width / 2;
9587
+ const cy = height / 2;
9588
+ const radius = Math.min(cx, cy) - 60;
9589
+ const positions = {};
9590
+ states.forEach((state, i) => {
9591
+ const angle = 2 * Math.PI * i / states.length - Math.PI / 2;
9592
+ positions[state] = {
9593
+ x: cx + radius * Math.cos(angle),
9594
+ y: cy + radius * Math.sin(angle)
9595
+ };
9596
+ });
9597
+ return positions;
9598
+ }
9599
+ var nextTransId = 100;
9600
+ function StateArchitectBoard({
9601
+ entity,
9602
+ stepDurationMs = 600,
9603
+ testEvent,
9604
+ completeEvent,
9605
+ className
9606
+ }) {
9607
+ const { emit } = useEventBus();
9608
+ const { t } = useTranslate();
9609
+ const [transitions, setTransitions] = useState(entity.transitions);
9610
+ const [headerError, setHeaderError] = useState(false);
9611
+ const [playState, setPlayState] = useState("editing");
9612
+ const [currentState, setCurrentState] = useState(entity.initialState);
9613
+ const [selectedState, setSelectedState] = useState(null);
9614
+ const [testResults, setTestResults] = useState([]);
9615
+ const [variables, setVariables] = useState(entity.variables);
9616
+ const [attempts, setAttempts] = useState(0);
9617
+ const timerRef = useRef(null);
9618
+ const [addingFrom, setAddingFrom] = useState(null);
9619
+ useEffect(() => () => {
9620
+ if (timerRef.current) clearTimeout(timerRef.current);
9621
+ }, []);
9622
+ const GRAPH_W = 500;
9623
+ const GRAPH_H = 400;
9624
+ const positions = useMemo(() => layoutStates(entity.states, GRAPH_W, GRAPH_H), [entity.states]);
9625
+ const handleStateClick = useCallback((state) => {
9626
+ if (playState !== "editing") return;
9627
+ if (addingFrom) {
9628
+ if (addingFrom !== state) {
9629
+ const event = entity.availableEvents[0] || "EVENT";
9630
+ const newTrans = {
9631
+ id: `t-${nextTransId++}`,
9632
+ from: addingFrom,
9633
+ to: state,
9634
+ event
9635
+ };
9636
+ setTransitions((prev) => [...prev, newTrans]);
9637
+ }
9638
+ setAddingFrom(null);
9639
+ } else {
9640
+ setSelectedState(state);
9641
+ }
9642
+ }, [playState, addingFrom, entity.availableEvents]);
9643
+ const handleStartAddTransition = useCallback(() => {
9644
+ if (!selectedState) return;
9645
+ setAddingFrom(selectedState);
9646
+ }, [selectedState]);
9647
+ const handleRemoveTransition = useCallback((transId) => {
9648
+ setTransitions((prev) => prev.filter((t2) => t2.id !== transId));
9649
+ }, []);
9650
+ const machine = useMemo(() => ({
9651
+ name: entity.entityName,
9652
+ description: entity.description,
9653
+ states: entity.states,
9654
+ currentState,
9655
+ transitions: transitions.map((t2) => ({
9656
+ from: t2.from,
9657
+ to: t2.to,
9658
+ event: t2.event,
9659
+ guardHint: t2.guardHint
9660
+ }))
9661
+ }), [entity, currentState, transitions]);
9662
+ const handleTest = useCallback(() => {
9663
+ if (playState !== "editing") return;
9664
+ if (testEvent) emit(`UI:${testEvent}`, {});
9665
+ setPlayState("testing");
9666
+ setTestResults([]);
9667
+ const results = [];
9668
+ let testIdx = 0;
9669
+ const runNextTest = () => {
9670
+ if (testIdx >= entity.testCases.length) {
9671
+ const allPassed = results.every((r) => r.passed);
9672
+ setPlayState(allPassed ? "success" : "fail");
9673
+ setTestResults(results);
9674
+ if (allPassed && completeEvent) {
9675
+ emit(`UI:${completeEvent}`, {
9676
+ success: true,
9677
+ passedTests: results.filter((r) => r.passed).length
9678
+ });
9679
+ }
9680
+ if (!allPassed) {
9681
+ setAttempts((prev) => prev + 1);
9682
+ }
9683
+ return;
9684
+ }
9685
+ const testCase = entity.testCases[testIdx];
9686
+ let state = entity.initialState;
9687
+ for (const event of testCase.events) {
9688
+ const trans = transitions.find((t2) => t2.from === state && t2.event === event);
9689
+ if (trans) {
9690
+ state = trans.to;
9691
+ }
9692
+ }
9693
+ setCurrentState(state);
9694
+ results.push({
9695
+ label: testCase.label,
9696
+ passed: state === testCase.expectedState,
9697
+ actualState: state,
9698
+ expectedState: testCase.expectedState
9699
+ });
9700
+ testIdx++;
9701
+ timerRef.current = setTimeout(runNextTest, stepDurationMs);
9702
+ };
9703
+ timerRef.current = setTimeout(runNextTest, stepDurationMs);
9704
+ }, [playState, transitions, entity, stepDurationMs, testEvent, completeEvent, emit]);
9705
+ const handleTryAgain = useCallback(() => {
9706
+ if (timerRef.current) clearTimeout(timerRef.current);
9707
+ setPlayState("editing");
9708
+ setCurrentState(entity.initialState);
9709
+ setTestResults([]);
9710
+ }, [entity.initialState]);
9711
+ const handleReset = useCallback(() => {
9712
+ if (timerRef.current) clearTimeout(timerRef.current);
9713
+ setTransitions(entity.transitions);
9714
+ setPlayState("editing");
9715
+ setCurrentState(entity.initialState);
9716
+ setTestResults([]);
9717
+ setVariables(entity.variables);
9718
+ setSelectedState(null);
9719
+ setAddingFrom(null);
9720
+ setAttempts(0);
9721
+ }, [entity]);
9722
+ const codeData = useMemo(() => ({
9723
+ name: entity.entityName,
9724
+ states: entity.states,
9725
+ initialState: entity.initialState,
9726
+ transitions: transitions.map((t2) => ({
9727
+ from: t2.from,
9728
+ to: t2.to,
9729
+ event: t2.event,
9730
+ ...t2.guardHint ? { guard: t2.guardHint } : {}
9731
+ }))
9732
+ }), [entity, transitions]);
9733
+ return /* @__PURE__ */ jsxs(
9734
+ VStack,
9735
+ {
9736
+ className: cn("p-4 gap-6", className),
9737
+ style: {
9738
+ backgroundImage: entity.theme?.background ? `url(${entity.theme.background})` : void 0,
9739
+ backgroundSize: "cover",
9740
+ backgroundPosition: "center"
9741
+ },
9742
+ children: [
9743
+ entity.headerImage && !headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 overflow-hidden rounded-lg", children: /* @__PURE__ */ jsx("img", { src: entity.headerImage, alt: "", onError: () => setHeaderError(true), className: "w-full h-full object-cover" }) }) : entity.headerImage && headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 rounded-lg bg-gradient-to-br from-[var(--color-muted)] to-[var(--color-accent)] opacity-60" }) : null,
9744
+ /* @__PURE__ */ jsxs(VStack, { gap: "xs", children: [
9745
+ /* @__PURE__ */ jsx(Typography, { variant: "h4", className: "text-foreground", children: entity.title }),
9746
+ /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-muted-foreground", children: entity.description }),
9747
+ /* @__PURE__ */ jsxs(HStack, { className: "items-center p-2 rounded bg-warning/10 border border-warning/30", gap: "xs", children: [
9748
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-warning font-bold", children: t("game.hint") + ":" }),
9749
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-foreground", children: entity.hint })
9750
+ ] })
9751
+ ] }),
9752
+ /* @__PURE__ */ jsxs(HStack, { className: "flex-wrap items-start", gap: "lg", children: [
9753
+ /* @__PURE__ */ jsxs(VStack, { gap: "sm", className: "flex-1 min-w-[300px]", children: [
9754
+ /* @__PURE__ */ jsxs(HStack, { className: "items-center justify-between", children: [
9755
+ /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-muted-foreground font-medium", children: t("stateArchitect.graph") }),
9756
+ addingFrom && /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-accent animate-pulse", children: t("stateArchitect.clickTarget", { state: addingFrom || "" }) })
9757
+ ] }),
9758
+ /* @__PURE__ */ jsxs(
9759
+ Box,
9760
+ {
9761
+ position: "relative",
9762
+ className: "rounded-lg border border-border bg-background overflow-hidden",
9763
+ style: { width: GRAPH_W, height: GRAPH_H },
9764
+ children: [
9765
+ /* @__PURE__ */ jsxs(
9766
+ "svg",
9767
+ {
9768
+ width: GRAPH_W,
9769
+ height: GRAPH_H,
9770
+ className: "absolute inset-0",
9771
+ style: { pointerEvents: "none" },
9772
+ children: [
9773
+ /* @__PURE__ */ jsxs("defs", { children: [
9774
+ /* @__PURE__ */ jsx("marker", { id: "arrowhead", markerWidth: "10", markerHeight: "7", refX: "10", refY: "3.5", orient: "auto", children: /* @__PURE__ */ jsx("polygon", { points: "0 0, 10 3.5, 0 7", fill: "var(--color-border)" }) }),
9775
+ /* @__PURE__ */ jsx("marker", { id: "arrowhead-active", markerWidth: "10", markerHeight: "7", refX: "10", refY: "3.5", orient: "auto", children: /* @__PURE__ */ jsx("polygon", { points: "0 0, 10 3.5, 0 7", fill: "var(--color-primary)" }) })
9776
+ ] }),
9777
+ transitions.map((t2) => {
9778
+ const fromPos = positions[t2.from];
9779
+ const toPos = positions[t2.to];
9780
+ if (!fromPos || !toPos) return null;
9781
+ const isActive = t2.from === currentState;
9782
+ return /* @__PURE__ */ jsx(
9783
+ TransitionArrow,
9784
+ {
9785
+ from: fromPos,
9786
+ to: toPos,
9787
+ eventLabel: t2.event,
9788
+ guardHint: t2.guardHint,
9789
+ isActive
9790
+ },
9791
+ t2.id
9792
+ );
9793
+ })
9794
+ ]
9795
+ }
9796
+ ),
9797
+ entity.states.map((state) => /* @__PURE__ */ jsx(
9798
+ StateNode2,
9799
+ {
9800
+ name: state,
9801
+ position: positions[state],
9802
+ isCurrent: state === currentState,
9803
+ isSelected: state === selectedState,
9804
+ isInitial: state === entity.initialState,
9805
+ onClick: () => handleStateClick(state)
9806
+ },
9807
+ state
9808
+ ))
9809
+ ]
9810
+ }
9811
+ ),
9812
+ playState === "editing" && /* @__PURE__ */ jsx(HStack, { gap: "sm", children: /* @__PURE__ */ jsx(
9813
+ Button,
9814
+ {
9815
+ variant: "ghost",
9816
+ onClick: handleStartAddTransition,
9817
+ disabled: !selectedState,
9818
+ children: selectedState ? t("stateArchitect.addTransition", { state: selectedState }) : t("stateArchitect.addTransitionPrompt")
9819
+ }
9820
+ ) }),
9821
+ transitions.length > 0 && /* @__PURE__ */ jsxs(VStack, { gap: "xs", className: "p-3 rounded-lg bg-muted/50 border border-border", children: [
9822
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-muted-foreground font-medium", children: t("stateArchitect.transitions", { count: transitions.length }) + ":" }),
9823
+ transitions.map((t2) => /* @__PURE__ */ jsxs(HStack, { className: "items-center text-xs", gap: "xs", children: [
9824
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-foreground", children: t2.from }),
9825
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-muted-foreground", children: "\u2014[" }),
9826
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-accent font-medium", children: t2.event }),
9827
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-muted-foreground", children: "]\u2192" }),
9828
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-success", children: t2.to }),
9829
+ t2.guardHint && /* @__PURE__ */ jsxs(Typography, { variant: "caption", className: "text-warning", children: [
9830
+ "(",
9831
+ t2.guardHint,
9832
+ ")"
9833
+ ] }),
9834
+ playState === "editing" && /* @__PURE__ */ jsx(
9835
+ Button,
9836
+ {
9837
+ variant: "ghost",
9838
+ onClick: () => handleRemoveTransition(t2.id),
9839
+ className: "text-xs ml-auto",
9840
+ children: "\xD7"
9841
+ }
9842
+ )
9843
+ ] }, t2.id))
9844
+ ] })
9845
+ ] }),
9846
+ /* @__PURE__ */ jsxs(VStack, { gap: "sm", className: "w-[280px] shrink-0", children: [
9847
+ /* @__PURE__ */ jsx(TraitStateViewer, { trait: machine, variant: "full", size: "sm" }),
9848
+ /* @__PURE__ */ jsx(
9849
+ VariablePanel,
9850
+ {
9851
+ entityName: entity.entityName,
9852
+ variables
9853
+ }
9854
+ ),
9855
+ testResults.length > 0 && /* @__PURE__ */ jsxs(VStack, { className: "p-3 rounded-lg bg-card border border-border", gap: "xs", children: [
9856
+ /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-muted-foreground font-medium", children: t("stateArchitect.testResults") + ":" }),
9857
+ testResults.map((r, i) => /* @__PURE__ */ jsxs(HStack, { className: "items-center text-xs", gap: "xs", children: [
9858
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", className: r.passed ? "text-success" : "text-error", children: r.passed ? "\u2714" : "\u2717" }),
9859
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-foreground flex-1", children: r.label }),
9860
+ !r.passed && /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-error", children: t("stateArchitect.gotState", { state: r.actualState }) })
9861
+ ] }, i))
9862
+ ] }),
9863
+ entity.showCodeView !== false && /* @__PURE__ */ jsx(CodeView, { data: codeData, label: "View Code" })
9864
+ ] })
9865
+ ] }),
9866
+ playState === "success" && /* @__PURE__ */ jsx(Box, { className: "p-4 rounded-lg bg-success/20 border border-success text-center", children: /* @__PURE__ */ jsx(Typography, { variant: "h5", className: "text-success", children: entity.successMessage || t("stateArchitect.allPassed") }) }),
9867
+ playState === "fail" && /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
9868
+ /* @__PURE__ */ jsx(Box, { className: "p-4 rounded-lg bg-warning/10 border border-warning/30 text-center", children: /* @__PURE__ */ jsx(Typography, { variant: "body1", className: "text-foreground font-medium", children: t(ENCOURAGEMENT_KEYS3[Math.min(attempts - 1, ENCOURAGEMENT_KEYS3.length - 1)] ?? ENCOURAGEMENT_KEYS3[0]) }) }),
9869
+ attempts >= 3 && entity.hint && /* @__PURE__ */ jsx(Box, { className: "p-3 rounded-lg bg-accent/10 border border-accent/30", children: /* @__PURE__ */ jsxs(HStack, { className: "items-start", gap: "xs", children: [
9870
+ /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-accent font-bold shrink-0", children: "\u{1F4A1} " + t("game.hint") + ":" }),
9871
+ /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-foreground", children: entity.hint })
9872
+ ] }) })
9873
+ ] }),
9874
+ /* @__PURE__ */ jsxs(HStack, { gap: "sm", children: [
9875
+ playState === "fail" ? /* @__PURE__ */ jsx(Button, { variant: "primary", onClick: handleTryAgain, children: "\u{1F504} " + t("puzzle.tryAgainButton") }) : /* @__PURE__ */ jsx(
9876
+ Button,
9877
+ {
9878
+ variant: "primary",
9879
+ onClick: handleTest,
9880
+ disabled: playState !== "editing",
9881
+ children: "\u25B6 " + t("game.runTests")
9882
+ }
9883
+ ),
9884
+ /* @__PURE__ */ jsx(Button, { variant: "ghost", onClick: handleReset, children: "\u21BA " + t("game.reset") })
9885
+ ] })
9886
+ ]
9887
+ }
9888
+ );
9889
+ }
9890
+ StateArchitectBoard.displayName = "StateArchitectBoard";
9891
+ function SimulatorBoard({
9892
+ entity,
9893
+ completeEvent = "PUZZLE_COMPLETE",
9894
+ className
9895
+ }) {
9896
+ const { emit } = useEventBus();
9897
+ const { t } = useTranslate();
9898
+ const [values, setValues] = useState(() => {
9899
+ const init = {};
9900
+ for (const p2 of entity.parameters) {
9901
+ init[p2.id] = p2.initial;
9902
+ }
9903
+ return init;
9904
+ });
9905
+ const [headerError, setHeaderError] = useState(false);
9906
+ const [submitted, setSubmitted] = useState(false);
9907
+ const [attempts, setAttempts] = useState(0);
9908
+ const [showHint, setShowHint] = useState(false);
9909
+ const computeOutput = useCallback((params) => {
9910
+ try {
9911
+ const fn = new Function("params", `return (${entity.computeExpression})`);
9912
+ return fn(params);
9913
+ } catch {
9914
+ return 0;
9915
+ }
9916
+ }, [entity.computeExpression]);
9917
+ const output = useMemo(() => computeOutput(values), [computeOutput, values]);
9918
+ const isCorrect = Math.abs(output - entity.targetValue) <= entity.targetTolerance;
9919
+ const handleParameterChange = (id, value) => {
9920
+ if (submitted) return;
9921
+ setValues((prev) => ({ ...prev, [id]: value }));
9922
+ };
9923
+ const handleSubmit = () => {
9924
+ setSubmitted(true);
9925
+ setAttempts((a) => a + 1);
9926
+ if (isCorrect) {
9927
+ emit(`UI:${completeEvent}`, { success: true, attempts: attempts + 1 });
9928
+ }
9929
+ };
9930
+ const handleReset = () => {
9931
+ setSubmitted(false);
9932
+ if (attempts >= 2 && entity.hint) {
9933
+ setShowHint(true);
9934
+ }
9935
+ };
9936
+ const handleFullReset = () => {
9937
+ const init = {};
9938
+ for (const p2 of entity.parameters) {
9939
+ init[p2.id] = p2.initial;
9940
+ }
9941
+ setValues(init);
9942
+ setSubmitted(false);
9943
+ setAttempts(0);
9944
+ setShowHint(false);
9945
+ };
9946
+ return /* @__PURE__ */ jsx(
9947
+ Box,
9948
+ {
9949
+ className,
9950
+ style: {
9951
+ backgroundImage: entity.theme?.background ? `url(${entity.theme.background})` : void 0,
9952
+ backgroundSize: "cover",
9953
+ backgroundPosition: "center"
9954
+ },
9955
+ children: /* @__PURE__ */ jsxs(VStack, { gap: "lg", className: "p-4", children: [
9956
+ entity.headerImage && !headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 overflow-hidden rounded-lg", children: /* @__PURE__ */ jsx("img", { src: entity.headerImage, alt: "", onError: () => setHeaderError(true), className: "w-full h-full object-cover" }) }) : entity.headerImage && headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 rounded-lg bg-gradient-to-br from-[var(--color-muted)] to-[var(--color-accent)] opacity-60" }) : null,
9957
+ /* @__PURE__ */ jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
9958
+ /* @__PURE__ */ jsx(Typography, { variant: "h4", weight: "bold", children: entity.title }),
9959
+ /* @__PURE__ */ jsx(Typography, { variant: "body", children: entity.description })
9960
+ ] }) }),
9961
+ /* @__PURE__ */ jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxs(VStack, { gap: "md", children: [
9962
+ /* @__PURE__ */ jsx(Typography, { variant: "small", weight: "bold", className: "uppercase tracking-wider text-[var(--color-muted-foreground)]", children: t("simulator.parameters") }),
9963
+ entity.parameters.map((param) => /* @__PURE__ */ jsxs(VStack, { gap: "xs", children: [
9964
+ /* @__PURE__ */ jsxs(HStack, { justify: "between", align: "center", children: [
9965
+ /* @__PURE__ */ jsx(Typography, { variant: "body", weight: "medium", children: param.label }),
9966
+ /* @__PURE__ */ jsxs(Badge, { size: "sm", children: [
9967
+ values[param.id],
9968
+ " ",
9969
+ param.unit
9970
+ ] })
9971
+ ] }),
9972
+ /* @__PURE__ */ jsx(
9973
+ "input",
9974
+ {
9975
+ type: "range",
9976
+ min: param.min,
9977
+ max: param.max,
9978
+ step: param.step,
9979
+ value: values[param.id],
9980
+ onChange: (e) => handleParameterChange(param.id, Number(e.target.value)),
9981
+ disabled: submitted,
9982
+ className: "w-full accent-[var(--color-foreground)]"
9983
+ }
9984
+ ),
9985
+ /* @__PURE__ */ jsxs(HStack, { justify: "between", children: [
9986
+ /* @__PURE__ */ jsxs(Typography, { variant: "caption", className: "text-[var(--color-muted-foreground)]", children: [
9987
+ param.min,
9988
+ " ",
9989
+ param.unit
9990
+ ] }),
9991
+ /* @__PURE__ */ jsxs(Typography, { variant: "caption", className: "text-[var(--color-muted-foreground)]", children: [
9992
+ param.max,
9993
+ " ",
9994
+ param.unit
9995
+ ] })
9996
+ ] })
9997
+ ] }, param.id))
9998
+ ] }) }),
9999
+ /* @__PURE__ */ jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", align: "center", children: [
10000
+ /* @__PURE__ */ jsx(Typography, { variant: "small", weight: "bold", className: "uppercase tracking-wider text-[var(--color-muted-foreground)]", children: entity.outputLabel }),
10001
+ /* @__PURE__ */ jsxs(Typography, { variant: "h3", weight: "bold", children: [
10002
+ output.toFixed(2),
10003
+ " ",
10004
+ entity.outputUnit
10005
+ ] }),
10006
+ submitted && /* @__PURE__ */ jsxs(HStack, { gap: "xs", align: "center", children: [
10007
+ /* @__PURE__ */ jsx(Icon, { icon: isCorrect ? CheckCircle : XCircle, size: "sm", className: isCorrect ? "text-green-600" : "text-red-600" }),
10008
+ /* @__PURE__ */ jsx(Typography, { variant: "body", className: isCorrect ? "text-green-600" : "text-red-600", children: isCorrect ? entity.successMessage ?? t("simulator.correct") : entity.failMessage ?? t("simulator.incorrect") })
10009
+ ] }),
10010
+ /* @__PURE__ */ jsxs(Typography, { variant: "caption", className: "text-[var(--color-muted-foreground)]", children: [
10011
+ t("simulator.target"),
10012
+ ": ",
10013
+ entity.targetValue,
10014
+ " ",
10015
+ entity.outputUnit,
10016
+ " (\xB1",
10017
+ entity.targetTolerance,
10018
+ ")"
10019
+ ] })
10020
+ ] }) }),
10021
+ showHint && entity.hint && /* @__PURE__ */ jsx(Card, { className: "p-4 border-l-4 border-l-yellow-500", children: /* @__PURE__ */ jsx(Typography, { variant: "body", children: entity.hint }) }),
10022
+ /* @__PURE__ */ jsxs(HStack, { gap: "sm", justify: "center", children: [
10023
+ !submitted ? /* @__PURE__ */ jsxs(Button, { variant: "primary", onClick: handleSubmit, children: [
10024
+ /* @__PURE__ */ jsx(Icon, { icon: Play, size: "sm" }),
10025
+ t("simulator.simulate")
10026
+ ] }) : !isCorrect ? /* @__PURE__ */ jsx(Button, { variant: "primary", onClick: handleReset, children: t("simulator.tryAgain") }) : null,
10027
+ /* @__PURE__ */ jsxs(Button, { variant: "secondary", onClick: handleFullReset, children: [
10028
+ /* @__PURE__ */ jsx(Icon, { icon: RotateCcw, size: "sm" }),
10029
+ t("simulator.reset")
10030
+ ] })
10031
+ ] })
10032
+ ] })
10033
+ }
10034
+ );
10035
+ }
10036
+ SimulatorBoard.displayName = "SimulatorBoard";
10037
+ function ClassifierBoard({
10038
+ entity,
10039
+ completeEvent = "PUZZLE_COMPLETE",
10040
+ className
10041
+ }) {
10042
+ const { emit } = useEventBus();
10043
+ const { t } = useTranslate();
10044
+ const [assignments, setAssignments] = useState({});
10045
+ const [headerError, setHeaderError] = useState(false);
10046
+ const [submitted, setSubmitted] = useState(false);
10047
+ const [attempts, setAttempts] = useState(0);
10048
+ const [showHint, setShowHint] = useState(false);
10049
+ const unassignedItems = entity.items.filter((item) => !assignments[item.id]);
10050
+ const allAssigned = Object.keys(assignments).length === entity.items.length;
10051
+ const results = submitted ? entity.items.map((item) => ({
10052
+ item,
10053
+ assigned: assignments[item.id],
10054
+ correct: assignments[item.id] === item.correctCategory
10055
+ })) : [];
10056
+ const allCorrect = results.length > 0 && results.every((r) => r.correct);
10057
+ const correctCount = results.filter((r) => r.correct).length;
10058
+ const handleAssign = (itemId, categoryId) => {
10059
+ if (submitted) return;
10060
+ setAssignments((prev) => ({ ...prev, [itemId]: categoryId }));
10061
+ };
10062
+ const handleUnassign = (itemId) => {
10063
+ if (submitted) return;
10064
+ setAssignments((prev) => {
10065
+ const next = { ...prev };
10066
+ delete next[itemId];
10067
+ return next;
10068
+ });
10069
+ };
10070
+ const handleSubmit = useCallback(() => {
10071
+ setSubmitted(true);
10072
+ setAttempts((a) => a + 1);
10073
+ const correct = entity.items.every((item) => assignments[item.id] === item.correctCategory);
10074
+ if (correct) {
10075
+ emit(`UI:${completeEvent}`, { success: true, attempts: attempts + 1 });
10076
+ }
10077
+ }, [entity.items, assignments, attempts, completeEvent, emit]);
10078
+ const handleReset = () => {
10079
+ setSubmitted(false);
10080
+ if (attempts >= 2 && entity.hint) {
10081
+ setShowHint(true);
10082
+ }
10083
+ };
10084
+ const handleFullReset = () => {
10085
+ setAssignments({});
10086
+ setSubmitted(false);
10087
+ setAttempts(0);
10088
+ setShowHint(false);
10089
+ };
10090
+ return /* @__PURE__ */ jsx(
10091
+ Box,
10092
+ {
10093
+ className,
10094
+ style: {
10095
+ backgroundImage: entity.theme?.background ? `url(${entity.theme.background})` : void 0,
10096
+ backgroundSize: "cover",
10097
+ backgroundPosition: "center"
10098
+ },
10099
+ children: /* @__PURE__ */ jsxs(VStack, { gap: "lg", className: "p-4", children: [
10100
+ entity.headerImage && !headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 overflow-hidden rounded-lg", children: /* @__PURE__ */ jsx("img", { src: entity.headerImage, alt: "", onError: () => setHeaderError(true), className: "w-full h-full object-cover" }) }) : entity.headerImage && headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 rounded-lg bg-gradient-to-br from-[var(--color-muted)] to-[var(--color-accent)] opacity-60" }) : null,
10101
+ /* @__PURE__ */ jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
10102
+ /* @__PURE__ */ jsx(Typography, { variant: "h4", weight: "bold", children: entity.title }),
10103
+ /* @__PURE__ */ jsx(Typography, { variant: "body", children: entity.description })
10104
+ ] }) }),
10105
+ unassignedItems.length > 0 && /* @__PURE__ */ jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
10106
+ /* @__PURE__ */ jsx(Typography, { variant: "small", weight: "bold", className: "uppercase tracking-wider text-[var(--color-muted-foreground)]", children: t("classifier.itemsToSort") }),
10107
+ /* @__PURE__ */ jsx(HStack, { gap: "sm", className: "flex-wrap", children: unassignedItems.map((item) => /* @__PURE__ */ jsx(Badge, { size: "md", className: "cursor-pointer", children: item.label }, item.id)) })
10108
+ ] }) }),
10109
+ /* @__PURE__ */ jsx(VStack, { gap: "md", children: entity.categories.map((cat) => {
10110
+ const catItems = entity.items.filter((item) => assignments[item.id] === cat.id);
10111
+ return /* @__PURE__ */ jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
10112
+ /* @__PURE__ */ jsxs(HStack, { justify: "between", align: "center", children: [
10113
+ /* @__PURE__ */ jsx(Typography, { variant: "body", weight: "bold", children: cat.label }),
10114
+ /* @__PURE__ */ jsx(Badge, { size: "sm", children: catItems.length })
10115
+ ] }),
10116
+ /* @__PURE__ */ jsx(HStack, { gap: "xs", className: "flex-wrap min-h-[2rem]", children: catItems.map((item) => {
10117
+ const result = results.find((r) => r.item.id === item.id);
10118
+ return /* @__PURE__ */ jsxs(
10119
+ Badge,
10120
+ {
10121
+ size: "sm",
10122
+ className: `cursor-pointer ${result ? result.correct ? "border-green-500 bg-green-50 dark:bg-green-950" : "border-red-500 bg-red-50 dark:bg-red-950" : ""}`,
10123
+ onClick: () => handleUnassign(item.id),
10124
+ children: [
10125
+ item.label,
10126
+ result && /* @__PURE__ */ jsx(Icon, { icon: result.correct ? CheckCircle : XCircle, size: "xs", className: result.correct ? "text-green-600" : "text-red-600" })
10127
+ ]
10128
+ },
10129
+ item.id
10130
+ );
10131
+ }) }),
10132
+ !submitted && unassignedItems.length > 0 && /* @__PURE__ */ jsx(HStack, { gap: "xs", className: "flex-wrap", children: unassignedItems.map((item) => /* @__PURE__ */ jsxs(
10133
+ Button,
10134
+ {
10135
+ size: "sm",
10136
+ variant: "ghost",
10137
+ onClick: () => handleAssign(item.id, cat.id),
10138
+ className: "text-xs opacity-50 hover:opacity-100",
10139
+ children: [
10140
+ "+ ",
10141
+ item.label
10142
+ ]
10143
+ },
10144
+ item.id
10145
+ )) })
10146
+ ] }) }, cat.id);
10147
+ }) }),
10148
+ submitted && /* @__PURE__ */ jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", align: "center", children: [
10149
+ /* @__PURE__ */ jsx(Icon, { icon: allCorrect ? CheckCircle : XCircle, size: "lg", className: allCorrect ? "text-green-600" : "text-red-600" }),
10150
+ /* @__PURE__ */ jsx(Typography, { variant: "body", weight: "bold", children: allCorrect ? entity.successMessage ?? t("classifier.allCorrect") : `${correctCount}/${entity.items.length} ${t("classifier.correct")}` }),
10151
+ !allCorrect && entity.failMessage && /* @__PURE__ */ jsx(Typography, { variant: "body", className: "text-[var(--color-muted-foreground)]", children: entity.failMessage })
10152
+ ] }) }),
10153
+ showHint && entity.hint && /* @__PURE__ */ jsx(Card, { className: "p-4 border-l-4 border-l-yellow-500", children: /* @__PURE__ */ jsx(Typography, { variant: "body", children: entity.hint }) }),
10154
+ /* @__PURE__ */ jsxs(HStack, { gap: "sm", justify: "center", children: [
10155
+ !submitted ? /* @__PURE__ */ jsxs(Button, { variant: "primary", onClick: handleSubmit, disabled: !allAssigned, children: [
10156
+ /* @__PURE__ */ jsx(Icon, { icon: Send, size: "sm" }),
10157
+ t("classifier.check")
10158
+ ] }) : !allCorrect ? /* @__PURE__ */ jsx(Button, { variant: "primary", onClick: handleReset, children: t("classifier.tryAgain") }) : null,
10159
+ /* @__PURE__ */ jsxs(Button, { variant: "secondary", onClick: handleFullReset, children: [
10160
+ /* @__PURE__ */ jsx(Icon, { icon: RotateCcw, size: "sm" }),
10161
+ t("classifier.reset")
10162
+ ] })
10163
+ ] })
10164
+ ] })
10165
+ }
10166
+ );
10167
+ }
10168
+ ClassifierBoard.displayName = "ClassifierBoard";
10169
+ function BuilderBoard({
10170
+ entity,
10171
+ completeEvent = "PUZZLE_COMPLETE",
10172
+ className
10173
+ }) {
10174
+ const { emit } = useEventBus();
10175
+ const { t } = useTranslate();
10176
+ const [placements, setPlacements] = useState({});
10177
+ const [headerError, setHeaderError] = useState(false);
10178
+ const [submitted, setSubmitted] = useState(false);
10179
+ const [attempts, setAttempts] = useState(0);
10180
+ const [showHint, setShowHint] = useState(false);
10181
+ const usedComponentIds = new Set(Object.values(placements));
10182
+ const availableComponents = entity.components.filter((c) => !usedComponentIds.has(c.id));
10183
+ const [selectedComponent, setSelectedComponent] = useState(null);
10184
+ const allPlaced = Object.keys(placements).length === entity.slots.length;
10185
+ const results = submitted ? entity.slots.map((slot) => ({
10186
+ slot,
10187
+ placed: placements[slot.id],
10188
+ correct: placements[slot.id] === slot.acceptsComponentId
10189
+ })) : [];
10190
+ const allCorrect = results.length > 0 && results.every((r) => r.correct);
10191
+ const handlePlaceComponent = (slotId) => {
10192
+ if (submitted || !selectedComponent) return;
10193
+ setPlacements((prev) => ({ ...prev, [slotId]: selectedComponent }));
10194
+ setSelectedComponent(null);
10195
+ };
10196
+ const handleRemoveFromSlot = (slotId) => {
10197
+ if (submitted) return;
10198
+ setPlacements((prev) => {
10199
+ const next = { ...prev };
10200
+ delete next[slotId];
10201
+ return next;
10202
+ });
10203
+ };
10204
+ const handleSubmit = useCallback(() => {
10205
+ setSubmitted(true);
10206
+ setAttempts((a) => a + 1);
10207
+ const correct = entity.slots.every((slot) => placements[slot.id] === slot.acceptsComponentId);
10208
+ if (correct) {
10209
+ emit(`UI:${completeEvent}`, { success: true, attempts: attempts + 1 });
10210
+ }
10211
+ }, [entity.slots, placements, attempts, completeEvent, emit]);
10212
+ const handleReset = () => {
10213
+ setSubmitted(false);
10214
+ if (attempts >= 2 && entity.hint) {
10215
+ setShowHint(true);
10216
+ }
10217
+ };
10218
+ const handleFullReset = () => {
10219
+ setPlacements({});
10220
+ setSubmitted(false);
10221
+ setSelectedComponent(null);
10222
+ setAttempts(0);
10223
+ setShowHint(false);
10224
+ };
10225
+ const getComponentById = (id) => entity.components.find((c) => c.id === id);
10226
+ return /* @__PURE__ */ jsx(
10227
+ Box,
10228
+ {
10229
+ className,
10230
+ style: {
10231
+ backgroundImage: entity.theme?.background ? `url(${entity.theme.background})` : void 0,
10232
+ backgroundSize: "cover",
10233
+ backgroundPosition: "center"
10234
+ },
10235
+ children: /* @__PURE__ */ jsxs(VStack, { gap: "lg", className: "p-4", children: [
10236
+ entity.headerImage && !headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 overflow-hidden rounded-lg", children: /* @__PURE__ */ jsx("img", { src: entity.headerImage, alt: "", onError: () => setHeaderError(true), className: "w-full h-full object-cover" }) }) : entity.headerImage && headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 rounded-lg bg-gradient-to-br from-[var(--color-muted)] to-[var(--color-accent)] opacity-60" }) : null,
10237
+ /* @__PURE__ */ jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
10238
+ /* @__PURE__ */ jsx(Typography, { variant: "h4", weight: "bold", children: entity.title }),
10239
+ /* @__PURE__ */ jsx(Typography, { variant: "body", children: entity.description })
10240
+ ] }) }),
10241
+ /* @__PURE__ */ jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
10242
+ /* @__PURE__ */ jsx(Typography, { variant: "small", weight: "bold", className: "uppercase tracking-wider text-[var(--color-muted-foreground)]", children: t("builder.components") }),
10243
+ /* @__PURE__ */ jsxs(HStack, { gap: "sm", className: "flex-wrap", children: [
10244
+ availableComponents.map((comp) => /* @__PURE__ */ jsxs(
10245
+ Button,
10246
+ {
10247
+ size: "sm",
10248
+ variant: selectedComponent === comp.id ? "primary" : "secondary",
10249
+ onClick: () => setSelectedComponent(selectedComponent === comp.id ? null : comp.id),
10250
+ disabled: submitted,
10251
+ children: [
10252
+ comp.iconUrl ? /* @__PURE__ */ jsx("img", { src: comp.iconUrl, alt: "", className: "w-5 h-5 object-contain inline-block mr-1" }) : comp.iconEmoji ? /* @__PURE__ */ jsxs(Fragment, { children: [
10253
+ comp.iconEmoji,
10254
+ " "
10255
+ ] }) : null,
10256
+ comp.label
10257
+ ]
10258
+ },
10259
+ comp.id
10260
+ )),
10261
+ availableComponents.length === 0 && !submitted && /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-[var(--color-muted-foreground)]", children: t("builder.allPlaced") })
10262
+ ] })
10263
+ ] }) }),
10264
+ /* @__PURE__ */ jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
10265
+ /* @__PURE__ */ jsx(Typography, { variant: "small", weight: "bold", className: "uppercase tracking-wider text-[var(--color-muted-foreground)]", children: t("builder.blueprint") }),
10266
+ /* @__PURE__ */ jsx(VStack, { gap: "sm", children: entity.slots.map((slot) => {
10267
+ const placedComp = placements[slot.id] ? getComponentById(placements[slot.id]) : null;
10268
+ const result = results.find((r) => r.slot.id === slot.id);
10269
+ return /* @__PURE__ */ jsxs(
10270
+ HStack,
10271
+ {
10272
+ gap: "sm",
10273
+ align: "center",
10274
+ className: `p-3 border-2 rounded ${result ? result.correct ? "border-green-500" : "border-red-500" : selectedComponent ? "border-dashed border-[var(--color-foreground)] cursor-pointer" : "border-[var(--color-border)]"}`,
10275
+ onClick: () => handlePlaceComponent(slot.id),
10276
+ children: [
10277
+ /* @__PURE__ */ jsxs(VStack, { gap: "none", className: "flex-1", children: [
10278
+ /* @__PURE__ */ jsx(Typography, { variant: "body", weight: "medium", children: slot.label }),
10279
+ slot.description && /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-[var(--color-muted-foreground)]", children: slot.description })
10280
+ ] }),
10281
+ placedComp ? /* @__PURE__ */ jsxs(HStack, { gap: "xs", align: "center", children: [
10282
+ /* @__PURE__ */ jsxs(Badge, { size: "sm", onClick: () => handleRemoveFromSlot(slot.id), children: [
10283
+ placedComp.iconUrl ? /* @__PURE__ */ jsx("img", { src: placedComp.iconUrl, alt: "", className: "w-4 h-4 object-contain inline-block mr-1" }) : placedComp.iconEmoji ? /* @__PURE__ */ jsxs(Fragment, { children: [
10284
+ placedComp.iconEmoji,
10285
+ " "
10286
+ ] }) : null,
10287
+ placedComp.label
10288
+ ] }),
10289
+ result && /* @__PURE__ */ jsx(Icon, { icon: result.correct ? CheckCircle : XCircle, size: "sm", className: result.correct ? "text-green-600" : "text-red-600" })
10290
+ ] }) : /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-[var(--color-muted-foreground)]", children: t("builder.empty") })
10291
+ ]
10292
+ },
10293
+ slot.id
10294
+ );
10295
+ }) })
10296
+ ] }) }),
10297
+ submitted && /* @__PURE__ */ jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", align: "center", children: [
10298
+ /* @__PURE__ */ jsx(Icon, { icon: allCorrect ? CheckCircle : XCircle, size: "lg", className: allCorrect ? "text-green-600" : "text-red-600" }),
10299
+ /* @__PURE__ */ jsx(Typography, { variant: "body", weight: "bold", children: allCorrect ? entity.successMessage ?? t("builder.success") : entity.failMessage ?? t("builder.incorrect") })
10300
+ ] }) }),
10301
+ showHint && entity.hint && /* @__PURE__ */ jsx(Card, { className: "p-4 border-l-4 border-l-yellow-500", children: /* @__PURE__ */ jsx(Typography, { variant: "body", children: entity.hint }) }),
10302
+ /* @__PURE__ */ jsxs(HStack, { gap: "sm", justify: "center", children: [
10303
+ !submitted ? /* @__PURE__ */ jsxs(Button, { variant: "primary", onClick: handleSubmit, disabled: !allPlaced, children: [
10304
+ /* @__PURE__ */ jsx(Icon, { icon: Wrench, size: "sm" }),
10305
+ t("builder.build")
10306
+ ] }) : !allCorrect ? /* @__PURE__ */ jsx(Button, { variant: "primary", onClick: handleReset, children: t("builder.tryAgain") }) : null,
10307
+ /* @__PURE__ */ jsxs(Button, { variant: "secondary", onClick: handleFullReset, children: [
10308
+ /* @__PURE__ */ jsx(Icon, { icon: RotateCcw, size: "sm" }),
10309
+ t("builder.reset")
10310
+ ] })
10311
+ ] })
10312
+ ] })
10313
+ }
10314
+ );
10315
+ }
10316
+ BuilderBoard.displayName = "BuilderBoard";
10317
+ function DebuggerBoard({
10318
+ entity,
10319
+ completeEvent = "PUZZLE_COMPLETE",
10320
+ className
10321
+ }) {
10322
+ const { emit } = useEventBus();
10323
+ const { t } = useTranslate();
10324
+ const [flaggedLines, setFlaggedLines] = useState(/* @__PURE__ */ new Set());
10325
+ const [headerError, setHeaderError] = useState(false);
10326
+ const [submitted, setSubmitted] = useState(false);
10327
+ const [attempts, setAttempts] = useState(0);
10328
+ const [showHint, setShowHint] = useState(false);
10329
+ const toggleLine = (lineId) => {
10330
+ if (submitted) return;
10331
+ setFlaggedLines((prev) => {
10332
+ const next = new Set(prev);
10333
+ if (next.has(lineId)) {
10334
+ next.delete(lineId);
10335
+ } else {
10336
+ next.add(lineId);
10337
+ }
10338
+ return next;
10339
+ });
10340
+ };
10341
+ const bugLines = entity.lines.filter((l) => l.isBug);
10342
+ const correctFlags = entity.lines.filter((l) => l.isBug && flaggedLines.has(l.id));
10343
+ const falseFlags = entity.lines.filter((l) => !l.isBug && flaggedLines.has(l.id));
10344
+ const allCorrect = submitted && correctFlags.length === bugLines.length && falseFlags.length === 0;
10345
+ const handleSubmit = useCallback(() => {
10346
+ setSubmitted(true);
10347
+ setAttempts((a) => a + 1);
10348
+ const correct = correctFlags.length === bugLines.length && falseFlags.length === 0;
10349
+ if (correct) {
10350
+ emit(`UI:${completeEvent}`, { success: true, attempts: attempts + 1 });
10351
+ }
10352
+ }, [correctFlags.length, bugLines.length, falseFlags.length, attempts, completeEvent, emit]);
10353
+ const handleReset = () => {
10354
+ setSubmitted(false);
10355
+ if (attempts >= 2 && entity.hint) {
10356
+ setShowHint(true);
10357
+ }
10358
+ };
10359
+ const handleFullReset = () => {
10360
+ setFlaggedLines(/* @__PURE__ */ new Set());
10361
+ setSubmitted(false);
10362
+ setAttempts(0);
10363
+ setShowHint(false);
10364
+ };
10365
+ return /* @__PURE__ */ jsx(
10366
+ Box,
10367
+ {
10368
+ className,
10369
+ style: {
10370
+ backgroundImage: entity.theme?.background ? `url(${entity.theme.background})` : void 0,
10371
+ backgroundSize: "cover",
10372
+ backgroundPosition: "center"
10373
+ },
10374
+ children: /* @__PURE__ */ jsxs(VStack, { gap: "lg", className: "p-4", children: [
10375
+ entity.headerImage && !headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 overflow-hidden rounded-lg", children: /* @__PURE__ */ jsx("img", { src: entity.headerImage, alt: "", onError: () => setHeaderError(true), className: "w-full h-full object-cover" }) }) : entity.headerImage && headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 rounded-lg bg-gradient-to-br from-[var(--color-muted)] to-[var(--color-accent)] opacity-60" }) : null,
10376
+ /* @__PURE__ */ jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
10377
+ /* @__PURE__ */ jsxs(HStack, { gap: "xs", align: "center", children: [
10378
+ /* @__PURE__ */ jsx(Icon, { icon: Bug, size: "sm" }),
10379
+ /* @__PURE__ */ jsx(Typography, { variant: "h4", weight: "bold", children: entity.title })
10380
+ ] }),
10381
+ /* @__PURE__ */ jsx(Typography, { variant: "body", children: entity.description }),
10382
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-[var(--color-muted-foreground)]", children: t("debugger.findBugs", { count: String(entity.bugCount) }) })
10383
+ ] }) }),
10384
+ /* @__PURE__ */ jsx(Card, { className: "p-0 overflow-hidden", children: /* @__PURE__ */ jsx(VStack, { gap: "none", children: entity.lines.map((line, i) => {
10385
+ const isFlagged = flaggedLines.has(line.id);
10386
+ let lineStyle = "";
10387
+ if (submitted) {
10388
+ if (line.isBug && isFlagged) lineStyle = "bg-green-50 dark:bg-green-950";
10389
+ else if (line.isBug && !isFlagged) lineStyle = "bg-yellow-50 dark:bg-yellow-950";
10390
+ else if (!line.isBug && isFlagged) lineStyle = "bg-red-50 dark:bg-red-950";
10391
+ } else if (isFlagged) {
10392
+ lineStyle = "bg-red-50 dark:bg-red-950";
10393
+ }
10394
+ return /* @__PURE__ */ jsxs(
10395
+ HStack,
10396
+ {
10397
+ gap: "none",
10398
+ align: "stretch",
10399
+ className: `border-b border-[var(--color-border)] cursor-pointer hover:bg-[var(--color-muted)] ${lineStyle}`,
10400
+ onClick: () => toggleLine(line.id),
10401
+ children: [
10402
+ /* @__PURE__ */ jsx(Box, { className: "w-10 flex-shrink-0 flex items-center justify-center border-r border-[var(--color-border)] text-[var(--color-muted-foreground)]", children: /* @__PURE__ */ jsx(Typography, { variant: "caption", children: i + 1 }) }),
10403
+ /* @__PURE__ */ jsx(Box, { className: "flex-1 px-3 py-1.5 font-mono text-sm whitespace-pre", children: /* @__PURE__ */ jsx(Typography, { variant: "body", className: "font-mono text-sm", children: line.content }) }),
10404
+ /* @__PURE__ */ jsxs(Box, { className: "w-8 flex-shrink-0 flex items-center justify-center", children: [
10405
+ isFlagged && /* @__PURE__ */ jsx(Icon, { icon: Bug, size: "xs", className: "text-red-600" }),
10406
+ submitted && line.isBug && !isFlagged && /* @__PURE__ */ jsx(Icon, { icon: Bug, size: "xs", className: "text-yellow-600" })
10407
+ ] })
10408
+ ]
10409
+ },
10410
+ line.id
10411
+ );
10412
+ }) }) }),
10413
+ submitted && /* @__PURE__ */ jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
10414
+ /* @__PURE__ */ jsx(Typography, { variant: "body", weight: "bold", children: allCorrect ? entity.successMessage ?? t("debugger.allFound") : `${correctFlags.length}/${bugLines.length} ${t("debugger.bugsFound")}` }),
10415
+ bugLines.map((line) => /* @__PURE__ */ jsxs(HStack, { gap: "xs", align: "start", children: [
10416
+ /* @__PURE__ */ jsx(
10417
+ Icon,
10418
+ {
10419
+ icon: flaggedLines.has(line.id) ? CheckCircle : XCircle,
10420
+ size: "xs",
10421
+ className: flaggedLines.has(line.id) ? "text-green-600 mt-0.5" : "text-yellow-600 mt-0.5"
10422
+ }
10423
+ ),
10424
+ /* @__PURE__ */ jsxs(VStack, { gap: "none", children: [
10425
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", weight: "bold", className: "font-mono", children: line.content.trim() }),
10426
+ line.explanation && /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-[var(--color-muted-foreground)]", children: line.explanation })
10427
+ ] })
10428
+ ] }, line.id))
10429
+ ] }) }),
10430
+ showHint && entity.hint && /* @__PURE__ */ jsx(Card, { className: "p-4 border-l-4 border-l-yellow-500", children: /* @__PURE__ */ jsx(Typography, { variant: "body", children: entity.hint }) }),
10431
+ /* @__PURE__ */ jsxs(HStack, { gap: "sm", justify: "center", children: [
10432
+ !submitted ? /* @__PURE__ */ jsxs(Button, { variant: "primary", onClick: handleSubmit, disabled: flaggedLines.size === 0, children: [
10433
+ /* @__PURE__ */ jsx(Icon, { icon: Send, size: "sm" }),
10434
+ t("debugger.submit")
10435
+ ] }) : !allCorrect ? /* @__PURE__ */ jsx(Button, { variant: "primary", onClick: handleReset, children: t("debugger.tryAgain") }) : null,
10436
+ /* @__PURE__ */ jsxs(Button, { variant: "secondary", onClick: handleFullReset, children: [
10437
+ /* @__PURE__ */ jsx(Icon, { icon: RotateCcw, size: "sm" }),
10438
+ t("debugger.reset")
10439
+ ] })
10440
+ ] })
10441
+ ] })
10442
+ }
10443
+ );
10444
+ }
10445
+ DebuggerBoard.displayName = "DebuggerBoard";
10446
+ function getOpponentAction(strategy, actions, history) {
10447
+ const actionIds = actions.map((a) => a.id);
10448
+ switch (strategy) {
10449
+ case "always-cooperate":
10450
+ return actionIds[0];
10451
+ case "always-defect":
10452
+ return actionIds[actionIds.length - 1];
10453
+ case "tit-for-tat":
10454
+ if (history.length === 0) return actionIds[0];
10455
+ return history[history.length - 1].playerAction;
10456
+ case "random":
10457
+ default:
10458
+ return actionIds[Math.floor(Math.random() * actionIds.length)];
10459
+ }
10460
+ }
10461
+ function NegotiatorBoard({
10462
+ entity,
10463
+ completeEvent = "PUZZLE_COMPLETE",
10464
+ className
10465
+ }) {
10466
+ const { emit } = useEventBus();
10467
+ const { t } = useTranslate();
10468
+ const [history, setHistory] = useState([]);
10469
+ const [headerError, setHeaderError] = useState(false);
10470
+ const [showHint, setShowHint] = useState(false);
10471
+ const currentRound = history.length;
10472
+ const isComplete = currentRound >= entity.totalRounds;
10473
+ const playerTotal = history.reduce((s, r) => s + r.playerPayoff, 0);
10474
+ const opponentTotal = history.reduce((s, r) => s + r.opponentPayoff, 0);
10475
+ const won = isComplete && playerTotal >= entity.targetScore;
10476
+ const handleAction = useCallback((actionId) => {
10477
+ if (isComplete) return;
10478
+ const opponentAction = getOpponentAction(entity.opponentStrategy, entity.actions, history);
10479
+ const payoff = entity.payoffMatrix.find(
10480
+ (p2) => p2.playerAction === actionId && p2.opponentAction === opponentAction
10481
+ );
10482
+ const result = {
10483
+ round: currentRound + 1,
10484
+ playerAction: actionId,
10485
+ opponentAction,
10486
+ playerPayoff: payoff?.playerPayoff ?? 0,
10487
+ opponentPayoff: payoff?.opponentPayoff ?? 0
10488
+ };
10489
+ const newHistory = [...history, result];
10490
+ setHistory(newHistory);
10491
+ if (newHistory.length >= entity.totalRounds) {
10492
+ const total = newHistory.reduce((s, r) => s + r.playerPayoff, 0);
10493
+ if (total >= entity.targetScore) {
10494
+ emit(`UI:${completeEvent}`, { success: true, score: total });
10495
+ }
10496
+ if (newHistory.length >= 3 && entity.hint) {
10497
+ setShowHint(true);
10498
+ }
10499
+ }
10500
+ }, [isComplete, entity, history, currentRound, completeEvent, emit]);
10501
+ const handleReset = () => {
10502
+ setHistory([]);
10503
+ setShowHint(false);
10504
+ };
10505
+ const getActionLabel = (id) => entity.actions.find((a) => a.id === id)?.label ?? id;
10506
+ return /* @__PURE__ */ jsx(
10507
+ Box,
10508
+ {
10509
+ className,
10510
+ style: {
10511
+ backgroundImage: entity.theme?.background ? `url(${entity.theme.background})` : void 0,
10512
+ backgroundSize: "cover",
10513
+ backgroundPosition: "center"
10514
+ },
10515
+ children: /* @__PURE__ */ jsxs(VStack, { gap: "lg", className: "p-4", children: [
10516
+ entity.headerImage && !headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 overflow-hidden rounded-lg", children: /* @__PURE__ */ jsx("img", { src: entity.headerImage, alt: "", onError: () => setHeaderError(true), className: "w-full h-full object-cover" }) }) : entity.headerImage && headerError ? /* @__PURE__ */ jsx(Box, { className: "w-full h-32 rounded-lg bg-gradient-to-br from-[var(--color-muted)] to-[var(--color-accent)] opacity-60" }) : null,
10517
+ /* @__PURE__ */ jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
10518
+ /* @__PURE__ */ jsx(Typography, { variant: "h4", weight: "bold", children: entity.title }),
10519
+ /* @__PURE__ */ jsx(Typography, { variant: "body", children: entity.description }),
10520
+ /* @__PURE__ */ jsxs(HStack, { gap: "md", children: [
10521
+ /* @__PURE__ */ jsx(Badge, { size: "sm", children: t("negotiator.round", { current: String(currentRound), total: String(entity.totalRounds) }) }),
10522
+ /* @__PURE__ */ jsxs(Badge, { size: "sm", children: [
10523
+ t("negotiator.target"),
10524
+ ": ",
10525
+ entity.targetScore
10526
+ ] })
10527
+ ] })
10528
+ ] }) }),
10529
+ /* @__PURE__ */ jsxs(HStack, { gap: "md", justify: "center", children: [
10530
+ /* @__PURE__ */ jsx(Card, { className: "p-4 flex-1 text-center", children: /* @__PURE__ */ jsxs(VStack, { gap: "xs", align: "center", children: [
10531
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-[var(--color-muted-foreground)]", children: t("negotiator.you") }),
10532
+ /* @__PURE__ */ jsx(Typography, { variant: "h3", weight: "bold", children: playerTotal })
10533
+ ] }) }),
10534
+ /* @__PURE__ */ jsx(Card, { className: "p-4 flex-1 text-center", children: /* @__PURE__ */ jsxs(VStack, { gap: "xs", align: "center", children: [
10535
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-[var(--color-muted-foreground)]", children: t("negotiator.opponent") }),
10536
+ /* @__PURE__ */ jsx(Typography, { variant: "h3", weight: "bold", children: opponentTotal })
10537
+ ] }) })
10538
+ ] }),
10539
+ !isComplete && /* @__PURE__ */ jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
10540
+ /* @__PURE__ */ jsx(Typography, { variant: "small", weight: "bold", className: "uppercase tracking-wider text-[var(--color-muted-foreground)]", children: t("negotiator.chooseAction") }),
10541
+ /* @__PURE__ */ jsx(HStack, { gap: "sm", justify: "center", className: "flex-wrap", children: entity.actions.map((action) => /* @__PURE__ */ jsx(
10542
+ Button,
10543
+ {
10544
+ variant: "primary",
10545
+ onClick: () => handleAction(action.id),
10546
+ children: action.label
10547
+ },
10548
+ action.id
10549
+ )) })
10550
+ ] }) }),
10551
+ history.length > 0 && /* @__PURE__ */ jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
10552
+ /* @__PURE__ */ jsx(Typography, { variant: "small", weight: "bold", className: "uppercase tracking-wider text-[var(--color-muted-foreground)]", children: t("negotiator.history") }),
10553
+ history.map((round) => /* @__PURE__ */ jsxs(HStack, { gap: "sm", align: "center", className: "text-sm", children: [
10554
+ /* @__PURE__ */ jsxs(Badge, { size: "sm", children: [
10555
+ "R",
10556
+ round.round
10557
+ ] }),
10558
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", children: getActionLabel(round.playerAction) }),
10559
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-[var(--color-muted-foreground)]", children: "vs" }),
10560
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", children: getActionLabel(round.opponentAction) }),
10561
+ /* @__PURE__ */ jsx(Icon, { icon: ArrowRight, size: "xs" }),
10562
+ /* @__PURE__ */ jsxs(Typography, { variant: "caption", weight: "bold", className: "text-green-600", children: [
10563
+ "+",
10564
+ round.playerPayoff
10565
+ ] }),
10566
+ /* @__PURE__ */ jsxs(Typography, { variant: "caption", className: "text-[var(--color-muted-foreground)]", children: [
10567
+ "/ +",
10568
+ round.opponentPayoff
10569
+ ] })
10570
+ ] }, round.round))
10571
+ ] }) }),
10572
+ isComplete && /* @__PURE__ */ jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", align: "center", children: [
10573
+ /* @__PURE__ */ jsx(Icon, { icon: CheckCircle, size: "lg", className: won ? "text-green-600" : "text-red-600" }),
10574
+ /* @__PURE__ */ jsx(Typography, { variant: "body", weight: "bold", children: won ? entity.successMessage ?? t("negotiator.success") : entity.failMessage ?? t("negotiator.failed") }),
10575
+ /* @__PURE__ */ jsxs(Typography, { variant: "caption", className: "text-[var(--color-muted-foreground)]", children: [
10576
+ t("negotiator.finalScore"),
10577
+ ": ",
10578
+ playerTotal,
10579
+ "/",
10580
+ entity.targetScore
10581
+ ] })
10582
+ ] }) }),
10583
+ showHint && entity.hint && !won && /* @__PURE__ */ jsx(Card, { className: "p-4 border-l-4 border-l-yellow-500", children: /* @__PURE__ */ jsx(Typography, { variant: "body", children: entity.hint }) }),
10584
+ isComplete && !won && /* @__PURE__ */ jsx(HStack, { justify: "center", children: /* @__PURE__ */ jsx(Button, { variant: "primary", onClick: handleReset, children: t("negotiator.playAgain") }) })
10585
+ ] })
10586
+ }
10587
+ );
10588
+ }
10589
+ NegotiatorBoard.displayName = "NegotiatorBoard";
10590
+ function SimulationCanvas({
10591
+ preset,
10592
+ width = 600,
10593
+ height = 400,
10594
+ running,
10595
+ speed = 1,
10596
+ className
10597
+ }) {
10598
+ const canvasRef = useRef(null);
10599
+ const bodiesRef = useRef(structuredClone(preset.bodies));
10600
+ useEffect(() => {
10601
+ bodiesRef.current = structuredClone(preset.bodies);
10602
+ }, [preset]);
10603
+ const step = useCallback(() => {
10604
+ const dt = 1 / 60 * speed;
10605
+ const gx = preset.gravity?.x ?? 0;
10606
+ const gy = preset.gravity?.y ?? 9.81;
10607
+ const bodies = bodiesRef.current;
10608
+ for (const body of bodies) {
10609
+ if (body.fixed) continue;
10610
+ body.vx += gx * dt;
10611
+ body.vy += gy * dt;
10612
+ body.x += body.vx * dt;
10613
+ body.y += body.vy * dt;
10614
+ if (body.y + body.radius > height) {
10615
+ body.y = height - body.radius;
10616
+ body.vy = -body.vy * 0.8;
10617
+ }
10618
+ if (body.x + body.radius > width) {
10619
+ body.x = width - body.radius;
10620
+ body.vx = -body.vx * 0.8;
10621
+ }
10622
+ if (body.x - body.radius < 0) {
10623
+ body.x = body.radius;
10624
+ body.vx = -body.vx * 0.8;
10625
+ }
10626
+ }
10627
+ if (preset.constraints) {
10628
+ for (const c of preset.constraints) {
10629
+ const a = bodies[c.bodyA];
10630
+ const b = bodies[c.bodyB];
10631
+ if (!a || !b) continue;
10632
+ const dx = b.x - a.x;
10633
+ const dy = b.y - a.y;
10634
+ const dist = Math.sqrt(dx * dx + dy * dy) || 1e-3;
10635
+ const diff = (dist - c.length) / dist;
10636
+ const fx = dx * diff * c.stiffness;
10637
+ const fy = dy * diff * c.stiffness;
10638
+ if (!a.fixed) {
10639
+ a.vx += fx * dt;
10640
+ a.vy += fy * dt;
10641
+ }
10642
+ if (!b.fixed) {
10643
+ b.vx -= fx * dt;
10644
+ b.vy -= fy * dt;
10645
+ }
10646
+ }
10647
+ }
10648
+ }, [preset, width, height, speed]);
10649
+ const draw = useCallback(() => {
10650
+ const canvas = canvasRef.current;
10651
+ if (!canvas) return;
10652
+ const ctx = canvas.getContext("2d");
10653
+ if (!ctx) return;
10654
+ const bodies = bodiesRef.current;
10655
+ ctx.clearRect(0, 0, width, height);
10656
+ ctx.fillStyle = preset.backgroundColor ?? "#1a1a2e";
10657
+ ctx.fillRect(0, 0, width, height);
10658
+ if (preset.constraints) {
10659
+ for (const c of preset.constraints) {
10660
+ const a = bodies[c.bodyA];
10661
+ const b = bodies[c.bodyB];
10662
+ if (a && b) {
10663
+ ctx.beginPath();
10664
+ ctx.moveTo(a.x, a.y);
10665
+ ctx.lineTo(b.x, b.y);
10666
+ ctx.strokeStyle = "#533483";
10667
+ ctx.lineWidth = 1;
10668
+ ctx.setLineDash([4, 4]);
10669
+ ctx.stroke();
10670
+ ctx.setLineDash([]);
10671
+ }
10672
+ }
10673
+ }
10674
+ for (const body of bodies) {
10675
+ ctx.beginPath();
10676
+ ctx.arc(body.x, body.y, body.radius, 0, Math.PI * 2);
10677
+ ctx.fillStyle = body.color ?? "#e94560";
10678
+ ctx.fill();
10679
+ if (preset.showVelocity) {
10680
+ ctx.beginPath();
10681
+ ctx.moveTo(body.x, body.y);
10682
+ ctx.lineTo(body.x + body.vx * 0.1, body.y + body.vy * 0.1);
10683
+ ctx.strokeStyle = "#16213e";
10684
+ ctx.lineWidth = 2;
10685
+ ctx.stroke();
10686
+ }
10687
+ }
10688
+ }, [width, height, preset]);
10689
+ useEffect(() => {
10690
+ if (!running) return;
10691
+ let raf;
10692
+ const loop = () => {
10693
+ step();
10694
+ draw();
10695
+ raf = requestAnimationFrame(loop);
10696
+ };
10697
+ raf = requestAnimationFrame(loop);
10698
+ return () => cancelAnimationFrame(raf);
10699
+ }, [running, step, draw]);
10700
+ useEffect(() => {
10701
+ draw();
10702
+ }, [draw]);
10703
+ return /* @__PURE__ */ jsx(Box, { className, children: /* @__PURE__ */ jsx("canvas", { ref: canvasRef, width, height, className: "rounded-md" }) });
10704
+ }
10705
+ SimulationCanvas.displayName = "SimulationCanvas";
10706
+ function SimulationControls({
10707
+ running,
10708
+ speed,
10709
+ parameters,
10710
+ onPlay,
10711
+ onPause,
10712
+ onStep,
10713
+ onReset,
10714
+ onSpeedChange,
10715
+ onParameterChange,
10716
+ className
10717
+ }) {
10718
+ return /* @__PURE__ */ jsxs(VStack, { gap: "md", className, children: [
10719
+ /* @__PURE__ */ jsxs(HStack, { gap: "sm", align: "center", children: [
10720
+ running ? /* @__PURE__ */ jsx(Button, { size: "sm", variant: "secondary", onClick: onPause, icon: Pause, children: "Pause" }) : /* @__PURE__ */ jsx(Button, { size: "sm", variant: "primary", onClick: onPlay, icon: Play, children: "Play" }),
10721
+ /* @__PURE__ */ jsx(Button, { size: "sm", variant: "ghost", onClick: onStep, icon: SkipForward, disabled: running, children: "Step" }),
10722
+ /* @__PURE__ */ jsx(Button, { size: "sm", variant: "ghost", onClick: onReset, icon: RotateCcw, children: "Reset" })
10723
+ ] }),
10724
+ /* @__PURE__ */ jsxs(VStack, { gap: "xs", children: [
10725
+ /* @__PURE__ */ jsxs(Typography, { variant: "caption", color: "muted", children: [
10726
+ "Speed: ",
10727
+ speed.toFixed(1),
10728
+ "x"
10729
+ ] }),
10730
+ /* @__PURE__ */ jsx(
10731
+ "input",
10732
+ {
10733
+ type: "range",
10734
+ min: 0.1,
10735
+ max: 5,
10736
+ step: 0.1,
10737
+ value: speed,
10738
+ onChange: (e) => onSpeedChange(parseFloat(e.target.value)),
10739
+ className: "w-full"
10740
+ }
10741
+ )
10742
+ ] }),
10743
+ Object.entries(parameters).map(([name, param]) => /* @__PURE__ */ jsxs(VStack, { gap: "xs", children: [
10744
+ /* @__PURE__ */ jsxs(Typography, { variant: "caption", color: "muted", children: [
10745
+ param.label,
10746
+ ": ",
10747
+ param.value.toFixed(2)
10748
+ ] }),
10749
+ /* @__PURE__ */ jsx(
10750
+ "input",
10751
+ {
10752
+ type: "range",
10753
+ min: param.min,
10754
+ max: param.max,
10755
+ step: param.step,
10756
+ value: param.value,
10757
+ onChange: (e) => onParameterChange(name, parseFloat(e.target.value)),
10758
+ className: "w-full"
10759
+ }
10760
+ )
10761
+ ] }, name))
10762
+ ] });
10763
+ }
10764
+ SimulationControls.displayName = "SimulationControls";
10765
+ function SimulationGraph({
10766
+ label,
10767
+ unit,
10768
+ data,
10769
+ maxPoints = 200,
10770
+ width = 300,
10771
+ height = 120,
10772
+ color = "#e94560",
10773
+ className
10774
+ }) {
10775
+ const canvasRef = useRef(null);
10776
+ const visibleData = data.slice(-maxPoints);
10777
+ useEffect(() => {
10778
+ const canvas = canvasRef.current;
10779
+ if (!canvas || visibleData.length < 2) return;
10780
+ const ctx = canvas.getContext("2d");
10781
+ if (!ctx) return;
10782
+ ctx.clearRect(0, 0, width, height);
10783
+ ctx.fillStyle = "#0f0f23";
10784
+ ctx.fillRect(0, 0, width, height);
10785
+ ctx.strokeStyle = "#1a1a3e";
10786
+ ctx.lineWidth = 0.5;
10787
+ for (let i = 0; i < 5; i++) {
10788
+ const y = height / 5 * i;
10789
+ ctx.beginPath();
10790
+ ctx.moveTo(0, y);
10791
+ ctx.lineTo(width, y);
10792
+ ctx.stroke();
10793
+ }
10794
+ let minVal = Infinity;
10795
+ let maxVal = -Infinity;
10796
+ for (const pt of visibleData) {
10797
+ if (pt.value < minVal) minVal = pt.value;
10798
+ if (pt.value > maxVal) maxVal = pt.value;
10799
+ }
10800
+ const range = maxVal - minVal || 1;
10801
+ const pad = height * 0.1;
10802
+ ctx.beginPath();
10803
+ ctx.strokeStyle = color;
10804
+ ctx.lineWidth = 2;
10805
+ for (let i = 0; i < visibleData.length; i++) {
10806
+ const x = i / (maxPoints - 1) * width;
10807
+ const y = pad + (maxVal - visibleData[i].value) / range * (height - 2 * pad);
10808
+ if (i === 0) ctx.moveTo(x, y);
10809
+ else ctx.lineTo(x, y);
10810
+ }
10811
+ ctx.stroke();
10812
+ const last = visibleData[visibleData.length - 1];
10813
+ ctx.fillStyle = color;
10814
+ ctx.font = "12px monospace";
10815
+ ctx.fillText(`${last.value.toFixed(2)} ${unit}`, width - 80, 16);
10816
+ }, [visibleData, width, height, color, unit, maxPoints]);
10817
+ return /* @__PURE__ */ jsx(Card, { padding: "sm", className, children: /* @__PURE__ */ jsxs(VStack, { gap: "xs", children: [
10818
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", weight: "bold", children: label }),
10819
+ /* @__PURE__ */ jsx("canvas", { ref: canvasRef, width, height, className: "rounded" })
10820
+ ] }) });
10821
+ }
10822
+ SimulationGraph.displayName = "SimulationGraph";
10823
+
10824
+ // components/organisms/game/physics-sim/presets/mechanics.ts
10825
+ var projectileMotion = {
10826
+ id: "mechanics-projectile",
10827
+ name: "Projectile Motion",
10828
+ description: "Launch a ball and observe parabolic trajectory under gravity.",
10829
+ domain: "natural",
10830
+ gravity: { x: 0, y: 9.81 },
10831
+ bodies: [
10832
+ { id: "ball", x: 50, y: 350, vx: 80, vy: -120, mass: 1, radius: 10, color: "#e94560", fixed: false },
10833
+ { id: "ground", x: 300, y: 390, vx: 0, vy: 0, mass: 1e3, radius: 400, color: "#333", fixed: true }
10834
+ ],
10835
+ showVelocity: true,
10836
+ parameters: {
10837
+ angle: { value: 45, min: 0, max: 90, step: 1, label: "Launch angle (deg)" },
10838
+ velocity: { value: 100, min: 10, max: 200, step: 5, label: "Initial velocity (m/s)" },
10839
+ gravity: { value: 9.81, min: 0, max: 20, step: 0.1, label: "Gravity (m/s\xB2)" }
10840
+ }
10841
+ };
10842
+ var pendulum = {
10843
+ id: "mechanics-pendulum",
10844
+ name: "Simple Pendulum",
10845
+ description: "A mass on a string swinging under gravity.",
10846
+ domain: "natural",
10847
+ gravity: { x: 0, y: 9.81 },
10848
+ bodies: [
10849
+ { id: "pivot", x: 300, y: 50, vx: 0, vy: 0, mass: 1e3, radius: 5, color: "#888", fixed: true },
10850
+ { id: "bob", x: 400, y: 200, vx: 0, vy: 0, mass: 5, radius: 15, color: "#e94560", fixed: false }
10851
+ ],
10852
+ constraints: [{ bodyA: 0, bodyB: 1, length: 180, stiffness: 1 }],
10853
+ parameters: {
10854
+ length: { value: 180, min: 50, max: 300, step: 10, label: "String length (px)" },
10855
+ mass: { value: 5, min: 1, max: 20, step: 0.5, label: "Mass (kg)" },
10856
+ gravity: { value: 9.81, min: 0, max: 20, step: 0.1, label: "Gravity (m/s\xB2)" }
10857
+ }
10858
+ };
10859
+ var springOscillator = {
10860
+ id: "mechanics-spring",
10861
+ name: "Spring Oscillator",
10862
+ description: "A mass bouncing on a spring \u2014 simple harmonic motion.",
10863
+ domain: "natural",
10864
+ gravity: { x: 0, y: 0 },
10865
+ bodies: [
10866
+ { id: "anchor", x: 300, y: 50, vx: 0, vy: 0, mass: 1e3, radius: 8, color: "#888", fixed: true },
10867
+ { id: "mass", x: 300, y: 250, vx: 0, vy: 0, mass: 2, radius: 20, color: "#0f3460", fixed: false }
10868
+ ],
10869
+ constraints: [{ bodyA: 0, bodyB: 1, length: 150, stiffness: 0.5 }],
10870
+ parameters: {
10871
+ stiffness: { value: 0.5, min: 0.1, max: 2, step: 0.05, label: "Spring stiffness (k)" },
10872
+ mass: { value: 2, min: 0.5, max: 10, step: 0.5, label: "Mass (kg)" },
10873
+ damping: { value: 0, min: 0, max: 0.5, step: 0.01, label: "Damping" }
10874
+ }
10875
+ };
10876
+
10877
+ // components/organisms/game/physics-sim/presets/index.ts
10878
+ var ALL_PRESETS = [
10879
+ projectileMotion,
10880
+ pendulum,
10881
+ springOscillator
10882
+ ];
10883
+ var eventIcons = {
10884
+ attack: Sword,
10885
+ defend: Shield,
10886
+ heal: Heart,
10887
+ move: Move,
10888
+ special: Zap,
10889
+ death: Sword,
10890
+ spawn: Zap
10891
+ };
10892
+ var eventColors = {
10893
+ attack: "text-error",
10894
+ defend: "text-info",
10895
+ heal: "text-success",
10896
+ move: "text-primary",
10897
+ special: "text-warning",
10898
+ death: "text-muted-foreground",
10899
+ spawn: "text-accent"
10900
+ };
10901
+ var eventBadgeVariants = {
10902
+ attack: "danger",
10903
+ defend: "primary",
10904
+ heal: "success",
10905
+ move: "warning",
10906
+ special: "secondary",
10907
+ death: "secondary",
10908
+ spawn: "secondary"
10909
+ };
10910
+ function CombatLog({
10911
+ events,
10912
+ maxVisible = 50,
10913
+ autoScroll = true,
10914
+ showTimestamps = false,
10915
+ className,
10916
+ title = "Combat Log"
10917
+ }) {
10918
+ const scrollRef = useRef(null);
10919
+ useEffect(() => {
10920
+ if (autoScroll && scrollRef.current) {
10921
+ scrollRef.current.scrollTop = scrollRef.current.scrollHeight;
10922
+ }
10923
+ }, [events, autoScroll]);
10924
+ const visibleEvents = events.slice(-maxVisible);
10925
+ return /* @__PURE__ */ jsxs(Card, { variant: "default", className: cn("flex flex-col", className), children: [
10926
+ /* @__PURE__ */ jsx(Box, { padding: "sm", border: true, className: "border-b-2 border-x-0 border-t-0 border-[var(--color-border)]", children: /* @__PURE__ */ jsxs(Box, { display: "flex", className: "items-center justify-between", children: [
10927
+ /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "font-bold", children: title }),
10928
+ /* @__PURE__ */ jsxs(Badge, { variant: "neutral", size: "sm", children: [
10929
+ events.length,
10930
+ " events"
10931
+ ] })
10932
+ ] }) }),
10933
+ /* @__PURE__ */ jsx(Box, { ref: scrollRef, overflow: "auto", className: "flex-1 max-h-64", children: visibleEvents.length === 0 ? /* @__PURE__ */ jsx(Box, { padding: "md", className: "text-center opacity-50", children: /* @__PURE__ */ jsx(Typography, { variant: "body2", children: "No events yet" }) }) : /* @__PURE__ */ jsx(Box, { padding: "xs", className: "space-y-1", children: visibleEvents.map((event) => {
10934
+ const EventIcon = eventIcons[event.type];
10935
+ const colorClass = eventColors[event.type];
10936
+ return /* @__PURE__ */ jsxs(
10937
+ Box,
10938
+ {
10939
+ display: "flex",
10940
+ padding: "xs",
10941
+ rounded: "sm",
10942
+ className: cn("items-start gap-2 hover:bg-[var(--color-muted)] transition-colors", event.type === "death" && "opacity-60"),
10943
+ children: [
10944
+ /* @__PURE__ */ jsx(Box, { className: cn("flex-shrink-0 mt-0.5", colorClass), children: /* @__PURE__ */ jsx(EventIcon, { className: "h-4 w-4" }) }),
10945
+ /* @__PURE__ */ jsxs(Box, { className: "flex-1 min-w-0", children: [
10946
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "block", children: event.message }),
10947
+ event.value !== void 0 && /* @__PURE__ */ jsxs(Badge, { variant: eventBadgeVariants[event.type], size: "sm", className: "mt-1", children: [
10948
+ event.type === "heal" ? "+" : event.type === "attack" ? "-" : "",
10949
+ event.value
10950
+ ] })
10951
+ ] }),
10952
+ (event.turn || showTimestamps) && /* @__PURE__ */ jsx(Box, { className: "flex-shrink-0", children: /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "opacity-40", children: event.turn ? `T${event.turn}` : "" }) })
10953
+ ]
10954
+ },
10955
+ event.id
10956
+ );
10957
+ }) }) })
10958
+ ] });
10959
+ }
10960
+ CombatLog.displayName = "CombatLog";
10961
+
10962
+ // components/organisms/game/types/game.ts
10963
+ function createInitialGameState(width, height, units, defaultTerrain = "floorStone") {
10964
+ const board = Array.from(
10965
+ { length: height },
10966
+ () => Array.from({ length: width }, () => ({
10967
+ terrain: defaultTerrain
10968
+ }))
10969
+ );
10970
+ const unitsMap = {};
10971
+ for (const unit of units) {
10972
+ unitsMap[unit.id] = unit;
10973
+ if (unit.position.y < height && unit.position.x < width) {
10974
+ board[unit.position.y][unit.position.x].unitId = unit.id;
10975
+ }
10976
+ }
10977
+ return {
10978
+ board,
10979
+ units: unitsMap,
10980
+ currentPhase: "observation",
10981
+ currentTurn: 1,
10982
+ activeTeam: "player",
10983
+ validMoves: [],
10984
+ attackTargets: []
10985
+ };
10986
+ }
10987
+ function calculateValidMoves(state, unitId) {
10988
+ const unit = state.units[unitId];
10989
+ if (!unit) return [];
10990
+ const moves = [];
10991
+ const { x, y } = unit.position;
10992
+ const range = unit.movement;
10993
+ for (let dy = -range; dy <= range; dy++) {
10994
+ for (let dx = -range; dx <= range; dx++) {
10995
+ const nx = x + dx;
10996
+ const ny = y + dy;
10997
+ const distance = Math.abs(dx) + Math.abs(dy);
10998
+ if (distance > 0 && distance <= range && ny >= 0 && ny < state.board.length && nx >= 0 && nx < state.board[0].length && !state.board[ny][nx].unitId && !state.board[ny][nx].isBlocked) {
10999
+ moves.push({ x: nx, y: ny });
11000
+ }
11001
+ }
11002
+ }
11003
+ return moves;
11004
+ }
11005
+ function calculateAttackTargets(state, unitId) {
11006
+ const unit = state.units[unitId];
11007
+ if (!unit) return [];
11008
+ const targets = [];
11009
+ const { x, y } = unit.position;
11010
+ const directions = [
11011
+ { dx: -1, dy: 0 },
11012
+ { dx: 1, dy: 0 },
11013
+ { dx: 0, dy: -1 },
11014
+ { dx: 0, dy: 1 }
11015
+ ];
11016
+ for (const { dx, dy } of directions) {
11017
+ const nx = x + dx;
11018
+ const ny = y + dy;
11019
+ if (ny >= 0 && ny < state.board.length && nx >= 0 && nx < state.board[0].length) {
11020
+ const targetTile = state.board[ny][nx];
11021
+ if (targetTile.unitId) {
11022
+ const targetUnit = state.units[targetTile.unitId];
11023
+ if (targetUnit && targetUnit.team !== unit.team) {
11024
+ targets.push({ x: nx, y: ny });
11025
+ }
11026
+ }
11027
+ }
11028
+ }
11029
+ return targets;
11030
+ }
11031
+
11032
+ // components/organisms/game/utils/combatEffects.ts
11033
+ var combatAnimations = {
11034
+ shake: `
11035
+ @keyframes combat-shake {
11036
+ 0%, 100% { transform: translateX(0); }
11037
+ 10%, 30%, 50%, 70%, 90% { transform: translateX(-5px); }
11038
+ 20%, 40%, 60%, 80% { transform: translateX(5px); }
11039
+ }
11040
+ `,
11041
+ flash: `
11042
+ @keyframes combat-flash {
11043
+ 0%, 100% { opacity: 1; }
11044
+ 50% { opacity: 0.3; filter: brightness(2); }
11045
+ }
11046
+ `,
11047
+ pulseRed: `
11048
+ @keyframes combat-pulse-red {
11049
+ 0%, 100% { box-shadow: 0 0 0 0 rgba(239, 68, 68, 0); }
11050
+ 50% { box-shadow: 0 0 20px 5px rgba(239, 68, 68, 0.6); }
11051
+ }
11052
+ `,
11053
+ healGlow: `
11054
+ @keyframes combat-heal-glow {
11055
+ 0%, 100% { box-shadow: 0 0 0 0 rgba(34, 197, 94, 0); }
11056
+ 50% { box-shadow: 0 0 30px 10px rgba(34, 197, 94, 0.5); }
11057
+ }
11058
+ `
11059
+ };
11060
+ var combatClasses = {
11061
+ shake: "animate-[combat-shake_0.3s_ease-in-out]",
11062
+ flash: "animate-[combat-flash_0.2s_ease-in-out_2]",
11063
+ pulseRed: "animate-[combat-pulse-red_0.5s_ease-in-out]",
11064
+ healGlow: "animate-[combat-heal-glow_0.8s_ease-in-out]",
11065
+ hit: "animate-pulse brightness-150",
11066
+ defend: "animate-bounce",
11067
+ critical: "animate-ping"
11068
+ };
11069
+ var combatEffects = {
11070
+ attack: { className: "animate-pulse brightness-125", duration: 300 },
11071
+ hit: { className: "animate-[shake_0.3s_ease-in-out] brightness-150", duration: 300 },
11072
+ critical: { className: "animate-ping scale-110", duration: 500 },
11073
+ defend: { className: "animate-bounce ring-4 ring-blue-400", duration: 400 },
11074
+ heal: { className: "brightness-125 ring-4 ring-green-400", duration: 600 },
11075
+ defeat: { className: "grayscale opacity-50 scale-75", duration: 800 },
11076
+ levelUp: { className: "animate-bounce ring-4 ring-yellow-400 brightness-150", duration: 1e3 }
11077
+ };
11078
+ function applyTemporaryEffect(element, effect, onComplete) {
11079
+ const originalClass = element.className;
11080
+ element.className = `${originalClass} ${effect.className}`;
11081
+ setTimeout(() => {
11082
+ element.className = originalClass;
11083
+ onComplete?.();
11084
+ }, effect.duration);
11085
+ }
11086
+ function calculateDamage(attack, defense, isDefending = false, criticalChance = 0.1) {
11087
+ const isCritical = Math.random() < criticalChance;
11088
+ const baseDamage = attack;
11089
+ const defenseMultiplier = isDefending ? 2 : 1;
11090
+ const effectiveDefense = defense * defenseMultiplier;
11091
+ let finalDamage = Math.max(1, baseDamage - effectiveDefense);
11092
+ if (isCritical) finalDamage = Math.floor(finalDamage * 2);
11093
+ const isBlocked = finalDamage <= 1 && effectiveDefense > baseDamage;
11094
+ const damageReduction = baseDamage - finalDamage;
11095
+ return { baseDamage, finalDamage, isCritical, isBlocked, damageReduction };
11096
+ }
11097
+ function generateCombatMessage(event) {
11098
+ return event.message;
11099
+ }
11100
+ function extractTitle(children) {
11101
+ if (!React42__default.isValidElement(children)) return void 0;
11102
+ const props = children.props;
11103
+ if (typeof props.title === "string") {
11104
+ return props.title;
11105
+ }
11106
+ return void 0;
11107
+ }
11108
+ var ModalSlot = ({
11109
+ children,
11110
+ title: overrideTitle,
11111
+ size = "md",
11112
+ className
11113
+ }) => {
11114
+ const eventBus = useEventBus();
11115
+ const isOpen = Boolean(children);
11116
+ const title = overrideTitle || extractTitle(children);
11117
+ const handleClose = () => {
11118
+ eventBus.emit("UI:CLOSE");
11119
+ eventBus.emit("UI:CANCEL");
11120
+ };
11121
+ if (!isOpen) return null;
11122
+ return /* @__PURE__ */ jsx(
11123
+ Modal,
11124
+ {
11125
+ isOpen,
11126
+ onClose: handleClose,
11127
+ title,
11128
+ size,
11129
+ className,
11130
+ children
11131
+ }
11132
+ );
11133
+ };
11134
+ ModalSlot.displayName = "ModalSlot";
11135
+ function extractTitle2(children) {
11136
+ if (!React42__default.isValidElement(children)) return void 0;
11137
+ const props = children.props;
11138
+ if (typeof props.title === "string") {
11139
+ return props.title;
11140
+ }
11141
+ return void 0;
11142
+ }
11143
+ var DrawerSlot = ({
11144
+ children,
11145
+ title: overrideTitle,
11146
+ position = "right",
11147
+ size = "md",
11148
+ className
11149
+ }) => {
11150
+ const eventBus = useEventBus();
11151
+ const isOpen = Boolean(children);
11152
+ const title = overrideTitle || extractTitle2(children);
11153
+ const handleClose = () => {
11154
+ eventBus.emit("UI:CLOSE");
11155
+ eventBus.emit("UI:CANCEL");
11156
+ };
11157
+ if (!isOpen) return null;
11158
+ return /* @__PURE__ */ jsx(
11159
+ Drawer,
11160
+ {
11161
+ isOpen,
11162
+ onClose: handleClose,
11163
+ title,
11164
+ position,
11165
+ width: size,
11166
+ className,
11167
+ children
11168
+ }
11169
+ );
11170
+ };
11171
+ DrawerSlot.displayName = "DrawerSlot";
11172
+ function extractToastProps(children) {
11173
+ if (!React42__default.isValidElement(children)) {
11174
+ if (typeof children === "string") {
11175
+ return { message: children };
11176
+ }
11177
+ return {};
11178
+ }
11179
+ const props = children.props;
11180
+ return {
11181
+ message: typeof props.message === "string" ? props.message : void 0,
11182
+ variant: props.variant,
11183
+ title: typeof props.title === "string" ? props.title : void 0
11184
+ };
11185
+ }
11186
+ var ToastSlot = ({
11187
+ children,
11188
+ variant: overrideVariant,
11189
+ title: overrideTitle,
11190
+ duration = 5e3,
11191
+ className
11192
+ }) => {
11193
+ const eventBus = useEventBus();
11194
+ const isVisible = Boolean(children);
11195
+ const extracted = extractToastProps(children);
11196
+ const variant = overrideVariant || extracted.variant || "info";
11197
+ const title = overrideTitle || extracted.title;
11198
+ const message = extracted.message || (typeof children === "string" ? children : "");
11199
+ const handleDismiss = () => {
11200
+ eventBus.emit("UI:DISMISS");
11201
+ eventBus.emit("UI:CLOSE");
11202
+ };
11203
+ if (!isVisible) return null;
11204
+ const isCustomContent = React42__default.isValidElement(children) && !message;
11205
+ return /* @__PURE__ */ jsx(Box, { className: "fixed bottom-4 right-4 z-50", children: isCustomContent ? children : /* @__PURE__ */ jsx(
11206
+ Toast,
11207
+ {
11208
+ variant,
11209
+ title,
11210
+ message: message || "Notification",
11211
+ duration,
11212
+ onDismiss: handleDismiss,
11213
+ className
11214
+ }
11215
+ ) });
11216
+ };
11217
+ ToastSlot.displayName = "ToastSlot";
11218
+ var CHART_COLORS = [
11219
+ "var(--color-primary)",
11220
+ "var(--color-success)",
11221
+ "var(--color-warning)",
8829
11222
  "var(--color-error)",
8830
11223
  "var(--color-info)",
8831
11224
  "var(--color-accent)"
@@ -9279,7 +11672,7 @@ var Meter = ({
9279
11672
  ] }) });
9280
11673
  };
9281
11674
  Meter.displayName = "Meter";
9282
- var STATUS_STYLES2 = {
11675
+ var STATUS_STYLES3 = {
9283
11676
  complete: {
9284
11677
  dotColor: "text-[var(--color-success)]",
9285
11678
  lineColor: "bg-[var(--color-success)]",
@@ -9321,7 +11714,7 @@ var Timeline = ({
9321
11714
  },
9322
11715
  [eventBus, entity]
9323
11716
  );
9324
- const items = React25__default.useMemo(() => {
11717
+ const items = React42__default.useMemo(() => {
9325
11718
  if (propItems) return propItems;
9326
11719
  if (!data) return [];
9327
11720
  return data.map((record, idx) => {
@@ -9369,7 +11762,7 @@ var Timeline = ({
9369
11762
  title && /* @__PURE__ */ jsx(Typography, { variant: "h5", weight: "semibold", children: title }),
9370
11763
  /* @__PURE__ */ jsx(VStack, { gap: "none", className: "relative", children: items.map((item, idx) => {
9371
11764
  const status = item.status || "pending";
9372
- const style = STATUS_STYLES2[status];
11765
+ const style = STATUS_STYLES3[status];
9373
11766
  const ItemIcon = item.icon || style.icon;
9374
11767
  const isLast = idx === items.length - 1;
9375
11768
  return /* @__PURE__ */ jsxs(HStack, { gap: "md", align: "start", className: "relative", children: [
@@ -9470,7 +11863,7 @@ var MediaGallery = ({
9470
11863
  const handleUpload = useCallback(() => {
9471
11864
  eventBus.emit("UI:MEDIA_UPLOAD", { entity });
9472
11865
  }, [eventBus, entity]);
9473
- const items = React25__default.useMemo(() => {
11866
+ const items = React42__default.useMemo(() => {
9474
11867
  if (propItems) return propItems;
9475
11868
  if (!data) return [];
9476
11869
  return data.map((record, idx) => ({
@@ -10804,7 +13197,7 @@ var NavLink = ({
10804
13197
  currentPath
10805
13198
  }) => {
10806
13199
  const isActive = currentPath === item.href || currentPath.startsWith(item.href + "/");
10807
- const Icon2 = item.icon;
13200
+ const Icon3 = item.icon;
10808
13201
  return /* @__PURE__ */ jsxs(
10809
13202
  Link,
10810
13203
  {
@@ -10815,7 +13208,7 @@ var NavLink = ({
10815
13208
  ),
10816
13209
  children: [
10817
13210
  /* @__PURE__ */ jsx(
10818
- Icon2,
13211
+ Icon3,
10819
13212
  {
10820
13213
  className: cn(
10821
13214
  "h-5 w-5",
@@ -11446,4 +13839,4 @@ function WorldMapTemplate({
11446
13839
  }
11447
13840
  WorldMapTemplate.displayName = "WorldMapTemplate";
11448
13841
 
11449
- export { AR_BOOK_FIELDS, AuthLayout, BattleBoard, BattleTemplate, BookChapterView, BookCoverPage, BookNavBar, BookTableOfContents, BookViewer, CanvasEffect, CastleBoard, CastleTemplate, Chart, CodeViewer, CollapsibleSection, ConfirmDialog, ContentRenderer, CounterTemplate, DIAMOND_TOP_Y, DashboardGrid, DashboardLayout, DialogueBox, DocumentViewer, StateMachineView as DomStateMachineVisualizer, DrawerSlot, EditorCheckbox, EditorSelect, EditorSlider, EditorTextInput, EditorToolbar, FEATURE_COLORS, FEATURE_TYPES, FLOOR_HEIGHT, FormActions, FormLayout, FormSection, GameAudioContext, GameAudioProvider, GameAudioToggle, GameHud, GameMenu, GameOverScreen, GameShell, GameTemplate, GenericAppTemplate, GraphCanvas, Header, IDENTITY_BOOK_FIELDS, InventoryPanel, IsometricCanvas, JazariStateMachine, List, MediaGallery, Meter, ModalSlot, Navigation, StateMachineView as OrbitalStateMachineView, OrbitalVisualization, PhysicsManager, SHEET_COLUMNS, SPRITE_SHEET_LAYOUT, Section, Sidebar, SignaturePad, Split, SplitPane, StateMachineView, StatusBar, TERRAIN_COLORS, TILE_HEIGHT, TILE_WIDTH, TabbedContainer, Table, TerrainPalette, Timeline, ToastSlot, TraitSlot, TraitStateViewer, UncontrolledBattleBoard, WizardContainer, WorldMapBoard, WorldMapTemplate, createUnitAnimationState, getCurrentFrame, inferDirection, isoToScreen, mapBookData, resolveFieldMap, resolveFrame, resolveSheetDirection, screenToIso, tickAnimationState, transitionAnimation, useBattleState, useCamera, useGameAudio, useGameAudioContext, useImageCache, usePhysics2D, useSpriteAnimations };
13842
+ export { ALL_PRESETS, AR_BOOK_FIELDS, ActionPalette, ActionTile, AuthLayout, BattleBoard, BattleTemplate, BookChapterView, BookCoverPage, BookNavBar, BookTableOfContents, BookViewer, BuilderBoard, CanvasEffect, CastleBoard, CastleTemplate, Chart, ClassifierBoard, CodeView, CodeViewer, CollapsibleSection, CombatLog, ConfirmDialog, ContentRenderer, CounterTemplate, DIAMOND_TOP_Y, DashboardGrid, DashboardLayout, DebuggerBoard, DialogueBox, DocumentViewer, StateMachineView as DomStateMachineVisualizer, DrawerSlot, EditorCheckbox, EditorSelect, EditorSlider, EditorTextInput, EditorToolbar, EventHandlerBoard, EventLog, FEATURE_COLORS, FEATURE_TYPES, FLOOR_HEIGHT, FormActions, FormLayout, FormSection, GameAudioContext, GameAudioProvider, GameAudioToggle, GameHud, GameMenu, GameOverScreen, GameShell, GameTemplate, GenericAppTemplate, GraphCanvas, Header, IDENTITY_BOOK_FIELDS, InventoryPanel, IsometricCanvas, JazariStateMachine, List, MediaGallery, Meter, ModalSlot, Navigation, NegotiatorBoard, ObjectRulePanel, StateMachineView as OrbitalStateMachineView, OrbitalVisualization, PhysicsManager, RuleEditor, SHEET_COLUMNS, SPRITE_SHEET_LAYOUT, Section, SequenceBar, SequencerBoard, Sidebar, SignaturePad, SimulationCanvas, SimulationControls, SimulationGraph, SimulatorBoard, Split, SplitPane, StateArchitectBoard, StateMachineView, StateNode2 as StateNode, StatusBar, TERRAIN_COLORS, TILE_HEIGHT, TILE_WIDTH, TabbedContainer, Table, TerrainPalette, Timeline, ToastSlot, TraitSlot, TraitStateViewer, TransitionArrow, UncontrolledBattleBoard, VariablePanel, WizardContainer, WorldMapBoard, WorldMapTemplate, applyTemporaryEffect, calculateAttackTargets, calculateDamage, calculateValidMoves, combatAnimations, combatClasses, combatEffects, createInitialGameState, createUnitAnimationState, generateCombatMessage, getCurrentFrame, inferDirection, isoToScreen, mapBookData, pendulum, projectileMotion, resolveFieldMap, resolveFrame, resolveSheetDirection, screenToIso, springOscillator, tickAnimationState, transitionAnimation, useBattleState, useCamera, useGameAudio, useGameAudioContext, useImageCache, usePhysics2D, useSpriteAnimations };