@almadar/ui 2.9.3 → 2.10.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.
@@ -5,7 +5,7 @@ import { cn, debugGroup, debug, debugGroupEnd, updateAssetStatus, bindCanvasCapt
5
5
  import { isPortalSlot } from './chunk-K2D5D3WK.js';
6
6
  import { __publicField } from './chunk-PKBMQBKP.js';
7
7
  import * as LucideIcons from 'lucide-react';
8
- import { Loader2, ChevronDown, X, ArrowRight, TrendingDown, TrendingUp, Check, Copy, AlertCircle, User, Sun, Moon, FileQuestion, Inbox, Search, Info, XCircle, CheckCircle, AlertTriangle, ChevronRight, Filter, Plus, ChevronLeft, HelpCircle, ChevronUp, Zap, Sword, Move, Heart, Shield, Minus, Star, FileWarning, Upload, MoreHorizontal, ArrowLeft, Calendar, Tag, Clock, CheckCircle2, DollarSign, FileText, Package } from 'lucide-react';
8
+ import { Loader2, ChevronDown, X, ArrowRight, TrendingDown, TrendingUp, Check, Copy, AlertCircle, User, Sun, Moon, FileQuestion, Inbox, Search, Info, XCircle, CheckCircle, AlertTriangle, ChevronRight, Filter, Plus, ChevronLeft, HelpCircle, ChevronUp, Zap, Sword, Move, Heart, Shield, Minus, Star, FileWarning, Upload, MoreHorizontal, ArrowLeft, Pause, Play, SkipForward, RotateCcw, Calendar, Tag, Clock, CheckCircle2, DollarSign, FileText, Package } from 'lucide-react';
9
9
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
10
10
  import * as React80 from 'react';
11
11
  import React80__default, { useCallback, useState, useRef, useLayoutEffect, useEffect, lazy, createContext, useMemo, useId, useContext, Suspense } from 'react';
@@ -667,7 +667,7 @@ var Avatar = ({
667
667
  alt,
668
668
  name,
669
669
  initials: providedInitials,
670
- icon: Icon2,
670
+ icon: Icon3,
671
671
  size = "md",
672
672
  status,
673
673
  badge,
@@ -680,7 +680,7 @@ var Avatar = ({
680
680
  const initials = providedInitials ?? (name ? generateInitials(name) : void 0);
681
681
  const hasImage = !!src;
682
682
  const hasInitials = !!initials;
683
- const hasIcon = !!Icon2;
683
+ const hasIcon = !!Icon3;
684
684
  const getInitialsBackground = () => "bg-[var(--color-primary)] text-[var(--color-primary-foreground)]";
685
685
  const isClickable = action || onClick;
686
686
  const handleClick = () => {
@@ -725,7 +725,7 @@ var Avatar = ({
725
725
  children: initials.substring(0, 2).toUpperCase()
726
726
  }
727
727
  ) : hasIcon ? /* @__PURE__ */ jsx(
728
- Icon2,
728
+ Icon3,
729
729
  {
730
730
  className: cn(
731
731
  "text-[var(--color-foreground)]",
@@ -3949,7 +3949,7 @@ var EmptyState = ({
3949
3949
  if (actionEvent) eventBus.emit(`UI:${actionEvent}`, {});
3950
3950
  onAction?.();
3951
3951
  };
3952
- const Icon2 = typeof icon === "string" ? ICON_MAP[icon] : icon;
3952
+ const Icon3 = typeof icon === "string" ? ICON_MAP[icon] : icon;
3953
3953
  const isDestructive = destructive || variant === "error";
3954
3954
  const isSuccess = variant === "success";
3955
3955
  const displayText = title || message || t("empty.noItems");
@@ -3962,7 +3962,7 @@ var EmptyState = ({
3962
3962
  className
3963
3963
  ),
3964
3964
  children: [
3965
- Icon2 && /* @__PURE__ */ jsx(
3965
+ Icon3 && /* @__PURE__ */ jsx(
3966
3966
  Box,
3967
3967
  {
3968
3968
  className: cn(
@@ -3970,7 +3970,7 @@ var EmptyState = ({
3970
3970
  isDestructive ? "bg-[var(--color-error)]/10" : isSuccess ? "bg-[var(--color-success)]/10" : "bg-[var(--color-muted)]"
3971
3971
  ),
3972
3972
  children: /* @__PURE__ */ jsx(
3973
- Icon2,
3973
+ Icon3,
3974
3974
  {
3975
3975
  className: cn(
3976
3976
  "h-8 w-8",
@@ -14047,7 +14047,7 @@ var StatCard = ({
14047
14047
  isLoading: externalLoading,
14048
14048
  error: externalError
14049
14049
  }) => {
14050
- const Icon2 = typeof iconProp === "string" ? resolveIcon(iconProp) ?? void 0 : iconProp;
14050
+ const Icon3 = typeof iconProp === "string" ? resolveIcon(iconProp) ?? void 0 : iconProp;
14051
14051
  const labelToUse = propLabel ?? propTitle;
14052
14052
  const eventBus = useEventBus();
14053
14053
  const { t } = useTranslate();
@@ -14190,7 +14190,7 @@ var StatCard = ({
14190
14190
  subtitle && !calculatedTrend && /* @__PURE__ */ jsx(Typography, { variant: "small", color: "secondary", children: subtitle })
14191
14191
  ] }),
14192
14192
  /* @__PURE__ */ jsxs(VStack, { gap: "xs", align: "end", children: [
14193
- Icon2 && /* @__PURE__ */ jsx(Box, { className: cn("p-3", iconBg), children: /* @__PURE__ */ jsx(Icon2, { className: cn("h-6 w-6", iconColor) }) }),
14193
+ Icon3 && /* @__PURE__ */ jsx(Box, { className: cn("p-3", iconBg), children: /* @__PURE__ */ jsx(Icon3, { className: cn("h-6 w-6", iconColor) }) }),
14194
14194
  sparklineData && sparklineData.length > 1 && (() => {
14195
14195
  const w = 80;
14196
14196
  const h = 32;
@@ -16836,6 +16836,585 @@ function CanvasEffect(props) {
16836
16836
  return /* @__PURE__ */ jsx(EmojiEffect, { ...enhancedProps });
16837
16837
  }
16838
16838
  CanvasEffect.displayName = "CanvasEffect";
16839
+
16840
+ // components/organisms/game/physics-sim/presets/mechanics.ts
16841
+ var projectileMotion = {
16842
+ id: "mechanics-projectile",
16843
+ name: "Projectile Motion",
16844
+ description: "Launch a ball and observe parabolic trajectory under gravity.",
16845
+ domain: "natural",
16846
+ gravity: { x: 0, y: 9.81 },
16847
+ bodies: [
16848
+ { id: "ball", x: 50, y: 350, vx: 80, vy: -120, mass: 1, radius: 10, color: "#e94560", fixed: false },
16849
+ { id: "ground", x: 300, y: 390, vx: 0, vy: 0, mass: 1e3, radius: 400, color: "#333", fixed: true }
16850
+ ],
16851
+ showVelocity: true,
16852
+ parameters: {
16853
+ angle: { value: 45, min: 0, max: 90, step: 1, label: "Launch angle (deg)" },
16854
+ velocity: { value: 100, min: 10, max: 200, step: 5, label: "Initial velocity (m/s)" },
16855
+ gravity: { value: 9.81, min: 0, max: 20, step: 0.1, label: "Gravity (m/s\xB2)" }
16856
+ }
16857
+ };
16858
+ var pendulum = {
16859
+ id: "mechanics-pendulum",
16860
+ name: "Simple Pendulum",
16861
+ description: "A mass on a string swinging under gravity.",
16862
+ domain: "natural",
16863
+ gravity: { x: 0, y: 9.81 },
16864
+ bodies: [
16865
+ { id: "pivot", x: 300, y: 50, vx: 0, vy: 0, mass: 1e3, radius: 5, color: "#888", fixed: true },
16866
+ { id: "bob", x: 400, y: 200, vx: 0, vy: 0, mass: 5, radius: 15, color: "#e94560", fixed: false }
16867
+ ],
16868
+ constraints: [{ bodyA: 0, bodyB: 1, length: 180, stiffness: 1 }],
16869
+ parameters: {
16870
+ length: { value: 180, min: 50, max: 300, step: 10, label: "String length (px)" },
16871
+ mass: { value: 5, min: 1, max: 20, step: 0.5, label: "Mass (kg)" },
16872
+ gravity: { value: 9.81, min: 0, max: 20, step: 0.1, label: "Gravity (m/s\xB2)" }
16873
+ }
16874
+ };
16875
+ var springOscillator = {
16876
+ id: "mechanics-spring",
16877
+ name: "Spring Oscillator",
16878
+ description: "A mass bouncing on a spring \u2014 simple harmonic motion.",
16879
+ domain: "natural",
16880
+ gravity: { x: 0, y: 0 },
16881
+ bodies: [
16882
+ { id: "anchor", x: 300, y: 50, vx: 0, vy: 0, mass: 1e3, radius: 8, color: "#888", fixed: true },
16883
+ { id: "mass", x: 300, y: 250, vx: 0, vy: 0, mass: 2, radius: 20, color: "#0f3460", fixed: false }
16884
+ ],
16885
+ constraints: [{ bodyA: 0, bodyB: 1, length: 150, stiffness: 0.5 }],
16886
+ parameters: {
16887
+ stiffness: { value: 0.5, min: 0.1, max: 2, step: 0.05, label: "Spring stiffness (k)" },
16888
+ mass: { value: 2, min: 0.5, max: 10, step: 0.5, label: "Mass (kg)" },
16889
+ damping: { value: 0, min: 0, max: 0.5, step: 0.01, label: "Damping" }
16890
+ }
16891
+ };
16892
+
16893
+ // components/organisms/game/physics-sim/presets/index.ts
16894
+ var ALL_PRESETS = [
16895
+ projectileMotion,
16896
+ pendulum,
16897
+ springOscillator
16898
+ ];
16899
+ function resolvePreset(preset) {
16900
+ if (typeof preset !== "string") return preset;
16901
+ const needle = preset.toLowerCase();
16902
+ return ALL_PRESETS.find(
16903
+ (p2) => p2.id === preset || p2.id.includes(needle) || p2.name.toLowerCase().includes(needle)
16904
+ ) ?? projectileMotion;
16905
+ }
16906
+ function SimulationCanvas({
16907
+ preset: presetProp,
16908
+ width = 600,
16909
+ height = 400,
16910
+ running,
16911
+ speed = 1,
16912
+ className
16913
+ }) {
16914
+ const preset = useMemo(() => resolvePreset(presetProp), [presetProp]);
16915
+ const canvasRef = useRef(null);
16916
+ const bodiesRef = useRef(structuredClone(preset.bodies));
16917
+ useEffect(() => {
16918
+ bodiesRef.current = structuredClone(preset.bodies);
16919
+ }, [preset]);
16920
+ const step = useCallback(() => {
16921
+ const dt = Math.min(1 / 60 * speed, 1 / 15);
16922
+ const gx = preset.gravity?.x ?? 0;
16923
+ const gy = preset.gravity?.y ?? 9.81;
16924
+ const bodies = bodiesRef.current;
16925
+ const maxVel = Math.max(width, height) * 5;
16926
+ for (const body of bodies) {
16927
+ if (body.fixed) continue;
16928
+ body.vx += gx * dt;
16929
+ body.vy += gy * dt;
16930
+ body.vx = Math.max(-maxVel, Math.min(maxVel, body.vx));
16931
+ body.vy = Math.max(-maxVel, Math.min(maxVel, body.vy));
16932
+ body.x += body.vx * dt;
16933
+ body.y += body.vy * dt;
16934
+ if (body.y + body.radius > height) {
16935
+ body.y = height - body.radius;
16936
+ body.vy = -body.vy * 0.8;
16937
+ }
16938
+ if (body.y - body.radius < 0) {
16939
+ body.y = body.radius;
16940
+ body.vy = -body.vy * 0.8;
16941
+ }
16942
+ if (body.x + body.radius > width) {
16943
+ body.x = width - body.radius;
16944
+ body.vx = -body.vx * 0.8;
16945
+ }
16946
+ if (body.x - body.radius < 0) {
16947
+ body.x = body.radius;
16948
+ body.vx = -body.vx * 0.8;
16949
+ }
16950
+ }
16951
+ if (preset.constraints) {
16952
+ for (const c of preset.constraints) {
16953
+ const a = bodies[c.bodyA];
16954
+ const b = bodies[c.bodyB];
16955
+ if (!a || !b) continue;
16956
+ const dx = b.x - a.x;
16957
+ const dy = b.y - a.y;
16958
+ const dist = Math.sqrt(dx * dx + dy * dy) || 1e-3;
16959
+ const diff = (dist - c.length) / dist;
16960
+ const fx = dx * diff * c.stiffness;
16961
+ const fy = dy * diff * c.stiffness;
16962
+ if (!a.fixed) {
16963
+ a.vx += fx * dt;
16964
+ a.vy += fy * dt;
16965
+ }
16966
+ if (!b.fixed) {
16967
+ b.vx -= fx * dt;
16968
+ b.vy -= fy * dt;
16969
+ }
16970
+ }
16971
+ }
16972
+ }, [preset, width, height, speed]);
16973
+ const draw = useCallback(() => {
16974
+ const canvas = canvasRef.current;
16975
+ if (!canvas) return;
16976
+ const ctx = canvas.getContext("2d");
16977
+ if (!ctx) return;
16978
+ const bodies = bodiesRef.current;
16979
+ ctx.clearRect(0, 0, width, height);
16980
+ ctx.fillStyle = preset.backgroundColor ?? "#1a1a2e";
16981
+ ctx.fillRect(0, 0, width, height);
16982
+ if (preset.constraints) {
16983
+ for (const c of preset.constraints) {
16984
+ const a = bodies[c.bodyA];
16985
+ const b = bodies[c.bodyB];
16986
+ if (a && b) {
16987
+ ctx.beginPath();
16988
+ ctx.moveTo(a.x, a.y);
16989
+ ctx.lineTo(b.x, b.y);
16990
+ ctx.strokeStyle = "#533483";
16991
+ ctx.lineWidth = 1;
16992
+ ctx.setLineDash([4, 4]);
16993
+ ctx.stroke();
16994
+ ctx.setLineDash([]);
16995
+ }
16996
+ }
16997
+ }
16998
+ for (const body of bodies) {
16999
+ ctx.beginPath();
17000
+ ctx.arc(body.x, body.y, body.radius, 0, Math.PI * 2);
17001
+ ctx.fillStyle = body.color ?? "#e94560";
17002
+ ctx.fill();
17003
+ if (preset.showVelocity) {
17004
+ ctx.beginPath();
17005
+ ctx.moveTo(body.x, body.y);
17006
+ ctx.lineTo(body.x + body.vx * 0.1, body.y + body.vy * 0.1);
17007
+ ctx.strokeStyle = "#16213e";
17008
+ ctx.lineWidth = 2;
17009
+ ctx.stroke();
17010
+ }
17011
+ }
17012
+ }, [width, height, preset]);
17013
+ useEffect(() => {
17014
+ if (!running) return;
17015
+ let raf;
17016
+ const loop = () => {
17017
+ step();
17018
+ draw();
17019
+ raf = requestAnimationFrame(loop);
17020
+ };
17021
+ raf = requestAnimationFrame(loop);
17022
+ return () => cancelAnimationFrame(raf);
17023
+ }, [running, step, draw]);
17024
+ useEffect(() => {
17025
+ draw();
17026
+ }, [draw]);
17027
+ useEffect(() => {
17028
+ if (typeof window === "undefined") return;
17029
+ const canvas = canvasRef.current;
17030
+ if (!canvas) return;
17031
+ bindCanvasCapture(() => canvas.toDataURL("image/png"));
17032
+ return () => {
17033
+ bindCanvasCapture(() => null);
17034
+ };
17035
+ }, []);
17036
+ return /* @__PURE__ */ jsx(Box, { className: cn("flex justify-center", className), children: /* @__PURE__ */ jsx(
17037
+ "canvas",
17038
+ {
17039
+ ref: canvasRef,
17040
+ width,
17041
+ height,
17042
+ className: "rounded-md block max-w-full h-auto"
17043
+ }
17044
+ ) });
17045
+ }
17046
+ SimulationCanvas.displayName = "SimulationCanvas";
17047
+ function SimulationControls({
17048
+ running,
17049
+ speed,
17050
+ parameters,
17051
+ onPlay,
17052
+ onPause,
17053
+ onStep,
17054
+ onReset,
17055
+ onSpeedChange,
17056
+ onParameterChange,
17057
+ className
17058
+ }) {
17059
+ return /* @__PURE__ */ jsxs(VStack, { gap: "md", className, children: [
17060
+ /* @__PURE__ */ jsxs(HStack, { gap: "sm", align: "center", children: [
17061
+ 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" }),
17062
+ /* @__PURE__ */ jsx(Button, { size: "sm", variant: "ghost", onClick: onStep, icon: SkipForward, disabled: running, children: "Step" }),
17063
+ /* @__PURE__ */ jsx(Button, { size: "sm", variant: "ghost", onClick: onReset, icon: RotateCcw, children: "Reset" })
17064
+ ] }),
17065
+ /* @__PURE__ */ jsxs(VStack, { gap: "xs", children: [
17066
+ /* @__PURE__ */ jsxs(Typography, { variant: "caption", color: "muted", children: [
17067
+ "Speed: ",
17068
+ speed.toFixed(1),
17069
+ "x"
17070
+ ] }),
17071
+ /* @__PURE__ */ jsx(
17072
+ "input",
17073
+ {
17074
+ type: "range",
17075
+ min: 0.1,
17076
+ max: 5,
17077
+ step: 0.1,
17078
+ value: speed,
17079
+ onChange: (e) => onSpeedChange(parseFloat(e.target.value)),
17080
+ className: "w-full"
17081
+ }
17082
+ )
17083
+ ] }),
17084
+ Object.entries(parameters).map(([name, param]) => /* @__PURE__ */ jsxs(VStack, { gap: "xs", children: [
17085
+ /* @__PURE__ */ jsxs(Typography, { variant: "caption", color: "muted", children: [
17086
+ param.label,
17087
+ ": ",
17088
+ param.value.toFixed(2)
17089
+ ] }),
17090
+ /* @__PURE__ */ jsx(
17091
+ "input",
17092
+ {
17093
+ type: "range",
17094
+ min: param.min,
17095
+ max: param.max,
17096
+ step: param.step,
17097
+ value: param.value,
17098
+ onChange: (e) => onParameterChange(name, parseFloat(e.target.value)),
17099
+ className: "w-full"
17100
+ }
17101
+ )
17102
+ ] }, name))
17103
+ ] });
17104
+ }
17105
+ SimulationControls.displayName = "SimulationControls";
17106
+ function SimulationGraph({
17107
+ label,
17108
+ unit,
17109
+ data,
17110
+ maxPoints = 200,
17111
+ width = 300,
17112
+ height = 120,
17113
+ color = "#e94560",
17114
+ className
17115
+ }) {
17116
+ const canvasRef = useRef(null);
17117
+ const visibleData = data.slice(-maxPoints);
17118
+ useEffect(() => {
17119
+ const canvas = canvasRef.current;
17120
+ if (!canvas || visibleData.length < 2) return;
17121
+ const ctx = canvas.getContext("2d");
17122
+ if (!ctx) return;
17123
+ ctx.clearRect(0, 0, width, height);
17124
+ ctx.fillStyle = "#0f0f23";
17125
+ ctx.fillRect(0, 0, width, height);
17126
+ ctx.strokeStyle = "#1a1a3e";
17127
+ ctx.lineWidth = 0.5;
17128
+ for (let i = 0; i < 5; i++) {
17129
+ const y = height / 5 * i;
17130
+ ctx.beginPath();
17131
+ ctx.moveTo(0, y);
17132
+ ctx.lineTo(width, y);
17133
+ ctx.stroke();
17134
+ }
17135
+ let minVal = Infinity;
17136
+ let maxVal = -Infinity;
17137
+ for (const pt of visibleData) {
17138
+ if (pt.value < minVal) minVal = pt.value;
17139
+ if (pt.value > maxVal) maxVal = pt.value;
17140
+ }
17141
+ const range = maxVal - minVal || 1;
17142
+ const pad = height * 0.1;
17143
+ ctx.beginPath();
17144
+ ctx.strokeStyle = color;
17145
+ ctx.lineWidth = 2;
17146
+ for (let i = 0; i < visibleData.length; i++) {
17147
+ const x = i / (maxPoints - 1) * width;
17148
+ const y = pad + (maxVal - visibleData[i].value) / range * (height - 2 * pad);
17149
+ if (i === 0) ctx.moveTo(x, y);
17150
+ else ctx.lineTo(x, y);
17151
+ }
17152
+ ctx.stroke();
17153
+ const last = visibleData[visibleData.length - 1];
17154
+ ctx.fillStyle = color;
17155
+ ctx.font = "12px monospace";
17156
+ ctx.fillText(`${last.value.toFixed(2)} ${unit}`, width - 80, 16);
17157
+ }, [visibleData, width, height, color, unit, maxPoints]);
17158
+ return /* @__PURE__ */ jsx(Card, { padding: "sm", className, children: /* @__PURE__ */ jsxs(VStack, { gap: "xs", children: [
17159
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", weight: "bold", children: label }),
17160
+ /* @__PURE__ */ jsx("canvas", { ref: canvasRef, width, height, className: "rounded" })
17161
+ ] }) });
17162
+ }
17163
+ SimulationGraph.displayName = "SimulationGraph";
17164
+ var CHART_COLORS = [
17165
+ "var(--color-primary)",
17166
+ "var(--color-success)",
17167
+ "var(--color-warning)",
17168
+ "var(--color-error)",
17169
+ "var(--color-info)",
17170
+ "var(--color-accent)"
17171
+ ];
17172
+ var BarChart = ({ data, height, showValues }) => {
17173
+ const maxValue = Math.max(...data.map((d) => d.value), 1);
17174
+ return /* @__PURE__ */ jsx(HStack, { gap: "xs", align: "end", className: "w-full", style: { height }, children: data.map((point, idx) => {
17175
+ const barHeight = point.value / maxValue * 100;
17176
+ const color = point.color || CHART_COLORS[idx % CHART_COLORS.length];
17177
+ return /* @__PURE__ */ jsxs(VStack, { gap: "xs", align: "center", flex: true, className: "min-w-0", children: [
17178
+ showValues && /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "secondary", className: "tabular-nums", children: point.value }),
17179
+ /* @__PURE__ */ jsx(
17180
+ Box,
17181
+ {
17182
+ className: cn(
17183
+ "w-full rounded-t-[var(--radius-sm)] transition-all duration-500 ease-out min-h-[4px]"
17184
+ ),
17185
+ style: {
17186
+ height: `${barHeight}%`,
17187
+ backgroundColor: color
17188
+ }
17189
+ }
17190
+ ),
17191
+ /* @__PURE__ */ jsx(
17192
+ Typography,
17193
+ {
17194
+ variant: "caption",
17195
+ color: "secondary",
17196
+ className: "truncate w-full text-center",
17197
+ children: point.label
17198
+ }
17199
+ )
17200
+ ] }, point.label);
17201
+ }) });
17202
+ };
17203
+ var PieChart = ({ data, height, showValues, donut = false }) => {
17204
+ const total = data.reduce((sum, d) => sum + d.value, 0);
17205
+ const size = Math.min(height, 200);
17206
+ const radius = size / 2 - 8;
17207
+ const innerRadius = donut ? radius * 0.6 : 0;
17208
+ const center = size / 2;
17209
+ const segments = useMemo(() => {
17210
+ let currentAngle = -Math.PI / 2;
17211
+ return data.map((point, idx) => {
17212
+ const angle = point.value / total * 2 * Math.PI;
17213
+ const startAngle = currentAngle;
17214
+ const endAngle = currentAngle + angle;
17215
+ currentAngle = endAngle;
17216
+ const largeArc = angle > Math.PI ? 1 : 0;
17217
+ const x1 = center + radius * Math.cos(startAngle);
17218
+ const y1 = center + radius * Math.sin(startAngle);
17219
+ const x2 = center + radius * Math.cos(endAngle);
17220
+ const y2 = center + radius * Math.sin(endAngle);
17221
+ let d;
17222
+ if (innerRadius > 0) {
17223
+ const ix1 = center + innerRadius * Math.cos(startAngle);
17224
+ const iy1 = center + innerRadius * Math.sin(startAngle);
17225
+ const ix2 = center + innerRadius * Math.cos(endAngle);
17226
+ const iy2 = center + innerRadius * Math.sin(endAngle);
17227
+ d = `M ${ix1} ${iy1} L ${x1} ${y1} A ${radius} ${radius} 0 ${largeArc} 1 ${x2} ${y2} L ${ix2} ${iy2} A ${innerRadius} ${innerRadius} 0 ${largeArc} 0 ${ix1} ${iy1} Z`;
17228
+ } else {
17229
+ d = `M ${center} ${center} L ${x1} ${y1} A ${radius} ${radius} 0 ${largeArc} 1 ${x2} ${y2} Z`;
17230
+ }
17231
+ return {
17232
+ d,
17233
+ color: point.color || CHART_COLORS[idx % CHART_COLORS.length],
17234
+ label: point.label,
17235
+ value: point.value,
17236
+ percentage: (point.value / total * 100).toFixed(1)
17237
+ };
17238
+ });
17239
+ }, [data, total, radius, innerRadius, center]);
17240
+ return /* @__PURE__ */ jsxs(HStack, { gap: "md", align: "center", justify: "center", className: "w-full", children: [
17241
+ /* @__PURE__ */ jsxs("svg", { width: size, height: size, viewBox: `0 0 ${size} ${size}`, children: [
17242
+ segments.map((seg, idx) => /* @__PURE__ */ jsx(
17243
+ "path",
17244
+ {
17245
+ d: seg.d,
17246
+ fill: seg.color,
17247
+ stroke: "var(--color-card)",
17248
+ strokeWidth: "2",
17249
+ className: "transition-opacity duration-200 hover:opacity-80"
17250
+ },
17251
+ idx
17252
+ )),
17253
+ donut && /* @__PURE__ */ jsx(
17254
+ "text",
17255
+ {
17256
+ x: center,
17257
+ y: center,
17258
+ textAnchor: "middle",
17259
+ dominantBaseline: "middle",
17260
+ fill: "var(--color-foreground)",
17261
+ fontSize: "14",
17262
+ fontWeight: "bold",
17263
+ children: total
17264
+ }
17265
+ )
17266
+ ] }),
17267
+ showValues && /* @__PURE__ */ jsx(VStack, { gap: "xs", children: segments.map((seg, idx) => /* @__PURE__ */ jsxs(HStack, { gap: "xs", align: "center", children: [
17268
+ /* @__PURE__ */ jsx(
17269
+ Box,
17270
+ {
17271
+ className: "w-3 h-3 rounded-[var(--radius-sm)] flex-shrink-0",
17272
+ style: { backgroundColor: seg.color }
17273
+ }
17274
+ ),
17275
+ /* @__PURE__ */ jsxs(Typography, { variant: "caption", color: "secondary", className: "truncate", children: [
17276
+ seg.label,
17277
+ ": ",
17278
+ seg.percentage,
17279
+ "%"
17280
+ ] })
17281
+ ] }, idx)) })
17282
+ ] });
17283
+ };
17284
+ var LineChart2 = ({ data, height, showValues, fill = false }) => {
17285
+ const maxValue = Math.max(...data.map((d) => d.value), 1);
17286
+ const width = 400;
17287
+ const padding = { top: 20, right: 20, bottom: 30, left: 40 };
17288
+ const chartWidth = width - padding.left - padding.right;
17289
+ const chartHeight = height - padding.top - padding.bottom;
17290
+ const points = useMemo(() => {
17291
+ return data.map((point, idx) => ({
17292
+ x: padding.left + idx / Math.max(data.length - 1, 1) * chartWidth,
17293
+ y: padding.top + chartHeight - point.value / maxValue * chartHeight,
17294
+ ...point
17295
+ }));
17296
+ }, [data, maxValue, chartWidth, chartHeight, padding]);
17297
+ const linePath = points.map((p2, i) => `${i === 0 ? "M" : "L"} ${p2.x} ${p2.y}`).join(" ");
17298
+ const areaPath = `${linePath} L ${points[points.length - 1]?.x ?? 0} ${padding.top + chartHeight} L ${padding.left} ${padding.top + chartHeight} Z`;
17299
+ return /* @__PURE__ */ jsxs("svg", { width: "100%", height, viewBox: `0 0 ${width} ${height}`, preserveAspectRatio: "xMidYMid meet", children: [
17300
+ [0, 0.25, 0.5, 0.75, 1].map((frac) => {
17301
+ const y = padding.top + chartHeight * (1 - frac);
17302
+ return /* @__PURE__ */ jsx(
17303
+ "line",
17304
+ {
17305
+ x1: padding.left,
17306
+ y1: y,
17307
+ x2: width - padding.right,
17308
+ y2: y,
17309
+ stroke: "var(--color-border)",
17310
+ strokeDasharray: "4 4",
17311
+ opacity: 0.5
17312
+ },
17313
+ frac
17314
+ );
17315
+ }),
17316
+ fill && /* @__PURE__ */ jsx("path", { d: areaPath, fill: "var(--color-primary)", opacity: 0.1 }),
17317
+ /* @__PURE__ */ jsx("path", { d: linePath, fill: "none", stroke: "var(--color-primary)", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }),
17318
+ points.map((p2, idx) => /* @__PURE__ */ jsxs("g", { children: [
17319
+ /* @__PURE__ */ jsx("circle", { cx: p2.x, cy: p2.y, r: "4", fill: "var(--color-card)", stroke: "var(--color-primary)", strokeWidth: "2" }),
17320
+ showValues && /* @__PURE__ */ jsx("text", { x: p2.x, y: p2.y - 10, textAnchor: "middle", fill: "var(--color-foreground)", fontSize: "10", fontWeight: "500", children: p2.value }),
17321
+ /* @__PURE__ */ jsx(
17322
+ "text",
17323
+ {
17324
+ x: p2.x,
17325
+ y: height - 8,
17326
+ textAnchor: "middle",
17327
+ fill: "var(--color-muted-foreground)",
17328
+ fontSize: "9",
17329
+ children: p2.label
17330
+ }
17331
+ )
17332
+ ] }, idx))
17333
+ ] });
17334
+ };
17335
+ var Chart = ({
17336
+ title,
17337
+ subtitle,
17338
+ chartType = "bar",
17339
+ series,
17340
+ data: simpleData,
17341
+ height = 200,
17342
+ showLegend = true,
17343
+ showValues = false,
17344
+ actions,
17345
+ entity,
17346
+ isLoading = false,
17347
+ error,
17348
+ className
17349
+ }) => {
17350
+ const eventBus = useEventBus();
17351
+ const { t } = useTranslate();
17352
+ const handleAction = useCallback(
17353
+ (action) => {
17354
+ if (action.event) {
17355
+ eventBus.emit(`UI:${action.event}`, {});
17356
+ }
17357
+ },
17358
+ [eventBus]
17359
+ );
17360
+ const normalizedData = useMemo(() => {
17361
+ if (simpleData) return simpleData;
17362
+ if (series && series.length > 0) return series[0].data;
17363
+ return [];
17364
+ }, [simpleData, series]);
17365
+ if (isLoading) {
17366
+ return /* @__PURE__ */ jsx(LoadingState, { message: "Loading chart...", className });
17367
+ }
17368
+ if (error) {
17369
+ return /* @__PURE__ */ jsx(
17370
+ ErrorState,
17371
+ {
17372
+ title: "Chart error",
17373
+ message: error.message,
17374
+ className
17375
+ }
17376
+ );
17377
+ }
17378
+ if (normalizedData.length === 0) {
17379
+ return /* @__PURE__ */ jsx(EmptyState, { title: t("empty.noData"), description: t("empty.noData"), className });
17380
+ }
17381
+ return /* @__PURE__ */ jsx(Card, { className: cn("p-6", className), children: /* @__PURE__ */ jsxs(VStack, { gap: "md", children: [
17382
+ (title || subtitle || actions && actions.length > 0) && /* @__PURE__ */ jsxs(HStack, { justify: "between", align: "start", children: [
17383
+ /* @__PURE__ */ jsxs(VStack, { gap: "xs", children: [
17384
+ title && /* @__PURE__ */ jsx(Typography, { variant: "h5", weight: "semibold", children: title }),
17385
+ subtitle && /* @__PURE__ */ jsx(Typography, { variant: "small", color: "secondary", children: subtitle })
17386
+ ] }),
17387
+ actions && actions.length > 0 && /* @__PURE__ */ jsx(HStack, { gap: "xs", children: actions.map((action, idx) => /* @__PURE__ */ jsx(
17388
+ Badge,
17389
+ {
17390
+ variant: "default",
17391
+ className: "cursor-pointer hover:opacity-80 transition-opacity",
17392
+ onClick: () => handleAction(action),
17393
+ children: action.label
17394
+ },
17395
+ idx
17396
+ )) })
17397
+ ] }),
17398
+ /* @__PURE__ */ jsxs(Box, { className: "w-full", children: [
17399
+ chartType === "bar" && /* @__PURE__ */ jsx(BarChart, { data: normalizedData, height, showValues }),
17400
+ chartType === "line" && /* @__PURE__ */ jsx(LineChart2, { data: normalizedData, height, showValues }),
17401
+ chartType === "area" && /* @__PURE__ */ jsx(LineChart2, { data: normalizedData, height, showValues, fill: true }),
17402
+ chartType === "pie" && /* @__PURE__ */ jsx(PieChart, { data: normalizedData, height, showValues: showLegend }),
17403
+ chartType === "donut" && /* @__PURE__ */ jsx(PieChart, { data: normalizedData, height, showValues: showLegend, donut: true })
17404
+ ] }),
17405
+ showLegend && series && series.length > 1 && /* @__PURE__ */ jsx(HStack, { gap: "md", justify: "center", wrap: true, children: series.map((s, idx) => /* @__PURE__ */ jsxs(HStack, { gap: "xs", align: "center", children: [
17406
+ /* @__PURE__ */ jsx(
17407
+ Box,
17408
+ {
17409
+ className: "w-3 h-3 rounded-[var(--radius-full)] flex-shrink-0",
17410
+ style: { backgroundColor: s.color || CHART_COLORS[idx % CHART_COLORS.length] }
17411
+ }
17412
+ ),
17413
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "secondary", children: s.name })
17414
+ ] }, idx)) })
17415
+ ] }) });
17416
+ };
17417
+ Chart.displayName = "Chart";
16839
17418
  function VStackPattern({
16840
17419
  gap = "md",
16841
17420
  align = "stretch",
@@ -16955,14 +17534,16 @@ function ButtonPattern({
16955
17534
  size = "md",
16956
17535
  disabled = false,
16957
17536
  onClick,
17537
+ event,
16958
17538
  icon,
16959
17539
  iconPosition = "left",
16960
17540
  className
16961
17541
  }) {
16962
17542
  const { emit } = useEventBus();
17543
+ const eventName = onClick ?? event;
16963
17544
  const handleClick = () => {
16964
- if (onClick && !disabled) {
16965
- emit(`UI:${onClick}`, {});
17545
+ if (eventName && !disabled) {
17546
+ emit(`UI:${eventName}`, {});
16966
17547
  }
16967
17548
  };
16968
17549
  return /* @__PURE__ */ jsxs(
@@ -17818,6 +18399,10 @@ var COMPONENT_REGISTRY = {
17818
18399
  InventoryPanel,
17819
18400
  GameHud,
17820
18401
  GameMenu,
18402
+ IsometricCanvas,
18403
+ PlatformerCanvas,
18404
+ SimulationCanvas,
18405
+ Chart,
17821
18406
  FilterGroup: ButtonGroup,
17822
18407
  ErrorState: EmptyState,
17823
18408
  Toast: AlertPattern,
@@ -18200,4 +18785,4 @@ function UISlotRenderer({
18200
18785
  }
18201
18786
  UISlotRenderer.displayName = "UISlotRenderer";
18202
18787
 
18203
- export { Accordion, ActionButton, ActionButtons, Alert, AnimatedCounter, Avatar, Badge, Box, Breadcrumb, Button, ButtonGroup, CalendarGrid, CanvasEffect, Card, Card2, CardBody, CardContent, CardFooter, CardGrid, CardHeader, CardTitle, Carousel, Center, ChartLegend, Checkbox, ChoiceButton, CodeBlock, CombatLog, ComboCounter, ConditionalWrapper, ConfettiEffect, Container, ControlButton, CraftingRecipe, DIAMOND_TOP_Y, DPad, DamageNumber, DataGrid, DataList, DataTable, DateRangeSelector, DayCell, DetailPanel, DialogueBox, DialogueBubble, Divider, Drawer, EmptyState, EnemyPlate, EntityDisplayEvents, ErrorBoundary, ErrorState, FEATURE_COLORS, FLOOR_HEIGHT, FilterGroup, Flex, FlipCard, FlipContainer, FloatingActionButton, Form, FormField, FormSectionHeader, GameCanvas2D, GameHud, GameMenu, GameOverScreen, GraphView, Grid, HStack, Heading, HealthBar, HealthPanel, Icon, InfiniteScrollSentinel, Input, InputGroup, InventoryGrid, InventoryPanel, IsometricCanvas, IsometricCanvas_default, ItemSlot, Label, LawReferenceTooltip, Lightbox, LineChart, LoadingState, MapView, MarkdownContent, MasterDetail, Menu, Meter, MiniMap, Modal, NumberStepper, Overlay, PageHeader, Pagination, PlatformerCanvas, Popover, PowerupSlots, ProgressBar, ProgressDots, PullToRefresh, QuestTracker, QuizBlock, Radio, RangeSlider, RelationSelect, RepeatableFormSection, ResourceBar, ResourceCounter, ScaledDiagram, ScoreBoard, ScoreDisplay, SearchInput, Select, SidePanel, SimpleGrid, Skeleton, SlotContentRenderer, SortableList, Spacer, Spinner, Sprite, Stack, StarRating, StatBadge, StatCard, StatDisplay, StateIndicator, StatusDot, StatusEffect, SuspenseConfigProvider, SwipeableRow, Switch, TILE_HEIGHT, TILE_WIDTH, Tabs, Text, TextHighlight, Textarea, ThemeSelector, ThemeToggle, TimeSlotCell, TimerDisplay, Toast, Tooltip, TrendIndicator, TurnIndicator, TurnPanel, TypewriterText, Typography, UISlotComponent, UISlotRenderer, UnitCommandBar, UploadDropZone, VStack, ViolationAlert, WaypointMarker, WizardNavigation, WizardProgress, XPBar, drawSprite, isoToScreen, screenToIso, useCamera, useImageCache };
18788
+ export { ALL_PRESETS, Accordion, ActionButton, ActionButtons, Alert, AnimatedCounter, Avatar, Badge, Box, Breadcrumb, Button, ButtonGroup, CalendarGrid, CanvasEffect, Card, Card2, CardBody, CardContent, CardFooter, CardGrid, CardHeader, CardTitle, Carousel, Center, Chart, ChartLegend, Checkbox, ChoiceButton, CodeBlock, CombatLog, ComboCounter, ConditionalWrapper, ConfettiEffect, Container, ControlButton, CraftingRecipe, DIAMOND_TOP_Y, DPad, DamageNumber, DataGrid, DataList, DataTable, DateRangeSelector, DayCell, DetailPanel, DialogueBox, DialogueBubble, Divider, Drawer, EmptyState, EnemyPlate, EntityDisplayEvents, ErrorBoundary, ErrorState, FEATURE_COLORS, FLOOR_HEIGHT, FilterGroup, Flex, FlipCard, FlipContainer, FloatingActionButton, Form, FormField, FormSectionHeader, GameCanvas2D, GameHud, GameMenu, GameOverScreen, GraphView, Grid, HStack, Heading, HealthBar, HealthPanel, Icon, InfiniteScrollSentinel, Input, InputGroup, InventoryGrid, InventoryPanel, IsometricCanvas, IsometricCanvas_default, ItemSlot, Label, LawReferenceTooltip, Lightbox, LineChart, LoadingState, MapView, MarkdownContent, MasterDetail, Menu, Meter, MiniMap, Modal, NumberStepper, Overlay, PageHeader, Pagination, PlatformerCanvas, Popover, PowerupSlots, ProgressBar, ProgressDots, PullToRefresh, QuestTracker, QuizBlock, Radio, RangeSlider, RelationSelect, RepeatableFormSection, ResourceBar, ResourceCounter, ScaledDiagram, ScoreBoard, ScoreDisplay, SearchInput, Select, SidePanel, SimpleGrid, SimulationCanvas, SimulationControls, SimulationGraph, Skeleton, SlotContentRenderer, SortableList, Spacer, Spinner, Sprite, Stack, StarRating, StatBadge, StatCard, StatDisplay, StateIndicator, StatusDot, StatusEffect, SuspenseConfigProvider, SwipeableRow, Switch, TILE_HEIGHT, TILE_WIDTH, Tabs, Text, TextHighlight, Textarea, ThemeSelector, ThemeToggle, TimeSlotCell, TimerDisplay, Toast, Tooltip, TrendIndicator, TurnIndicator, TurnPanel, TypewriterText, Typography, UISlotComponent, UISlotRenderer, UnitCommandBar, UploadDropZone, VStack, ViolationAlert, WaypointMarker, WizardNavigation, WizardProgress, XPBar, drawSprite, isoToScreen, pendulum, projectileMotion, screenToIso, springOscillator, useCamera, useImageCache };