@almadar/ui 2.6.0 → 2.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,16 +2,16 @@ import { useAuthContext } from '../chunk-2QM732NQ.js';
2
2
  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, usePinchZoom, usePlayer, usePreview, useResolvedEntity, useSelectedEntity, useSendOrbitalEvent, useSingletonEntity, useUIEvents, useUpdateEntity, useValidation } from '../chunk-2QM732NQ.js';
3
3
  import { DEFAULT_CONFIG, renderStateMachineToDomData, parseContentSegments } from '../chunk-N6DJVKZ6.js';
4
4
  import '../chunk-3HJHHULT.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, StatBadge, Select, Drawer, Toast, Tabs, Input, ThemeToggle, EntityDisplayEvents, StateIndicator, Container } from '../chunk-LEWQP2UP.js';
6
- export { Accordion, ActionButtons, Card2 as ActionCard, Alert, AnimatedCounter, Avatar, Badge, Box, Breadcrumb, Button, ButtonGroup, CalendarGrid, Card, CardBody, CardContent, CardFooter, CardGrid, CardHeader, CardTitle, Carousel, Center, ChartLegend, Checkbox, CodeBlock, ConditionalWrapper, ConfettiEffect, Container, ControlButton, DPad, DataGrid, DataList, DataTable, DateRangeSelector, DayCell, DetailPanel, Divider, Drawer, EmptyState, EntityDisplayEvents, ErrorBoundary, ErrorState, FilterGroup, Flex, FlipCard, FlipContainer, FloatingActionButton, Form, FormField, FormSectionHeader, GraphView, Grid, HStack, Heading, HealthBar, Icon, InfiniteScrollSentinel, Input, InputGroup, Label, LawReferenceTooltip, Lightbox, LineChart, LoadingState, MapView, MarkdownContent, MasterDetail, Menu, Modal, NumberStepper, Overlay, PageHeader, Pagination, Popover, ProgressBar, ProgressDots, PullToRefresh, QuizBlock, Radio, RangeSlider, RelationSelect, RepeatableFormSection, ScaledDiagram, ScoreDisplay, SearchInput, Select, SidePanel, SimpleGrid, Skeleton, SlotContentRenderer, SortableList, Spacer, Spinner, Sprite, Stack, StarRating, StatBadge, StatCard, StateIndicator, StatusDot, SwipeableRow, Switch, Tabs, Text, TextHighlight, Textarea, ThemeSelector, ThemeToggle, TimeSlotCell, Toast, Tooltip, TrendIndicator, TypewriterText, Typography, UISlotComponent, UISlotRenderer, UploadDropZone, VStack, ViolationAlert, WizardNavigation, WizardProgress, drawSprite } from '../chunk-LEWQP2UP.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, StatBadge, Select, Drawer, Toast, Tabs, Input, ThemeToggle, EntityDisplayEvents, StateIndicator, Container } from '../chunk-VJP2HCLY.js';
6
+ export { Accordion, ActionButtons, Card2 as ActionCard, Alert, AnimatedCounter, Avatar, Badge, Box, Breadcrumb, Button, ButtonGroup, CalendarGrid, Card, CardBody, CardContent, CardFooter, CardGrid, CardHeader, CardTitle, Carousel, Center, ChartLegend, Checkbox, CodeBlock, ConditionalWrapper, ConfettiEffect, Container, ControlButton, DPad, DataGrid, DataList, DataTable, DateRangeSelector, DayCell, DetailPanel, Divider, Drawer, EmptyState, EntityDisplayEvents, ErrorBoundary, ErrorState, FilterGroup, Flex, FlipCard, FlipContainer, FloatingActionButton, Form, FormField, FormSectionHeader, GraphView, Grid, HStack, Heading, HealthBar, Icon, InfiniteScrollSentinel, Input, InputGroup, Label, LawReferenceTooltip, Lightbox, LineChart, LoadingState, MapView, MarkdownContent, MasterDetail, Menu, Meter, Modal, NumberStepper, Overlay, PageHeader, Pagination, Popover, ProgressBar, ProgressDots, PullToRefresh, QuizBlock, Radio, RangeSlider, RelationSelect, RepeatableFormSection, ScaledDiagram, ScoreDisplay, SearchInput, Select, SidePanel, SimpleGrid, Skeleton, SlotContentRenderer, SortableList, Spacer, Spinner, Sprite, Stack, StarRating, StatBadge, StatCard, StatDisplay, StateIndicator, StatusDot, SwipeableRow, Switch, Tabs, Text, TextHighlight, Textarea, ThemeSelector, ThemeToggle, TimeSlotCell, Toast, Tooltip, TrendIndicator, TypewriterText, Typography, UISlotComponent, UISlotRenderer, UploadDropZone, VStack, ViolationAlert, WizardNavigation, WizardProgress, drawSprite } from '../chunk-VJP2HCLY.js';
7
7
  import '../chunk-DKQN5FVU.js';
8
8
  import { useTranslate } from '../chunk-WGJIL4YR.js';
9
9
  export { EntityDataProvider, I18nProvider, createTranslate, entityDataKeys, parseQueryBinding, useDragReorder, useEntity, useEntityDataAdapter, useEntityDetail, useEntityList, useEntityListSuspense, useEntitySuspense, useInfiniteScroll, useLongPress, usePullToRefresh, useQuerySingleton, useSwipeGesture, useTranslate } from '../chunk-WGJIL4YR.js';
