rud-dashboard 0.1.4

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.
@@ -0,0 +1,1656 @@
1
+ "use client"
2
+
3
+ // src/components/Dashboard.tsx
4
+ import * as React2 from "react";
5
+ import { useState as useState3, useRef as useRef2, useCallback as useCallback2, useEffect as useEffect2, useMemo } from "react";
6
+ import { Plus as Plus2, X as X2, MoreHorizontal } from "lucide-react";
7
+
8
+ // src/components/DashboardToolbar.tsx
9
+ import React from "react";
10
+ import { Zap, Edit3, Eye, Plus, X, Grid as Grid2, TrendingUp, ChevronLeft, ChevronRight } from "lucide-react";
11
+
12
+ // src/ui/button.tsx
13
+ import { cva } from "class-variance-authority";
14
+
15
+ // src/lib/utils.ts
16
+ import { clsx } from "clsx";
17
+ import { twMerge } from "tailwind-merge";
18
+ function cn(...inputs) {
19
+ return twMerge(clsx(inputs));
20
+ }
21
+
22
+ // src/ui/button.tsx
23
+ import { jsx } from "preact/jsx-runtime";
24
+ var buttonVariants = cva(
25
+ "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
26
+ {
27
+ variants: {
28
+ variant: {
29
+ default: "bg-primary text-primary-foreground hover:bg-primary/90",
30
+ destructive: "bg-destructive text-destructive-foreground hover:bg-destructive/90",
31
+ outline: "border border-input bg-background hover:bg-accent hover:text-accent-foreground",
32
+ secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
33
+ ghost: "hover:bg-accent hover:text-accent-foreground",
34
+ link: "text-primary underline-offset-4 hover:underline"
35
+ },
36
+ size: {
37
+ default: "h-10 px-4 py-2",
38
+ sm: "h-9 rounded-md px-3",
39
+ lg: "h-11 rounded-md px-8",
40
+ icon: "h-10 w-10"
41
+ }
42
+ },
43
+ defaultVariants: {
44
+ variant: "default",
45
+ size: "default"
46
+ }
47
+ }
48
+ );
49
+ function Button({ className, variant, size, ref, ...props }) {
50
+ return /* @__PURE__ */ jsx(
51
+ "button",
52
+ {
53
+ className: cn(buttonVariants({ variant, size }), className),
54
+ ref,
55
+ ...props
56
+ }
57
+ );
58
+ }
59
+ Button.displayName = "Button";
60
+
61
+ // src/ui/tooltip.tsx
62
+ import { createContext, useContext, useState, useRef, useEffect, useLayoutEffect } from "react";
63
+ import { createPortal } from "react-dom";
64
+ import { Fragment, jsx as jsx2 } from "preact/jsx-runtime";
65
+ var TooltipContext = createContext(void 0);
66
+ function TooltipProvider({ children }) {
67
+ return /* @__PURE__ */ jsx2(Fragment, { children });
68
+ }
69
+ function Tooltip({ children, delayDuration = 200 }) {
70
+ const [open, setOpen] = useState(false);
71
+ const triggerRef = useRef(null);
72
+ const content = /* @__PURE__ */ jsx2("div", { ref: triggerRef, className: "relative inline-block", children });
73
+ return /* @__PURE__ */ jsx2(TooltipContext.Provider, { value: { open, setOpen, delay: delayDuration, triggerRef }, children: content });
74
+ }
75
+ function TooltipTrigger({ children, asChild, className, ...props }) {
76
+ const context = useContext(TooltipContext);
77
+ if (!context) throw new Error("TooltipTrigger must be used within Tooltip");
78
+ const { setOpen, delay } = context;
79
+ const timeoutRef = useRef();
80
+ const handleMouseEnter = () => {
81
+ timeoutRef.current = setTimeout(() => setOpen(true), delay);
82
+ };
83
+ const handleMouseLeave = () => {
84
+ if (timeoutRef.current) clearTimeout(timeoutRef.current);
85
+ setOpen(false);
86
+ };
87
+ useEffect(() => {
88
+ return () => {
89
+ if (timeoutRef.current) clearTimeout(timeoutRef.current);
90
+ };
91
+ }, []);
92
+ return /* @__PURE__ */ jsx2(
93
+ "div",
94
+ {
95
+ onMouseEnter: handleMouseEnter,
96
+ onMouseLeave: handleMouseLeave,
97
+ className: cn("inline-block", className),
98
+ ...props,
99
+ children
100
+ }
101
+ );
102
+ }
103
+ TooltipTrigger.displayName = "TooltipTrigger";
104
+ function TooltipContent({ className, sideOffset = 4, side = "top", children, ...props }) {
105
+ const context = useContext(TooltipContext);
106
+ const contentRef = useRef(null);
107
+ const [position, setPosition] = useState({ top: 0, left: 0 });
108
+ const [isPositioned, setIsPositioned] = useState(false);
109
+ if (!context) throw new Error("TooltipContent must be used within Tooltip");
110
+ const { open, triggerRef } = context;
111
+ useLayoutEffect(() => {
112
+ if (open && triggerRef.current && contentRef.current) {
113
+ const updatePosition = () => {
114
+ const triggerRect = triggerRef.current.getBoundingClientRect();
115
+ const contentRect = contentRef.current.getBoundingClientRect();
116
+ const scrollY = window.scrollY;
117
+ const scrollX = window.scrollX;
118
+ let top = 0;
119
+ let left = 0;
120
+ left = triggerRect.left + scrollX + triggerRect.width / 2 - contentRect.width / 2;
121
+ top = triggerRect.top + scrollY - contentRect.height - sideOffset;
122
+ if (triggerRect.top - contentRect.height - sideOffset < 0) {
123
+ top = triggerRect.bottom + scrollY + sideOffset;
124
+ }
125
+ setPosition({ top, left });
126
+ setIsPositioned(true);
127
+ };
128
+ updatePosition();
129
+ window.addEventListener("resize", updatePosition);
130
+ window.addEventListener("scroll", updatePosition, true);
131
+ return () => {
132
+ window.removeEventListener("resize", updatePosition);
133
+ window.removeEventListener("scroll", updatePosition, true);
134
+ setIsPositioned(false);
135
+ };
136
+ }
137
+ }, [open, sideOffset]);
138
+ if (!open) return null;
139
+ return createPortal(
140
+ /* @__PURE__ */ jsx2(
141
+ "div",
142
+ {
143
+ ref: contentRef,
144
+ style: {
145
+ top: position.top,
146
+ left: position.left,
147
+ position: "absolute",
148
+ opacity: isPositioned ? 1 : 0,
149
+ pointerEvents: "none"
150
+ },
151
+ className: cn(
152
+ "z-50 overflow-hidden rounded-md border border-gray-200 dark:border-gray-700",
153
+ "bg-white dark:bg-gray-800 px-3 py-1.5 text-sm",
154
+ "text-gray-900 dark:text-gray-100 shadow-lg dark:shadow-gray-900/50",
155
+ "animate-in fade-in-0 zoom-in-95 duration-200",
156
+ "whitespace-nowrap",
157
+ className
158
+ ),
159
+ ...props,
160
+ children
161
+ }
162
+ ),
163
+ document.body
164
+ );
165
+ }
166
+ TooltipContent.displayName = "TooltipContent";
167
+
168
+ // src/components/widgets/BasicWidget.tsx
169
+ import { Grid } from "lucide-react";
170
+ import { jsx as jsx3, jsxs } from "preact/jsx-runtime";
171
+ function BasicWidget({ id, title }) {
172
+ return /* @__PURE__ */ jsx3("div", { className: "p-4 h-full flex flex-col justify-center overflow-hidden", children: /* @__PURE__ */ jsxs("div", { className: "text-center", children: [
173
+ /* @__PURE__ */ jsx3("div", { className: "w-12 h-12 rounded-full flex items-center justify-center mx-auto mb-3 bg-gradient-to-br from-blue-100 to-purple-100 dark:from-gray-700 dark:to-gray-600", children: /* @__PURE__ */ jsx3(Grid, { size: 20, className: "text-blue-600 dark:text-gray-200" }) }),
174
+ /* @__PURE__ */ jsx3("h3", { className: "text-lg font-semibold text-gray-800 dark:text-gray-100 mb-2", children: title || "Basic Widget" }),
175
+ /* @__PURE__ */ jsx3("p", { className: "text-sm text-gray-500 dark:text-gray-400", children: "Customizable content area" }),
176
+ id && /* @__PURE__ */ jsxs("div", { className: "mt-2 text-xs text-gray-400 dark:text-gray-500", children: [
177
+ "ID: ",
178
+ id
179
+ ] })
180
+ ] }) });
181
+ }
182
+ function BasicWidgetPreview() {
183
+ return /* @__PURE__ */ jsx3("div", { className: "p-2 h-16 flex items-center justify-center bg-gray-50 dark:bg-gray-800 rounded", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
184
+ /* @__PURE__ */ jsx3("div", { className: "w-8 h-8 rounded-full bg-gradient-to-br from-blue-100 to-purple-100 dark:from-gray-700 dark:to-gray-600 flex items-center justify-center", children: /* @__PURE__ */ jsx3(Grid, { size: 14, className: "text-blue-600 dark:text-gray-300" }) }),
185
+ /* @__PURE__ */ jsx3("div", { className: "text-xs text-gray-600 dark:text-gray-400", children: "Basic Widget" })
186
+ ] }) });
187
+ }
188
+
189
+ // src/components/widgets/ProgressBarWidget.tsx
190
+ import { jsx as jsx4, jsxs as jsxs2 } from "preact/jsx-runtime";
191
+ function ProgressBarWidget({ data }) {
192
+ const defaultData = {
193
+ label: "Progress",
194
+ value: 75,
195
+ max: 100,
196
+ color: "bg-blue-500"
197
+ };
198
+ const widgetData = data || defaultData;
199
+ const percentage = widgetData.value / widgetData.max * 100;
200
+ return /* @__PURE__ */ jsx4("div", { className: "p-4 h-full flex flex-col justify-center overflow-hidden", children: /* @__PURE__ */ jsxs2("div", { className: "text-center", children: [
201
+ /* @__PURE__ */ jsx4("h3", { className: "text-lg font-semibold text-gray-800 dark:text-gray-100 mb-2", children: widgetData.label }),
202
+ /* @__PURE__ */ jsxs2("div", { className: "text-3xl font-bold text-gray-900 dark:text-gray-100 mb-4", children: [
203
+ widgetData.value,
204
+ "%"
205
+ ] }),
206
+ /* @__PURE__ */ jsx4("div", { className: "w-full bg-gray-200 dark:bg-gray-700 rounded-full h-3 mb-2", children: /* @__PURE__ */ jsx4(
207
+ "div",
208
+ {
209
+ className: `h-3 rounded-full ${widgetData.color || "bg-blue-500"} transition-all duration-300`,
210
+ style: { width: `${percentage}%` }
211
+ }
212
+ ) }),
213
+ /* @__PURE__ */ jsxs2("p", { className: "text-xs text-gray-500 dark:text-gray-400", children: [
214
+ widgetData.value,
215
+ " of ",
216
+ widgetData.max
217
+ ] })
218
+ ] }) });
219
+ }
220
+ function ProgressBarPreview() {
221
+ return /* @__PURE__ */ jsx4("div", { className: "p-2 h-16 flex items-center justify-center bg-gray-50 dark:bg-gray-800 rounded", children: /* @__PURE__ */ jsxs2("div", { className: "w-full max-w-20", children: [
222
+ /* @__PURE__ */ jsx4("div", { className: "text-xs font-medium text-gray-600 dark:text-gray-400 mb-1 text-center", children: "Progress" }),
223
+ /* @__PURE__ */ jsx4("div", { className: "w-full bg-gray-200 dark:bg-gray-600 rounded-full h-1.5", children: /* @__PURE__ */ jsx4("div", { className: "bg-blue-400 h-1.5 rounded-full w-3/4 animate-pulse" }) })
224
+ ] }) });
225
+ }
226
+
227
+ // src/components/widgets/PieChartWidget.tsx
228
+ import { jsx as jsx5, jsxs as jsxs3 } from "preact/jsx-runtime";
229
+ function PieChartWidget({ data }) {
230
+ const defaultData = {
231
+ label: "Sales Distribution",
232
+ segments: [
233
+ { name: "Product A", value: 40, color: "#3B82F6" },
234
+ { name: "Product B", value: 30, color: "#EF4444" },
235
+ { name: "Product C", value: 20, color: "#10B981" },
236
+ { name: "Product D", value: 10, color: "#F59E0B" }
237
+ ]
238
+ };
239
+ const widgetData = data || defaultData;
240
+ const total = widgetData.segments.reduce((sum, segment) => sum + segment.value, 0);
241
+ let cumulativePercentage = 0;
242
+ const gradientStops = widgetData.segments.map((segment) => {
243
+ const percentage = segment.value / total * 100;
244
+ const start = cumulativePercentage;
245
+ const end = cumulativePercentage + percentage;
246
+ cumulativePercentage = end;
247
+ return `${segment.color} ${start}% ${end}%`;
248
+ }).join(", ");
249
+ return /* @__PURE__ */ jsxs3("div", { className: "p-4 h-full flex flex-col overflow-hidden", children: [
250
+ /* @__PURE__ */ jsx5("h3", { className: "text-lg font-semibold text-gray-800 dark:text-gray-100 mb-2 text-center flex-shrink-0", children: widgetData.label }),
251
+ /* @__PURE__ */ jsx5("div", { className: "flex-1 flex items-center justify-center min-h-0", children: /* @__PURE__ */ jsxs3("div", { className: "relative", children: [
252
+ /* @__PURE__ */ jsx5(
253
+ "div",
254
+ {
255
+ className: "w-24 h-24 rounded-full",
256
+ style: {
257
+ background: `conic-gradient(${gradientStops})`
258
+ }
259
+ }
260
+ ),
261
+ /* @__PURE__ */ jsx5("div", { className: "absolute inset-0 flex items-center justify-center", children: /* @__PURE__ */ jsx5("div", { className: "w-8 h-8 bg-white dark:bg-gray-800 rounded-full flex items-center justify-center", children: /* @__PURE__ */ jsx5("span", { className: "text-xs font-bold text-gray-600 dark:text-gray-300", children: total }) }) })
262
+ ] }) }),
263
+ /* @__PURE__ */ jsx5("div", { className: "grid grid-cols-2 gap-1 mt-2 flex-shrink-0", children: widgetData.segments.map((segment, index) => /* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-1 min-w-0", children: [
264
+ /* @__PURE__ */ jsx5(
265
+ "div",
266
+ {
267
+ className: "w-2 h-2 rounded-full flex-shrink-0",
268
+ style: { backgroundColor: segment.color }
269
+ }
270
+ ),
271
+ /* @__PURE__ */ jsx5("span", { className: "text-xs text-gray-600 dark:text-gray-400 truncate", children: segment.name })
272
+ ] }, index)) })
273
+ ] });
274
+ }
275
+ function PieChartPreview() {
276
+ return /* @__PURE__ */ jsx5("div", { className: "p-2 h-16 flex items-center justify-center bg-gray-50 dark:bg-gray-800 rounded", children: /* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-2", children: [
277
+ /* @__PURE__ */ jsxs3("div", { className: "relative", children: [
278
+ /* @__PURE__ */ jsx5("div", { className: "w-8 h-8 rounded-full bg-gradient-to-r from-blue-400 via-red-400 to-green-400 animate-pulse" }),
279
+ /* @__PURE__ */ jsx5("div", { className: "absolute inset-0 flex items-center justify-center", children: /* @__PURE__ */ jsx5("div", { className: "w-3 h-3 bg-gray-50 dark:bg-gray-800 rounded-full" }) })
280
+ ] }),
281
+ /* @__PURE__ */ jsx5("div", { className: "text-xs text-gray-600 dark:text-gray-400", children: "Pie Chart" })
282
+ ] }) });
283
+ }
284
+
285
+ // src/components/widgets/BarChartWidget.tsx
286
+ import { jsx as jsx6, jsxs as jsxs4 } from "preact/jsx-runtime";
287
+ function BarChartWidget({ data }) {
288
+ const defaultData = {
289
+ label: "Monthly Revenue",
290
+ data: [
291
+ { name: "Jan", value: 65, color: "#3B82F6" },
292
+ { name: "Feb", value: 85, color: "#3B82F6" },
293
+ { name: "Mar", value: 75, color: "#3B82F6" },
294
+ { name: "Apr", value: 95, color: "#3B82F6" },
295
+ { name: "May", value: 80, color: "#3B82F6" },
296
+ { name: "Jun", value: 90, color: "#3B82F6" }
297
+ ]
298
+ };
299
+ const widgetData = data || defaultData;
300
+ const maxValue = Math.max(...widgetData.data.map((item) => item.value));
301
+ return /* @__PURE__ */ jsxs4("div", { className: "p-4 h-full flex flex-col overflow-hidden", children: [
302
+ /* @__PURE__ */ jsx6("h3", { className: "text-lg font-semibold text-gray-800 dark:text-gray-100 mb-2 text-center flex-shrink-0", children: widgetData.label }),
303
+ /* @__PURE__ */ jsx6("div", { className: "flex-1 flex items-end justify-between gap-1 px-2 min-h-0", children: widgetData.data.map((item, index) => {
304
+ const height = item.value / maxValue * 100;
305
+ return /* @__PURE__ */ jsxs4("div", { className: "flex-1 flex flex-col items-center", children: [
306
+ /* @__PURE__ */ jsx6("div", { className: "flex-1 flex items-end w-full", children: /* @__PURE__ */ jsx6(
307
+ "div",
308
+ {
309
+ className: "w-full rounded-t transition-all duration-500 hover:opacity-80",
310
+ style: {
311
+ height: `${height}%`,
312
+ backgroundColor: item.color || "#3B82F6",
313
+ minHeight: "4px"
314
+ }
315
+ }
316
+ ) }),
317
+ /* @__PURE__ */ jsx6("div", { className: "text-xs text-gray-600 dark:text-gray-400 mt-1 text-center", children: item.name }),
318
+ /* @__PURE__ */ jsx6("div", { className: "text-xs text-gray-500 dark:text-gray-500", children: item.value })
319
+ ] }, index);
320
+ }) })
321
+ ] });
322
+ }
323
+ function BarChartPreview() {
324
+ const heights = [60, 80, 40, 90, 70];
325
+ return /* @__PURE__ */ jsxs4("div", { className: "p-2 h-16 flex items-center justify-center bg-gray-50 dark:bg-gray-800 rounded", children: [
326
+ /* @__PURE__ */ jsx6("div", { className: "flex items-end gap-1 h-8", children: heights.map((height, index) => /* @__PURE__ */ jsx6(
327
+ "div",
328
+ {
329
+ className: "w-2 bg-blue-400 rounded-t animate-pulse",
330
+ style: {
331
+ height: `${height}%`,
332
+ animationDelay: `${index * 0.1}s`
333
+ }
334
+ },
335
+ index
336
+ )) }),
337
+ /* @__PURE__ */ jsx6("div", { className: "ml-2 text-xs text-gray-600 dark:text-gray-400", children: "Bar Chart" })
338
+ ] });
339
+ }
340
+
341
+ // src/components/widgets/LineChartWidget.tsx
342
+ import { jsx as jsx7, jsxs as jsxs5 } from "preact/jsx-runtime";
343
+ function LineChartWidget({ data }) {
344
+ const defaultData = {
345
+ label: "Performance Trend",
346
+ data: [
347
+ { name: "Jan", value: 30 },
348
+ { name: "Feb", value: 45 },
349
+ { name: "Mar", value: 35 },
350
+ { name: "Apr", value: 60 },
351
+ { name: "May", value: 55 },
352
+ { name: "Jun", value: 75 },
353
+ { name: "Jul", value: 70 }
354
+ ],
355
+ color: "#3B82F6"
356
+ };
357
+ const widgetData = data || defaultData;
358
+ const maxValue = Math.max(...widgetData.data.map((item) => item.value));
359
+ const minValue = Math.min(...widgetData.data.map((item) => item.value));
360
+ const range = maxValue - minValue;
361
+ const width = 200;
362
+ const height = 80;
363
+ const padding = 10;
364
+ const points = widgetData.data.map((item, index) => {
365
+ const x = padding + index / (widgetData.data.length - 1) * (width - 2 * padding);
366
+ const y = height - padding - (item.value - minValue) / range * (height - 2 * padding);
367
+ return { x, y, value: item.value, name: item.name };
368
+ });
369
+ const pathData = points.reduce((path, point, index) => {
370
+ const command = index === 0 ? "M" : "L";
371
+ return `${path} ${command} ${point.x} ${point.y}`;
372
+ }, "");
373
+ return /* @__PURE__ */ jsxs5("div", { className: "p-4 h-full flex flex-col overflow-hidden", children: [
374
+ /* @__PURE__ */ jsx7("h3", { className: "text-lg font-semibold text-gray-800 dark:text-gray-100 mb-2 text-center flex-shrink-0", children: widgetData.label }),
375
+ /* @__PURE__ */ jsx7("div", { className: "flex-1 flex items-center justify-center min-h-0", children: /* @__PURE__ */ jsx7("div", { className: "relative max-w-full", children: /* @__PURE__ */ jsxs5("svg", { width, height, className: "max-w-full h-auto", children: [
376
+ [0, 25, 50, 75, 100].map((percent, index) => {
377
+ const y = height - padding - percent / 100 * (height - 2 * padding);
378
+ return /* @__PURE__ */ jsx7(
379
+ "line",
380
+ {
381
+ x1: padding,
382
+ y1: y,
383
+ x2: width - padding,
384
+ y2: y,
385
+ stroke: "currentColor",
386
+ strokeWidth: "0.5",
387
+ opacity: "0.2",
388
+ className: "text-gray-400"
389
+ },
390
+ index
391
+ );
392
+ }),
393
+ /* @__PURE__ */ jsx7(
394
+ "path",
395
+ {
396
+ d: pathData,
397
+ fill: "none",
398
+ stroke: widgetData.color || "#3B82F6",
399
+ strokeWidth: "2",
400
+ strokeLinecap: "round",
401
+ strokeLinejoin: "round"
402
+ }
403
+ ),
404
+ points.map((point, index) => /* @__PURE__ */ jsx7(
405
+ "circle",
406
+ {
407
+ cx: point.x,
408
+ cy: point.y,
409
+ r: "3",
410
+ fill: widgetData.color || "#3B82F6",
411
+ className: "hover:r-4 transition-all"
412
+ },
413
+ index
414
+ ))
415
+ ] }) }) }),
416
+ /* @__PURE__ */ jsx7("div", { className: "flex justify-between text-xs text-gray-600 dark:text-gray-400 mt-2 flex-shrink-0 gap-1", children: widgetData.data.map((item, index) => /* @__PURE__ */ jsx7("span", { className: "text-center truncate min-w-0 flex-1", children: item.name }, index)) })
417
+ ] });
418
+ }
419
+ function LineChartPreview() {
420
+ return /* @__PURE__ */ jsx7("div", { className: "p-2 h-16 flex items-center justify-center bg-gray-50 dark:bg-gray-800 rounded", children: /* @__PURE__ */ jsxs5("div", { className: "flex items-center gap-2", children: [
421
+ /* @__PURE__ */ jsxs5("svg", { width: "32", height: "20", className: "overflow-visible", children: [
422
+ /* @__PURE__ */ jsx7(
423
+ "path",
424
+ {
425
+ d: "M 2 18 L 8 12 L 14 15 L 20 8 L 26 10 L 30 6",
426
+ fill: "none",
427
+ stroke: "#3B82F6",
428
+ strokeWidth: "1.5",
429
+ strokeLinecap: "round",
430
+ className: "animate-pulse"
431
+ }
432
+ ),
433
+ [2, 8, 14, 20, 26, 30].map((x, index) => /* @__PURE__ */ jsx7(
434
+ "circle",
435
+ {
436
+ cx: x,
437
+ cy: [18, 12, 15, 8, 10, 6][index],
438
+ r: "1",
439
+ fill: "#3B82F6",
440
+ className: "animate-pulse",
441
+ style: { animationDelay: `${index * 0.1}s` }
442
+ },
443
+ index
444
+ ))
445
+ ] }),
446
+ /* @__PURE__ */ jsx7("div", { className: "text-xs text-gray-600 dark:text-gray-400", children: "Line Chart" })
447
+ ] }) });
448
+ }
449
+
450
+ // src/components/widgets/index.ts
451
+ var widgetRegistry = {
452
+ basic: BasicWidget,
453
+ progress: ProgressBarWidget,
454
+ pie: PieChartWidget,
455
+ bar: BarChartWidget,
456
+ line: LineChartWidget
457
+ };
458
+ var previewRegistry = {
459
+ basic: BasicWidgetPreview,
460
+ progress: ProgressBarPreview,
461
+ pie: PieChartPreview,
462
+ bar: BarChartPreview,
463
+ line: LineChartPreview
464
+ };
465
+ function getWidgetComponent(type) {
466
+ return widgetRegistry[type] || BasicWidget;
467
+ }
468
+ function getPreviewComponent(type) {
469
+ return previewRegistry[type] || BasicWidgetPreview;
470
+ }
471
+
472
+ // src/components/DashboardToolbar.tsx
473
+ import { Fragment as Fragment2, jsx as jsx8, jsxs as jsxs6 } from "preact/jsx-runtime";
474
+ var defaultWidgetTypes = [
475
+ {
476
+ id: "basic",
477
+ type: "basic",
478
+ title: "Basic Widget",
479
+ icon: /* @__PURE__ */ jsx8(Grid2, { size: 20 }),
480
+ defaultSize: { w: 4, h: 4 },
481
+ description: "A simple widget for general content",
482
+ component: widgetRegistry.basic,
483
+ preview: previewRegistry.basic
484
+ },
485
+ {
486
+ id: "progress",
487
+ type: "progress",
488
+ title: "Progress Bar",
489
+ icon: /* @__PURE__ */ jsx8(TrendingUp, { size: 20 }),
490
+ defaultSize: { w: 4, h: 3 },
491
+ description: "Display progress with visual bar",
492
+ component: widgetRegistry.progress,
493
+ preview: previewRegistry.progress
494
+ }
495
+ ];
496
+ function DashboardToolbar({
497
+ isEditMode,
498
+ onToggleMode,
499
+ onAutoOrganize,
500
+ onToggleFixedHeight,
501
+ isFixedHeight,
502
+ gridDimensions,
503
+ itemCount,
504
+ isAddWidgetMode = false,
505
+ onToggleAddWidgetMode,
506
+ onAddWidget,
507
+ availableWidgetTypes = defaultWidgetTypes
508
+ }) {
509
+ const [scrollPosition, setScrollPosition] = React.useState(0);
510
+ const carouselRef = React.useRef(null);
511
+ const scroll = (direction) => {
512
+ if (!carouselRef.current) return;
513
+ const scrollAmount = 200;
514
+ const newPosition = direction === "left" ? Math.max(0, scrollPosition - scrollAmount) : scrollPosition + scrollAmount;
515
+ carouselRef.current.scrollTo({ left: newPosition, behavior: "smooth" });
516
+ setScrollPosition(newPosition);
517
+ };
518
+ return /* @__PURE__ */ jsx8(TooltipProvider, { children: /* @__PURE__ */ jsxs6("div", { className: "bg-white dark:bg-gray-800 shadow-sm border border-gray-200 dark:border-gray-700 rounded-lg p-4 mb-6", children: [
519
+ /* @__PURE__ */ jsxs6("div", { className: "flex items-center justify-between", children: [
520
+ /* @__PURE__ */ jsx8("div", { className: "flex items-center gap-4", children: /* @__PURE__ */ jsxs6("div", { className: "flex items-center gap-2", children: [
521
+ /* @__PURE__ */ jsx8(
522
+ Button,
523
+ {
524
+ onClick: onToggleMode,
525
+ variant: isEditMode ? "default" : "outline",
526
+ size: "sm",
527
+ className: "gap-2 bg-gray-100 dark:bg-gray-700 text-gray-800 dark:text-gray-200 hover:bg-gray-200 dark:hover:bg-gray-600",
528
+ children: isEditMode ? /* @__PURE__ */ jsxs6(Fragment2, { children: [
529
+ /* @__PURE__ */ jsx8(Edit3, { size: 16 }),
530
+ "Edit Mode"
531
+ ] }) : /* @__PURE__ */ jsxs6(Fragment2, { children: [
532
+ /* @__PURE__ */ jsx8(Eye, { size: 16 }),
533
+ "View Mode"
534
+ ] })
535
+ }
536
+ ),
537
+ /* @__PURE__ */ jsxs6("div", { className: "text-sm text-gray-500 dark:text-gray-200 px-2", children: [
538
+ "Grid: ",
539
+ gridDimensions.cols,
540
+ "\xD7",
541
+ gridDimensions.rows
542
+ ] })
543
+ ] }) }),
544
+ /* @__PURE__ */ jsx8("div", { className: "flex items-center gap-3", children: isEditMode && /* @__PURE__ */ jsxs6(Fragment2, { children: [
545
+ isAddWidgetMode ? /* @__PURE__ */ jsxs6(
546
+ Button,
547
+ {
548
+ onClick: onToggleAddWidgetMode,
549
+ variant: "outline",
550
+ size: "sm",
551
+ className: "gap-2 text-red-600 hover:text-red-700 border-red-200 hover:border-red-300 bg-white dark:bg-gray-800",
552
+ children: [
553
+ /* @__PURE__ */ jsx8(X, { size: 16 }),
554
+ "Cancel"
555
+ ]
556
+ }
557
+ ) : /* @__PURE__ */ jsxs6(
558
+ Button,
559
+ {
560
+ onClick: onToggleAddWidgetMode,
561
+ variant: "default",
562
+ size: "sm",
563
+ className: "gap-2 bg-gray-100 dark:bg-gray-700 text-gray-800 dark:text-gray-200 hover:bg-gray-200 dark:hover:bg-gray-600",
564
+ children: [
565
+ /* @__PURE__ */ jsx8(Plus, { size: 16 }),
566
+ "Add Widget"
567
+ ]
568
+ }
569
+ ),
570
+ /* @__PURE__ */ jsx8(
571
+ Button,
572
+ {
573
+ onClick: onToggleFixedHeight,
574
+ variant: "secondary",
575
+ size: "sm",
576
+ className: "bg-gray-100 dark:bg-gray-700 text-gray-800 dark:text-gray-200",
577
+ children: isFixedHeight ? "Auto Height" : "Fix Height"
578
+ }
579
+ ),
580
+ /* @__PURE__ */ jsxs6(
581
+ Button,
582
+ {
583
+ onClick: onAutoOrganize,
584
+ variant: "outline",
585
+ size: "sm",
586
+ disabled: itemCount === 0,
587
+ className: "gap-2 border-gray-300 dark:border-gray-600 text-gray-700 dark:text-gray-200 bg-white dark:bg-gray-800",
588
+ children: [
589
+ /* @__PURE__ */ jsx8(Zap, { size: 16 }),
590
+ "Auto Organize"
591
+ ]
592
+ }
593
+ )
594
+ ] }) })
595
+ ] }),
596
+ isEditMode && isAddWidgetMode && /* @__PURE__ */ jsxs6("div", { className: "mt-4 pt-4 border-t border-gray-200 dark:border-gray-700", children: [
597
+ /* @__PURE__ */ jsx8("div", { className: "flex items-center gap-2 mb-3", children: /* @__PURE__ */ jsx8("h3", { className: "font-medium text-gray-800 dark:text-gray-200", children: "Select a widget to add:" }) }),
598
+ /* @__PURE__ */ jsx8("div", { className: "relative", children: /* @__PURE__ */ jsxs6("div", { className: "flex items-center gap-2", children: [
599
+ /* @__PURE__ */ jsx8(
600
+ Button,
601
+ {
602
+ variant: "outline",
603
+ size: "sm",
604
+ onClick: () => scroll("left"),
605
+ className: "flex-shrink-0 h-12 w-8 bg-gray-100 dark:bg-gray-700 text-gray-700 dark:text-gray-200",
606
+ disabled: scrollPosition === 0,
607
+ children: /* @__PURE__ */ jsx8(ChevronLeft, { size: 16 })
608
+ }
609
+ ),
610
+ /* @__PURE__ */ jsx8(
611
+ "div",
612
+ {
613
+ ref: carouselRef,
614
+ className: "flex gap-3 overflow-x-auto scrollbar-hide flex-1",
615
+ style: { scrollbarWidth: "none", msOverflowStyle: "none" },
616
+ children: availableWidgetTypes.map((widget) => {
617
+ const PreviewComponent = widget.preview || getPreviewComponent(widget.type);
618
+ return /* @__PURE__ */ jsxs6(Tooltip, { children: [
619
+ /* @__PURE__ */ jsx8(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxs6(
620
+ Button,
621
+ {
622
+ variant: "outline",
623
+ onClick: () => onAddWidget?.(widget),
624
+ className: "flex-shrink-0 h-20 w-24 p-2 flex flex-col items-center gap-1 hover:border-blue-300 hover:bg-blue-50 dark:hover:bg-blue-900/50 bg-white dark:bg-gray-800 transition-colors group border-gray-200 dark:border-gray-700",
625
+ children: [
626
+ /* @__PURE__ */ jsx8("div", { className: "text-blue-600 dark:text-blue-400 group-hover:text-blue-700 dark:group-hover:text-blue-300", children: widget.icon }),
627
+ /* @__PURE__ */ jsx8("span", { className: "text-xs font-medium text-center leading-tight text-gray-700 dark:text-gray-200", children: widget.title })
628
+ ]
629
+ }
630
+ ) }),
631
+ /* @__PURE__ */ jsx8(TooltipContent, { children: /* @__PURE__ */ jsxs6("div", { className: "p-3", children: [
632
+ /* @__PURE__ */ jsxs6("div", { className: "flex items-center gap-2 mb-2", children: [
633
+ /* @__PURE__ */ jsx8("div", { className: "text-blue-600 dark:text-blue-400", children: widget.icon }),
634
+ /* @__PURE__ */ jsxs6("div", { children: [
635
+ /* @__PURE__ */ jsx8("h4", { className: "font-medium text-sm text-gray-800 dark:text-gray-200", children: widget.title }),
636
+ /* @__PURE__ */ jsxs6("p", { className: "text-xs text-gray-500 dark:text-gray-200", children: [
637
+ "Size: ",
638
+ widget.defaultSize.w,
639
+ "\xD7",
640
+ widget.defaultSize.h
641
+ ] })
642
+ ] })
643
+ ] }),
644
+ /* @__PURE__ */ jsx8("p", { className: "text-xs text-gray-600 dark:text-gray-200 mb-3", children: widget.description }),
645
+ /* @__PURE__ */ jsx8("div", { className: "h-16 border border-gray-200 dark:border-gray-700 rounded bg-gray-50 dark:bg-gray-750 overflow-hidden", children: /* @__PURE__ */ jsx8(PreviewComponent, {}) })
646
+ ] }) })
647
+ ] }, widget.id);
648
+ })
649
+ }
650
+ ),
651
+ /* @__PURE__ */ jsx8(
652
+ Button,
653
+ {
654
+ variant: "outline",
655
+ size: "sm",
656
+ onClick: () => scroll("right"),
657
+ className: "flex-shrink-0 h-12 w-8 bg-gray-100 dark:bg-gray-700 text-gray-700 dark:text-gray-200",
658
+ children: /* @__PURE__ */ jsx8(ChevronRight, { size: 16 })
659
+ }
660
+ )
661
+ ] }) })
662
+ ] })
663
+ ] }) });
664
+ }
665
+
666
+ // src/lib/useDashboardController.ts
667
+ import { useState as useState2, useCallback } from "react";
668
+ function useDashboardController({
669
+ initialItems = [],
670
+ initialEditMode = false
671
+ } = {}) {
672
+ const [items, setItems] = useState2(initialItems);
673
+ const [isEditMode, setIsEditMode] = useState2(initialEditMode);
674
+ const addItem = useCallback((item) => {
675
+ setItems((prev) => [...prev, item]);
676
+ }, []);
677
+ const removeItem = useCallback((id) => {
678
+ setItems((prev) => prev.filter((item) => item.id !== id));
679
+ }, []);
680
+ const updateItem = useCallback((id, updates) => {
681
+ setItems(
682
+ (prev) => prev.map((item) => item.id === id ? { ...item, ...updates } : item)
683
+ );
684
+ }, []);
685
+ const clear = useCallback(() => {
686
+ setItems([]);
687
+ }, []);
688
+ const save = useCallback(() => {
689
+ const serializedItems = items.map(({ content: _content, ...rest }) => rest);
690
+ return {
691
+ items: serializedItems
692
+ };
693
+ }, [items]);
694
+ const load = useCallback((state) => {
695
+ if (state && Array.isArray(state.items)) {
696
+ setItems(state.items);
697
+ }
698
+ }, []);
699
+ const toggleEditMode = useCallback(() => {
700
+ setIsEditMode((prev) => !prev);
701
+ }, []);
702
+ return {
703
+ items,
704
+ addItem,
705
+ removeItem,
706
+ updateItem,
707
+ setItems,
708
+ save,
709
+ load,
710
+ clear,
711
+ isEditMode,
712
+ toggleEditMode,
713
+ setEditMode: setIsEditMode
714
+ };
715
+ }
716
+
717
+ // src/constants.ts
718
+ var GRID_SIZE = 40;
719
+ var MARGIN = 8;
720
+ var CELL_SIZE = GRID_SIZE + MARGIN;
721
+ var ANIMATION_DURATION = 300;
722
+ var MIN_SIZE = 2;
723
+ var MAX_SIZE = 24;
724
+ var CONTAINER_PADDING = 16;
725
+ var MIN_CONTAINER_HEIGHT = 200;
726
+ var DEBOUNCE_DELAY = 150;
727
+
728
+ // src/components/Dashboard.tsx
729
+ import { Fragment as Fragment3, jsx as jsx9, jsxs as jsxs7 } from "preact/jsx-runtime";
730
+ var createWidgetRenderer = (widgetRegistry2) => (item) => {
731
+ let WidgetComponent;
732
+ if (widgetRegistry2 && widgetRegistry2[item.type]) {
733
+ WidgetComponent = widgetRegistry2[item.type];
734
+ } else {
735
+ WidgetComponent = getWidgetComponent(item.type);
736
+ }
737
+ return /* @__PURE__ */ jsx9(WidgetComponent, { id: item.id, title: item.title, type: item.type });
738
+ };
739
+ function Dashboard({
740
+ availableWidgetTypes = [],
741
+ initialItems = [],
742
+ widgetRegistry: widgetRegistry2,
743
+ onItemsChange,
744
+ className = "",
745
+ enableEditMode = true,
746
+ defaultEditMode = true,
747
+ // Grid appearance
748
+ gridMode = "elegant",
749
+ // Toolbar customization options
750
+ showDefaultToolbar = true,
751
+ customToolbar,
752
+ toolbarClassName = "",
753
+ // Exposed action callbacks
754
+ onEditModeChange,
755
+ onAddWidgetModeChange,
756
+ onFixedHeightChange,
757
+ controller: externalController
758
+ }) {
759
+ const renderWidgetContent = useMemo(() => createWidgetRenderer(widgetRegistry2), [widgetRegistry2]);
760
+ const transformInitialItems = useCallback2((items2) => {
761
+ return items2.map((item) => ({
762
+ ...item,
763
+ content: () => renderWidgetContent(item)
764
+ }));
765
+ }, [renderWidgetContent]);
766
+ const internalController = useDashboardController({
767
+ initialItems: transformInitialItems(initialItems),
768
+ initialEditMode: enableEditMode ? defaultEditMode : false
769
+ });
770
+ const controller = externalController || internalController;
771
+ const items = useMemo(() => {
772
+ return controller.items.map((item) => {
773
+ if (!item.content) {
774
+ return { ...item, content: () => renderWidgetContent(item) };
775
+ }
776
+ return item;
777
+ });
778
+ }, [controller.items, renderWidgetContent]);
779
+ const setItems = controller.setItems;
780
+ const isEditMode = controller.isEditMode;
781
+ const setIsEditMode = controller.setEditMode;
782
+ const [nextId, setNextId] = useState3(() => {
783
+ if (initialItems.length === 0) return 1;
784
+ const maxId = Math.max(...initialItems.map((item) => parseInt(item.id) || 0));
785
+ return maxId + 1;
786
+ });
787
+ const [dragState, setDragState] = useState3(null);
788
+ const [resizeState, setResizeState] = useState3(null);
789
+ const [preview, setPreview] = useState3(null);
790
+ const [gridDimensions, setGridDimensions] = useState3({ width: 800, height: 600, cols: 16, rows: 12 });
791
+ const [maxHeight, setMaxHeight] = useState3(null);
792
+ const [isAddWidgetMode, setIsAddWidgetMode] = useState3(false);
793
+ const containerRef = useRef2(null);
794
+ const debounceTimer = useRef2(null);
795
+ const isInitialLoad = useRef2(true);
796
+ useEffect2(() => {
797
+ if (onItemsChange && !isInitialLoad.current) {
798
+ const itemsForCallback = items.map(({ content, ...item }) => item);
799
+ onItemsChange(itemsForCallback);
800
+ }
801
+ isInitialLoad.current = false;
802
+ }, [items, onItemsChange]);
803
+ useEffect2(() => {
804
+ if (externalController) return;
805
+ const hasNewItems = initialItems.some((initItem) => !items.find((item) => item.id === initItem.id));
806
+ const hasMissingItems = items.some((item) => !initialItems.find((initItem) => initItem.id === item.id) && initialItems.length > 0);
807
+ if (hasNewItems || hasMissingItems && items.length < initialItems.length) {
808
+ const transformedItems = transformInitialItems(initialItems);
809
+ setItems(transformedItems);
810
+ isInitialLoad.current = true;
811
+ }
812
+ }, [initialItems, transformInitialItems, externalController]);
813
+ const itemsOverlap = (a, b) => !(a.x + a.w <= b.x || b.x + b.w <= a.x || a.y + a.h <= b.y || b.y + b.h <= a.y);
814
+ const isValidPosition = (item, allItems, excludeId) => {
815
+ if (item.w < MIN_SIZE || item.h < MIN_SIZE) return false;
816
+ if (item.x < 0 || item.y < 0 || item.x + item.w > gridDimensions.cols || item.y + item.h > gridDimensions.rows) {
817
+ return false;
818
+ }
819
+ return !allItems.some((other) => {
820
+ if (other.id === item.id || other.id === excludeId) return false;
821
+ return itemsOverlap(item, other);
822
+ });
823
+ };
824
+ function pixelToGrid(px, py, forCoord = "generic") {
825
+ if (forCoord === "x") return Math.round(px / CELL_SIZE);
826
+ if (forCoord === "y") return Math.round(py / CELL_SIZE);
827
+ return {
828
+ // Fallback to old logic if used generically, but prefer specific coord.
829
+ x: Math.round((px - CONTAINER_PADDING) / CELL_SIZE),
830
+ y: Math.round((py - CONTAINER_PADDING) / CELL_SIZE)
831
+ };
832
+ }
833
+ const gridToPixel = (x, y) => ({
834
+ x: x * CELL_SIZE,
835
+ y: y * CELL_SIZE
836
+ });
837
+ const findSafePosition = (item, existingItems, preferCurrentOverStoredOriginal = false) => {
838
+ let currentW = Math.min(item.w, gridDimensions.cols);
839
+ let currentH = item.h;
840
+ let startX = item.x;
841
+ let startY = item.y;
842
+ if (item.originalX !== void 0 && item.originalY !== void 0 && !preferCurrentOverStoredOriginal) {
843
+ startX = item.originalX;
844
+ startY = item.originalY;
845
+ if (item.originalW !== void 0) currentW = Math.min(item.originalW, gridDimensions.cols);
846
+ if (item.originalH !== void 0) currentH = item.originalH;
847
+ }
848
+ startX = Math.min(startX, gridDimensions.cols - currentW);
849
+ const testItemInitial = { ...item, x: startX, y: startY, w: currentW, h: currentH };
850
+ if (isValidPosition(testItemInitial, existingItems)) {
851
+ return { ...testItemInitial, isAnimating: true };
852
+ }
853
+ if (startX === 0 && startY === 0 && existingItems.length > 0) {
854
+ const maxY = Math.max(...existingItems.map((it) => it.y + it.h), 0);
855
+ for (let y = 0; y <= Math.max(maxY, gridDimensions.rows - currentH); y++) {
856
+ for (let x = 0; x <= gridDimensions.cols - currentW; x++) {
857
+ const candidateItem = { ...item, x, y, w: currentW, h: currentH };
858
+ if (isValidPosition(candidateItem, existingItems)) {
859
+ return { ...candidateItem, isAnimating: true };
860
+ }
861
+ }
862
+ }
863
+ }
864
+ const maxSearchDistance = Math.max(gridDimensions.cols, gridDimensions.rows);
865
+ for (let distance = 1; distance <= maxSearchDistance; distance++) {
866
+ for (let dy = -distance; dy <= distance; dy++) {
867
+ for (let dx = -distance; dx <= distance; dx++) {
868
+ if (Math.abs(dx) !== distance && Math.abs(dy) !== distance) continue;
869
+ const testX = startX + dx;
870
+ const testY = startY + dy;
871
+ if (testX < 0 || testX + currentW > gridDimensions.cols || testY < 0 || testY + currentH > gridDimensions.rows) continue;
872
+ const candidateItem = { ...item, x: testX, y: testY, w: currentW, h: currentH };
873
+ if (isValidPosition(candidateItem, existingItems)) {
874
+ return { ...candidateItem, isAnimating: true };
875
+ }
876
+ }
877
+ }
878
+ }
879
+ for (let y = 0; y <= gridDimensions.rows - currentH; y++) {
880
+ for (let x = 0; x <= gridDimensions.cols - currentW; x++) {
881
+ const candidateItem = { ...item, x, y, w: currentW, h: currentH };
882
+ if (isValidPosition(candidateItem, existingItems)) {
883
+ return { ...candidateItem, isAnimating: true };
884
+ }
885
+ }
886
+ }
887
+ const finalW = Math.min(currentW, gridDimensions.cols);
888
+ const finalH = Math.min(currentH, gridDimensions.rows);
889
+ return { ...item, x: 0, y: 0, w: finalW, h: finalH, isAnimating: true };
890
+ };
891
+ const detectAndFixOverlaps = (currentItems, tryReAnchorToOriginals = false) => {
892
+ const result = currentItems.map((i) => ({ ...i }));
893
+ let hasOverlaps = true;
894
+ let attempts = 0;
895
+ const maxAttempts = Math.max(20, result.length * 2);
896
+ while (hasOverlaps && attempts < maxAttempts) {
897
+ hasOverlaps = false;
898
+ attempts++;
899
+ result.sort((a, b) => a.y - b.y || a.x - b.x);
900
+ for (let i = 0; i < result.length; i++) {
901
+ for (let j = i + 1; j < result.length; j++) {
902
+ if (itemsOverlap(result[i], result[j])) {
903
+ hasOverlaps = true;
904
+ const itemToMoveIndex = result[j].y > result[i].y || result[j].y === result[i].y && result[j].x > result[i].x ? j : i;
905
+ const itemToMove = result[itemToMoveIndex];
906
+ const fixedItems = result.filter((_, idx) => idx !== itemToMoveIndex);
907
+ const newPositionData = findSafePosition(itemToMove, fixedItems, !tryReAnchorToOriginals);
908
+ result[itemToMoveIndex] = {
909
+ ...itemToMove,
910
+ x: newPositionData.x,
911
+ y: newPositionData.y,
912
+ w: newPositionData.w,
913
+ h: newPositionData.h,
914
+ isAnimating: newPositionData.isAnimating
915
+ };
916
+ }
917
+ }
918
+ }
919
+ }
920
+ return result;
921
+ };
922
+ const calculateDimensions = useCallback2(() => {
923
+ if (!containerRef.current) return;
924
+ const rect = containerRef.current.getBoundingClientRect();
925
+ const availableWidth = rect.width - CONTAINER_PADDING * 2;
926
+ const newCols = Math.max(MIN_SIZE, Math.floor((availableWidth + MARGIN) / CELL_SIZE));
927
+ const tempClampedItems = items.map((it) => ({
928
+ ...it,
929
+ w: Math.min(it.originalW !== void 0 ? it.originalW : it.w, newCols)
930
+ // Use originalW if available
931
+ }));
932
+ const maxY = tempClampedItems.length > 0 ? Math.max(...tempClampedItems.map((item) => item.y + item.h), 0) : MIN_SIZE;
933
+ const minRowsForMinHeight = Math.ceil((MIN_CONTAINER_HEIGHT - CONTAINER_PADDING * 2 + MARGIN) / CELL_SIZE);
934
+ const requiredRows = Math.max(minRowsForMinHeight, maxY + 4);
935
+ const newHeight = maxHeight || Math.max(
936
+ MIN_CONTAINER_HEIGHT,
937
+ requiredRows * CELL_SIZE - MARGIN + CONTAINER_PADDING * 2
938
+ );
939
+ const oldCols = gridDimensions.cols;
940
+ const oldRows = gridDimensions.rows;
941
+ setGridDimensions({
942
+ width: rect.width,
943
+ // Updated to use full container width
944
+ height: newHeight,
945
+ cols: newCols,
946
+ rows: requiredRows
947
+ });
948
+ if ((newCols !== oldCols || requiredRows > oldRows + 2) && newCols > 0 && items.length > 0) {
949
+ reflowItems(newCols, requiredRows);
950
+ }
951
+ }, [items, maxHeight, gridDimensions.cols, gridDimensions.rows]);
952
+ const reflowItems = (newCols, newRows) => {
953
+ const processedItems = [];
954
+ const isValidPositionForNewGrid = (item, allItems, excludeId) => {
955
+ if (item.w < MIN_SIZE || item.h < MIN_SIZE) return false;
956
+ if (item.x < 0 || item.y < 0 || item.x + item.w > newCols || item.y + item.h > newRows) {
957
+ return false;
958
+ }
959
+ return !allItems.some((other) => {
960
+ if (other.id === item.id || other.id === excludeId) return false;
961
+ return itemsOverlap(item, other);
962
+ });
963
+ };
964
+ const sortedItems = [...items].sort((a, b) => {
965
+ const aHasOriginal = a.originalX !== void 0 && a.originalY !== void 0;
966
+ const bHasOriginal = b.originalX !== void 0 && b.originalY !== void 0;
967
+ if (aHasOriginal && !bHasOriginal) return -1;
968
+ if (!aHasOriginal && bHasOriginal) return 1;
969
+ if (aHasOriginal && bHasOriginal) {
970
+ if (a.originalY !== b.originalY) return a.originalY - b.originalY;
971
+ return a.originalX - b.originalX;
972
+ }
973
+ return parseInt(a.id) - parseInt(b.id);
974
+ });
975
+ const itemsToProcess = sortedItems.map((item) => {
976
+ const targetW = item.originalW !== void 0 ? Math.min(item.originalW, newCols) : Math.min(item.w, newCols);
977
+ const targetH = item.originalH !== void 0 ? item.originalH : item.h;
978
+ return {
979
+ ...item,
980
+ w: Math.max(MIN_SIZE, targetW),
981
+ // Ensure min width
982
+ h: Math.max(MIN_SIZE, targetH),
983
+ // Ensure min height
984
+ isAnimating: true
985
+ };
986
+ });
987
+ if (newCols > gridDimensions.cols) {
988
+ const restored = [];
989
+ const needsRepositioning = [];
990
+ for (const item of itemsToProcess) {
991
+ if (item.originalX !== void 0 && item.originalY !== void 0) {
992
+ const restoredItem = {
993
+ ...item,
994
+ // Contains already adjusted w/h
995
+ x: item.originalX,
996
+ y: item.originalY
997
+ };
998
+ if (restoredItem.x + restoredItem.w <= newCols && restoredItem.y + restoredItem.h <= newRows && isValidPositionForNewGrid(restoredItem, restored)) {
999
+ restored.push(restoredItem);
1000
+ } else {
1001
+ needsRepositioning.push(item);
1002
+ }
1003
+ } else {
1004
+ needsRepositioning.push(item);
1005
+ }
1006
+ }
1007
+ for (const item of needsRepositioning) {
1008
+ const safePosData = findSafePositionForNewGrid(item, restored, false, newCols, newRows);
1009
+ restored.push({
1010
+ ...item,
1011
+ // Contains item's target w/h
1012
+ x: safePosData.x,
1013
+ y: safePosData.y,
1014
+ w: safePosData.w,
1015
+ // Use width from findSafePosition
1016
+ h: safePosData.h
1017
+ // Use height from findSafePosition
1018
+ // originalX/Y/W/H are preserved from item
1019
+ });
1020
+ }
1021
+ setItems(detectAndFixOverlaps(restored, true));
1022
+ } else {
1023
+ for (const item of itemsToProcess) {
1024
+ let targetX = item.x;
1025
+ if (newCols < gridDimensions.cols) {
1026
+ targetX = Math.min(item.x, Math.max(0, newCols - item.w));
1027
+ }
1028
+ const itemForSafePos = { ...item, x: targetX };
1029
+ const safePosData = findSafePositionForNewGrid(itemForSafePos, processedItems, true, newCols, newRows);
1030
+ processedItems.push({
1031
+ ...item,
1032
+ // Contains item's target w/h
1033
+ x: safePosData.x,
1034
+ y: safePosData.y,
1035
+ w: safePosData.w,
1036
+ h: safePosData.h
1037
+ });
1038
+ }
1039
+ setItems(detectAndFixOverlaps(processedItems, true));
1040
+ }
1041
+ setTimeout(() => setItems((prev) => prev.map((it) => ({ ...it, isAnimating: false }))), ANIMATION_DURATION);
1042
+ };
1043
+ const findSafePositionForNewGrid = (item, existingItems, preferCurrentOverStoredOriginal = false, cols, rows) => {
1044
+ let currentW = Math.min(item.w, cols);
1045
+ let currentH = item.h;
1046
+ let startX = item.x;
1047
+ let startY = item.y;
1048
+ if (item.originalX !== void 0 && item.originalY !== void 0 && !preferCurrentOverStoredOriginal) {
1049
+ startX = item.originalX;
1050
+ startY = item.originalY;
1051
+ if (item.originalW !== void 0) currentW = Math.min(item.originalW, cols);
1052
+ if (item.originalH !== void 0) currentH = item.originalH;
1053
+ }
1054
+ startX = Math.min(startX, cols - currentW);
1055
+ const testItemInitial = { ...item, x: startX, y: startY, w: currentW, h: currentH };
1056
+ const isValidForNewGrid = (testItem) => {
1057
+ if (testItem.w < MIN_SIZE || testItem.h < MIN_SIZE) return false;
1058
+ if (testItem.x < 0 || testItem.y < 0 || testItem.x + testItem.w > cols || testItem.y + testItem.h > rows) {
1059
+ return false;
1060
+ }
1061
+ return !existingItems.some((other) => {
1062
+ if (other.id === testItem.id) return false;
1063
+ return itemsOverlap(testItem, other);
1064
+ });
1065
+ };
1066
+ if (isValidForNewGrid(testItemInitial)) {
1067
+ return { ...testItemInitial, isAnimating: true };
1068
+ }
1069
+ const maxSearchDistance = Math.max(cols, rows);
1070
+ for (let distance = 1; distance <= maxSearchDistance; distance++) {
1071
+ for (let dy = -distance; dy <= distance; dy++) {
1072
+ for (let dx = -distance; dx <= distance; dx++) {
1073
+ if (Math.abs(dx) !== distance && Math.abs(dy) !== distance) continue;
1074
+ const testX = startX + dx;
1075
+ const testY = startY + dy;
1076
+ if (testX < 0 || testX + currentW > cols || testY < 0 || testY + currentH > rows) continue;
1077
+ const candidateItem = { ...item, x: testX, y: testY, w: currentW, h: currentH };
1078
+ if (isValidForNewGrid(candidateItem)) {
1079
+ return { ...candidateItem, isAnimating: true };
1080
+ }
1081
+ }
1082
+ }
1083
+ }
1084
+ for (let y = 0; y <= rows - currentH; y++) {
1085
+ for (let x = 0; x <= cols - currentW; x++) {
1086
+ const candidateItem = { ...item, x, y, w: currentW, h: currentH };
1087
+ if (isValidForNewGrid(candidateItem)) {
1088
+ return { ...candidateItem, isAnimating: true };
1089
+ }
1090
+ }
1091
+ }
1092
+ const finalW = Math.min(currentW, cols);
1093
+ const finalH = Math.min(currentH, rows);
1094
+ return { ...item, x: 0, y: 0, w: finalW, h: finalH, isAnimating: true };
1095
+ };
1096
+ const handleMouseMove = useCallback2((e) => {
1097
+ const gridContentRect = containerRef.current?.getBoundingClientRect();
1098
+ if (!gridContentRect) return;
1099
+ const mouseXInGridContent = e.clientX - gridContentRect.left - CONTAINER_PADDING;
1100
+ const mouseYInGridContent = e.clientY - gridContentRect.top - CONTAINER_PADDING;
1101
+ if (dragState && preview) {
1102
+ const itemX = mouseXInGridContent - dragState.startX;
1103
+ const itemY = mouseYInGridContent - dragState.startY;
1104
+ const gridPos = {
1105
+ x: pixelToGrid(itemX, 0, "x"),
1106
+ // Only X coordinate matters for X grid position
1107
+ y: pixelToGrid(0, itemY, "y")
1108
+ // Only Y coordinate matters for Y grid position
1109
+ };
1110
+ let newX = Math.max(0, Math.min(gridDimensions.cols - preview.w, gridPos.x));
1111
+ let newY = Math.max(0, Math.min(gridDimensions.rows - preview.h, gridPos.y));
1112
+ setPreview({ ...preview, x: newX, y: newY });
1113
+ }
1114
+ if (resizeState && preview) {
1115
+ let { x, y, w, h } = resizeState.originalItem;
1116
+ const handle = resizeState.handle;
1117
+ const dxInGridUnits = pixelToGrid(mouseXInGridContent - resizeState.startX, 0, "x");
1118
+ const dyInGridUnits = pixelToGrid(0, mouseYInGridContent - resizeState.startY, "y");
1119
+ if (handle.includes("e")) w = Math.max(MIN_SIZE, Math.min(MAX_SIZE, resizeState.originalItem.w + dxInGridUnits));
1120
+ if (handle.includes("w")) {
1121
+ const newW = Math.max(MIN_SIZE, Math.min(MAX_SIZE, resizeState.originalItem.w - dxInGridUnits));
1122
+ x = resizeState.originalItem.x + (resizeState.originalItem.w - newW);
1123
+ w = newW;
1124
+ }
1125
+ if (handle.includes("s")) h = Math.max(MIN_SIZE, Math.min(MAX_SIZE, resizeState.originalItem.h + dyInGridUnits));
1126
+ if (handle.includes("n")) {
1127
+ const newH = Math.max(MIN_SIZE, Math.min(MAX_SIZE, resizeState.originalItem.h - dyInGridUnits));
1128
+ y = resizeState.originalItem.y + (resizeState.originalItem.h - newH);
1129
+ h = newH;
1130
+ }
1131
+ x = Math.max(0, x);
1132
+ y = Math.max(0, y);
1133
+ w = Math.min(w, gridDimensions.cols - x);
1134
+ h = Math.min(h, gridDimensions.rows - y);
1135
+ w = Math.max(MIN_SIZE, w);
1136
+ h = Math.max(MIN_SIZE, h);
1137
+ setPreview({ ...preview, x, y, w, h });
1138
+ }
1139
+ }, [dragState, resizeState, preview, gridDimensions.cols, gridDimensions.rows]);
1140
+ const handleMouseUp = useCallback2(() => {
1141
+ if ((dragState || resizeState) && preview) {
1142
+ const finalX = Math.max(0, Math.min(preview.x, gridDimensions.cols - MIN_SIZE));
1143
+ const finalY = Math.max(0, Math.min(preview.y, gridDimensions.rows - MIN_SIZE));
1144
+ const finalW = Math.max(MIN_SIZE, Math.min(preview.w, gridDimensions.cols - finalX));
1145
+ const finalH = Math.max(MIN_SIZE, Math.min(preview.h, gridDimensions.rows - finalY));
1146
+ const finalPreview = {
1147
+ ...preview,
1148
+ x: finalX,
1149
+ y: finalY,
1150
+ w: finalW,
1151
+ h: finalH
1152
+ };
1153
+ const updatedItems = items.map((item) => {
1154
+ if (item.id === (dragState?.id || resizeState?.id)) {
1155
+ return {
1156
+ ...finalPreview,
1157
+ originalX: finalPreview.x,
1158
+ originalY: finalPreview.y,
1159
+ originalW: finalPreview.w,
1160
+ originalH: finalPreview.h,
1161
+ isAnimating: true
1162
+ };
1163
+ }
1164
+ return item;
1165
+ });
1166
+ setItems(updatedItems);
1167
+ setItems((prev) => detectAndFixOverlaps(prev.map((it) => ({ ...it, isAnimating: false })), true));
1168
+ }
1169
+ setDragState(null);
1170
+ setResizeState(null);
1171
+ setPreview(null);
1172
+ }, [dragState, resizeState, preview, items, gridDimensions.cols, gridDimensions.rows]);
1173
+ useEffect2(() => {
1174
+ if (dragState || resizeState) {
1175
+ document.addEventListener("mousemove", handleMouseMove);
1176
+ document.addEventListener("mouseup", handleMouseUp);
1177
+ return () => {
1178
+ document.removeEventListener("mousemove", handleMouseMove);
1179
+ document.removeEventListener("mouseup", handleMouseUp);
1180
+ };
1181
+ }
1182
+ }, [dragState, resizeState, handleMouseMove, handleMouseUp]);
1183
+ useEffect2(() => {
1184
+ const observer = new ResizeObserver(() => {
1185
+ if (debounceTimer.current) clearTimeout(debounceTimer.current);
1186
+ debounceTimer.current = setTimeout(calculateDimensions, DEBOUNCE_DELAY);
1187
+ });
1188
+ if (containerRef.current) {
1189
+ observer.observe(containerRef.current);
1190
+ calculateDimensions();
1191
+ }
1192
+ return () => {
1193
+ observer.disconnect();
1194
+ if (debounceTimer.current) clearTimeout(debounceTimer.current);
1195
+ };
1196
+ }, [calculateDimensions]);
1197
+ const toggleEditMode = () => {
1198
+ if (!enableEditMode) return;
1199
+ const newEditMode = !isEditMode;
1200
+ setIsEditMode(newEditMode);
1201
+ setIsAddWidgetMode(false);
1202
+ setDragState(null);
1203
+ setResizeState(null);
1204
+ setPreview(null);
1205
+ onEditModeChange?.(newEditMode);
1206
+ };
1207
+ const toggleAddWidgetMode = () => {
1208
+ if (!isEditMode) return;
1209
+ const newAddWidgetMode = !isAddWidgetMode;
1210
+ setIsAddWidgetMode(newAddWidgetMode);
1211
+ onAddWidgetModeChange?.(newAddWidgetMode);
1212
+ };
1213
+ const toggleFixedHeight = () => {
1214
+ const newMaxHeight = maxHeight === null ? MIN_CONTAINER_HEIGHT * 1.5 : null;
1215
+ setMaxHeight(newMaxHeight);
1216
+ onFixedHeightChange?.(newMaxHeight !== null);
1217
+ };
1218
+ const addWidgetAtPosition = (widgetConfig, x, y) => {
1219
+ if (!isEditMode) return;
1220
+ const tempItemForPositioning = {
1221
+ id: "temp",
1222
+ x: x ?? 0,
1223
+ y: y ?? 0,
1224
+ w: widgetConfig.defaultSize.w,
1225
+ h: widgetConfig.defaultSize.h,
1226
+ type: widgetConfig.type,
1227
+ title: widgetConfig.title,
1228
+ content: () => null,
1229
+ originalX: x ?? 0,
1230
+ originalY: y ?? 0,
1231
+ originalW: widgetConfig.defaultSize.w,
1232
+ originalH: widgetConfig.defaultSize.h
1233
+ };
1234
+ const safePosData = findSafePosition(tempItemForPositioning, items, false);
1235
+ const newItem = {
1236
+ id: nextId.toString(),
1237
+ x: safePosData.x,
1238
+ y: safePosData.y,
1239
+ w: safePosData.w,
1240
+ h: safePosData.h,
1241
+ type: widgetConfig.type,
1242
+ title: widgetConfig.title,
1243
+ content: () => renderWidgetContent({ id: nextId.toString(), type: widgetConfig.type, title: widgetConfig.title }),
1244
+ originalX: safePosData.x,
1245
+ originalY: safePosData.y,
1246
+ originalW: safePosData.w,
1247
+ originalH: safePosData.h,
1248
+ onMenuClick: widgetConfig.onMenuClick,
1249
+ menuIcon: widgetConfig.menuIcon,
1250
+ isAnimating: true
1251
+ };
1252
+ if (isValidPosition(newItem, items)) {
1253
+ const newItems = [...items, newItem];
1254
+ const itemsWithOverlapsFixed = detectAndFixOverlaps(newItems, true);
1255
+ setItems(itemsWithOverlapsFixed);
1256
+ setNextId((prevId) => prevId + 1);
1257
+ setTimeout(() => {
1258
+ setItems((prev) => prev.map((it) => ({ ...it, isAnimating: false })));
1259
+ }, ANIMATION_DURATION);
1260
+ } else {
1261
+ const fallbackItem = { ...newItem, x: 0, y: 0, originalX: 0, originalY: 0 };
1262
+ const newItems = [...items, fallbackItem];
1263
+ const itemsWithOverlapsFixed = detectAndFixOverlaps(newItems, true);
1264
+ setItems(itemsWithOverlapsFixed);
1265
+ setNextId((prevId) => prevId + 1);
1266
+ setTimeout(() => {
1267
+ setItems((prev) => prev.map((it) => ({ ...it, isAnimating: false })));
1268
+ }, ANIMATION_DURATION);
1269
+ }
1270
+ };
1271
+ const removeItem = (id) => {
1272
+ if (!isEditMode) return;
1273
+ setItems(items.filter((item) => item.id !== id));
1274
+ };
1275
+ const autoOrganize = () => {
1276
+ if (!isEditMode) return;
1277
+ const sorted = [...items].sort((a, b) => {
1278
+ const aArea = (a.originalW ?? a.w) * (a.originalH ?? a.h);
1279
+ const bArea = (b.originalW ?? b.w) * (b.originalH ?? b.h);
1280
+ return bArea - aArea || parseInt(a.id) - parseInt(b.id);
1281
+ });
1282
+ const organized = [];
1283
+ for (const item of sorted) {
1284
+ const targetW = Math.min(
1285
+ item.originalW ?? item.w,
1286
+ gridDimensions.cols
1287
+ );
1288
+ const targetH = item.originalH ?? item.h;
1289
+ const itemToPlace = {
1290
+ ...item,
1291
+ w: Math.max(MIN_SIZE, targetW),
1292
+ h: Math.max(MIN_SIZE, targetH),
1293
+ isAnimating: true
1294
+ };
1295
+ let bestPosition = null;
1296
+ let bestY = Infinity;
1297
+ let bestX = Infinity;
1298
+ for (let y = 0; y <= gridDimensions.rows - itemToPlace.h; y++) {
1299
+ for (let x = 0; x <= gridDimensions.cols - itemToPlace.w; x++) {
1300
+ const candidateItem = { ...itemToPlace, x, y };
1301
+ const isValid = !organized.some(
1302
+ (placedItem) => itemsOverlap(candidateItem, placedItem)
1303
+ );
1304
+ if (isValid) {
1305
+ if (y < bestY || y === bestY && x < bestX) {
1306
+ bestPosition = { x, y };
1307
+ bestY = y;
1308
+ bestX = x;
1309
+ break;
1310
+ }
1311
+ }
1312
+ }
1313
+ if (bestPosition && bestY === y) break;
1314
+ }
1315
+ if (bestPosition) {
1316
+ const organizedItem = {
1317
+ ...item,
1318
+ x: bestPosition.x,
1319
+ y: bestPosition.y,
1320
+ w: itemToPlace.w,
1321
+ h: itemToPlace.h,
1322
+ originalX: bestPosition.x,
1323
+ originalY: bestPosition.y,
1324
+ originalW: itemToPlace.w,
1325
+ originalH: itemToPlace.h,
1326
+ isAnimating: true
1327
+ };
1328
+ organized.push(organizedItem);
1329
+ } else {
1330
+ const safePositionData = findSafePosition(itemToPlace, organized, false);
1331
+ const organizedItem = {
1332
+ ...item,
1333
+ x: safePositionData.x,
1334
+ y: safePositionData.y,
1335
+ w: safePositionData.w,
1336
+ h: safePositionData.h,
1337
+ originalX: safePositionData.x,
1338
+ originalY: safePositionData.y,
1339
+ originalW: safePositionData.w,
1340
+ originalH: safePositionData.h,
1341
+ isAnimating: true
1342
+ };
1343
+ organized.push(organizedItem);
1344
+ }
1345
+ }
1346
+ setItems(organized);
1347
+ setTimeout(() => {
1348
+ setItems((prev) => prev.map((item) => ({ ...item, isAnimating: false })));
1349
+ }, ANIMATION_DURATION);
1350
+ };
1351
+ const renderItem = (item, isPreview = false) => {
1352
+ const pos = gridToPixel(item.x, item.y);
1353
+ const itemWidth = Math.max(0, item.w);
1354
+ const itemHeight = Math.max(0, item.h);
1355
+ const size = {
1356
+ width: itemWidth * GRID_SIZE + (itemWidth > 0 ? (itemWidth - 1) * MARGIN : 0),
1357
+ height: itemHeight * GRID_SIZE + (itemHeight > 0 ? (itemHeight - 1) * MARGIN : 0)
1358
+ };
1359
+ const isActive = dragState?.id === item.id || resizeState?.id === item.id;
1360
+ const handleMouseDownOnWidget = (e) => {
1361
+ if (!isEditMode || isPreview) return;
1362
+ e.preventDefault();
1363
+ const rect = e.currentTarget.getBoundingClientRect();
1364
+ const mouseX = e.clientX - rect.left;
1365
+ const mouseY = e.clientY - rect.top;
1366
+ const cornerSize = 20;
1367
+ const isInTopLeft = mouseX <= cornerSize && mouseY <= cornerSize;
1368
+ const isInTopRight = mouseX >= rect.width - cornerSize && mouseY <= cornerSize;
1369
+ const isInBottomLeft = mouseX <= cornerSize && mouseY >= rect.height - cornerSize;
1370
+ const isInBottomRight = mouseX >= rect.width - cornerSize && mouseY >= rect.height - cornerSize;
1371
+ if (isInTopLeft || isInTopRight || isInBottomLeft || isInBottomRight) {
1372
+ let handle = "";
1373
+ if (isInTopLeft) handle = "nw";
1374
+ else if (isInTopRight) handle = "ne";
1375
+ else if (isInBottomLeft) handle = "sw";
1376
+ else if (isInBottomRight) handle = "se";
1377
+ const gridContentRect = containerRef.current?.getBoundingClientRect();
1378
+ if (!gridContentRect) return;
1379
+ setResizeState({
1380
+ id: item.id,
1381
+ startX: e.clientX - gridContentRect.left - CONTAINER_PADDING,
1382
+ startY: e.clientY - gridContentRect.top - CONTAINER_PADDING,
1383
+ originalItem: { ...item },
1384
+ handle
1385
+ });
1386
+ setPreview({ ...item });
1387
+ } else {
1388
+ const gridContentRect = containerRef.current?.getBoundingClientRect();
1389
+ if (!gridContentRect) return;
1390
+ const mouseXInGridContent = e.clientX - gridContentRect.left - CONTAINER_PADDING;
1391
+ const mouseYInGridContent = e.clientY - gridContentRect.top - CONTAINER_PADDING;
1392
+ const itemPixelPos = gridToPixel(item.x, item.y);
1393
+ setDragState({
1394
+ id: item.id,
1395
+ startX: mouseXInGridContent - itemPixelPos.x,
1396
+ startY: mouseYInGridContent - itemPixelPos.y,
1397
+ originalItem: { ...item }
1398
+ });
1399
+ setPreview({ ...item });
1400
+ }
1401
+ };
1402
+ return /* @__PURE__ */ jsxs7(
1403
+ "div",
1404
+ {
1405
+ className: `group absolute rounded-lg border ${item.isAnimating || isPreview ? "transition-all duration-300" : ""} ${isPreview ? "bg-blue-100 dark:bg-blue-900/30 border-blue-300 dark:border-blue-600 border-2 opacity-80 z-50" : `bg-white dark:bg-gray-800 border-gray-200 dark:border-gray-700 shadow-lg hover:shadow-xl dark:shadow-gray-900/50 z-10 ${isActive && !isPreview ? "opacity-50" : ""}`} ${isEditMode && !isPreview ? "cursor-grab active:cursor-grabbing" : "cursor-default"}`,
1406
+ style: { left: pos.x, top: pos.y, width: size.width, height: size.height },
1407
+ onMouseDown: handleMouseDownOnWidget,
1408
+ children: [
1409
+ !isPreview && isEditMode && /* @__PURE__ */ jsxs7(Fragment3, { children: [
1410
+ /* @__PURE__ */ jsx9("div", { className: "absolute top-0 left-0 w-5 h-5 cursor-nw-resize z-20" }),
1411
+ /* @__PURE__ */ jsx9("div", { className: "absolute top-0 right-0 w-5 h-5 cursor-ne-resize z-20" }),
1412
+ /* @__PURE__ */ jsx9("div", { className: "absolute bottom-0 left-0 w-5 h-5 cursor-sw-resize z-20" }),
1413
+ /* @__PURE__ */ jsx9("div", { className: "absolute bottom-0 right-0 w-5 h-5 cursor-se-resize z-20" })
1414
+ ] }),
1415
+ /* @__PURE__ */ jsxs7(
1416
+ "div",
1417
+ {
1418
+ className: `flex items-center justify-between p-2 text-xs border-b select-none ${isPreview ? "bg-blue-50 dark:bg-blue-900/50 border-blue-200 dark:border-blue-700" : "bg-gradient-to-r from-blue-50 to-purple-50 dark:from-gray-700 dark:to-gray-700 border-gray-200 dark:border-gray-700 hover:from-blue-100 hover:to-purple-100 dark:hover:from-gray-600 dark:hover:to-gray-600"}`,
1419
+ children: [
1420
+ /* @__PURE__ */ jsx9("span", { className: `font-medium truncate ${isPreview ? "text-blue-700 dark:text-blue-300" : "text-gray-700 dark:text-gray-200"}`, children: item.title }),
1421
+ !isPreview && /* @__PURE__ */ jsxs7("div", { className: "flex items-center gap-0.5 flex-shrink-0", children: [
1422
+ isEditMode && item.onMenuClick && /* @__PURE__ */ jsx9(
1423
+ "button",
1424
+ {
1425
+ className: "text-gray-400 dark:text-gray-500 hover:text-gray-600 dark:hover:text-gray-300 p-0.5",
1426
+ onClick: (e) => {
1427
+ e.stopPropagation();
1428
+ item.onMenuClick?.(e);
1429
+ },
1430
+ onMouseDown: (e) => e.stopPropagation(),
1431
+ title: "Widget options",
1432
+ children: item.menuIcon || /* @__PURE__ */ jsx9(MoreHorizontal, { size: 14 })
1433
+ }
1434
+ ),
1435
+ isEditMode && /* @__PURE__ */ jsx9(
1436
+ "button",
1437
+ {
1438
+ onClick: (e) => {
1439
+ e.stopPropagation();
1440
+ removeItem(item.id);
1441
+ },
1442
+ onMouseDown: (e) => e.stopPropagation(),
1443
+ className: "text-gray-400 dark:text-gray-500 hover:text-red-500 dark:hover:text-red-400 p-0.5 hover:bg-red-50 dark:hover:bg-red-900/20 rounded",
1444
+ title: "Remove widget",
1445
+ children: /* @__PURE__ */ jsx9(X2, { size: 14 })
1446
+ }
1447
+ )
1448
+ ] })
1449
+ ]
1450
+ }
1451
+ ),
1452
+ /* @__PURE__ */ jsx9("div", { className: "p-1 flex items-center justify-center h-[calc(100%-30px)] overflow-auto", children: item.content ? item.content() : null })
1453
+ ]
1454
+ },
1455
+ `${isPreview ? "preview-" : ""}${item.id}`
1456
+ );
1457
+ };
1458
+ const dashboardActions = {
1459
+ toggleEditMode,
1460
+ toggleAddWidgetMode,
1461
+ autoOrganize,
1462
+ toggleFixedHeight,
1463
+ addWidget: addWidgetAtPosition,
1464
+ removeItem
1465
+ };
1466
+ const dashboardState = {
1467
+ isEditMode,
1468
+ isAddWidgetMode,
1469
+ isFixedHeight: maxHeight !== null,
1470
+ gridDimensions,
1471
+ itemCount: items.length,
1472
+ items
1473
+ };
1474
+ const customToolbarProps = {
1475
+ state: dashboardState,
1476
+ actions: dashboardActions,
1477
+ availableWidgetTypes
1478
+ };
1479
+ const renderElegantGrid = () => /* @__PURE__ */ jsxs7(Fragment3, { children: [
1480
+ /* @__PURE__ */ jsx9(
1481
+ "div",
1482
+ {
1483
+ className: "absolute pointer-events-none opacity-20",
1484
+ style: {
1485
+ top: CONTAINER_PADDING,
1486
+ left: CONTAINER_PADDING,
1487
+ right: CONTAINER_PADDING,
1488
+ bottom: CONTAINER_PADDING,
1489
+ backgroundImage: `
1490
+ linear-gradient(to right, rgba(156, 163, 175, 0.25) 1px, transparent 1px),
1491
+ linear-gradient(to bottom, rgba(156, 163, 175, 0.25) 1px, transparent 1px)
1492
+ `,
1493
+ backgroundSize: `${CELL_SIZE}px ${CELL_SIZE}px`
1494
+ }
1495
+ }
1496
+ ),
1497
+ /* @__PURE__ */ jsx9(
1498
+ "div",
1499
+ {
1500
+ className: "absolute pointer-events-none dark:block hidden opacity-15",
1501
+ style: {
1502
+ top: CONTAINER_PADDING,
1503
+ left: CONTAINER_PADDING,
1504
+ right: CONTAINER_PADDING,
1505
+ bottom: CONTAINER_PADDING,
1506
+ backgroundImage: `
1507
+ linear-gradient(to right, rgba(75, 85, 99, 0.3) 1px, transparent 1px),
1508
+ linear-gradient(to bottom, rgba(75, 85, 99, 0.3) 1px, transparent 1px)
1509
+ `,
1510
+ backgroundSize: `${CELL_SIZE}px ${CELL_SIZE}px`
1511
+ }
1512
+ }
1513
+ )
1514
+ ] });
1515
+ const renderDotsGrid = () => /* @__PURE__ */ jsx9(
1516
+ "div",
1517
+ {
1518
+ className: "absolute pointer-events-none opacity-30 dark:opacity-20",
1519
+ style: {
1520
+ top: CONTAINER_PADDING,
1521
+ left: CONTAINER_PADDING,
1522
+ right: CONTAINER_PADDING,
1523
+ bottom: CONTAINER_PADDING,
1524
+ backgroundImage: `
1525
+ radial-gradient(circle at center, rgba(156, 163, 175, 0.4) 1px, transparent 1px),
1526
+ radial-gradient(circle at center, rgba(75, 85, 99, 0.5) 1px, transparent 1px)
1527
+ `,
1528
+ backgroundSize: `${CELL_SIZE}px ${CELL_SIZE}px`,
1529
+ backgroundPosition: `${CELL_SIZE / 2}px ${CELL_SIZE / 2}px`
1530
+ }
1531
+ }
1532
+ );
1533
+ const renderHarshGrid = () => /* @__PURE__ */ jsx9(
1534
+ "div",
1535
+ {
1536
+ className: "absolute opacity-20 dark:opacity-10 pointer-events-none",
1537
+ style: {
1538
+ top: CONTAINER_PADDING,
1539
+ left: CONTAINER_PADDING,
1540
+ right: CONTAINER_PADDING,
1541
+ bottom: CONTAINER_PADDING,
1542
+ backgroundImage: `
1543
+ linear-gradient(to right, #ccc 1px, transparent 1px),
1544
+ linear-gradient(to bottom, #ccc 1px, transparent 1px)
1545
+ `,
1546
+ backgroundSize: `${CELL_SIZE}px ${CELL_SIZE}px`
1547
+ }
1548
+ }
1549
+ );
1550
+ const renderGrid = () => {
1551
+ switch (gridMode) {
1552
+ case "dots":
1553
+ return renderDotsGrid();
1554
+ case "harsh":
1555
+ return renderHarshGrid();
1556
+ case "blank":
1557
+ return null;
1558
+ case "elegant":
1559
+ default:
1560
+ return renderElegantGrid();
1561
+ }
1562
+ };
1563
+ return /* @__PURE__ */ jsxs7("div", { className: `p-4 bg-gray-50 dark:bg-gray-900 min-h-screen ${className}`, children: [
1564
+ showDefaultToolbar && !customToolbar && /* @__PURE__ */ jsx9(
1565
+ DashboardToolbar,
1566
+ {
1567
+ isEditMode,
1568
+ onToggleMode: toggleEditMode,
1569
+ onAutoOrganize: autoOrganize,
1570
+ onToggleFixedHeight: toggleFixedHeight,
1571
+ isFixedHeight: maxHeight !== null,
1572
+ gridDimensions,
1573
+ itemCount: items.length,
1574
+ isAddWidgetMode,
1575
+ onToggleAddWidgetMode: toggleAddWidgetMode,
1576
+ onAddWidget: addWidgetAtPosition,
1577
+ availableWidgetTypes
1578
+ }
1579
+ ),
1580
+ customToolbar && /* @__PURE__ */ jsx9("div", { className: toolbarClassName, children: React2.isValidElement(customToolbar) ? customToolbar : React2.createElement(customToolbar, customToolbarProps) }),
1581
+ /* @__PURE__ */ jsx9("div", { className: "w-full", children: /* @__PURE__ */ jsxs7(
1582
+ "div",
1583
+ {
1584
+ ref: containerRef,
1585
+ className: `relative bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg w-full ${maxHeight ? "overflow-auto" : "overflow-hidden"}`,
1586
+ style: { height: gridDimensions.height, minHeight: MIN_CONTAINER_HEIGHT, padding: CONTAINER_PADDING },
1587
+ children: [
1588
+ renderGrid(),
1589
+ /* @__PURE__ */ jsxs7(
1590
+ "div",
1591
+ {
1592
+ className: "relative w-full",
1593
+ style: { height: Math.max(0, gridDimensions.rows * CELL_SIZE - MARGIN), minHeight: `calc(100% - ${CONTAINER_PADDING * 2}px)` },
1594
+ children: [
1595
+ items.map((item) => renderItem(item)),
1596
+ preview && renderItem(preview, true),
1597
+ items.length === 0 && !isAddWidgetMode && /* @__PURE__ */ jsx9("div", { className: "absolute inset-0 flex items-center justify-center text-gray-400 dark:text-gray-500", children: /* @__PURE__ */ jsxs7("div", { className: "text-center", children: [
1598
+ /* @__PURE__ */ jsx9("div", { className: "w-16 h-16 bg-gray-100 dark:bg-gray-700 rounded-full flex items-center justify-center mx-auto mb-3", children: /* @__PURE__ */ jsx9(Plus2, { size: 24 }) }),
1599
+ /* @__PURE__ */ jsx9("p", { children: isEditMode ? 'Click "Add Widget" to start' : "No widgets" })
1600
+ ] }) })
1601
+ ]
1602
+ }
1603
+ )
1604
+ ]
1605
+ }
1606
+ ) })
1607
+ ] });
1608
+ }
1609
+
1610
+ // #style-inject:#style-inject
1611
+ function styleInject(css, { insertAt } = {}) {
1612
+ if (!css || typeof document === "undefined") return;
1613
+ const head = document.head || document.getElementsByTagName("head")[0];
1614
+ const style = document.createElement("style");
1615
+ style.type = "text/css";
1616
+ if (insertAt === "top") {
1617
+ if (head.firstChild) {
1618
+ head.insertBefore(style, head.firstChild);
1619
+ } else {
1620
+ head.appendChild(style);
1621
+ }
1622
+ } else {
1623
+ head.appendChild(style);
1624
+ }
1625
+ if (style.styleSheet) {
1626
+ style.styleSheet.cssText = css;
1627
+ } else {
1628
+ style.appendChild(document.createTextNode(css));
1629
+ }
1630
+ }
1631
+
1632
+ // src/styles.css
1633
+ styleInject('*,\n::before,\n::after {\n --tw-border-spacing-x: 0;\n --tw-border-spacing-y: 0;\n --tw-translate-x: 0;\n --tw-translate-y: 0;\n --tw-rotate: 0;\n --tw-skew-x: 0;\n --tw-skew-y: 0;\n --tw-scale-x: 1;\n --tw-scale-y: 1;\n --tw-pan-x: ;\n --tw-pan-y: ;\n --tw-pinch-zoom: ;\n --tw-scroll-snap-strictness: proximity;\n --tw-gradient-from-position: ;\n --tw-gradient-via-position: ;\n --tw-gradient-to-position: ;\n --tw-ordinal: ;\n --tw-slashed-zero: ;\n --tw-numeric-figure: ;\n --tw-numeric-spacing: ;\n --tw-numeric-fraction: ;\n --tw-ring-inset: ;\n --tw-ring-offset-width: 0px;\n --tw-ring-offset-color: #fff;\n --tw-ring-color: rgb(59 130 246 / 0.5);\n --tw-ring-offset-shadow: 0 0 #0000;\n --tw-ring-shadow: 0 0 #0000;\n --tw-shadow: 0 0 #0000;\n --tw-shadow-colored: 0 0 #0000;\n --tw-blur: ;\n --tw-brightness: ;\n --tw-contrast: ;\n --tw-grayscale: ;\n --tw-hue-rotate: ;\n --tw-invert: ;\n --tw-saturate: ;\n --tw-sepia: ;\n --tw-drop-shadow: ;\n --tw-backdrop-blur: ;\n --tw-backdrop-brightness: ;\n --tw-backdrop-contrast: ;\n --tw-backdrop-grayscale: ;\n --tw-backdrop-hue-rotate: ;\n --tw-backdrop-invert: ;\n --tw-backdrop-opacity: ;\n --tw-backdrop-saturate: ;\n --tw-backdrop-sepia: ;\n --tw-contain-size: ;\n --tw-contain-layout: ;\n --tw-contain-paint: ;\n --tw-contain-style: ;\n}\n::backdrop {\n --tw-border-spacing-x: 0;\n --tw-border-spacing-y: 0;\n --tw-translate-x: 0;\n --tw-translate-y: 0;\n --tw-rotate: 0;\n --tw-skew-x: 0;\n --tw-skew-y: 0;\n --tw-scale-x: 1;\n --tw-scale-y: 1;\n --tw-pan-x: ;\n --tw-pan-y: ;\n --tw-pinch-zoom: ;\n --tw-scroll-snap-strictness: proximity;\n --tw-gradient-from-position: ;\n --tw-gradient-via-position: ;\n --tw-gradient-to-position: ;\n --tw-ordinal: ;\n --tw-slashed-zero: ;\n --tw-numeric-figure: ;\n --tw-numeric-spacing: ;\n --tw-numeric-fraction: ;\n --tw-ring-inset: ;\n --tw-ring-offset-width: 0px;\n --tw-ring-offset-color: #fff;\n --tw-ring-color: rgb(59 130 246 / 0.5);\n --tw-ring-offset-shadow: 0 0 #0000;\n --tw-ring-shadow: 0 0 #0000;\n --tw-shadow: 0 0 #0000;\n --tw-shadow-colored: 0 0 #0000;\n --tw-blur: ;\n --tw-brightness: ;\n --tw-contrast: ;\n --tw-grayscale: ;\n --tw-hue-rotate: ;\n --tw-invert: ;\n --tw-saturate: ;\n --tw-sepia: ;\n --tw-drop-shadow: ;\n --tw-backdrop-blur: ;\n --tw-backdrop-brightness: ;\n --tw-backdrop-contrast: ;\n --tw-backdrop-grayscale: ;\n --tw-backdrop-hue-rotate: ;\n --tw-backdrop-invert: ;\n --tw-backdrop-opacity: ;\n --tw-backdrop-saturate: ;\n --tw-backdrop-sepia: ;\n --tw-contain-size: ;\n --tw-contain-layout: ;\n --tw-contain-paint: ;\n --tw-contain-style: ;\n}\n*,\n::before,\n::after {\n box-sizing: border-box;\n border-width: 0;\n border-style: solid;\n border-color: #e5e7eb;\n}\n::before,\n::after {\n --tw-content: "";\n}\nhtml,\n:host {\n line-height: 1.5;\n -webkit-text-size-adjust: 100%;\n -moz-tab-size: 4;\n -o-tab-size: 4;\n tab-size: 4;\n font-family:\n ui-sans-serif,\n system-ui,\n sans-serif,\n "Apple Color Emoji",\n "Segoe UI Emoji",\n "Segoe UI Symbol",\n "Noto Color Emoji";\n font-feature-settings: normal;\n font-variation-settings: normal;\n -webkit-tap-highlight-color: transparent;\n}\nbody {\n margin: 0;\n line-height: inherit;\n}\nhr {\n height: 0;\n color: inherit;\n border-top-width: 1px;\n}\nabbr:where([title]) {\n -webkit-text-decoration: underline dotted;\n text-decoration: underline dotted;\n}\nh1,\nh2,\nh3,\nh4,\nh5,\nh6 {\n font-size: inherit;\n font-weight: inherit;\n}\na {\n color: inherit;\n text-decoration: inherit;\n}\nb,\nstrong {\n font-weight: bolder;\n}\ncode,\nkbd,\nsamp,\npre {\n font-family:\n ui-monospace,\n SFMono-Regular,\n Menlo,\n Monaco,\n Consolas,\n "Liberation Mono",\n "Courier New",\n monospace;\n font-feature-settings: normal;\n font-variation-settings: normal;\n font-size: 1em;\n}\nsmall {\n font-size: 80%;\n}\nsub,\nsup {\n font-size: 75%;\n line-height: 0;\n position: relative;\n vertical-align: baseline;\n}\nsub {\n bottom: -0.25em;\n}\nsup {\n top: -0.5em;\n}\ntable {\n text-indent: 0;\n border-color: inherit;\n border-collapse: collapse;\n}\nbutton,\ninput,\noptgroup,\nselect,\ntextarea {\n font-family: inherit;\n font-feature-settings: inherit;\n font-variation-settings: inherit;\n font-size: 100%;\n font-weight: inherit;\n line-height: inherit;\n letter-spacing: inherit;\n color: inherit;\n margin: 0;\n padding: 0;\n}\nbutton,\nselect {\n text-transform: none;\n}\nbutton,\ninput:where([type=button]),\ninput:where([type=reset]),\ninput:where([type=submit]) {\n -webkit-appearance: button;\n background-color: transparent;\n background-image: none;\n}\n:-moz-focusring {\n outline: auto;\n}\n:-moz-ui-invalid {\n box-shadow: none;\n}\nprogress {\n vertical-align: baseline;\n}\n::-webkit-inner-spin-button,\n::-webkit-outer-spin-button {\n height: auto;\n}\n[type=search] {\n -webkit-appearance: textfield;\n outline-offset: -2px;\n}\n::-webkit-search-decoration {\n -webkit-appearance: none;\n}\n::-webkit-file-upload-button {\n -webkit-appearance: button;\n font: inherit;\n}\nsummary {\n display: list-item;\n}\nblockquote,\ndl,\ndd,\nh1,\nh2,\nh3,\nh4,\nh5,\nh6,\nhr,\nfigure,\np,\npre {\n margin: 0;\n}\nfieldset {\n margin: 0;\n padding: 0;\n}\nlegend {\n padding: 0;\n}\nol,\nul,\nmenu {\n list-style: none;\n margin: 0;\n padding: 0;\n}\ndialog {\n padding: 0;\n}\ntextarea {\n resize: vertical;\n}\ninput::-moz-placeholder,\ntextarea::-moz-placeholder {\n opacity: 1;\n color: #9ca3af;\n}\ninput::placeholder,\ntextarea::placeholder {\n opacity: 1;\n color: #9ca3af;\n}\nbutton,\n[role=button] {\n cursor: pointer;\n}\n:disabled {\n cursor: default;\n}\nimg,\nsvg,\nvideo,\ncanvas,\naudio,\niframe,\nembed,\nobject {\n display: block;\n vertical-align: middle;\n}\nimg,\nvideo {\n max-width: 100%;\n height: auto;\n}\n[hidden]:where(:not([hidden=until-found])) {\n display: none;\n}\n:root {\n --background: 0 0% 100%;\n --foreground: 222.2 84% 4.9%;\n --card: 0 0% 100%;\n --card-foreground: 222.2 84% 4.9%;\n --popover: 0 0% 100%;\n --popover-foreground: 222.2 84% 4.9%;\n --primary: 222.2 47.4% 11.2%;\n --primary-foreground: 210 40% 98%;\n --secondary: 210 40% 96.1%;\n --secondary-foreground: 222.2 47.4% 11.2%;\n --muted: 210 40% 96.1%;\n --muted-foreground: 215.4 16.3% 46.9%;\n --accent: 210 40% 96.1%;\n --accent-foreground: 222.2 47.4% 11.2%;\n --destructive: 0 84.2% 60.2%;\n --destructive-foreground: 210 40% 98%;\n --border: 214.3 31.8% 91.4%;\n --input: 214.3 31.8% 91.4%;\n --ring: 222.2 84% 4.9%;\n --radius: 0.5rem;\n}\n* {\n border-color: hsl(var(--border));\n}\nbody {\n background-color: hsl(var(--background));\n color: hsl(var(--foreground));\n width: 100vw;\n max-width: 100vw;\n}\n#root {\n width: 100%;\n height: 100%;\n}\n.container {\n width: 100%;\n}\n@media (min-width: 640px) {\n .container {\n max-width: 640px;\n }\n}\n@media (min-width: 768px) {\n .container {\n max-width: 768px;\n }\n}\n@media (min-width: 1024px) {\n .container {\n max-width: 1024px;\n }\n}\n@media (min-width: 1280px) {\n .container {\n max-width: 1280px;\n }\n}\n@media (min-width: 1536px) {\n .container {\n max-width: 1536px;\n }\n}\n.pointer-events-none {\n pointer-events: none;\n}\n.invisible {\n visibility: hidden;\n}\n.fixed {\n position: fixed;\n}\n.absolute {\n position: absolute;\n}\n.relative {\n position: relative;\n}\n.inset-0 {\n inset: 0px;\n}\n.bottom-0 {\n bottom: 0px;\n}\n.left-0 {\n left: 0px;\n}\n.right-0 {\n right: 0px;\n}\n.right-4 {\n right: 1rem;\n}\n.top-0 {\n top: 0px;\n}\n.top-4 {\n top: 1rem;\n}\n.z-10 {\n z-index: 10;\n}\n.z-20 {\n z-index: 20;\n}\n.z-40 {\n z-index: 40;\n}\n.z-50 {\n z-index: 50;\n}\n.mx-auto {\n margin-left: auto;\n margin-right: auto;\n}\n.my-1 {\n margin-top: 0.25rem;\n margin-bottom: 0.25rem;\n}\n.mb-1 {\n margin-bottom: 0.25rem;\n}\n.mb-2 {\n margin-bottom: 0.5rem;\n}\n.mb-3 {\n margin-bottom: 0.75rem;\n}\n.mb-4 {\n margin-bottom: 1rem;\n}\n.mb-6 {\n margin-bottom: 1.5rem;\n}\n.mb-8 {\n margin-bottom: 2rem;\n}\n.ml-2 {\n margin-left: 0.5rem;\n}\n.mt-1 {\n margin-top: 0.25rem;\n}\n.mt-2 {\n margin-top: 0.5rem;\n}\n.mt-4 {\n margin-top: 1rem;\n}\n.mt-8 {\n margin-top: 2rem;\n}\n.inline-block {\n display: inline-block;\n}\n.flex {\n display: flex;\n}\n.inline-flex {\n display: inline-flex;\n}\n.grid {\n display: grid;\n}\n.hidden {\n display: none;\n}\n.h-1\\.5 {\n height: 0.375rem;\n}\n.h-10 {\n height: 2.5rem;\n}\n.h-11 {\n height: 2.75rem;\n}\n.h-12 {\n height: 3rem;\n}\n.h-16 {\n height: 4rem;\n}\n.h-2 {\n height: 0.5rem;\n}\n.h-20 {\n height: 5rem;\n}\n.h-24 {\n height: 6rem;\n}\n.h-3 {\n height: 0.75rem;\n}\n.h-5 {\n height: 1.25rem;\n}\n.h-8 {\n height: 2rem;\n}\n.h-9 {\n height: 2.25rem;\n}\n.h-\\[calc\\(100\\%-30px\\)\\] {\n height: calc(100% - 30px);\n}\n.h-auto {\n height: auto;\n}\n.h-full {\n height: 100%;\n}\n.min-h-0 {\n min-height: 0px;\n}\n.min-h-screen {\n min-height: 100vh;\n}\n.w-10 {\n width: 2.5rem;\n}\n.w-12 {\n width: 3rem;\n}\n.w-16 {\n width: 4rem;\n}\n.w-2 {\n width: 0.5rem;\n}\n.w-24 {\n width: 6rem;\n}\n.w-3 {\n width: 0.75rem;\n}\n.w-3\\/4 {\n width: 75%;\n}\n.w-48 {\n width: 12rem;\n}\n.w-5 {\n width: 1.25rem;\n}\n.w-8 {\n width: 2rem;\n}\n.w-full {\n width: 100%;\n}\n.min-w-0 {\n min-width: 0px;\n}\n.max-w-20 {\n max-width: 5rem;\n}\n.max-w-7xl {\n max-width: 80rem;\n}\n.max-w-full {\n max-width: 100%;\n}\n.flex-1 {\n flex: 1 1 0%;\n}\n.flex-shrink-0 {\n flex-shrink: 0;\n}\n@keyframes pulse {\n 50% {\n opacity: .5;\n }\n}\n.animate-pulse {\n animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;\n}\n.cursor-default {\n cursor: default;\n}\n.cursor-grab {\n cursor: grab;\n}\n.cursor-ne-resize {\n cursor: ne-resize;\n}\n.cursor-nw-resize {\n cursor: nw-resize;\n}\n.cursor-se-resize {\n cursor: se-resize;\n}\n.cursor-sw-resize {\n cursor: sw-resize;\n}\n.select-none {\n -webkit-user-select: none;\n -moz-user-select: none;\n user-select: none;\n}\n.resize {\n resize: both;\n}\n.grid-cols-2 {\n grid-template-columns: repeat(2, minmax(0, 1fr));\n}\n.flex-col {\n flex-direction: column;\n}\n.items-start {\n align-items: flex-start;\n}\n.items-end {\n align-items: flex-end;\n}\n.items-center {\n align-items: center;\n}\n.justify-center {\n justify-content: center;\n}\n.justify-between {\n justify-content: space-between;\n}\n.gap-0\\.5 {\n gap: 0.125rem;\n}\n.gap-1 {\n gap: 0.25rem;\n}\n.gap-2 {\n gap: 0.5rem;\n}\n.gap-3 {\n gap: 0.75rem;\n}\n.gap-4 {\n gap: 1rem;\n}\n.overflow-auto {\n overflow: auto;\n}\n.overflow-hidden {\n overflow: hidden;\n}\n.overflow-visible {\n overflow: visible;\n}\n.overflow-x-auto {\n overflow-x: auto;\n}\n.truncate {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.whitespace-nowrap {\n white-space: nowrap;\n}\n.rounded {\n border-radius: 0.25rem;\n}\n.rounded-full {\n border-radius: 9999px;\n}\n.rounded-lg {\n border-radius: var(--radius);\n}\n.rounded-md {\n border-radius: calc(var(--radius) - 2px);\n}\n.rounded-t {\n border-top-left-radius: 0.25rem;\n border-top-right-radius: 0.25rem;\n}\n.border {\n border-width: 1px;\n}\n.border-2 {\n border-width: 2px;\n}\n.border-b {\n border-bottom-width: 1px;\n}\n.border-t {\n border-top-width: 1px;\n}\n.border-blue-200 {\n --tw-border-opacity: 1;\n border-color: rgb(191 219 254 / var(--tw-border-opacity, 1));\n}\n.border-blue-300 {\n --tw-border-opacity: 1;\n border-color: rgb(147 197 253 / var(--tw-border-opacity, 1));\n}\n.border-gray-200 {\n --tw-border-opacity: 1;\n border-color: rgb(229 231 235 / var(--tw-border-opacity, 1));\n}\n.border-gray-300 {\n --tw-border-opacity: 1;\n border-color: rgb(209 213 219 / var(--tw-border-opacity, 1));\n}\n.border-green-200 {\n --tw-border-opacity: 1;\n border-color: rgb(187 247 208 / var(--tw-border-opacity, 1));\n}\n.border-input {\n border-color: hsl(var(--input));\n}\n.border-red-200 {\n --tw-border-opacity: 1;\n border-color: rgb(254 202 202 / var(--tw-border-opacity, 1));\n}\n.bg-background {\n background-color: hsl(var(--background));\n}\n.bg-blue-100 {\n --tw-bg-opacity: 1;\n background-color: rgb(219 234 254 / var(--tw-bg-opacity, 1));\n}\n.bg-blue-400 {\n --tw-bg-opacity: 1;\n background-color: rgb(96 165 250 / var(--tw-bg-opacity, 1));\n}\n.bg-blue-50 {\n --tw-bg-opacity: 1;\n background-color: rgb(239 246 255 / var(--tw-bg-opacity, 1));\n}\n.bg-blue-500 {\n --tw-bg-opacity: 1;\n background-color: rgb(59 130 246 / var(--tw-bg-opacity, 1));\n}\n.bg-destructive {\n background-color: hsl(var(--destructive));\n}\n.bg-gray-100 {\n --tw-bg-opacity: 1;\n background-color: rgb(243 244 246 / var(--tw-bg-opacity, 1));\n}\n.bg-gray-200 {\n --tw-bg-opacity: 1;\n background-color: rgb(229 231 235 / var(--tw-bg-opacity, 1));\n}\n.bg-gray-50 {\n --tw-bg-opacity: 1;\n background-color: rgb(249 250 251 / var(--tw-bg-opacity, 1));\n}\n.bg-green-50 {\n --tw-bg-opacity: 1;\n background-color: rgb(240 253 244 / var(--tw-bg-opacity, 1));\n}\n.bg-primary {\n background-color: hsl(var(--primary));\n}\n.bg-secondary {\n background-color: hsl(var(--secondary));\n}\n.bg-white {\n --tw-bg-opacity: 1;\n background-color: rgb(255 255 255 / var(--tw-bg-opacity, 1));\n}\n.bg-gradient-to-br {\n background-image: linear-gradient(to bottom right, var(--tw-gradient-stops));\n}\n.bg-gradient-to-r {\n background-image: linear-gradient(to right, var(--tw-gradient-stops));\n}\n.from-blue-100 {\n --tw-gradient-from: #dbeafe var(--tw-gradient-from-position);\n --tw-gradient-to: rgb(219 234 254 / 0) var(--tw-gradient-to-position);\n --tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to);\n}\n.from-blue-400 {\n --tw-gradient-from: #60a5fa var(--tw-gradient-from-position);\n --tw-gradient-to: rgb(96 165 250 / 0) var(--tw-gradient-to-position);\n --tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to);\n}\n.from-blue-50 {\n --tw-gradient-from: #eff6ff var(--tw-gradient-from-position);\n --tw-gradient-to: rgb(239 246 255 / 0) var(--tw-gradient-to-position);\n --tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to);\n}\n.via-red-400 {\n --tw-gradient-to: rgb(248 113 113 / 0) var(--tw-gradient-to-position);\n --tw-gradient-stops:\n var(--tw-gradient-from),\n #f87171 var(--tw-gradient-via-position),\n var(--tw-gradient-to);\n}\n.to-green-400 {\n --tw-gradient-to: #4ade80 var(--tw-gradient-to-position);\n}\n.to-purple-100 {\n --tw-gradient-to: #f3e8ff var(--tw-gradient-to-position);\n}\n.to-purple-50 {\n --tw-gradient-to: #faf5ff var(--tw-gradient-to-position);\n}\n.p-0\\.5 {\n padding: 0.125rem;\n}\n.p-1 {\n padding: 0.25rem;\n}\n.p-2 {\n padding: 0.5rem;\n}\n.p-3 {\n padding: 0.75rem;\n}\n.p-4 {\n padding: 1rem;\n}\n.p-6 {\n padding: 1.5rem;\n}\n.p-8 {\n padding: 2rem;\n}\n.px-2 {\n padding-left: 0.5rem;\n padding-right: 0.5rem;\n}\n.px-3 {\n padding-left: 0.75rem;\n padding-right: 0.75rem;\n}\n.px-4 {\n padding-left: 1rem;\n padding-right: 1rem;\n}\n.px-6 {\n padding-left: 1.5rem;\n padding-right: 1.5rem;\n}\n.px-8 {\n padding-left: 2rem;\n padding-right: 2rem;\n}\n.py-1 {\n padding-top: 0.25rem;\n padding-bottom: 0.25rem;\n}\n.py-1\\.5 {\n padding-top: 0.375rem;\n padding-bottom: 0.375rem;\n}\n.py-2 {\n padding-top: 0.5rem;\n padding-bottom: 0.5rem;\n}\n.py-3 {\n padding-top: 0.75rem;\n padding-bottom: 0.75rem;\n}\n.pt-4 {\n padding-top: 1rem;\n}\n.text-left {\n text-align: left;\n}\n.text-center {\n text-align: center;\n}\n.text-2xl {\n font-size: 1.5rem;\n line-height: 2rem;\n}\n.text-3xl {\n font-size: 1.875rem;\n line-height: 2.25rem;\n}\n.text-lg {\n font-size: 1.125rem;\n line-height: 1.75rem;\n}\n.text-sm {\n font-size: 0.875rem;\n line-height: 1.25rem;\n}\n.text-xs {\n font-size: 0.75rem;\n line-height: 1rem;\n}\n.font-bold {\n font-weight: 700;\n}\n.font-medium {\n font-weight: 500;\n}\n.font-semibold {\n font-weight: 600;\n}\n.leading-tight {\n line-height: 1.25;\n}\n.text-blue-600 {\n --tw-text-opacity: 1;\n color: rgb(37 99 235 / var(--tw-text-opacity, 1));\n}\n.text-blue-700 {\n --tw-text-opacity: 1;\n color: rgb(29 78 216 / var(--tw-text-opacity, 1));\n}\n.text-destructive-foreground {\n color: hsl(var(--destructive-foreground));\n}\n.text-gray-400 {\n --tw-text-opacity: 1;\n color: rgb(156 163 175 / var(--tw-text-opacity, 1));\n}\n.text-gray-500 {\n --tw-text-opacity: 1;\n color: rgb(107 114 128 / var(--tw-text-opacity, 1));\n}\n.text-gray-600 {\n --tw-text-opacity: 1;\n color: rgb(75 85 99 / var(--tw-text-opacity, 1));\n}\n.text-gray-700 {\n --tw-text-opacity: 1;\n color: rgb(55 65 81 / var(--tw-text-opacity, 1));\n}\n.text-gray-800 {\n --tw-text-opacity: 1;\n color: rgb(31 41 55 / var(--tw-text-opacity, 1));\n}\n.text-gray-900 {\n --tw-text-opacity: 1;\n color: rgb(17 24 39 / var(--tw-text-opacity, 1));\n}\n.text-green-700 {\n --tw-text-opacity: 1;\n color: rgb(21 128 61 / var(--tw-text-opacity, 1));\n}\n.text-green-900 {\n --tw-text-opacity: 1;\n color: rgb(20 83 45 / var(--tw-text-opacity, 1));\n}\n.text-primary {\n color: hsl(var(--primary));\n}\n.text-primary-foreground {\n color: hsl(var(--primary-foreground));\n}\n.text-red-600 {\n --tw-text-opacity: 1;\n color: rgb(220 38 38 / var(--tw-text-opacity, 1));\n}\n.text-secondary-foreground {\n color: hsl(var(--secondary-foreground));\n}\n.text-white {\n --tw-text-opacity: 1;\n color: rgb(255 255 255 / var(--tw-text-opacity, 1));\n}\n.underline-offset-4 {\n text-underline-offset: 4px;\n}\n.opacity-15 {\n opacity: 0.15;\n}\n.opacity-20 {\n opacity: 0.2;\n}\n.opacity-30 {\n opacity: 0.3;\n}\n.opacity-50 {\n opacity: 0.5;\n}\n.opacity-80 {\n opacity: 0.8;\n}\n.shadow-lg {\n --tw-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);\n --tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color);\n box-shadow:\n var(--tw-ring-offset-shadow, 0 0 #0000),\n var(--tw-ring-shadow, 0 0 #0000),\n var(--tw-shadow);\n}\n.shadow-sm {\n --tw-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05);\n --tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color);\n box-shadow:\n var(--tw-ring-offset-shadow, 0 0 #0000),\n var(--tw-ring-shadow, 0 0 #0000),\n var(--tw-shadow);\n}\n.outline {\n outline-style: solid;\n}\n.ring-1 {\n --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);\n --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);\n box-shadow:\n var(--tw-ring-offset-shadow),\n var(--tw-ring-shadow),\n var(--tw-shadow, 0 0 #0000);\n}\n.ring-black {\n --tw-ring-opacity: 1;\n --tw-ring-color: rgb(0 0 0 / var(--tw-ring-opacity, 1));\n}\n.ring-opacity-5 {\n --tw-ring-opacity: 0.05;\n}\n.ring-offset-background {\n --tw-ring-offset-color: hsl(var(--background));\n}\n.filter {\n filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow);\n}\n.transition-all {\n transition-property: all;\n transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);\n transition-duration: 150ms;\n}\n.transition-colors {\n transition-property:\n color,\n background-color,\n border-color,\n text-decoration-color,\n fill,\n stroke;\n transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);\n transition-duration: 150ms;\n}\n.duration-200 {\n transition-duration: 200ms;\n}\n.duration-300 {\n transition-duration: 300ms;\n}\n.duration-500 {\n transition-duration: 500ms;\n}\n@theme { --color-background: hsl(var(--background)); --color-foreground: hsl(var(--foreground)); --color-card: hsl(var(--card)); --color-card-foreground: hsl(var(--card-foreground)); --color-popover: hsl(var(--popover)); --color-popover-foreground: hsl(var(--popover-foreground)); --color-primary: hsl(var(--primary)); --color-primary-foreground: hsl(var(--primary-foreground)); --color-secondary: hsl(var(--secondary)); --color-secondary-foreground: hsl(var(--secondary-foreground)); --color-muted: hsl(var(--muted)); --color-muted-foreground: hsl(var(--muted-foreground)); --color-accent: hsl(var(--accent)); --color-accent-foreground: hsl(var(--accent-foreground)); --color-destructive: hsl(var(--destructive)); --color-destructive-foreground: hsl(var(--destructive-foreground)); --color-border: hsl(var(--border)); --color-input: hsl(var(--input)); --color-ring: hsl(var(--ring)); --radius-lg: var(--radius); --radius-md: calc(var(--radius) - 2px); --radius-sm: calc(var(--radius) - 4px); }\n.hover\\:border-blue-300:hover {\n --tw-border-opacity: 1;\n border-color: rgb(147 197 253 / var(--tw-border-opacity, 1));\n}\n.hover\\:border-red-300:hover {\n --tw-border-opacity: 1;\n border-color: rgb(252 165 165 / var(--tw-border-opacity, 1));\n}\n.hover\\:bg-accent:hover {\n background-color: hsl(var(--accent));\n}\n.hover\\:bg-blue-50:hover {\n --tw-bg-opacity: 1;\n background-color: rgb(239 246 255 / var(--tw-bg-opacity, 1));\n}\n.hover\\:bg-destructive\\/90:hover {\n background-color: hsl(var(--destructive) / 0.9);\n}\n.hover\\:bg-gray-100:hover {\n --tw-bg-opacity: 1;\n background-color: rgb(243 244 246 / var(--tw-bg-opacity, 1));\n}\n.hover\\:bg-gray-200:hover {\n --tw-bg-opacity: 1;\n background-color: rgb(229 231 235 / var(--tw-bg-opacity, 1));\n}\n.hover\\:bg-primary\\/90:hover {\n background-color: hsl(var(--primary) / 0.9);\n}\n.hover\\:bg-red-50:hover {\n --tw-bg-opacity: 1;\n background-color: rgb(254 242 242 / var(--tw-bg-opacity, 1));\n}\n.hover\\:bg-secondary\\/80:hover {\n background-color: hsl(var(--secondary) / 0.8);\n}\n.hover\\:from-blue-100:hover {\n --tw-gradient-from: #dbeafe var(--tw-gradient-from-position);\n --tw-gradient-to: rgb(219 234 254 / 0) var(--tw-gradient-to-position);\n --tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to);\n}\n.hover\\:to-purple-100:hover {\n --tw-gradient-to: #f3e8ff var(--tw-gradient-to-position);\n}\n.hover\\:text-accent-foreground:hover {\n color: hsl(var(--accent-foreground));\n}\n.hover\\:text-gray-600:hover {\n --tw-text-opacity: 1;\n color: rgb(75 85 99 / var(--tw-text-opacity, 1));\n}\n.hover\\:text-red-500:hover {\n --tw-text-opacity: 1;\n color: rgb(239 68 68 / var(--tw-text-opacity, 1));\n}\n.hover\\:text-red-700:hover {\n --tw-text-opacity: 1;\n color: rgb(185 28 28 / var(--tw-text-opacity, 1));\n}\n.hover\\:underline:hover {\n text-decoration-line: underline;\n}\n.hover\\:opacity-80:hover {\n opacity: 0.8;\n}\n.hover\\:shadow-xl:hover {\n --tw-shadow: 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1);\n --tw-shadow-colored: 0 20px 25px -5px var(--tw-shadow-color), 0 8px 10px -6px var(--tw-shadow-color);\n box-shadow:\n var(--tw-ring-offset-shadow, 0 0 #0000),\n var(--tw-ring-shadow, 0 0 #0000),\n var(--tw-shadow);\n}\n.focus-visible\\:outline-none:focus-visible {\n outline: 2px solid transparent;\n outline-offset: 2px;\n}\n.focus-visible\\:ring-2:focus-visible {\n --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);\n --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);\n box-shadow:\n var(--tw-ring-offset-shadow),\n var(--tw-ring-shadow),\n var(--tw-shadow, 0 0 #0000);\n}\n.focus-visible\\:ring-ring:focus-visible {\n --tw-ring-color: hsl(var(--ring));\n}\n.focus-visible\\:ring-offset-2:focus-visible {\n --tw-ring-offset-width: 2px;\n}\n.active\\:cursor-grabbing:active {\n cursor: grabbing;\n}\n.disabled\\:pointer-events-none:disabled {\n pointer-events: none;\n}\n.disabled\\:opacity-50:disabled {\n opacity: 0.5;\n}\n.group:hover .group-hover\\:text-blue-700 {\n --tw-text-opacity: 1;\n color: rgb(29 78 216 / var(--tw-text-opacity, 1));\n}\n@media (prefers-color-scheme: dark) {\n .dark\\:block {\n display: block;\n }\n .dark\\:border-blue-600 {\n --tw-border-opacity: 1;\n border-color: rgb(37 99 235 / var(--tw-border-opacity, 1));\n }\n .dark\\:border-blue-700 {\n --tw-border-opacity: 1;\n border-color: rgb(29 78 216 / var(--tw-border-opacity, 1));\n }\n .dark\\:border-gray-600 {\n --tw-border-opacity: 1;\n border-color: rgb(75 85 99 / var(--tw-border-opacity, 1));\n }\n .dark\\:border-gray-700 {\n --tw-border-opacity: 1;\n border-color: rgb(55 65 81 / var(--tw-border-opacity, 1));\n }\n .dark\\:bg-blue-900\\/30 {\n background-color: rgb(30 58 138 / 0.3);\n }\n .dark\\:bg-blue-900\\/50 {\n background-color: rgb(30 58 138 / 0.5);\n }\n .dark\\:bg-gray-600 {\n --tw-bg-opacity: 1;\n background-color: rgb(75 85 99 / var(--tw-bg-opacity, 1));\n }\n .dark\\:bg-gray-700 {\n --tw-bg-opacity: 1;\n background-color: rgb(55 65 81 / var(--tw-bg-opacity, 1));\n }\n .dark\\:bg-gray-800 {\n --tw-bg-opacity: 1;\n background-color: rgb(31 41 55 / var(--tw-bg-opacity, 1));\n }\n .dark\\:bg-gray-900 {\n --tw-bg-opacity: 1;\n background-color: rgb(17 24 39 / var(--tw-bg-opacity, 1));\n }\n .dark\\:from-gray-700 {\n --tw-gradient-from: #374151 var(--tw-gradient-from-position);\n --tw-gradient-to: rgb(55 65 81 / 0) var(--tw-gradient-to-position);\n --tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to);\n }\n .dark\\:to-gray-600 {\n --tw-gradient-to: #4b5563 var(--tw-gradient-to-position);\n }\n .dark\\:to-gray-700 {\n --tw-gradient-to: #374151 var(--tw-gradient-to-position);\n }\n .dark\\:text-blue-300 {\n --tw-text-opacity: 1;\n color: rgb(147 197 253 / var(--tw-text-opacity, 1));\n }\n .dark\\:text-blue-400 {\n --tw-text-opacity: 1;\n color: rgb(96 165 250 / var(--tw-text-opacity, 1));\n }\n .dark\\:text-gray-100 {\n --tw-text-opacity: 1;\n color: rgb(243 244 246 / var(--tw-text-opacity, 1));\n }\n .dark\\:text-gray-200 {\n --tw-text-opacity: 1;\n color: rgb(229 231 235 / var(--tw-text-opacity, 1));\n }\n .dark\\:text-gray-300 {\n --tw-text-opacity: 1;\n color: rgb(209 213 219 / var(--tw-text-opacity, 1));\n }\n .dark\\:text-gray-400 {\n --tw-text-opacity: 1;\n color: rgb(156 163 175 / var(--tw-text-opacity, 1));\n }\n .dark\\:text-gray-500 {\n --tw-text-opacity: 1;\n color: rgb(107 114 128 / var(--tw-text-opacity, 1));\n }\n .dark\\:text-red-400 {\n --tw-text-opacity: 1;\n color: rgb(248 113 113 / var(--tw-text-opacity, 1));\n }\n .dark\\:opacity-10 {\n opacity: 0.1;\n }\n .dark\\:opacity-20 {\n opacity: 0.2;\n }\n .dark\\:shadow-gray-900\\/50 {\n --tw-shadow-color: rgb(17 24 39 / 0.5);\n --tw-shadow: var(--tw-shadow-colored);\n }\n .dark\\:hover\\:bg-blue-900\\/50:hover {\n background-color: rgb(30 58 138 / 0.5);\n }\n .dark\\:hover\\:bg-gray-600:hover {\n --tw-bg-opacity: 1;\n background-color: rgb(75 85 99 / var(--tw-bg-opacity, 1));\n }\n .dark\\:hover\\:bg-gray-700:hover {\n --tw-bg-opacity: 1;\n background-color: rgb(55 65 81 / var(--tw-bg-opacity, 1));\n }\n .dark\\:hover\\:bg-red-900\\/20:hover {\n background-color: rgb(127 29 29 / 0.2);\n }\n .dark\\:hover\\:from-gray-600:hover {\n --tw-gradient-from: #4b5563 var(--tw-gradient-from-position);\n --tw-gradient-to: rgb(75 85 99 / 0) var(--tw-gradient-to-position);\n --tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to);\n }\n .dark\\:hover\\:to-gray-600:hover {\n --tw-gradient-to: #4b5563 var(--tw-gradient-to-position);\n }\n .dark\\:hover\\:text-gray-300:hover {\n --tw-text-opacity: 1;\n color: rgb(209 213 219 / var(--tw-text-opacity, 1));\n }\n .dark\\:hover\\:text-red-400:hover {\n --tw-text-opacity: 1;\n color: rgb(248 113 113 / var(--tw-text-opacity, 1));\n }\n .group:hover .dark\\:group-hover\\:text-blue-300 {\n --tw-text-opacity: 1;\n color: rgb(147 197 253 / var(--tw-text-opacity, 1));\n }\n}\n.\\[\\&_svg\\]\\:pointer-events-none svg {\n pointer-events: none;\n}\n.\\[\\&_svg\\]\\:size-4 svg {\n width: 1rem;\n height: 1rem;\n}\n.\\[\\&_svg\\]\\:shrink-0 svg {\n flex-shrink: 0;\n}\n');
1634
+
1635
+ // src/index.preact.ts
1636
+ var index_preact_default = Dashboard;
1637
+ export {
1638
+ ANIMATION_DURATION,
1639
+ Button,
1640
+ CELL_SIZE,
1641
+ CONTAINER_PADDING,
1642
+ DEBOUNCE_DELAY,
1643
+ Dashboard,
1644
+ DashboardToolbar,
1645
+ GRID_SIZE,
1646
+ MARGIN,
1647
+ MAX_SIZE,
1648
+ MIN_CONTAINER_HEIGHT,
1649
+ MIN_SIZE,
1650
+ Tooltip,
1651
+ TooltipContent,
1652
+ TooltipProvider,
1653
+ TooltipTrigger,
1654
+ index_preact_default as default
1655
+ };
1656
+ //# sourceMappingURL=index.preact.js.map