10
10
  import { useEventBus, useEventListener } from '../chunk-YXZM3WCF.js';
11
11
  export { useEmitEvent, useEventBus, useEventListener } from '../chunk-YXZM3WCF.js';
12
12
  export { DEFAULT_SLOTS, useUISlotManager } from '../chunk-3JGAROCW.js';
13
- import { cn, getNestedValue } from '../chunk-KKCVDUK7.js';
14
- export { cn } from '../chunk-KKCVDUK7.js';
13
+ import { cn, getNestedValue, updateAssetStatus, bindCanvasCapture } from '../chunk-RPYMP7ZC.js';
14
+ export { cn } from '../chunk-RPYMP7ZC.js';
15
15
  import '../chunk-TSETXL2E.js';
16
16
  import '../chunk-K2D5D3WK.js';
17
17
  export { clearEntities, getAllEntities, getByType, getEntity, getSingleton, removeEntity, spawnEntity, updateEntity, updateSingleton } from '../chunk-N7MVUW4R.js';
@@ -3766,12 +3766,15 @@ function useImageCache(urls) {
3766
3766
  cache.set(url, img);
3767
3767
  loading.delete(url);
3768
3768
  setPendingCount((prev) => Math.max(0, prev - 1));
3769
+ updateAssetStatus(url, "loaded");
3769
3770
  };
3770
3771
  img.onerror = () => {
3771
3772
  if (cancelled) return;
3772
3773
  loading.delete(url);
3773
3774
  setPendingCount((prev) => Math.max(0, prev - 1));
3775
+ updateAssetStatus(url, "failed");
3774
3776
  };
3777
+ updateAssetStatus(url, "pending");
3775
3778
  img.src = url;
3776
3779
  }
3777
3780
  return () => {
@@ -4040,6 +4043,15 @@ function IsometricCanvas({
4040
4043
  return [...new Set(urls.filter(Boolean))];
4041
4044
  }, [sortedTiles, features, units, getTerrainSprite, getFeatureSprite, getUnitSprite, effectSpriteUrls, backgroundImage, assetManifest, resolveManifestUrl]);
4042
4045
  const { getImage } = useImageCache(spriteUrls);
4046
+ useEffect(() => {
4047
+ if (typeof window === "undefined") return;
4048
+ const canvas = canvasRef.current;
4049
+ if (!canvas) return;
4050
+ bindCanvasCapture(() => canvas.toDataURL("image/png"));
4051
+ return () => {
4052
+ bindCanvasCapture(() => null);
4053
+ };
4054
+ }, []);
4043
4055
  const {
4044
4056
  cameraRef,
4045
4057
  targetCameraRef,
@@ -4621,6 +4633,10 @@ function PlatformerCanvas({
4621
4633
  canvasHeight = 400,
4622
4634
  followCamera = true,
4623
4635
  bgColor,
4636
+ playerSprite,
4637
+ tileSprites,
4638
+ backgroundImage,
4639
+ assetBaseUrl = "",
4624
4640
  leftEvent = "MOVE_LEFT",
4625
4641
  rightEvent = "MOVE_RIGHT",
4626
4642
  jumpEvent = "JUMP",
@@ -4631,6 +4647,35 @@ function PlatformerCanvas({
4631
4647
  const canvasRef = useRef(null);
4632
4648
  const eventBus = useEventBus();
4633
4649
  const keysRef = useRef(/* @__PURE__ */ new Set());
4650
+ const imageCache = useRef(/* @__PURE__ */ new Map());
4651
+ const loadImage = useCallback((url) => {
4652
+ const fullUrl = url.startsWith("http") ? url : `${assetBaseUrl}${url}`;
4653
+ const cached = imageCache.current.get(fullUrl);
4654
+ if (cached?.complete && cached.naturalWidth > 0) return cached;
4655
+ if (!cached) {
4656
+ const img = new Image();
4657
+ img.crossOrigin = "anonymous";
4658
+ img.src = fullUrl;
4659
+ img.onload = () => {
4660
+ updateAssetStatus(fullUrl, "loaded");
4661
+ };
4662
+ img.onerror = () => {
4663
+ updateAssetStatus(fullUrl, "failed");
4664
+ };
4665
+ imageCache.current.set(fullUrl, img);
4666
+ updateAssetStatus(fullUrl, "pending");
4667
+ }
4668
+ return null;
4669
+ }, [assetBaseUrl]);
4670
+ useEffect(() => {
4671
+ if (typeof window === "undefined") return;
4672
+ const canvas = canvasRef.current;
4673
+ if (!canvas) return;
4674
+ bindCanvasCapture(() => canvas.toDataURL("image/png"));
4675
+ return () => {
4676
+ bindCanvasCapture(() => null);
4677
+ };
4678
+ }, []);
4634
4679
  const resolvedPlayer = player ?? {
4635
4680
  x: entity?.x ?? 50,
4636
4681
  y: entity?.y ?? 300,
@@ -4695,7 +4740,10 @@ function PlatformerCanvas({
4695
4740
  camX = Math.max(0, Math.min(resolvedPlayer.x - canvasWidth / 2, worldWidth - canvasWidth));
4696
4741
  camY = Math.max(0, Math.min(resolvedPlayer.y - canvasHeight / 2 - 50, worldHeight - canvasHeight));
4697
4742
  }
4698
- if (bgColor) {
4743
+ const bgImg = backgroundImage ? loadImage(backgroundImage) : null;
4744
+ if (bgImg) {
4745
+ ctx.drawImage(bgImg, 0, 0, canvasWidth, canvasHeight);
4746
+ } else if (bgColor) {
4699
4747
  ctx.fillStyle = bgColor;
4700
4748
  ctx.fillRect(0, 0, canvasWidth, canvasHeight);
4701
4749
  } else {
@@ -4723,58 +4771,86 @@ function PlatformerCanvas({
4723
4771
  for (const plat of platforms) {
4724
4772
  const px = plat.x - camX;
4725
4773
  const py = plat.y - camY;
4726
- const color = PLATFORM_COLORS[plat.type ?? "ground"] ?? PLATFORM_COLORS.ground;
4727
- ctx.fillStyle = color;
4728
- ctx.fillRect(px, py, plat.width, plat.height);
4729
- ctx.fillStyle = "rgba(255, 255, 255, 0.15)";
4730
- ctx.fillRect(px, py, plat.width, 3);
4731
- ctx.fillStyle = "rgba(0, 0, 0, 0.3)";
4732
- ctx.fillRect(px, py + plat.height - 2, plat.width, 2);
4733
- if (plat.type === "hazard") {
4734
- ctx.strokeStyle = "#e74c3c";
4735
- ctx.lineWidth = 2;
4736
- for (let sx = px; sx < px + plat.width; sx += 12) {
4774
+ const platType = plat.type ?? "ground";
4775
+ const spriteUrl = tileSprites?.[platType];
4776
+ const tileImg = spriteUrl ? loadImage(spriteUrl) : null;
4777
+ if (tileImg) {
4778
+ const tileW = tileImg.naturalWidth;
4779
+ const tileH = tileImg.naturalHeight;
4780
+ const scaleH = plat.height / tileH;
4781
+ const scaledW = tileW * scaleH;
4782
+ for (let tx = 0; tx < plat.width; tx += scaledW) {
4783
+ const drawW = Math.min(scaledW, plat.width - tx);
4784
+ const srcW = drawW / scaleH;
4785
+ ctx.drawImage(tileImg, 0, 0, srcW, tileH, px + tx, py, drawW, plat.height);
4786
+ }
4787
+ } else {
4788
+ const color = PLATFORM_COLORS[platType] ?? PLATFORM_COLORS.ground;
4789
+ ctx.fillStyle = color;
4790
+ ctx.fillRect(px, py, plat.width, plat.height);
4791
+ ctx.fillStyle = "rgba(255, 255, 255, 0.15)";
4792
+ ctx.fillRect(px, py, plat.width, 3);
4793
+ ctx.fillStyle = "rgba(0, 0, 0, 0.3)";
4794
+ ctx.fillRect(px, py + plat.height - 2, plat.width, 2);
4795
+ if (platType === "hazard") {
4796
+ ctx.strokeStyle = "#e74c3c";
4797
+ ctx.lineWidth = 2;
4798
+ for (let sx = px; sx < px + plat.width; sx += 12) {
4799
+ ctx.beginPath();
4800
+ ctx.moveTo(sx, py);
4801
+ ctx.lineTo(sx + 6, py + plat.height);
4802
+ ctx.stroke();
4803
+ }
4804
+ }
4805
+ if (platType === "goal") {
4806
+ ctx.fillStyle = "rgba(241, 196, 15, 0.5)";
4737
4807
  ctx.beginPath();
4738
- ctx.moveTo(sx, py);
4739
- ctx.lineTo(sx + 6, py + plat.height);
4740
- ctx.stroke();
4808
+ ctx.arc(px + plat.width / 2, py - 10, 8, 0, Math.PI * 2);
4809
+ ctx.fill();
4741
4810
  }
4742
4811
  }
4743
- if (plat.type === "goal") {
4744
- ctx.fillStyle = "rgba(241, 196, 15, 0.5)";
4745
- ctx.beginPath();
4746
- ctx.arc(px + plat.width / 2, py - 10, 8, 0, Math.PI * 2);
4747
- ctx.fill();
4748
- }
4749
4812
  }
4750
4813
  const pw = resolvedPlayer.width ?? 24;
4751
4814
  const ph = resolvedPlayer.height ?? 32;
4752
4815
  const ppx = resolvedPlayer.x - camX;
4753
4816
  const ppy = resolvedPlayer.y - camY;
4754
- ctx.fillStyle = PLAYER_COLOR;
4755
- const radius = Math.min(pw, ph) * 0.25;
4756
- ctx.beginPath();
4757
- ctx.moveTo(ppx + radius, ppy);
4758
- ctx.lineTo(ppx + pw - radius, ppy);
4759
- ctx.quadraticCurveTo(ppx + pw, ppy, ppx + pw, ppy + radius);
4760
- ctx.lineTo(ppx + pw, ppy + ph - radius);
4761
- ctx.quadraticCurveTo(ppx + pw, ppy + ph, ppx + pw - radius, ppy + ph);
4762
- ctx.lineTo(ppx + radius, ppy + ph);
4763
- ctx.quadraticCurveTo(ppx, ppy + ph, ppx, ppy + ph - radius);
4764
- ctx.lineTo(ppx, ppy + radius);
4765
- ctx.quadraticCurveTo(ppx, ppy, ppx + radius, ppy);
4766
- ctx.fill();
4767
- const eyeY = ppy + ph * 0.3;
4768
- const eyeSize = 3;
4769
4817
  const facingRight = resolvedPlayer.facingRight ?? true;
4770
- const eyeOffsetX = facingRight ? pw * 0.55 : pw * 0.2;
4771
- ctx.fillStyle = PLAYER_EYE_COLOR;
4772
- ctx.beginPath();
4773
- ctx.arc(ppx + eyeOffsetX, eyeY, eyeSize, 0, Math.PI * 2);
4774
- ctx.fill();
4775
- ctx.beginPath();
4776
- ctx.arc(ppx + eyeOffsetX + 7, eyeY, eyeSize, 0, Math.PI * 2);
4777
- ctx.fill();
4818
+ const playerImg = playerSprite ? loadImage(playerSprite) : null;
4819
+ if (playerImg) {
4820
+ ctx.save();
4821
+ if (!facingRight) {
4822
+ ctx.translate(ppx + pw, ppy);
4823
+ ctx.scale(-1, 1);
4824
+ ctx.drawImage(playerImg, 0, 0, pw, ph);
4825
+ } else {
4826
+ ctx.drawImage(playerImg, ppx, ppy, pw, ph);
4827
+ }
4828
+ ctx.restore();
4829
+ } else {
4830
+ ctx.fillStyle = PLAYER_COLOR;
4831
+ const radius = Math.min(pw, ph) * 0.25;
4832
+ ctx.beginPath();
4833
+ ctx.moveTo(ppx + radius, ppy);
4834
+ ctx.lineTo(ppx + pw - radius, ppy);
4835
+ ctx.quadraticCurveTo(ppx + pw, ppy, ppx + pw, ppy + radius);
4836
+ ctx.lineTo(ppx + pw, ppy + ph - radius);
4837
+ ctx.quadraticCurveTo(ppx + pw, ppy + ph, ppx + pw - radius, ppy + ph);
4838
+ ctx.lineTo(ppx + radius, ppy + ph);
4839
+ ctx.quadraticCurveTo(ppx, ppy + ph, ppx, ppy + ph - radius);
4840
+ ctx.lineTo(ppx, ppy + radius);
4841
+ ctx.quadraticCurveTo(ppx, ppy, ppx + radius, ppy);
4842
+ ctx.fill();
4843
+ const eyeY = ppy + ph * 0.3;
4844
+ const eyeSize = 3;
4845
+ const eyeOffsetX = facingRight ? pw * 0.55 : pw * 0.2;
4846
+ ctx.fillStyle = PLAYER_EYE_COLOR;
4847
+ ctx.beginPath();
4848
+ ctx.arc(ppx + eyeOffsetX, eyeY, eyeSize, 0, Math.PI * 2);
4849
+ ctx.fill();
4850
+ ctx.beginPath();
4851
+ ctx.arc(ppx + eyeOffsetX + 7, eyeY, eyeSize, 0, Math.PI * 2);
4852
+ ctx.fill();
4853
+ }
4778
4854
  });
4779
4855
  return /* @__PURE__ */ jsx(
4780
4856
  "canvas",
@@ -10841,20 +10917,27 @@ function SimulationCanvas({
10841
10917
  bodiesRef.current = structuredClone(preset.bodies);
10842
10918
  }, [preset]);
10843
10919
  const step = useCallback(() => {
10844
- const dt = 1 / 60 * speed;
10920
+ const dt = Math.min(1 / 60 * speed, 1 / 15);
10845
10921
  const gx = preset.gravity?.x ?? 0;
10846
10922
  const gy = preset.gravity?.y ?? 9.81;
10847
10923
  const bodies = bodiesRef.current;
10924
+ const maxVel = Math.max(width, height) * 5;
10848
10925
  for (const body of bodies) {
10849
10926
  if (body.fixed) continue;
10850
10927
  body.vx += gx * dt;
10851
10928
  body.vy += gy * dt;
10929
+ body.vx = Math.max(-maxVel, Math.min(maxVel, body.vx));
10930
+ body.vy = Math.max(-maxVel, Math.min(maxVel, body.vy));
10852
10931
  body.x += body.vx * dt;
10853
10932
  body.y += body.vy * dt;
10854
10933
  if (body.y + body.radius > height) {
10855
10934
  body.y = height - body.radius;
10856
10935
  body.vy = -body.vy * 0.8;
10857
10936
  }
10937
+ if (body.y - body.radius < 0) {
10938
+ body.y = body.radius;
10939
+ body.vy = -body.vy * 0.8;
10940
+ }
10858
10941
  if (body.x + body.radius > width) {
10859
10942
  body.x = width - body.radius;
10860
10943
  body.vx = -body.vx * 0.8;
@@ -10940,7 +11023,24 @@ function SimulationCanvas({
10940
11023
  useEffect(() => {
10941
11024
  draw();
10942
11025
  }, [draw]);
10943
- return /* @__PURE__ */ jsx(Box, { className, children: /* @__PURE__ */ jsx("canvas", { ref: canvasRef, width, height, className: "rounded-md" }) });
11026
+ useEffect(() => {
11027
+ if (typeof window === "undefined") return;
11028
+ const canvas = canvasRef.current;
11029
+ if (!canvas) return;
11030
+ bindCanvasCapture(() => canvas.toDataURL("image/png"));
11031
+ return () => {
11032
+ bindCanvasCapture(() => null);
11033
+ };
11034
+ }, []);
11035
+ return /* @__PURE__ */ jsx(Box, { className: cn("flex justify-center", className), children: /* @__PURE__ */ jsx(
11036
+ "canvas",
11037
+ {
11038
+ ref: canvasRef,
11039
+ width,
11040
+ height,
11041
+ className: "rounded-md block max-w-full h-auto"
11042
+ }
11043
+ ) });
10944
11044
  }
10945
11045
  SimulationCanvas.displayName = "SimulationCanvas";
10946
11046
  function SimulationControls({
@@ -11698,209 +11798,6 @@ var Chart = ({
11698
11798
  ] }) });
11699
11799
  };
11700
11800
  Chart.displayName = "Chart";
11701
- var DEFAULT_THRESHOLDS = [
11702
- { value: 30, color: "var(--color-error)" },
11703
- { value: 70, color: "var(--color-warning)" },
11704
- { value: 100, color: "var(--color-success)" }
11705
- ];
11706
- function getColorForValue(value, max, thresholds) {
11707
- const percentage = value / max * 100;
11708
- for (const threshold of thresholds) {
11709
- if (percentage <= threshold.value) {
11710
- return threshold.color;
11711
- }
11712
- }
11713
- return thresholds[thresholds.length - 1]?.color ?? "var(--color-primary)";
11714
- }
11715
- var radialSizes = {
11716
- sm: { size: 80, stroke: 6, fontSize: "12px" },
11717
- md: { size: 120, stroke: 8, fontSize: "16px" },
11718
- lg: { size: 160, stroke: 10, fontSize: "20px" }
11719
- };
11720
- var Meter = ({
11721
- value,
11722
- min = 0,
11723
- max = 100,
11724
- label,
11725
- unit,
11726
- variant = "linear",
11727
- thresholds = DEFAULT_THRESHOLDS,
11728
- segments = 5,
11729
- showValue = true,
11730
- size = "md",
11731
- actions,
11732
- entity,
11733
- isLoading = false,
11734
- error,
11735
- className
11736
- }) => {
11737
- const eventBus = useEventBus();
11738
- const handleAction = useCallback(
11739
- (action) => {
11740
- if (action.event) {
11741
- eventBus.emit(`UI:${action.event}`, { value });
11742
- }
11743
- },
11744
- [eventBus, value]
11745
- );
11746
- const percentage = useMemo(() => {
11747
- const range = max - min;
11748
- if (range <= 0) return 0;
11749
- return Math.min(Math.max((value - min) / range * 100, 0), 100);
11750
- }, [value, min, max]);
11751
- const activeColor = useMemo(
11752
- () => getColorForValue(value, max, thresholds),
11753
- [value, max, thresholds]
11754
- );
11755
- const displayValue = useMemo(() => {
11756
- const formatted = Number.isInteger(value) ? value : value.toFixed(1);
11757
- return unit ? `${formatted}${unit}` : `${formatted}`;
11758
- }, [value, unit]);
11759
- if (isLoading) {
11760
- return /* @__PURE__ */ jsx(LoadingState, { message: "Loading meter...", className });
11761
- }
11762
- if (error) {
11763
- return /* @__PURE__ */ jsx(
11764
- ErrorState,
11765
- {
11766
- title: "Meter error",
11767
- message: error.message,
11768
- className
11769
- }
11770
- );
11771
- }
11772
- if (variant === "radial") {
11773
- const dims = radialSizes[size];
11774
- const radius = (dims.size - dims.stroke * 2) / 2;
11775
- const circumference = 2 * Math.PI * radius;
11776
- const offset = circumference - percentage / 100 * circumference;
11777
- const center = dims.size / 2;
11778
- return /* @__PURE__ */ jsx(Card, { className: cn("p-4", className), children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", align: "center", children: [
11779
- label && /* @__PURE__ */ jsx(Typography, { variant: "small", color: "secondary", weight: "medium", children: label }),
11780
- /* @__PURE__ */ jsxs(Box, { className: "relative inline-flex items-center justify-center", children: [
11781
- /* @__PURE__ */ jsxs(
11782
- "svg",
11783
- {
11784
- width: dims.size,
11785
- height: dims.size,
11786
- viewBox: `0 0 ${dims.size} ${dims.size}`,
11787
- className: "transform -rotate-90",
11788
- children: [
11789
- /* @__PURE__ */ jsx(
11790
- "circle",
11791
- {
11792
- cx: center,
11793
- cy: center,
11794
- r: radius,
11795
- fill: "none",
11796
- stroke: "var(--color-muted)",
11797
- strokeWidth: dims.stroke
11798
- }
11799
- ),
11800
- /* @__PURE__ */ jsx(
11801
- "circle",
11802
- {
11803
- cx: center,
11804
- cy: center,
11805
- r: radius,
11806
- fill: "none",
11807
- stroke: activeColor,
11808
- strokeWidth: dims.stroke,
11809
- strokeDasharray: circumference,
11810
- strokeDashoffset: offset,
11811
- strokeLinecap: "round",
11812
- className: "transition-all duration-500 ease-out"
11813
- }
11814
- )
11815
- ]
11816
- }
11817
- ),
11818
- showValue && /* @__PURE__ */ jsx(Box, { className: "absolute inset-0 flex items-center justify-center", children: /* @__PURE__ */ jsx(
11819
- Typography,
11820
- {
11821
- variant: "h5",
11822
- weight: "bold",
11823
- className: "tabular-nums",
11824
- style: { fontSize: dims.fontSize },
11825
- children: displayValue
11826
- }
11827
- ) })
11828
- ] }),
11829
- actions && actions.length > 0 && /* @__PURE__ */ jsx(HStack, { gap: "xs", children: actions.map((action, idx) => /* @__PURE__ */ jsx(
11830
- Badge,
11831
- {
11832
- variant: "default",
11833
- className: "cursor-pointer hover:opacity-80 transition-opacity",
11834
- onClick: () => handleAction(action),
11835
- children: action.label
11836
- },
11837
- idx
11838
- )) })
11839
- ] }) });
11840
- }
11841
- if (variant === "segmented") {
11842
- const activeSegments = Math.round(percentage / 100 * segments);
11843
- return /* @__PURE__ */ jsx(Card, { className: cn("p-4", className), children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
11844
- (label || showValue) && /* @__PURE__ */ jsxs(HStack, { justify: "between", align: "center", children: [
11845
- label && /* @__PURE__ */ jsx(Typography, { variant: "small", color: "secondary", weight: "medium", children: label }),
11846
- showValue && /* @__PURE__ */ jsx(Typography, { variant: "small", weight: "bold", className: "tabular-nums", children: displayValue })
11847
- ] }),
11848
- /* @__PURE__ */ jsx(HStack, { gap: "xs", className: "w-full", children: Array.from({ length: segments }).map((_, idx) => {
11849
- const isActive = idx < activeSegments;
11850
- const segColor = isActive ? getColorForValue((idx + 1) / segments * max, max, thresholds) : void 0;
11851
- return /* @__PURE__ */ jsx(
11852
- Box,
11853
- {
11854
- className: cn(
11855
- "h-2 flex-1 rounded-[var(--radius-sm)] transition-all duration-300",
11856
- !isActive && "bg-[var(--color-muted)]"
11857
- ),
11858
- style: isActive ? { backgroundColor: segColor } : void 0
11859
- },
11860
- idx
11861
- );
11862
- }) }),
11863
- thresholds.some((t) => t.label) && /* @__PURE__ */ jsx(HStack, { justify: "between", className: "w-full", children: thresholds.map((t, idx) => /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "secondary", children: t.label || "" }, idx)) })
11864
- ] }) });
11865
- }
11866
- return /* @__PURE__ */ jsx(Card, { className: cn("p-4", className), children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
11867
- (label || showValue) && /* @__PURE__ */ jsxs(HStack, { justify: "between", align: "center", children: [
11868
- label && /* @__PURE__ */ jsx(Typography, { variant: "small", color: "secondary", weight: "medium", children: label }),
11869
- showValue && /* @__PURE__ */ jsx(Typography, { variant: "small", weight: "bold", className: "tabular-nums", children: displayValue })
11870
- ] }),
11871
- /* @__PURE__ */ jsx(Box, { className: "w-full h-3 bg-[var(--color-muted)] rounded-[var(--radius-full)] overflow-hidden", children: /* @__PURE__ */ jsx(
11872
- Box,
11873
- {
11874
- className: "h-full rounded-[var(--radius-full)] transition-all duration-500 ease-out",
11875
- style: {
11876
- width: `${percentage}%`,
11877
- backgroundColor: activeColor
11878
- }
11879
- }
11880
- ) }),
11881
- /* @__PURE__ */ jsxs(HStack, { justify: "between", className: "w-full", children: [
11882
- /* @__PURE__ */ jsxs(Typography, { variant: "caption", color: "secondary", children: [
11883
- min,
11884
- unit ? ` ${unit}` : ""
11885
- ] }),
11886
- /* @__PURE__ */ jsxs(Typography, { variant: "caption", color: "secondary", children: [
11887
- max,
11888
- unit ? ` ${unit}` : ""
11889
- ] })
11890
- ] }),
11891
- actions && actions.length > 0 && /* @__PURE__ */ jsx(HStack, { gap: "xs", children: actions.map((action, idx) => /* @__PURE__ */ jsx(
11892
- Badge,
11893
- {
11894
- variant: "default",
11895
- className: "cursor-pointer hover:opacity-80 transition-opacity",
11896
- onClick: () => handleAction(action),
11897
- children: action.label
11898
- },
11899
- idx
11900
- )) })
11901
- ] }) });
11902
- };
11903
- Meter.displayName = "Meter";
11904
11801
  var STATUS_STYLES3 = {
11905
11802
  complete: {
11906
11803
  dotColor: "text-[var(--color-success)]",
@@ -12647,7 +12544,7 @@ var GraphCanvas = ({
12647
12544
  const handleNodeClick = useCallback(
12648
12545
  (node) => {
12649
12546
  if (nodeClickEvent) {
12650
- eventBus.emit(`UI:${nodeClickEvent}`, { row: node });
12547
+ eventBus.emit(`UI:${nodeClickEvent}`, { id: node.id, row: node });
12651
12548
  }
12652
12549
  onNodeClick?.(node);
12653
12550
  },
@@ -14064,4 +13961,4 @@ function WorldMapTemplate({
14064
13961
  }
14065
13962
  WorldMapTemplate.displayName = "WorldMapTemplate";
14066
13963
 
14067
- 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, NotifyListener, ObjectRulePanel, StateMachineView as OrbitalStateMachineView, OrbitalVisualization, PhysicsManager, PlatformerCanvas, 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 };
13964
+ 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, ModalSlot, Navigation, NegotiatorBoard, NotifyListener, ObjectRulePanel, StateMachineView as OrbitalStateMachineView, OrbitalVisualization, PhysicsManager, PlatformerCanvas, 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 };
@@ -353,6 +353,14 @@ declare function getBridgeHealth(): BridgeHealth | null;
353
353
  declare function getSummary(): VerificationSummary;
354
354
  declare function getSnapshot(): VerificationSnapshot;
355
355
  declare function subscribeToVerification(listener: ChangeListener): () => void;
356
+ /** Asset load status for canvas verification */
357
+ type AssetLoadStatus = "loaded" | "failed" | "pending";
358
+ /** Event bus log entry for verification */
359
+ interface EventLogEntry {
360
+ type: string;
361
+ payload?: Record<string, unknown>;
362
+ timestamp: number;
363
+ }
356
364
  /** Exposed on window for Playwright to query */
357
365
  interface OrbitalVerificationAPI {
358
366
  getSnapshot: () => VerificationSnapshot;
@@ -366,6 +374,14 @@ interface OrbitalVerificationAPI {
366
374
  sendEvent?: (event: string, payload?: Record<string, unknown>) => void;
367
375
  /** Get current trait state */
368
376
  getTraitState?: (traitName: string) => string | undefined;
377
+ /** Capture a canvas frame as PNG data URL. Registered by game organisms. */
378
+ captureFrame?: () => string | null;
379
+ /** Asset load status map. Registered by game organisms. */
380
+ assetStatus?: Record<string, AssetLoadStatus>;
381
+ /** Event bus log. Records all emit() calls for verification. */
382
+ eventLog?: EventLogEntry[];
383
+ /** Clear the event log. */
384
+ clearEventLog?: () => void;
369
385
  }
370
386
  declare global {
371
387
  interface Window {
@@ -378,16 +394,31 @@ declare global {
378
394
  */
379
395
  declare function waitForTransition(event: string, timeoutMs?: number): Promise<TransitionTrace | null>;
380
396
  /**
381
- * Bind the EventBus so automation can send events.
397
+ * Bind the EventBus so automation can send events and log emissions.
382
398
  * Call this during app initialization.
383
399
  */
384
400
  declare function bindEventBus(eventBus: {
385
401
  emit: (type: string, payload?: Record<string, unknown>) => void;
402
+ onAny?: (listener: (event: {
403
+ type: string;
404
+ payload?: Record<string, unknown>;
405
+ }) => void) => () => void;
386
406
  }): void;
387
407
  /**
388
408
  * Bind a trait state getter so automation can query current states.
389
409
  */
390
410
  declare function bindTraitStateGetter(getter: (traitName: string) => string | undefined): void;
411
+ /**
412
+ * Register a canvas frame capture function.
413
+ * Called by game organisms (IsometricCanvas, PlatformerCanvas, SimulationCanvas)
414
+ * so the verifier can snapshot canvas content at checkpoints.
415
+ */
416
+ declare function bindCanvasCapture(captureFn: () => string | null): void;
417
+ /**
418
+ * Update asset load status for canvas verification.
419
+ * Game organisms call this when images load or fail.
420
+ */
421
+ declare function updateAssetStatus(url: string, status: AssetLoadStatus): void;
391
422
  declare function clearVerification(): void;
392
423
 
393
424
  /**
@@ -424,4 +455,4 @@ declare function getNestedValue(obj: Record<string, unknown> | null | undefined,
424
455
  */
425
456
  declare function formatNestedFieldLabel(path: string): string;
426
457
 
427
- export { ApiError, type BridgeHealth, type CheckStatus, type DebugEvent, type DebugEventType, type EffectTrace, type EntitySnapshot, type EntityState, type GuardContext, type GuardEvaluation, type PersistentEntityInfo, type RuntimeEntity, type TickExecution, type TraitDebugInfo, type TraitGuard, type TraitTransition, type TransitionTrace, type VerificationCheck, type VerificationSnapshot, type VerificationSummary, apiClient, bindEventBus, bindTraitStateGetter, clearDebugEvents, clearEntityProvider, clearGuardHistory, clearTicks, clearTraits, clearVerification, debug, debugCollision, debugError, debugGameState, debugGroup, debugGroupEnd, debugInput, debugPhysics, debugTable, debugTime, debugTimeEnd, debugWarn, formatNestedFieldLabel, getAllChecks, getAllTicks, getAllTraits, getBridgeHealth, getDebugEvents, getEntitiesByType, getEntityById, getEntitySnapshot, getEventsBySource, getEventsByType, getGuardEvaluationsForTrait, getGuardHistory, getNestedValue, getRecentEvents, getRecentGuardEvaluations, getSnapshot, getSummary, getTick, getTrait, getTransitions, getTransitionsForTrait, initDebugShortcut, isDebugEnabled, logDebugEvent, logEffectExecuted, logError, logEventFired, logInfo, logStateChange, logWarning, onDebugToggle, recordGuardEvaluation, recordTransition, registerCheck, registerTick, registerTrait, setDebugEnabled, setEntityProvider, setTickActive, subscribeToDebugEvents, subscribeToGuardChanges, subscribeToTickChanges, subscribeToTraitChanges, subscribeToVerification, toggleDebug, unregisterTick, unregisterTrait, updateBridgeHealth, updateCheck, updateGuardResult, updateTickExecution, updateTraitState, waitForTransition };
458
+ export { ApiError, type AssetLoadStatus, type BridgeHealth, type CheckStatus, type DebugEvent, type DebugEventType, type EffectTrace, type EntitySnapshot, type EntityState, type EventLogEntry, type GuardContext, type GuardEvaluation, type PersistentEntityInfo, type RuntimeEntity, type TickExecution, type TraitDebugInfo, type TraitGuard, type TraitTransition, type TransitionTrace, type VerificationCheck, type VerificationSnapshot, type VerificationSummary, apiClient, bindCanvasCapture, bindEventBus, bindTraitStateGetter, clearDebugEvents, clearEntityProvider, clearGuardHistory, clearTicks, clearTraits, clearVerification, debug, debugCollision, debugError, debugGameState, debugGroup, debugGroupEnd, debugInput, debugPhysics, debugTable, debugTime, debugTimeEnd, debugWarn, formatNestedFieldLabel, getAllChecks, getAllTicks, getAllTraits, getBridgeHealth, getDebugEvents, getEntitiesByType, getEntityById, getEntitySnapshot, getEventsBySource, getEventsByType, getGuardEvaluationsForTrait, getGuardHistory, getNestedValue, getRecentEvents, getRecentGuardEvaluations, getSnapshot, getSummary, getTick, getTrait, getTransitions, getTransitionsForTrait, initDebugShortcut, isDebugEnabled, logDebugEvent, logEffectExecuted, logError, logEventFired, logInfo, logStateChange, logWarning, onDebugToggle, recordGuardEvaluation, recordTransition, registerCheck, registerTick, registerTrait, setDebugEnabled, setEntityProvider, setTickActive, subscribeToDebugEvents, subscribeToGuardChanges, subscribeToTickChanges, subscribeToTraitChanges, subscribeToVerification, toggleDebug, unregisterTick, unregisterTrait, updateAssetStatus, updateBridgeHealth, updateCheck, updateGuardResult, updateTickExecution, updateTraitState, waitForTransition };
package/dist/lib/index.js CHANGED
@@ -1,7 +1,6 @@
1
1
  export { DEFAULT_CONFIG, extractOutputsFromTransitions, extractStateMachine, formatGuard, getEffectSummary, parseContentSegments, parseMarkdownWithCodeBlocks, renderStateMachineToDomData, renderStateMachineToSvg } from '../chunk-N6DJVKZ6.js';
2
2
  export { ApiError, apiClient } from '../chunk-3HJHHULT.js';
3
- export { bindEventBus, bindTraitStateGetter, clearVerification, getAllChecks, getBridgeHealth, getSnapshot, getSummary, getTransitions, getTransitionsForTrait, recordTransition, registerCheck, subscribeToVerification, updateBridgeHealth, updateCheck, waitForTransition } from '../chunk-45CTDYBT.js';
4
- export { cn, debug, debugCollision, debugError, debugGameState, debugGroup, debugGroupEnd, debugInput, debugPhysics, debugTable, debugTime, debugTimeEnd, debugWarn, formatNestedFieldLabel, getNestedValue, isDebugEnabled } from '../chunk-KKCVDUK7.js';
3
+ export { bindCanvasCapture, bindEventBus, bindTraitStateGetter, clearVerification, cn, debug, debugCollision, debugError, debugGameState, debugGroup, debugGroupEnd, debugInput, debugPhysics, debugTable, debugTime, debugTimeEnd, debugWarn, formatNestedFieldLabel, getAllChecks, getBridgeHealth, getNestedValue, getSnapshot, getSummary, getTransitions, getTransitionsForTrait, isDebugEnabled, recordTransition, registerCheck, subscribeToVerification, updateAssetStatus, updateBridgeHealth, updateCheck, waitForTransition } from '../chunk-RPYMP7ZC.js';
5
4
  import '../chunk-PKBMQBKP.js';
6
5
 
7
6
  // lib/debugUtils.ts
@@ -1,12 +1,11 @@
1
- import { SuspenseConfigProvider } from '../chunk-LEWQP2UP.js';
1
+ import { SuspenseConfigProvider } from '../chunk-VJP2HCLY.js';
2
2
  import { ThemeProvider } from '../chunk-DKQN5FVU.js';
3
3
  import { SelectionProvider, EntityDataProvider } from '../chunk-WGJIL4YR.js';
4
4
  export { SelectionContext, SelectionProvider, useSelection, useSelectionOptional } from '../chunk-WGJIL4YR.js';
5
5
  import { useEventBus, EventBusProvider } from '../chunk-YXZM3WCF.js';
6
6
  export { EventBusContext, EventBusProvider } from '../chunk-YXZM3WCF.js';
7
7
  import '../chunk-3JGAROCW.js';
8
- import { recordTransition, registerCheck, bindEventBus, bindTraitStateGetter } from '../chunk-45CTDYBT.js';
9
- import '../chunk-KKCVDUK7.js';
8
+ import { recordTransition, registerCheck, bindEventBus, bindTraitStateGetter } from '../chunk-RPYMP7ZC.js';
10
9
  import '../chunk-TSETXL2E.js';
11
10
  import { useOfflineExecutor } from '../chunk-K2D5D3WK.js';
12
11
  import '../chunk-PKBMQBKP.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@almadar/ui",
3
- "version": "2.6.0",
3
+ "version": "2.7.0",
4
4
  "description": "React UI components, hooks, and providers for Almadar",
5
5
  "type": "module",
6
6
  "main": "./dist/components/index.js",