@anvilkit/puck-studio 0.0.1

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.
package/dist/index.js ADDED
@@ -0,0 +1,4066 @@
1
+ import { usePuck, useGetPuck, Puck, AutoField } from '@puckeditor/core';
2
+ import { ArrowLeft, Undo2, Redo2, Download, Search, Plus, Bold, Italic, Link, ChevronRightIcon, Info, Sparkles, ChevronDownIcon, CheckIcon, GripVertical, Copy, Trash2, SearchIcon, ChevronUpIcon, FileDown, Sun, Moon, Send, Layers2, Image, Type, Bot, Lock, Globe, Users, Link2, Check } from 'lucide-react';
3
+ import { Button as Button$1 } from '@base-ui/react/button';
4
+ import { cva } from 'class-variance-authority';
5
+ import { clsx } from 'clsx';
6
+ import { twMerge } from 'tailwind-merge';
7
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
8
+ import { Separator as Separator$1 } from '@base-ui/react/separator';
9
+ import { createStore, useStore } from 'zustand';
10
+ import * as React18 from 'react';
11
+ import React18__default, { useState, useRef, useMemo, useCallback, useEffect } from 'react';
12
+ import * as TooltipPrimitive from '@radix-ui/react-tooltip';
13
+ import { Menu } from '@base-ui/react/menu';
14
+ import { ScrollArea as ScrollArea$1 } from '@base-ui/react/scroll-area';
15
+ import { Input as Input$1 } from '@base-ui/react/input';
16
+ import { mergeProps } from '@base-ui/react/merge-props';
17
+ import { useRender } from '@base-ui/react/use-render';
18
+ import { Select as Select$1 } from '@base-ui/react/select';
19
+ import { useSensors, useSensor, PointerSensor, DndContext, closestCenter } from '@dnd-kit/core';
20
+ import { SortableContext, verticalListSortingStrategy, arrayMove, useSortable } from '@dnd-kit/sortable';
21
+ import { CSS } from '@dnd-kit/utilities';
22
+ import { Popover as Popover$1 } from '@base-ui-components/react/popover';
23
+ import { motion, AnimatePresence, LayoutGroup, isMotionComponent } from 'motion/react';
24
+ import { Toggle as Toggle$1 } from '@base-ui/react/toggle';
25
+ import { Command as Command$1 } from 'cmdk';
26
+ import '@base-ui/react/dialog';
27
+ import { createAiPlugin } from '@puckeditor/plugin-ai';
28
+ import '@puckeditor/plugin-ai/styles.css';
29
+ import '@puckeditor/core/puck.css';
30
+ import { Tabs as Tabs$1 } from '@base-ui-components/react/tabs';
31
+ import * as motion9 from 'motion/react-client';
32
+ import { FloatingArrow, useFloating, autoUpdate, offset, flip, shift, arrow, FloatingPortal } from '@floating-ui/react';
33
+ import { Avatar as Avatar$1 } from '@base-ui/react/avatar';
34
+ import { persist } from 'zustand/middleware';
35
+
36
+ // src/components/overrides/layout/EditorHeader.tsx
37
+ function cn(...inputs) {
38
+ return twMerge(clsx(inputs));
39
+ }
40
+ var buttonVariants = cva(
41
+ "group/button inline-flex shrink-0 items-center justify-center rounded-lg border border-transparent bg-clip-padding text-sm font-medium whitespace-nowrap transition-all outline-none select-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 active:translate-y-px disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
42
+ {
43
+ variants: {
44
+ variant: {
45
+ default: "bg-primary text-primary-foreground hover:bg-primary/90",
46
+ outline: "border-border bg-background hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50",
47
+ secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80 aria-expanded:bg-secondary aria-expanded:text-secondary-foreground",
48
+ ghost: "hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:hover:bg-muted/50",
49
+ destructive: "bg-destructive/10 text-destructive hover:bg-destructive/20 focus-visible:border-destructive/40 focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:hover:bg-destructive/30 dark:focus-visible:ring-destructive/40",
50
+ link: "text-primary underline-offset-4 hover:underline"
51
+ },
52
+ size: {
53
+ default: "h-8 gap-1.5 px-2.5",
54
+ xs: "h-6 gap-1 rounded-[min(var(--radius-md),10px)] px-2 text-xs [&_svg:not([class*='size-'])]:size-3",
55
+ sm: "h-7 gap-1 rounded-[min(var(--radius-md),12px)] px-2.5 text-[0.8rem] [&_svg:not([class*='size-'])]:size-3.5",
56
+ lg: "h-9 gap-1.5 px-2.5",
57
+ icon: "size-8",
58
+ "icon-xs": "size-6 rounded-[min(var(--radius-md),10px)] [&_svg:not([class*='size-'])]:size-3",
59
+ "icon-sm": "size-7 rounded-[min(var(--radius-md),12px)]",
60
+ "icon-lg": "size-9"
61
+ }
62
+ },
63
+ defaultVariants: {
64
+ variant: "default",
65
+ size: "default"
66
+ }
67
+ }
68
+ );
69
+ function Button({
70
+ className,
71
+ variant = "default",
72
+ size = "default",
73
+ ...props
74
+ }) {
75
+ return /* @__PURE__ */ jsx(
76
+ Button$1,
77
+ {
78
+ "data-slot": "button",
79
+ className: cn(buttonVariants({ variant, size, className })),
80
+ ...props
81
+ }
82
+ );
83
+ }
84
+ function Separator({
85
+ className,
86
+ orientation = "horizontal",
87
+ ...props
88
+ }) {
89
+ return /* @__PURE__ */ jsx(
90
+ Separator$1,
91
+ {
92
+ "data-slot": "separator",
93
+ orientation,
94
+ className: cn(
95
+ "shrink-0 bg-border data-horizontal:h-px data-horizontal:w-full data-vertical:w-px data-vertical:self-stretch",
96
+ className
97
+ ),
98
+ ...props
99
+ }
100
+ );
101
+ }
102
+ function getStrictContext(name) {
103
+ const Context = React18.createContext(void 0);
104
+ const Provider2 = ({
105
+ value,
106
+ children
107
+ }) => /* @__PURE__ */ jsx(Context.Provider, { value, children });
108
+ const useSafeContext = () => {
109
+ const ctx = React18.useContext(Context);
110
+ if (ctx === void 0) {
111
+ throw new Error(`useContext must be used within ${name ?? "a Provider"}`);
112
+ }
113
+ return ctx;
114
+ };
115
+ return [Provider2, useSafeContext];
116
+ }
117
+
118
+ // src/store/ui-context.ts
119
+ var [EditorUiStoreProvider, useEditorUiStoreApi] = getStrictContext("EditorUiStoreProvider");
120
+
121
+ // src/store/i18n-context.ts
122
+ var [EditorI18nStoreProvider, useEditorI18nStoreApi] = getStrictContext("EditorI18nStoreProvider");
123
+
124
+ // src/store/hooks.ts
125
+ function useActiveTab() {
126
+ return useStore(useEditorUiStoreApi(), (s) => s.activeTab);
127
+ }
128
+ function useSetActiveTab() {
129
+ return useStore(useEditorUiStoreApi(), (s) => s.setActiveTab);
130
+ }
131
+ function useDrawerSearch() {
132
+ return useStore(useEditorUiStoreApi(), (s) => s.drawerSearch);
133
+ }
134
+ function useSetDrawerSearch() {
135
+ return useStore(useEditorUiStoreApi(), (s) => s.setDrawerSearch);
136
+ }
137
+ function useTheme() {
138
+ return useStore(useEditorUiStoreApi(), (s) => s.theme);
139
+ }
140
+ function useToggleTheme() {
141
+ return useStore(useEditorUiStoreApi(), (s) => s.toggleTheme);
142
+ }
143
+ function useMsg(key) {
144
+ return useStore(useEditorI18nStoreApi(), (s) => s.messages[key] ?? key);
145
+ }
146
+ var TooltipProvider = TooltipPrimitive.Provider;
147
+ var Tooltip = TooltipPrimitive.Root;
148
+ var TooltipTrigger = TooltipPrimitive.Trigger;
149
+ var TooltipContent = React18.forwardRef(({ className, sideOffset = 4, ...props }, ref) => /* @__PURE__ */ jsx(TooltipPrimitive.Portal, { children: /* @__PURE__ */ jsx(
150
+ TooltipPrimitive.Content,
151
+ {
152
+ ref,
153
+ sideOffset,
154
+ className: cn(
155
+ "z-50 overflow-hidden rounded-md bg-primary px-3 py-1.5 text-xs text-primary-foreground animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 origin-[--radix-tooltip-content-transform-origin]",
156
+ className
157
+ ),
158
+ ...props
159
+ }
160
+ ) }));
161
+ TooltipContent.displayName = TooltipPrimitive.Content.displayName;
162
+ function DropdownMenu({ ...props }) {
163
+ return /* @__PURE__ */ jsx(Menu.Root, { "data-slot": "dropdown-menu", ...props });
164
+ }
165
+ function DropdownMenuTrigger({ ...props }) {
166
+ return /* @__PURE__ */ jsx(Menu.Trigger, { "data-slot": "dropdown-menu-trigger", ...props });
167
+ }
168
+ function DropdownMenuContent({
169
+ align = "start",
170
+ alignOffset = 0,
171
+ side = "bottom",
172
+ sideOffset = 4,
173
+ className,
174
+ ...props
175
+ }) {
176
+ return /* @__PURE__ */ jsx(Menu.Portal, { children: /* @__PURE__ */ jsx(
177
+ Menu.Positioner,
178
+ {
179
+ className: "isolate z-50 outline-none",
180
+ align,
181
+ alignOffset,
182
+ side,
183
+ sideOffset,
184
+ children: /* @__PURE__ */ jsx(
185
+ Menu.Popup,
186
+ {
187
+ "data-slot": "dropdown-menu-content",
188
+ className: cn(
189
+ "data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-open:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 ring-foreground/10 bg-popover text-popover-foreground min-w-32 rounded-lg p-1 shadow-md ring-1 duration-100 data-[side=inline-start]:slide-in-from-right-2 data-[side=inline-end]:slide-in-from-left-2 z-50 max-h-(--available-height) w-(--anchor-width) origin-(--transform-origin) overflow-x-hidden overflow-y-auto outline-none data-closed:overflow-hidden",
190
+ className
191
+ ),
192
+ ...props
193
+ }
194
+ )
195
+ }
196
+ ) });
197
+ }
198
+ function DropdownMenuItem({
199
+ className,
200
+ inset,
201
+ variant = "default",
202
+ ...props
203
+ }) {
204
+ return /* @__PURE__ */ jsx(
205
+ Menu.Item,
206
+ {
207
+ "data-slot": "dropdown-menu-item",
208
+ "data-inset": inset,
209
+ "data-variant": variant,
210
+ className: cn(
211
+ "focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/20 data-[variant=destructive]:focus:text-destructive data-[variant=destructive]:*:[svg]:text-destructive not-data-[variant=destructive]:focus:**:text-accent-foreground gap-1.5 rounded-md px-1.5 py-1 text-sm data-inset:pl-7 [&_svg:not([class*='size-'])]:size-4 group/dropdown-menu-item relative flex cursor-default items-center outline-hidden select-none data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0",
212
+ className
213
+ ),
214
+ ...props
215
+ }
216
+ );
217
+ }
218
+ function EditorHeader({
219
+ actions,
220
+ children
221
+ }) {
222
+ const { history, appState } = usePuck();
223
+ const undo = useMsg("header.undo");
224
+ const undoTooltip = useMsg("header.undo.tooltip");
225
+ const redo = useMsg("header.redo");
226
+ const redoTooltip = useMsg("header.redo.tooltip");
227
+ const exportLabel = useMsg("header.export");
228
+ const exportJson = useMsg("header.export.json");
229
+ return /* @__PURE__ */ jsx(TooltipProvider, { children: /* @__PURE__ */ jsxs("header", { className: "flex h-12 items-center justify-between border-b bg-background px-4 gap-2", children: [
230
+ /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "icon", children: /* @__PURE__ */ jsx(ArrowLeft, { className: "h-4 w-4" }) }),
231
+ /* @__PURE__ */ jsx("div", { className: "flex items-center gap-1", children: appState?.data?.root?.props?.title || "" }),
232
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
233
+ /* @__PURE__ */ jsxs(Tooltip, { children: [
234
+ /* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
235
+ Button,
236
+ {
237
+ variant: "ghost",
238
+ size: "icon",
239
+ disabled: !history.hasPast,
240
+ onClick: () => history.back(),
241
+ "aria-label": undo,
242
+ children: /* @__PURE__ */ jsx(Undo2, { className: "h-4 w-4" })
243
+ }
244
+ ) }),
245
+ /* @__PURE__ */ jsx(TooltipContent, { children: undoTooltip })
246
+ ] }),
247
+ /* @__PURE__ */ jsxs(Tooltip, { children: [
248
+ /* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
249
+ Button,
250
+ {
251
+ variant: "ghost",
252
+ size: "icon",
253
+ disabled: !history.hasFuture,
254
+ onClick: () => history.forward(),
255
+ "aria-label": redo,
256
+ children: /* @__PURE__ */ jsx(Redo2, { className: "h-4 w-4" })
257
+ }
258
+ ) }),
259
+ /* @__PURE__ */ jsx(TooltipContent, { children: redoTooltip })
260
+ ] }),
261
+ /* @__PURE__ */ jsx(Separator, { orientation: "vertical", className: "h-5 mx-1" }),
262
+ /* @__PURE__ */ jsxs(DropdownMenu, { children: [
263
+ /* @__PURE__ */ jsxs(Tooltip, { children: [
264
+ /* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx(DropdownMenuTrigger, { "aria-label": exportLabel, className: "inline-flex items-center justify-center rounded-md h-9 w-9 hover:bg-accent hover:text-accent-foreground", children: /* @__PURE__ */ jsx(Download, { className: "h-4 w-4" }) }) }),
265
+ /* @__PURE__ */ jsx(TooltipContent, { children: exportLabel })
266
+ ] }),
267
+ /* @__PURE__ */ jsx(DropdownMenuContent, { align: "end", children: /* @__PURE__ */ jsx(DropdownMenuItem, { children: exportJson }) })
268
+ ] }),
269
+ actions
270
+ ] })
271
+ ] }) });
272
+ }
273
+ function EditorHeaderActions({
274
+ children
275
+ }) {
276
+ return /* @__PURE__ */ jsx(TooltipProvider, { children: /* @__PURE__ */ jsx("div", { className: "flex items-center gap-2", children }) });
277
+ }
278
+ function ScrollArea({
279
+ className,
280
+ children,
281
+ ...props
282
+ }) {
283
+ return /* @__PURE__ */ jsxs(
284
+ ScrollArea$1.Root,
285
+ {
286
+ "data-slot": "scroll-area",
287
+ className: cn("relative", className),
288
+ ...props,
289
+ children: [
290
+ /* @__PURE__ */ jsx(
291
+ ScrollArea$1.Viewport,
292
+ {
293
+ "data-slot": "scroll-area-viewport",
294
+ className: "size-full rounded-[inherit] transition-[color,box-shadow] outline-none focus-visible:ring-[3px] focus-visible:ring-ring/50 focus-visible:outline-1",
295
+ children
296
+ }
297
+ ),
298
+ /* @__PURE__ */ jsx(ScrollBar, {}),
299
+ /* @__PURE__ */ jsx(ScrollArea$1.Corner, {})
300
+ ]
301
+ }
302
+ );
303
+ }
304
+ function ScrollBar({
305
+ className,
306
+ orientation = "vertical",
307
+ ...props
308
+ }) {
309
+ return /* @__PURE__ */ jsx(
310
+ ScrollArea$1.Scrollbar,
311
+ {
312
+ "data-slot": "scroll-area-scrollbar",
313
+ "data-orientation": orientation,
314
+ orientation,
315
+ className: cn(
316
+ "flex touch-none p-px transition-colors select-none data-horizontal:h-2.5 data-horizontal:flex-col data-horizontal:border-t data-horizontal:border-t-transparent data-vertical:h-full data-vertical:w-2.5 data-vertical:border-l data-vertical:border-l-transparent",
317
+ className
318
+ ),
319
+ ...props,
320
+ children: /* @__PURE__ */ jsx(
321
+ ScrollArea$1.Thumb,
322
+ {
323
+ "data-slot": "scroll-area-thumb",
324
+ className: "relative flex-1 rounded-full bg-border"
325
+ }
326
+ )
327
+ }
328
+ );
329
+ }
330
+ function Input({ className, type, ...props }) {
331
+ return /* @__PURE__ */ jsx(
332
+ Input$1,
333
+ {
334
+ type,
335
+ "data-slot": "input",
336
+ className: cn(
337
+ "h-8 w-full min-w-0 rounded-lg border border-input bg-transparent px-2.5 py-1 text-base transition-colors outline-none file:inline-flex file:h-6 file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:pointer-events-none disabled:cursor-not-allowed disabled:bg-input/50 disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 md:text-sm dark:bg-input/30 dark:disabled:bg-input/80 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40",
338
+ className
339
+ ),
340
+ ...props
341
+ }
342
+ );
343
+ }
344
+ function EditorDrawer({
345
+ children
346
+ }) {
347
+ const search = useDrawerSearch();
348
+ const setSearch = useSetDrawerSearch();
349
+ const drawerTitle = useMsg("drawer.title");
350
+ const searchPlaceholder = useMsg("drawer.search.placeholder");
351
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-col h-full", children: [
352
+ /* @__PURE__ */ jsx("div", { className: "px-3 pt-3 pb-1 text-xs font-semibold text-muted-foreground uppercase tracking-wider", children: drawerTitle }),
353
+ /* @__PURE__ */ jsx("div", { className: "p-2", children: /* @__PURE__ */ jsxs("div", { className: "relative", children: [
354
+ /* @__PURE__ */ jsx(Search, { className: "absolute left-2 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" }),
355
+ /* @__PURE__ */ jsx(
356
+ Input,
357
+ {
358
+ className: "pl-8",
359
+ placeholder: searchPlaceholder,
360
+ value: search,
361
+ onChange: (e) => setSearch(e.target.value)
362
+ }
363
+ )
364
+ ] }) }),
365
+ /* @__PURE__ */ jsx(ScrollArea, { className: "flex-1", children })
366
+ ] });
367
+ }
368
+ function EditorComponents({
369
+ children
370
+ }) {
371
+ return /* @__PURE__ */ jsxs("div", { "data-puck-components-grid": true, className: "p-2", children: [
372
+ /* @__PURE__ */ jsx("style", { children: `
373
+ [data-puck-components-grid] [data-puck-drawer] {
374
+ display: grid;
375
+ grid-template-columns: repeat(3, minmax(0, 1fr));
376
+ gap: 6px;
377
+ }
378
+ /* flatten item wrapper and draggable wrapper */
379
+ [data-puck-components-grid] [data-puck-drawer] > div,
380
+ [data-puck-components-grid] [data-puck-drawer] > div > div {
381
+ display: contents;
382
+ }
383
+ /* hide the ghost/bg copy */
384
+ [data-puck-components-grid] [data-puck-drawer] > div > div > div:first-child {
385
+ display: none;
386
+ }
387
+ /* flatten the real draggable wrapper */
388
+ [data-puck-components-grid] [data-puck-drawer] > div > div > div:last-child {
389
+ display: contents;
390
+ }
391
+ ` }),
392
+ children
393
+ ] });
394
+ }
395
+ function getPlaceholderUrl(name) {
396
+ return `https://picsum.photos/seed/${encodeURIComponent(name)}/120/80`;
397
+ }
398
+ function DrawerItem({
399
+ children,
400
+ name
401
+ }) {
402
+ const { appState } = usePuck();
403
+ const componentConfig = appState.config?.components?.[name];
404
+ const thumbnail = componentConfig?.metadata?.thumbnail;
405
+ const src = thumbnail ?? getPlaceholderUrl(name);
406
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-col rounded-md border border-border bg-muted/40 cursor-grab select-none transition-colors hover:bg-muted active:cursor-grabbing overflow-hidden", children: [
407
+ /* @__PURE__ */ jsx("div", { className: "w-full h-16 bg-muted overflow-hidden", children: /* @__PURE__ */ jsx(
408
+ "img",
409
+ {
410
+ src,
411
+ alt: name,
412
+ className: "w-full h-full object-cover",
413
+ onError: (e) => {
414
+ e.currentTarget.style.display = "none";
415
+ }
416
+ }
417
+ ) }),
418
+ /* @__PURE__ */ jsx("div", { className: "px-2 py-1.5 text-xs font-medium truncate", children: name ?? children })
419
+ ] });
420
+ }
421
+ function EditorOutline({
422
+ children
423
+ }) {
424
+ const { selectedItem } = usePuck();
425
+ return /* @__PURE__ */ jsxs("div", { className: "flex h-full flex-col", children: [
426
+ /* @__PURE__ */ jsx("div", { className: "px-3 py-2 border-b", children: /* @__PURE__ */ jsx("span", { className: "text-xs font-semibold text-muted-foreground uppercase tracking-wide", children: "Outline" }) }),
427
+ selectedItem && /* @__PURE__ */ jsxs(Fragment, { children: [
428
+ /* @__PURE__ */ jsxs("div", { className: "px-3 py-1.5 text-xs text-muted-foreground truncate", children: [
429
+ "Selected:",
430
+ " ",
431
+ /* @__PURE__ */ jsx("span", { className: "font-medium text-foreground", children: selectedItem.type ?? "Component" })
432
+ ] }),
433
+ /* @__PURE__ */ jsx(Separator, {})
434
+ ] }),
435
+ /* @__PURE__ */ jsx(ScrollArea, { className: "flex-1", children: /* @__PURE__ */ jsx("div", { className: "p-2 text-sm", children }) })
436
+ ] });
437
+ }
438
+ var CANVAS_CSS = `
439
+ *, *::before, *::after { box-sizing: border-box; }
440
+ :root {
441
+ --background: 0 0% 100%;
442
+ --foreground: 222.2 84% 4.9%;
443
+ --primary: 222.2 47.4% 11.2%;
444
+ --primary-foreground: 210 40% 98%;
445
+ --secondary: 210 40% 96.1%;
446
+ --muted: 210 40% 96.1%;
447
+ --muted-foreground: 215.4 16.3% 46.9%;
448
+ --border: 214.3 31.8% 91.4%;
449
+ --radius: 0.5rem;
450
+ }
451
+ .dark {
452
+ --background: 222.2 84% 4.9%;
453
+ --foreground: 210 40% 98%;
454
+ --primary: 210 40% 98%;
455
+ --primary-foreground: 222.2 47.4% 11.2%;
456
+ --secondary: 217.2 32.6% 17.5%;
457
+ --muted: 217.2 32.6% 17.5%;
458
+ --muted-foreground: 215 20.2% 65.1%;
459
+ --border: 217.2 32.6% 17.5%;
460
+ }
461
+ body { margin: 0; font-family: system-ui, sans-serif; }
462
+ `;
463
+ function isImageUrl(val) {
464
+ return /\.(jpg|jpeg|png|gif|webp|svg|avif)(\?.*)?$/i.test(val) || val.includes("picsum.photos") || val.includes("unsplash.com") || val.includes("images.") || val.startsWith("data:image/");
465
+ }
466
+ function replaceImageInProps(props, newSrc) {
467
+ const result = {};
468
+ for (const key of Object.keys(props)) {
469
+ const val = props[key];
470
+ if (typeof val === "string" && isImageUrl(val)) {
471
+ result[key] = newSrc;
472
+ } else if (Array.isArray(val)) {
473
+ result[key] = val.map(
474
+ (item) => item && typeof item === "object" ? replaceImageInProps(item, newSrc) : item
475
+ );
476
+ } else if (val && typeof val === "object") {
477
+ result[key] = replaceImageInProps(val, newSrc);
478
+ } else {
479
+ result[key] = val;
480
+ }
481
+ }
482
+ return result;
483
+ }
484
+ function replaceTextInProps(props, newText, targetText) {
485
+ for (const key of Object.keys(props)) {
486
+ if (key === "id") continue;
487
+ const val = props[key];
488
+ if (typeof val === "string" && val === targetText && !isImageUrl(val)) {
489
+ return { result: { ...props, [key]: newText }, replaced: true };
490
+ }
491
+ }
492
+ for (const key of Object.keys(props)) {
493
+ if (key === "id") continue;
494
+ const val = props[key];
495
+ if (typeof val === "string" && !isImageUrl(val) && val.length > 0) {
496
+ return { result: { ...props, [key]: newText }, replaced: true };
497
+ }
498
+ }
499
+ return { result: props, replaced: false };
500
+ }
501
+ function CanvasIframe({
502
+ children,
503
+ document: iframeDoc
504
+ }) {
505
+ const getPuck = useGetPuck();
506
+ const theme = useTheme();
507
+ React18.useEffect(() => {
508
+ if (!iframeDoc) return;
509
+ const existing = iframeDoc.getElementById("__anvilkit_styles__");
510
+ if (existing) existing.remove();
511
+ const style = iframeDoc.createElement("style");
512
+ style.id = "__anvilkit_styles__";
513
+ style.textContent = CANVAS_CSS;
514
+ iframeDoc.head.appendChild(style);
515
+ }, [iframeDoc]);
516
+ React18.useEffect(() => {
517
+ if (!iframeDoc) return;
518
+ iframeDoc.documentElement.classList.toggle("dark", theme === "dark");
519
+ }, [iframeDoc, theme]);
520
+ React18.useEffect(() => {
521
+ if (!iframeDoc) return;
522
+ const iframeEl = iframeDoc.defaultView?.frameElement;
523
+ if (!iframeEl) return;
524
+ let highlightedEl = null;
525
+ function iframeCoords(clientX, clientY) {
526
+ const rect = iframeEl.getBoundingClientRect();
527
+ const x = clientX - rect.left;
528
+ const y = clientY - rect.top;
529
+ if (x < 0 || y < 0 || x > rect.width || y > rect.height) return null;
530
+ return { x, y };
531
+ }
532
+ function getComponentElAt(clientX, clientY) {
533
+ const coords = iframeCoords(clientX, clientY);
534
+ if (!coords) return null;
535
+ const el = iframeDoc.elementFromPoint(coords.x, coords.y);
536
+ if (!el) return null;
537
+ const comp = el.closest("[data-puck-component]");
538
+ return comp;
539
+ }
540
+ function getImgInComponent(compEl, clientX, clientY) {
541
+ const imgs = Array.from(compEl.querySelectorAll("img"));
542
+ if (!imgs.length) return null;
543
+ if (imgs.length === 1) return imgs[0];
544
+ const rect = iframeEl.getBoundingClientRect();
545
+ const x = clientX - rect.left;
546
+ const y = clientY - rect.top;
547
+ let closest = null;
548
+ let minDist = Infinity;
549
+ for (const img of imgs) {
550
+ const r = img.getBoundingClientRect();
551
+ const cx = r.left + r.width / 2;
552
+ const cy = r.top + r.height / 2;
553
+ const dist = Math.hypot(cx - x, cy - y);
554
+ if (dist < minDist) {
555
+ minDist = dist;
556
+ closest = img;
557
+ }
558
+ }
559
+ return closest;
560
+ }
561
+ const TEXT_TAGS = /* @__PURE__ */ new Set(["P", "H1", "H2", "H3", "H4", "H5", "H6", "SPAN", "A", "LI", "BUTTON", "LABEL"]);
562
+ function getTextElInComponent(compEl, clientX, clientY) {
563
+ const coords = iframeCoords(clientX, clientY);
564
+ if (!coords) return null;
565
+ const el = iframeDoc.elementFromPoint(coords.x, coords.y);
566
+ if (el && compEl.contains(el)) {
567
+ let cur = el;
568
+ while (cur && cur !== iframeDoc.body) {
569
+ if (TEXT_TAGS.has(cur.tagName) && cur.textContent?.trim()) return cur;
570
+ cur = cur.parentElement;
571
+ }
572
+ }
573
+ for (const tag of TEXT_TAGS) {
574
+ const found = compEl.querySelector(tag.toLowerCase());
575
+ if (found?.textContent?.trim()) return found;
576
+ }
577
+ return null;
578
+ }
579
+ function setHighlight(el, color) {
580
+ if (highlightedEl && highlightedEl !== el) {
581
+ highlightedEl.style.outline = "";
582
+ highlightedEl.style.outlineOffset = "";
583
+ }
584
+ if (el) {
585
+ el.style.outline = `2px solid ${color}`;
586
+ el.style.outlineOffset = "2px";
587
+ }
588
+ highlightedEl = el;
589
+ }
590
+ function clearHighlight() {
591
+ setHighlight(null, "");
592
+ }
593
+ let activeLibrary = null;
594
+ function onLibraryDragStart(e) {
595
+ activeLibrary = e.detail.type;
596
+ }
597
+ function onPointerMove(e) {
598
+ if (!activeLibrary) return;
599
+ const compEl = getComponentElAt(e.clientX, e.clientY);
600
+ if (!compEl) {
601
+ clearHighlight();
602
+ return;
603
+ }
604
+ if (activeLibrary === "image") {
605
+ const img = getImgInComponent(compEl, e.clientX, e.clientY);
606
+ setHighlight(img, "#6366f1");
607
+ } else {
608
+ const textEl = getTextElInComponent(compEl, e.clientX, e.clientY);
609
+ setHighlight(textEl, "#f59e0b");
610
+ }
611
+ }
612
+ function onPointerUp() {
613
+ activeLibrary = null;
614
+ clearHighlight();
615
+ }
616
+ function dispatchReplace(componentId, updatedProps) {
617
+ const { dispatch, getItemById, getSelectorForId } = getPuck();
618
+ const item = getItemById(componentId);
619
+ const selector = getSelectorForId(componentId);
620
+ if (!item || !selector) return false;
621
+ dispatch({
622
+ type: "replace",
623
+ destinationIndex: selector.index,
624
+ destinationZone: selector.zone,
625
+ data: { ...item, props: { ...item.props, ...updatedProps } }
626
+ });
627
+ return true;
628
+ }
629
+ function onImageDrop(e) {
630
+ const { src, clientX, clientY } = e.detail;
631
+ clearHighlight();
632
+ activeLibrary = null;
633
+ if (!src) return;
634
+ const compEl = getComponentElAt(clientX, clientY);
635
+ if (!compEl) return;
636
+ const componentId = compEl.getAttribute("data-puck-component");
637
+ const { getItemById } = getPuck();
638
+ const item = getItemById(componentId);
639
+ if (!item) return;
640
+ const updatedProps = replaceImageInProps(item.props, src);
641
+ dispatchReplace(componentId, updatedProps);
642
+ }
643
+ function onTextDrop(e) {
644
+ const { text, clientX, clientY } = e.detail;
645
+ clearHighlight();
646
+ activeLibrary = null;
647
+ if (!text) return;
648
+ const compEl = getComponentElAt(clientX, clientY);
649
+ if (!compEl) return;
650
+ const componentId = compEl.getAttribute("data-puck-component");
651
+ const textEl = getTextElInComponent(compEl, clientX, clientY);
652
+ const targetText = textEl?.textContent?.trim() ?? "";
653
+ const { getItemById } = getPuck();
654
+ const item = getItemById(componentId);
655
+ if (!item) return;
656
+ const { result: updatedProps, replaced } = replaceTextInProps(
657
+ item.props,
658
+ text,
659
+ targetText
660
+ );
661
+ if (replaced) dispatchReplace(componentId, updatedProps);
662
+ }
663
+ window.addEventListener("anvilkit:librarydragstart", onLibraryDragStart);
664
+ window.addEventListener("pointermove", onPointerMove);
665
+ window.addEventListener("pointerup", onPointerUp);
666
+ window.addEventListener("anvilkit:imagedrop", onImageDrop);
667
+ window.addEventListener("anvilkit:textdrop", onTextDrop);
668
+ return () => {
669
+ window.removeEventListener("anvilkit:librarydragstart", onLibraryDragStart);
670
+ window.removeEventListener("pointermove", onPointerMove);
671
+ window.removeEventListener("pointerup", onPointerUp);
672
+ window.removeEventListener("anvilkit:imagedrop", onImageDrop);
673
+ window.removeEventListener("anvilkit:textdrop", onTextDrop);
674
+ };
675
+ }, [iframeDoc, getPuck]);
676
+ return /* @__PURE__ */ jsx(Fragment, { children });
677
+ }
678
+ function CanvasPreview({
679
+ children
680
+ }) {
681
+ return /* @__PURE__ */ jsx("div", { className: "w-full h-full px-3 py-2 text-sm font-medium text-foreground", children });
682
+ }
683
+ function ComponentOverlay({
684
+ children,
685
+ hover,
686
+ isSelected
687
+ }) {
688
+ return /* @__PURE__ */ jsx(
689
+ "div",
690
+ {
691
+ className: [
692
+ "absolute inset-0 rounded-sm pointer-events-none z-10 transition-colors",
693
+ isSelected ? "border-2 border-primary/80" : hover ? "border-2 border-primary/40" : ""
694
+ ].filter(Boolean).join(" "),
695
+ children
696
+ }
697
+ );
698
+ }
699
+ function ActionBar({
700
+ children,
701
+ label,
702
+ parentAction
703
+ }) {
704
+ return /* @__PURE__ */ jsx(TooltipProvider, { children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-0.5 rounded-md border bg-background shadow-md px-1 py-0.5", children: [
705
+ label && /* @__PURE__ */ jsxs(Fragment, { children: [
706
+ /* @__PURE__ */ jsx("span", { className: "px-2 text-xs font-medium text-muted-foreground truncate max-w-[120px]", children: label }),
707
+ /* @__PURE__ */ jsx(Separator, { orientation: "vertical", className: "h-5" })
708
+ ] }),
709
+ parentAction && /* @__PURE__ */ jsxs(Fragment, { children: [
710
+ parentAction,
711
+ /* @__PURE__ */ jsx(Separator, { orientation: "vertical", className: "h-5" })
712
+ ] }),
713
+ children
714
+ ] }) });
715
+ }
716
+ function cn2(...inputs) {
717
+ return twMerge(clsx(inputs));
718
+ }
719
+ function Label({ className, ...props }) {
720
+ return /* @__PURE__ */ jsx(
721
+ "label",
722
+ {
723
+ "data-slot": "label",
724
+ className: cn2(
725
+ "gap-2 text-sm leading-none font-medium group-data-[disabled=true]:opacity-50 peer-disabled:opacity-50 flex items-center select-none group-data-[disabled=true]:pointer-events-none peer-disabled:cursor-not-allowed",
726
+ className
727
+ ),
728
+ ...props
729
+ }
730
+ );
731
+ }
732
+ function Breadcrumb({ className, ...props }) {
733
+ return /* @__PURE__ */ jsx(
734
+ "nav",
735
+ {
736
+ "aria-label": "breadcrumb",
737
+ "data-slot": "breadcrumb",
738
+ className: cn2(className),
739
+ ...props
740
+ }
741
+ );
742
+ }
743
+ function BreadcrumbList({ className, ...props }) {
744
+ return /* @__PURE__ */ jsx(
745
+ "ol",
746
+ {
747
+ "data-slot": "breadcrumb-list",
748
+ className: cn2(
749
+ "text-muted-foreground gap-1.5 text-sm flex flex-wrap items-center wrap-break-word",
750
+ className
751
+ ),
752
+ ...props
753
+ }
754
+ );
755
+ }
756
+ function BreadcrumbItem({ className, ...props }) {
757
+ return /* @__PURE__ */ jsx(
758
+ "li",
759
+ {
760
+ "data-slot": "breadcrumb-item",
761
+ className: cn2("gap-1 inline-flex items-center", className),
762
+ ...props
763
+ }
764
+ );
765
+ }
766
+ function BreadcrumbLink({
767
+ className,
768
+ render,
769
+ ...props
770
+ }) {
771
+ return useRender({
772
+ defaultTagName: "a",
773
+ props: mergeProps(
774
+ {
775
+ className: cn2("hover:text-foreground transition-colors", className)
776
+ },
777
+ props
778
+ ),
779
+ render,
780
+ state: {
781
+ slot: "breadcrumb-link"
782
+ }
783
+ });
784
+ }
785
+ function BreadcrumbPage({ className, ...props }) {
786
+ return /* @__PURE__ */ jsx(
787
+ "span",
788
+ {
789
+ "data-slot": "breadcrumb-page",
790
+ role: "link",
791
+ "aria-disabled": "true",
792
+ "aria-current": "page",
793
+ className: cn2("text-foreground font-normal", className),
794
+ ...props
795
+ }
796
+ );
797
+ }
798
+ function BreadcrumbSeparator({
799
+ children,
800
+ className,
801
+ ...props
802
+ }) {
803
+ return /* @__PURE__ */ jsx(
804
+ "li",
805
+ {
806
+ "data-slot": "breadcrumb-separator",
807
+ role: "presentation",
808
+ "aria-hidden": "true",
809
+ className: cn2("[&>svg]:size-3.5", className),
810
+ ...props,
811
+ children: children ?? /* @__PURE__ */ jsx(ChevronRightIcon, { className: "cn-rtl-flip" })
812
+ }
813
+ );
814
+ }
815
+ function useBreadcrumbs() {
816
+ const { appState, dispatch, selectedItem, getParentById, getSelectorForId } = usePuck();
817
+ const { itemSelector } = appState.ui;
818
+ const selectRoot = () => dispatch({ type: "setUi", ui: { itemSelector: null } });
819
+ if (!itemSelector || !selectedItem) {
820
+ return [{ label: "Page" }];
821
+ }
822
+ const selectedType = selectedItem.type ?? "Component";
823
+ const parent = getParentById(selectedItem.props?.id ?? "");
824
+ if (!parent) {
825
+ return [{ label: "Page", onSelect: selectRoot }, { label: selectedType }];
826
+ }
827
+ parent.type ?? "Component";
828
+ let parentSelector = void 0;
829
+ try {
830
+ parentSelector = getSelectorForId(parent.props?.id ?? "");
831
+ } catch {
832
+ parentSelector = void 0;
833
+ }
834
+ return [{ label: "Page", onSelect: selectRoot }, { label: selectedType }];
835
+ }
836
+ function FieldWrapper({
837
+ children
838
+ }) {
839
+ const crumbs = useBreadcrumbs();
840
+ return /* @__PURE__ */ jsx(ScrollArea, { className: "h-full", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-0", children: [
841
+ /* @__PURE__ */ jsx("div", { className: "px-3 py-2 border-b", children: /* @__PURE__ */ jsx(Breadcrumb, { children: /* @__PURE__ */ jsx(BreadcrumbList, { children: crumbs.map((crumb, i) => {
842
+ const isLast = i === crumbs.length - 1;
843
+ return /* @__PURE__ */ jsxs(React18.Fragment, { children: [
844
+ i > 0 && /* @__PURE__ */ jsx(BreadcrumbSeparator, {}),
845
+ /* @__PURE__ */ jsx(BreadcrumbItem, { children: isLast ? /* @__PURE__ */ jsx(BreadcrumbPage, { children: crumb.label }) : /* @__PURE__ */ jsx(
846
+ BreadcrumbLink,
847
+ {
848
+ className: "cursor-pointer",
849
+ onClick: crumb.onSelect,
850
+ children: crumb.label
851
+ }
852
+ ) })
853
+ ] }, i);
854
+ }) }) }) }),
855
+ children
856
+ ] }) });
857
+ }
858
+ function FieldLabel({
859
+ children,
860
+ label,
861
+ labelIcon,
862
+ el,
863
+ type: _type,
864
+ readOnly,
865
+ className
866
+ }) {
867
+ console.log("Lable type", labelIcon);
868
+ const El = el ?? "div";
869
+ return /* @__PURE__ */ jsx(TooltipProvider, { children: /* @__PURE__ */ jsxs(El, { className: `flex flex-col gap-1.5 ${className ?? ""}`, children: [
870
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
871
+ labelIcon && /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: labelIcon }),
872
+ /* @__PURE__ */ jsx(Label, { className: "text-xs font-medium text-muted-foreground", children: label }),
873
+ label && /* @__PURE__ */ jsxs(Tooltip, { children: [
874
+ /* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx(Info, { className: "h-3 w-3 text-muted-foreground/60 cursor-help" }) }),
875
+ /* @__PURE__ */ jsx(TooltipContent, { side: "right", children: label })
876
+ ] }),
877
+ readOnly && /* @__PURE__ */ jsx("span", { className: "ml-auto text-xs text-muted-foreground/50", children: "Read only" })
878
+ ] }),
879
+ children
880
+ ] }) });
881
+ }
882
+ var MOCK_VALUES = {
883
+ default: ["AI-generated content", "Smart suggestion", "Generated text"]
884
+ };
885
+ function getMock(instructions) {
886
+ if (instructions?.toLowerCase().includes("caps")) {
887
+ return "AI GENERATED TITLE";
888
+ }
889
+ const pool = MOCK_VALUES.default;
890
+ return pool[Math.floor(Math.random() * pool.length)];
891
+ }
892
+ function AiButton({ ai, onGenerate }) {
893
+ const [loading, setLoading] = React18.useState(false);
894
+ const handleClick = () => {
895
+ setLoading(true);
896
+ setTimeout(() => {
897
+ onGenerate(getMock(ai.instructions));
898
+ setLoading(false);
899
+ }, 600);
900
+ };
901
+ return /* @__PURE__ */ jsx(
902
+ Button,
903
+ {
904
+ type: "button",
905
+ variant: "outline",
906
+ size: "icon",
907
+ className: "h-8 w-8 shrink-0 text-muted-foreground hover:text-primary",
908
+ onClick: handleClick,
909
+ disabled: loading,
910
+ "aria-label": "Generate with AI",
911
+ children: /* @__PURE__ */ jsx(Sparkles, { className: `h-3.5 w-3.5 ${loading ? "animate-pulse" : ""}` })
912
+ }
913
+ );
914
+ }
915
+ function TextField({
916
+ field,
917
+ value,
918
+ onChange,
919
+ readOnly,
920
+ placeholder,
921
+ label,
922
+ labelIcon
923
+ }) {
924
+ const [local, setLocal] = React18.useState(value ?? "");
925
+ React18.useEffect(() => {
926
+ setLocal(value ?? "");
927
+ }, [value]);
928
+ React18.useEffect(() => {
929
+ const timer = setTimeout(() => {
930
+ if (local !== value) onChange(local);
931
+ }, 200);
932
+ return () => clearTimeout(timer);
933
+ }, [local]);
934
+ return /* @__PURE__ */ jsx(FieldLabel, { label: label ?? "", labelIcon, readOnly, children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
935
+ /* @__PURE__ */ jsx(
936
+ Input,
937
+ {
938
+ value: local,
939
+ onChange: (e) => setLocal(e.target.value),
940
+ readOnly,
941
+ placeholder,
942
+ className: "h-8 text-sm flex-1"
943
+ }
944
+ ),
945
+ field?.ai && /* @__PURE__ */ jsx(AiButton, { ai: field.ai, onGenerate: (v) => {
946
+ setLocal(v);
947
+ onChange(v);
948
+ } })
949
+ ] }) });
950
+ }
951
+ function Textarea({ className, ...props }) {
952
+ return /* @__PURE__ */ jsx(
953
+ "textarea",
954
+ {
955
+ "data-slot": "textarea",
956
+ className: cn(
957
+ "flex field-sizing-content min-h-16 w-full rounded-lg border border-input bg-transparent px-2.5 py-2 text-base transition-colors outline-none placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:bg-input/50 disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 md:text-sm dark:bg-input/30 dark:disabled:bg-input/80 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40",
958
+ className
959
+ ),
960
+ ...props
961
+ }
962
+ );
963
+ }
964
+ function TextareaField({ value, onChange, readOnly, placeholder, label }) {
965
+ const [local, setLocal] = React18.useState(value ?? "");
966
+ React18.useEffect(() => {
967
+ setLocal(value ?? "");
968
+ }, [value]);
969
+ React18.useEffect(() => {
970
+ const timer = setTimeout(() => {
971
+ if (local !== value) onChange(local);
972
+ }, 200);
973
+ return () => clearTimeout(timer);
974
+ }, [local]);
975
+ return /* @__PURE__ */ jsx(FieldLabel, { label: label ?? "", readOnly, children: /* @__PURE__ */ jsx(
976
+ Textarea,
977
+ {
978
+ value: local,
979
+ onChange: (e) => setLocal(e.target.value),
980
+ readOnly,
981
+ placeholder,
982
+ className: "min-h-[80px] text-sm resize-y"
983
+ }
984
+ ) });
985
+ }
986
+ function NumberField({ value, onChange, readOnly, min, max, step, label }) {
987
+ const [local, setLocal] = React18.useState(String(value ?? ""));
988
+ React18.useEffect(() => {
989
+ setLocal(String(value ?? ""));
990
+ }, [value]);
991
+ React18.useEffect(() => {
992
+ const parsed = parseFloat(local);
993
+ if (!isNaN(parsed) && parsed !== value) {
994
+ const timer = setTimeout(() => onChange(parsed), 200);
995
+ return () => clearTimeout(timer);
996
+ }
997
+ }, [local]);
998
+ return /* @__PURE__ */ jsx(FieldLabel, { label: label ?? "", readOnly, children: /* @__PURE__ */ jsx(
999
+ Input,
1000
+ {
1001
+ type: "number",
1002
+ value: local,
1003
+ onChange: (e) => setLocal(e.target.value),
1004
+ readOnly,
1005
+ min,
1006
+ max,
1007
+ step,
1008
+ className: "h-8 text-sm"
1009
+ }
1010
+ ) });
1011
+ }
1012
+ var Select = Select$1.Root;
1013
+ function SelectGroup({ className, ...props }) {
1014
+ return /* @__PURE__ */ jsx(
1015
+ Select$1.Group,
1016
+ {
1017
+ "data-slot": "select-group",
1018
+ className: cn("scroll-my-1 p-1", className),
1019
+ ...props
1020
+ }
1021
+ );
1022
+ }
1023
+ function SelectValue({ className, ...props }) {
1024
+ return /* @__PURE__ */ jsx(
1025
+ Select$1.Value,
1026
+ {
1027
+ "data-slot": "select-value",
1028
+ className: cn("flex flex-1 text-left", className),
1029
+ ...props
1030
+ }
1031
+ );
1032
+ }
1033
+ function SelectTrigger({
1034
+ className,
1035
+ size = "default",
1036
+ children,
1037
+ ...props
1038
+ }) {
1039
+ return /* @__PURE__ */ jsxs(
1040
+ Select$1.Trigger,
1041
+ {
1042
+ "data-slot": "select-trigger",
1043
+ "data-size": size,
1044
+ className: cn(
1045
+ "border-input data-placeholder:text-muted-foreground dark:bg-input/30 dark:hover:bg-input/50 focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 gap-1.5 rounded-lg border bg-transparent py-2 pr-2 pl-2.5 text-sm transition-colors select-none focus-visible:ring-3 aria-invalid:ring-3 data-[size=default]:h-8 data-[size=sm]:h-7 data-[size=sm]:rounded-[min(var(--radius-md),10px)] *:data-[slot=select-value]:gap-1.5 [&_svg:not([class*='size-'])]:size-4 flex w-fit items-center justify-between whitespace-nowrap outline-none disabled:cursor-not-allowed disabled:opacity-50 *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center [&_svg]:pointer-events-none [&_svg]:shrink-0",
1046
+ className
1047
+ ),
1048
+ ...props,
1049
+ children: [
1050
+ children,
1051
+ /* @__PURE__ */ jsx(
1052
+ Select$1.Icon,
1053
+ {
1054
+ render: /* @__PURE__ */ jsx(ChevronDownIcon, { className: "text-muted-foreground size-4 pointer-events-none" })
1055
+ }
1056
+ )
1057
+ ]
1058
+ }
1059
+ );
1060
+ }
1061
+ function SelectContent({
1062
+ className,
1063
+ children,
1064
+ side = "bottom",
1065
+ sideOffset = 4,
1066
+ align = "center",
1067
+ alignOffset = 0,
1068
+ alignItemWithTrigger = true,
1069
+ ...props
1070
+ }) {
1071
+ return /* @__PURE__ */ jsx(Select$1.Portal, { children: /* @__PURE__ */ jsx(
1072
+ Select$1.Positioner,
1073
+ {
1074
+ side,
1075
+ sideOffset,
1076
+ align,
1077
+ alignOffset,
1078
+ alignItemWithTrigger,
1079
+ className: "isolate z-50",
1080
+ children: /* @__PURE__ */ jsxs(
1081
+ Select$1.Popup,
1082
+ {
1083
+ "data-slot": "select-content",
1084
+ "data-align-trigger": alignItemWithTrigger,
1085
+ className: cn(
1086
+ "bg-popover text-popover-foreground data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-open:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 ring-foreground/10 min-w-36 rounded-lg shadow-md ring-1 duration-100 data-[side=inline-start]:slide-in-from-right-2 data-[side=inline-end]:slide-in-from-left-2 relative isolate z-50 max-h-(--available-height) w-(--anchor-width) origin-(--transform-origin) overflow-x-hidden overflow-y-auto data-[align-trigger=true]:animate-none",
1087
+ className
1088
+ ),
1089
+ ...props,
1090
+ children: [
1091
+ /* @__PURE__ */ jsx(SelectScrollUpButton, {}),
1092
+ /* @__PURE__ */ jsx(Select$1.List, { children }),
1093
+ /* @__PURE__ */ jsx(SelectScrollDownButton, {})
1094
+ ]
1095
+ }
1096
+ )
1097
+ }
1098
+ ) });
1099
+ }
1100
+ function SelectItem({
1101
+ className,
1102
+ children,
1103
+ ...props
1104
+ }) {
1105
+ return /* @__PURE__ */ jsxs(
1106
+ Select$1.Item,
1107
+ {
1108
+ "data-slot": "select-item",
1109
+ className: cn(
1110
+ "focus:bg-accent focus:text-accent-foreground not-data-[variant=destructive]:focus:**:text-accent-foreground gap-1.5 rounded-md py-1 pr-8 pl-1.5 text-sm [&_svg:not([class*='size-'])]:size-4 *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2 relative flex w-full cursor-default items-center outline-hidden select-none data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0",
1111
+ className
1112
+ ),
1113
+ ...props,
1114
+ children: [
1115
+ /* @__PURE__ */ jsx(Select$1.ItemText, { className: "flex flex-1 gap-2 shrink-0 whitespace-nowrap", children }),
1116
+ /* @__PURE__ */ jsx(
1117
+ Select$1.ItemIndicator,
1118
+ {
1119
+ render: /* @__PURE__ */ jsx("span", { className: "pointer-events-none absolute right-2 flex size-4 items-center justify-center", children: /* @__PURE__ */ jsx(CheckIcon, { className: "pointer-events-none" }) })
1120
+ }
1121
+ )
1122
+ ]
1123
+ }
1124
+ );
1125
+ }
1126
+ function SelectScrollUpButton({
1127
+ className,
1128
+ ...props
1129
+ }) {
1130
+ return /* @__PURE__ */ jsx(
1131
+ Select$1.ScrollUpArrow,
1132
+ {
1133
+ "data-slot": "select-scroll-up-button",
1134
+ className: cn(
1135
+ "bg-popover z-10 flex cursor-default items-center justify-center py-1 [&_svg:not([class*='size-'])]:size-4 top-0 w-full",
1136
+ className
1137
+ ),
1138
+ ...props,
1139
+ children: /* @__PURE__ */ jsx(ChevronUpIcon, {})
1140
+ }
1141
+ );
1142
+ }
1143
+ function SelectScrollDownButton({
1144
+ className,
1145
+ ...props
1146
+ }) {
1147
+ return /* @__PURE__ */ jsx(
1148
+ Select$1.ScrollDownArrow,
1149
+ {
1150
+ "data-slot": "select-scroll-down-button",
1151
+ className: cn(
1152
+ "bg-popover z-10 flex cursor-default items-center justify-center py-1 [&_svg:not([class*='size-'])]:size-4 bottom-0 w-full",
1153
+ className
1154
+ ),
1155
+ ...props,
1156
+ children: /* @__PURE__ */ jsx(ChevronDownIcon, {})
1157
+ }
1158
+ );
1159
+ }
1160
+ function SelectField({ field, value, onChange, id, readOnly, label }) {
1161
+ return /* @__PURE__ */ jsx(FieldLabel, { label: label ?? "", readOnly, children: /* @__PURE__ */ jsxs(
1162
+ Select,
1163
+ {
1164
+ value: String(value ?? ""),
1165
+ onValueChange: (v) => {
1166
+ if (v === null) return;
1167
+ const match = field.options.find((o) => String(o.value) === v);
1168
+ onChange(match ? match.value : v);
1169
+ },
1170
+ children: [
1171
+ /* @__PURE__ */ jsx(SelectTrigger, { id, className: "h-8 text-sm w-full", disabled: readOnly, children: /* @__PURE__ */ jsx(SelectValue, { placeholder: "Select..." }) }),
1172
+ /* @__PURE__ */ jsx(SelectContent, { children: /* @__PURE__ */ jsx(SelectGroup, { children: field.options.map((opt) => /* @__PURE__ */ jsx(SelectItem, { value: String(opt.value), children: opt.label }, String(opt.value))) }) })
1173
+ ]
1174
+ }
1175
+ ) });
1176
+ }
1177
+ var buttonGroupVariants = cva(
1178
+ "has-[>[data-slot=button-group]]:gap-2 has-[select[aria-hidden=true]:last-child]:[&>[data-slot=select-trigger]:last-of-type]:rounded-r-lg flex w-fit items-stretch *:focus-visible:relative *:focus-visible:z-10 [&>[data-slot=select-trigger]:not([class*='w-'])]:w-fit [&>input]:flex-1",
1179
+ {
1180
+ variants: {
1181
+ orientation: {
1182
+ horizontal: "[&>[data-slot]:not(:has(~[data-slot]))]:rounded-r-lg! *:data-slot:rounded-r-none [&>[data-slot]~[data-slot]]:rounded-l-none [&>[data-slot]~[data-slot]]:border-l-0",
1183
+ vertical: "[&>[data-slot]:not(:has(~[data-slot]))]:rounded-b-lg! flex-col *:data-slot:rounded-b-none [&>[data-slot]~[data-slot]]:rounded-t-none [&>[data-slot]~[data-slot]]:border-t-0"
1184
+ }
1185
+ },
1186
+ defaultVariants: {
1187
+ orientation: "horizontal"
1188
+ }
1189
+ }
1190
+ );
1191
+ function ButtonGroup({
1192
+ className,
1193
+ orientation,
1194
+ ...props
1195
+ }) {
1196
+ return /* @__PURE__ */ jsx(
1197
+ "div",
1198
+ {
1199
+ role: "group",
1200
+ "data-slot": "button-group",
1201
+ "data-orientation": orientation,
1202
+ className: cn2(buttonGroupVariants({ orientation }), className),
1203
+ ...props
1204
+ }
1205
+ );
1206
+ }
1207
+ function RadioField({ field, value, onChange, readOnly, label }) {
1208
+ const options = field.options ?? [];
1209
+ return /* @__PURE__ */ jsx(FieldLabel, { label: label ?? "", readOnly, el: "div", children: /* @__PURE__ */ jsx(ButtonGroup, { className: "w-full", children: options.map((opt) => {
1210
+ const selected = String(opt.value) === String(value ?? "");
1211
+ return /* @__PURE__ */ jsx(
1212
+ Button,
1213
+ {
1214
+ type: "button",
1215
+ variant: selected ? "default" : "outline",
1216
+ size: "sm",
1217
+ className: "flex-1 text-xs",
1218
+ disabled: readOnly,
1219
+ onClick: () => onChange(opt.value),
1220
+ children: opt.label
1221
+ },
1222
+ String(opt.value)
1223
+ );
1224
+ }) }) });
1225
+ }
1226
+ function ItemGroup({ className, ...props }) {
1227
+ return /* @__PURE__ */ jsx(
1228
+ "div",
1229
+ {
1230
+ role: "list",
1231
+ "data-slot": "item-group",
1232
+ className: cn2(
1233
+ "gap-4 has-data-[size=sm]:gap-2.5 has-data-[size=xs]:gap-2 group/item-group flex w-full flex-col",
1234
+ className
1235
+ ),
1236
+ ...props
1237
+ }
1238
+ );
1239
+ }
1240
+ var itemVariants = cva(
1241
+ "[a]:hover:bg-muted rounded-lg border text-sm group/item flex w-full flex-wrap items-center transition-colors duration-100 outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 [a]:transition-colors",
1242
+ {
1243
+ variants: {
1244
+ variant: {
1245
+ default: "border-transparent",
1246
+ outline: "border-border",
1247
+ muted: "bg-muted/50 border-transparent"
1248
+ },
1249
+ size: {
1250
+ default: "gap-2.5 px-3 py-2.5",
1251
+ sm: "gap-2.5 px-3 py-2.5",
1252
+ xs: "gap-2 px-2.5 py-2 in-data-[slot=dropdown-menu-content]:p-0"
1253
+ }
1254
+ },
1255
+ defaultVariants: {
1256
+ variant: "default",
1257
+ size: "default"
1258
+ }
1259
+ }
1260
+ );
1261
+ function Item({
1262
+ className,
1263
+ variant = "default",
1264
+ size = "default",
1265
+ render,
1266
+ ...props
1267
+ }) {
1268
+ return useRender({
1269
+ defaultTagName: "div",
1270
+ props: mergeProps(
1271
+ {
1272
+ className: cn2(itemVariants({ variant, size, className }))
1273
+ },
1274
+ props
1275
+ ),
1276
+ render,
1277
+ state: {
1278
+ slot: "item",
1279
+ variant,
1280
+ size
1281
+ }
1282
+ });
1283
+ }
1284
+ cva(
1285
+ "gap-2 group-has-data-[slot=item-description]/item:translate-y-0.5 group-has-data-[slot=item-description]/item:self-start flex shrink-0 items-center justify-center [&_svg]:pointer-events-none",
1286
+ {
1287
+ variants: {
1288
+ variant: {
1289
+ default: "bg-transparent",
1290
+ icon: "[&_svg:not([class*='size-'])]:size-4",
1291
+ image: "size-10 overflow-hidden rounded-sm group-data-[size=sm]/item:size-8 group-data-[size=xs]/item:size-6 [&_img]:size-full [&_img]:object-cover"
1292
+ }
1293
+ },
1294
+ defaultVariants: {
1295
+ variant: "default"
1296
+ }
1297
+ }
1298
+ );
1299
+ function ItemContent({ className, ...props }) {
1300
+ return /* @__PURE__ */ jsx(
1301
+ "div",
1302
+ {
1303
+ "data-slot": "item-content",
1304
+ className: cn2(
1305
+ "gap-1 group-data-[size=xs]/item:gap-0 flex flex-1 flex-col [&+[data-slot=item-content]]:flex-none",
1306
+ className
1307
+ ),
1308
+ ...props
1309
+ }
1310
+ );
1311
+ }
1312
+ function ItemTitle({ className, ...props }) {
1313
+ return /* @__PURE__ */ jsx(
1314
+ "div",
1315
+ {
1316
+ "data-slot": "item-title",
1317
+ className: cn2(
1318
+ "gap-2 text-sm leading-snug font-medium underline-offset-4 line-clamp-1 flex w-fit items-center",
1319
+ className
1320
+ ),
1321
+ ...props
1322
+ }
1323
+ );
1324
+ }
1325
+ function ItemActions({ className, ...props }) {
1326
+ return /* @__PURE__ */ jsx(
1327
+ "div",
1328
+ {
1329
+ "data-slot": "item-actions",
1330
+ className: cn2("gap-2 flex items-center", className),
1331
+ ...props
1332
+ }
1333
+ );
1334
+ }
1335
+ function useControlledState(props) {
1336
+ const { value, defaultValue, onChange } = props;
1337
+ const [state, setInternalState] = React18.useState(
1338
+ value !== void 0 ? value : defaultValue
1339
+ );
1340
+ React18.useEffect(() => {
1341
+ if (value !== void 0) setInternalState(value);
1342
+ }, [value]);
1343
+ const setState = React18.useCallback(
1344
+ (next, ...args) => {
1345
+ setInternalState(next);
1346
+ onChange?.(next, ...args);
1347
+ },
1348
+ [onChange]
1349
+ );
1350
+ return [state, setState];
1351
+ }
1352
+ var [PopoverProvider, usePopover] = getStrictContext("PopoverContext");
1353
+ function Popover(props) {
1354
+ const [isOpen, setIsOpen] = useControlledState({
1355
+ value: props?.open,
1356
+ defaultValue: props?.defaultOpen,
1357
+ onChange: props?.onOpenChange
1358
+ });
1359
+ return /* @__PURE__ */ jsx(PopoverProvider, { value: { isOpen, setIsOpen }, children: /* @__PURE__ */ jsx(
1360
+ Popover$1.Root,
1361
+ {
1362
+ "data-slot": "popover",
1363
+ ...props,
1364
+ onOpenChange: setIsOpen
1365
+ }
1366
+ ) });
1367
+ }
1368
+ function PopoverTrigger(props) {
1369
+ return /* @__PURE__ */ jsx(Popover$1.Trigger, { "data-slot": "popover-trigger", ...props });
1370
+ }
1371
+ function PopoverPortal(props) {
1372
+ const { isOpen } = usePopover();
1373
+ return /* @__PURE__ */ jsx(AnimatePresence, { children: isOpen && /* @__PURE__ */ jsx(
1374
+ Popover$1.Portal,
1375
+ {
1376
+ keepMounted: true,
1377
+ "data-slot": "popover-portal",
1378
+ ...props
1379
+ }
1380
+ ) });
1381
+ }
1382
+ function PopoverPositioner(props) {
1383
+ return /* @__PURE__ */ jsx(Popover$1.Positioner, { "data-slot": "popover-positioner", ...props });
1384
+ }
1385
+ function PopoverPopup({
1386
+ initialFocus,
1387
+ finalFocus,
1388
+ transition = { type: "spring", stiffness: 300, damping: 25 },
1389
+ ...props
1390
+ }) {
1391
+ return /* @__PURE__ */ jsx(
1392
+ Popover$1.Popup,
1393
+ {
1394
+ initialFocus,
1395
+ finalFocus,
1396
+ render: /* @__PURE__ */ jsx(
1397
+ motion.div,
1398
+ {
1399
+ "data-slot": "popover-popup",
1400
+ initial: { opacity: 0, scale: 0.5 },
1401
+ animate: { opacity: 1, scale: 1 },
1402
+ exit: { opacity: 0, scale: 0.5 },
1403
+ transition,
1404
+ ...props
1405
+ },
1406
+ "popover-popup"
1407
+ )
1408
+ }
1409
+ );
1410
+ }
1411
+ function PopoverTitle(props) {
1412
+ return /* @__PURE__ */ jsx(Popover$1.Title, { "data-slot": "popover-title", ...props });
1413
+ }
1414
+ function Popover2(props) {
1415
+ return /* @__PURE__ */ jsx(Popover, { ...props });
1416
+ }
1417
+ function PopoverTrigger2(props) {
1418
+ return /* @__PURE__ */ jsx(PopoverTrigger, { ...props });
1419
+ }
1420
+ function PopoverPanel({
1421
+ className,
1422
+ align = "center",
1423
+ sideOffset = 4,
1424
+ initialFocus,
1425
+ finalFocus,
1426
+ style,
1427
+ children,
1428
+ ...props
1429
+ }) {
1430
+ return /* @__PURE__ */ jsx(PopoverPortal, { children: /* @__PURE__ */ jsx(
1431
+ PopoverPositioner,
1432
+ {
1433
+ align,
1434
+ sideOffset,
1435
+ className: "z-50",
1436
+ ...props,
1437
+ children: /* @__PURE__ */ jsx(
1438
+ PopoverPopup,
1439
+ {
1440
+ initialFocus,
1441
+ finalFocus,
1442
+ className: cn(
1443
+ "bg-popover text-popover-foreground w-72 rounded-md border p-4 shadow-md outline-hidden origin-(--transform-origin)",
1444
+ className
1445
+ ),
1446
+ style,
1447
+ children
1448
+ }
1449
+ )
1450
+ }
1451
+ ) });
1452
+ }
1453
+ function PopoverTitle2(props) {
1454
+ return /* @__PURE__ */ jsx(PopoverTitle, { ...props });
1455
+ }
1456
+ function SortableItem({
1457
+ id,
1458
+ index,
1459
+ item,
1460
+ field,
1461
+ readOnly,
1462
+ atMax,
1463
+ atMin,
1464
+ fieldId,
1465
+ open,
1466
+ onOpenChange,
1467
+ onDuplicate,
1468
+ onRemove,
1469
+ onUpdate,
1470
+ getSummary
1471
+ }) {
1472
+ const {
1473
+ attributes,
1474
+ listeners,
1475
+ setNodeRef,
1476
+ transform,
1477
+ transition,
1478
+ isDragging
1479
+ } = useSortable({ id });
1480
+ const style = {
1481
+ transform: CSS.Transform.toString(transform),
1482
+ transition,
1483
+ opacity: isDragging ? 0.5 : 1
1484
+ };
1485
+ return /* @__PURE__ */ jsx("div", { ref: setNodeRef, style, children: /* @__PURE__ */ jsxs(Popover2, { open, onOpenChange, children: [
1486
+ /* @__PURE__ */ jsxs(Item, { variant: "outline", size: "sm", className: "cursor-pointer", children: [
1487
+ /* @__PURE__ */ jsx(
1488
+ "button",
1489
+ {
1490
+ type: "button",
1491
+ className: "cursor-grab active:cursor-grabbing touch-none p-0.5 text-muted-foreground/40 hover:text-muted-foreground",
1492
+ ...attributes,
1493
+ ...listeners,
1494
+ children: /* @__PURE__ */ jsx(GripVertical, { className: "h-3.5 w-3.5 shrink-0" })
1495
+ }
1496
+ ),
1497
+ /* @__PURE__ */ jsx(PopoverTrigger2, { className: "flex-1 min-w-0 text-left", children: /* @__PURE__ */ jsx(ItemContent, { children: /* @__PURE__ */ jsx(ItemTitle, { className: "text-xs", children: getSummary(item, index) }) }) }),
1498
+ !readOnly && /* @__PURE__ */ jsxs(ItemActions, { children: [
1499
+ /* @__PURE__ */ jsx(
1500
+ Button,
1501
+ {
1502
+ variant: "ghost",
1503
+ size: "icon",
1504
+ className: "h-5 w-5",
1505
+ disabled: atMax,
1506
+ onClick: (e) => {
1507
+ e.stopPropagation();
1508
+ onDuplicate(index);
1509
+ },
1510
+ title: "Duplicate",
1511
+ children: /* @__PURE__ */ jsx(Copy, { className: "h-3 w-3" })
1512
+ }
1513
+ ),
1514
+ /* @__PURE__ */ jsx(
1515
+ Button,
1516
+ {
1517
+ variant: "ghost",
1518
+ size: "icon",
1519
+ className: "h-5 w-5 text-destructive hover:text-destructive",
1520
+ disabled: atMin,
1521
+ onClick: (e) => {
1522
+ e.stopPropagation();
1523
+ onRemove(index);
1524
+ },
1525
+ title: "Delete",
1526
+ children: /* @__PURE__ */ jsx(Trash2, { className: "h-3 w-3" })
1527
+ }
1528
+ )
1529
+ ] })
1530
+ ] }),
1531
+ /* @__PURE__ */ jsx(
1532
+ PopoverPanel,
1533
+ {
1534
+ side: "left",
1535
+ sideOffset: 60,
1536
+ align: "start",
1537
+ alignOffset: -14,
1538
+ className: "w-56 p-3 flex flex-col gap-2",
1539
+ children: Object.entries(field.arrayFields).map(([subName, subField]) => /* @__PURE__ */ jsx(
1540
+ AutoField,
1541
+ {
1542
+ field: { ...subField, label: subField.label ?? subName },
1543
+ value: item[subName],
1544
+ onChange: (val) => onUpdate(index, subName, val),
1545
+ readOnly,
1546
+ id: `${fieldId}-${index}-${subName}`
1547
+ },
1548
+ subName
1549
+ ))
1550
+ }
1551
+ )
1552
+ ] }) });
1553
+ }
1554
+ function ArrayField({
1555
+ field,
1556
+ value = [],
1557
+ onChange,
1558
+ readOnly,
1559
+ label,
1560
+ id = ""
1561
+ }) {
1562
+ const atMax = field.max !== void 0 && value.length >= field.max;
1563
+ const atMin = field.min !== void 0 && value.length <= field.min;
1564
+ const [openIndex, setOpenIndex] = React18.useState(null);
1565
+ const itemIds = React18.useMemo(
1566
+ () => value.map((_, i) => `${id}-item-${i}`),
1567
+ [value.length, id]
1568
+ );
1569
+ const sensors = useSensors(useSensor(PointerSensor));
1570
+ const handleDragEnd = (event) => {
1571
+ const { active, over } = event;
1572
+ if (!over || active.id === over.id) return;
1573
+ const from = itemIds.indexOf(active.id);
1574
+ const to = itemIds.indexOf(over.id);
1575
+ if (from === -1 || to === -1) return;
1576
+ onChange(arrayMove(value, from, to));
1577
+ };
1578
+ const defaultItem = () => {
1579
+ if (!field.defaultItemProps) return {};
1580
+ return typeof field.defaultItemProps === "function" ? field.defaultItemProps(value.length) : { ...field.defaultItemProps };
1581
+ };
1582
+ const addItem = () => {
1583
+ if (atMax || readOnly) return;
1584
+ const newIndex = value.length;
1585
+ onChange([...value, defaultItem()]);
1586
+ setOpenIndex(newIndex);
1587
+ };
1588
+ const removeItem = (i) => {
1589
+ if (atMin || readOnly) return;
1590
+ const next = [...value];
1591
+ next.splice(i, 1);
1592
+ onChange(next);
1593
+ };
1594
+ const duplicateItem = (i) => {
1595
+ if (atMax || readOnly) return;
1596
+ const next = [...value];
1597
+ next.splice(i + 1, 0, { ...value[i] });
1598
+ onChange(next);
1599
+ };
1600
+ const updateItem = (i, subName, val) => {
1601
+ onChange(
1602
+ value.map(
1603
+ (item, idx) => idx === i ? { ...item, [subName]: val } : item
1604
+ )
1605
+ );
1606
+ };
1607
+ const getSummary = (item, i) => {
1608
+ if (field.getItemSummary) return field.getItemSummary(item, i);
1609
+ const first = Object.values(item).find((v) => typeof v === "string" && v);
1610
+ return first || `Item ${i + 1}`;
1611
+ };
1612
+ return /* @__PURE__ */ jsx(FieldLabel, { label: label ?? field.label ?? "", readOnly, el: "div", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1.5", children: [
1613
+ /* @__PURE__ */ jsx(
1614
+ DndContext,
1615
+ {
1616
+ sensors,
1617
+ collisionDetection: closestCenter,
1618
+ onDragEnd: handleDragEnd,
1619
+ children: /* @__PURE__ */ jsx(
1620
+ SortableContext,
1621
+ {
1622
+ items: itemIds,
1623
+ strategy: verticalListSortingStrategy,
1624
+ children: /* @__PURE__ */ jsx(ItemGroup, { children: value.map((item, i) => /* @__PURE__ */ jsx(
1625
+ SortableItem,
1626
+ {
1627
+ id: itemIds[i],
1628
+ index: i,
1629
+ item,
1630
+ field,
1631
+ readOnly,
1632
+ atMax,
1633
+ atMin,
1634
+ fieldId: id,
1635
+ open: openIndex === i,
1636
+ onOpenChange: (o) => setOpenIndex(o ? i : null),
1637
+ onDuplicate: duplicateItem,
1638
+ onRemove: removeItem,
1639
+ onUpdate: updateItem,
1640
+ getSummary
1641
+ },
1642
+ itemIds[i]
1643
+ )) })
1644
+ }
1645
+ )
1646
+ }
1647
+ ),
1648
+ !readOnly && !atMax && /* @__PURE__ */ jsxs(
1649
+ Button,
1650
+ {
1651
+ variant: "outline",
1652
+ size: "sm",
1653
+ onClick: addItem,
1654
+ className: "w-full gap-1.5 mt-0.5",
1655
+ children: [
1656
+ /* @__PURE__ */ jsx(Plus, { className: "h-3.5 w-3.5" }),
1657
+ "Add item"
1658
+ ]
1659
+ }
1660
+ )
1661
+ ] }) });
1662
+ }
1663
+ function ObjectField({ children, name, readOnly }) {
1664
+ return /* @__PURE__ */ jsx(FieldLabel, { label: name, el: "div", readOnly, children: /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-3 pl-3 border-l-2 border-border/60 ml-1", children }) });
1665
+ }
1666
+ function Card({
1667
+ className,
1668
+ size = "default",
1669
+ ...props
1670
+ }) {
1671
+ return /* @__PURE__ */ jsx(
1672
+ "div",
1673
+ {
1674
+ "data-slot": "card",
1675
+ "data-size": size,
1676
+ className: cn(
1677
+ "group/card flex flex-col gap-4 overflow-hidden rounded-xl bg-card py-4 text-sm text-card-foreground ring-1 ring-foreground/10 has-data-[slot=card-footer]:pb-0 has-[>img:first-child]:pt-0 data-[size=sm]:gap-3 data-[size=sm]:py-3 data-[size=sm]:has-data-[slot=card-footer]:pb-0 *:[img:first-child]:rounded-t-xl *:[img:last-child]:rounded-b-xl",
1678
+ className
1679
+ ),
1680
+ ...props
1681
+ }
1682
+ );
1683
+ }
1684
+ function CardContent({ className, ...props }) {
1685
+ return /* @__PURE__ */ jsx(
1686
+ "div",
1687
+ {
1688
+ "data-slot": "card-content",
1689
+ className: cn("px-4 group-data-[size=sm]/card:px-3", className),
1690
+ ...props
1691
+ }
1692
+ );
1693
+ }
1694
+ function SlotField({ children, label }) {
1695
+ return /* @__PURE__ */ jsx(FieldLabel, { label: label ?? "", el: "div", children: /* @__PURE__ */ jsx(Card, { className: "border-dashed", children: /* @__PURE__ */ jsx(CardContent, { className: "py-3 px-3", children: children ?? /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground text-center", children: "Slot content is managed directly on the canvas." }) }) }) });
1696
+ }
1697
+ function CustomField({ children, label }) {
1698
+ return /* @__PURE__ */ jsx(FieldLabel, { label: label ?? "", el: "div", children: /* @__PURE__ */ jsx("div", { className: "rounded-md border border-border/60 p-3", children }) });
1699
+ }
1700
+ var toggleVariants = cva(
1701
+ "group/toggle inline-flex items-center justify-center gap-1 rounded-lg text-sm font-medium whitespace-nowrap transition-all outline-none hover:bg-muted hover:text-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 aria-pressed:bg-muted data-[state=on]:bg-muted dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
1702
+ {
1703
+ variants: {
1704
+ variant: {
1705
+ default: "bg-transparent",
1706
+ outline: "border border-input bg-transparent hover:bg-muted"
1707
+ },
1708
+ size: {
1709
+ default: "h-8 min-w-8 px-2",
1710
+ sm: "h-7 min-w-7 rounded-[min(var(--radius-md),12px)] px-1.5 text-[0.8rem]",
1711
+ lg: "h-9 min-w-9 px-2.5"
1712
+ }
1713
+ },
1714
+ defaultVariants: {
1715
+ variant: "default",
1716
+ size: "default"
1717
+ }
1718
+ }
1719
+ );
1720
+ function Toggle({
1721
+ className,
1722
+ variant = "default",
1723
+ size = "default",
1724
+ ...props
1725
+ }) {
1726
+ return /* @__PURE__ */ jsx(
1727
+ Toggle$1,
1728
+ {
1729
+ "data-slot": "toggle",
1730
+ className: cn(toggleVariants({ variant, size, className })),
1731
+ ...props
1732
+ }
1733
+ );
1734
+ }
1735
+ function RichtextField({ value, onChange, readOnly, label }) {
1736
+ const ref = React18.useRef(null);
1737
+ const boldLabel = useMsg("richtext.bold");
1738
+ const italicLabel = useMsg("richtext.italic");
1739
+ const linkLabel = useMsg("richtext.link");
1740
+ const linkPrompt = useMsg("richtext.link.prompt");
1741
+ React18.useEffect(() => {
1742
+ if (ref.current && ref.current.innerHTML !== value) {
1743
+ ref.current.innerHTML = value ?? "";
1744
+ }
1745
+ }, [value]);
1746
+ const exec = (cmd, val) => {
1747
+ document.execCommand(cmd, false, val);
1748
+ if (ref.current) onChange(ref.current.innerHTML);
1749
+ };
1750
+ return /* @__PURE__ */ jsx(FieldLabel, { label: label ?? "", readOnly, children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1 rounded-md border border-input overflow-hidden", children: [
1751
+ !readOnly && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-0.5 border-b px-1 py-0.5 bg-muted/30", children: [
1752
+ /* @__PURE__ */ jsx(Toggle, { size: "sm", onPressedChange: () => exec("bold"), "aria-label": boldLabel, children: /* @__PURE__ */ jsx(Bold, { className: "h-3.5 w-3.5" }) }),
1753
+ /* @__PURE__ */ jsx(Toggle, { size: "sm", onPressedChange: () => exec("italic"), "aria-label": italicLabel, children: /* @__PURE__ */ jsx(Italic, { className: "h-3.5 w-3.5" }) }),
1754
+ /* @__PURE__ */ jsx(
1755
+ Toggle,
1756
+ {
1757
+ size: "sm",
1758
+ onPressedChange: () => {
1759
+ const url = window.prompt(linkPrompt);
1760
+ if (url) exec("createLink", url);
1761
+ },
1762
+ "aria-label": linkLabel,
1763
+ children: /* @__PURE__ */ jsx(Link, { className: "h-3.5 w-3.5" })
1764
+ }
1765
+ )
1766
+ ] }),
1767
+ /* @__PURE__ */ jsx(
1768
+ "div",
1769
+ {
1770
+ ref,
1771
+ contentEditable: !readOnly,
1772
+ suppressContentEditableWarning: true,
1773
+ onInput: () => {
1774
+ if (ref.current) onChange(ref.current.innerHTML);
1775
+ },
1776
+ className: "min-h-[60px] px-3 py-2 text-sm outline-none"
1777
+ }
1778
+ )
1779
+ ] }) });
1780
+ }
1781
+ function InputGroup({ className, ...props }) {
1782
+ return /* @__PURE__ */ jsx(
1783
+ "div",
1784
+ {
1785
+ "data-slot": "input-group",
1786
+ role: "group",
1787
+ className: cn(
1788
+ "group/input-group border-input dark:bg-input/30 shadow-xs relative flex w-full items-center rounded-md border outline-none transition-[color,box-shadow]",
1789
+ "h-9 has-[>textarea]:h-auto",
1790
+ // Variants based on alignment.
1791
+ "has-[>[data-align=inline-start]]:[&>input]:pl-2",
1792
+ "has-[>[data-align=inline-end]]:[&>input]:pr-2",
1793
+ "has-[>[data-align=block-start]]:h-auto has-[>[data-align=block-start]]:flex-col has-[>[data-align=block-start]]:[&>input]:pb-3",
1794
+ "has-[>[data-align=block-end]]:h-auto has-[>[data-align=block-end]]:flex-col has-[>[data-align=block-end]]:[&>input]:pt-3",
1795
+ // Focus state.
1796
+ "has-[[data-slot=input-group-control]:focus-visible]:ring-ring has-[[data-slot=input-group-control]:focus-visible]:ring-1",
1797
+ // Error state.
1798
+ "has-[[data-slot][aria-invalid=true]]:ring-destructive/20 has-[[data-slot][aria-invalid=true]]:border-destructive dark:has-[[data-slot][aria-invalid=true]]:ring-destructive/40",
1799
+ className
1800
+ ),
1801
+ ...props
1802
+ }
1803
+ );
1804
+ }
1805
+ var inputGroupAddonVariants = cva(
1806
+ "text-muted-foreground flex h-auto cursor-text select-none items-center justify-center gap-2 py-1.5 text-sm font-medium group-data-[disabled=true]/input-group:opacity-50 [&>kbd]:rounded-[calc(var(--radius)-5px)] [&>svg:not([class*='size-'])]:size-4",
1807
+ {
1808
+ variants: {
1809
+ align: {
1810
+ "inline-start": "order-first pl-3 has-[>button]:ml-[-0.45rem] has-[>kbd]:ml-[-0.35rem]",
1811
+ "inline-end": "order-last pr-3 has-[>button]:mr-[-0.4rem] has-[>kbd]:mr-[-0.35rem]",
1812
+ "block-start": "[.border-b]:pb-3 order-first w-full justify-start px-3 pt-3 group-has-[>input]/input-group:pt-2.5",
1813
+ "block-end": "[.border-t]:pt-3 order-last w-full justify-start px-3 pb-3 group-has-[>input]/input-group:pb-2.5"
1814
+ }
1815
+ },
1816
+ defaultVariants: {
1817
+ align: "inline-start"
1818
+ }
1819
+ }
1820
+ );
1821
+ function InputGroupAddon({
1822
+ className,
1823
+ align = "inline-start",
1824
+ ...props
1825
+ }) {
1826
+ return /* @__PURE__ */ jsx(
1827
+ "div",
1828
+ {
1829
+ role: "group",
1830
+ "data-slot": "input-group-addon",
1831
+ "data-align": align,
1832
+ className: cn(inputGroupAddonVariants({ align }), className),
1833
+ onClick: (e) => {
1834
+ if (e.target.closest("button")) {
1835
+ return;
1836
+ }
1837
+ e.currentTarget.parentElement?.querySelector("input")?.focus();
1838
+ },
1839
+ ...props
1840
+ }
1841
+ );
1842
+ }
1843
+ cva(
1844
+ "flex items-center gap-2 text-sm shadow-none",
1845
+ {
1846
+ variants: {
1847
+ size: {
1848
+ xs: "h-6 gap-1 rounded-[calc(var(--radius)-5px)] px-2 has-[>svg]:px-2 [&>svg:not([class*='size-'])]:size-3.5",
1849
+ sm: "h-8 gap-1.5 rounded-md px-2.5 has-[>svg]:px-2.5",
1850
+ "icon-xs": "size-6 rounded-[calc(var(--radius)-5px)] p-0 has-[>svg]:p-0",
1851
+ "icon-sm": "size-8 p-0 has-[>svg]:p-0"
1852
+ }
1853
+ },
1854
+ defaultVariants: {
1855
+ size: "xs"
1856
+ }
1857
+ }
1858
+ );
1859
+ function Command({
1860
+ className,
1861
+ ...props
1862
+ }) {
1863
+ return /* @__PURE__ */ jsx(
1864
+ Command$1,
1865
+ {
1866
+ "data-slot": "command",
1867
+ className: cn(
1868
+ "flex size-full flex-col overflow-hidden rounded-xl! bg-popover p-1 text-popover-foreground",
1869
+ className
1870
+ ),
1871
+ ...props
1872
+ }
1873
+ );
1874
+ }
1875
+ function CommandInput({
1876
+ className,
1877
+ ...props
1878
+ }) {
1879
+ return /* @__PURE__ */ jsx("div", { "data-slot": "command-input-wrapper", className: "p-1 pb-0", children: /* @__PURE__ */ jsxs(InputGroup, { className: "h-8! rounded-lg! border-input/30 bg-input/30 shadow-none! *:data-[slot=input-group-addon]:pl-2!", children: [
1880
+ /* @__PURE__ */ jsx(
1881
+ Command$1.Input,
1882
+ {
1883
+ "data-slot": "command-input",
1884
+ className: cn(
1885
+ "w-full text-sm outline-hidden disabled:cursor-not-allowed disabled:opacity-50",
1886
+ className
1887
+ ),
1888
+ ...props
1889
+ }
1890
+ ),
1891
+ /* @__PURE__ */ jsx(InputGroupAddon, { children: /* @__PURE__ */ jsx(SearchIcon, { className: "size-4 shrink-0 opacity-50" }) })
1892
+ ] }) });
1893
+ }
1894
+ function CommandList({
1895
+ className,
1896
+ ...props
1897
+ }) {
1898
+ return /* @__PURE__ */ jsx(
1899
+ Command$1.List,
1900
+ {
1901
+ "data-slot": "command-list",
1902
+ className: cn(
1903
+ "no-scrollbar max-h-72 scroll-py-1 overflow-x-hidden overflow-y-auto outline-none",
1904
+ className
1905
+ ),
1906
+ ...props
1907
+ }
1908
+ );
1909
+ }
1910
+ function CommandEmpty({
1911
+ className,
1912
+ ...props
1913
+ }) {
1914
+ return /* @__PURE__ */ jsx(
1915
+ Command$1.Empty,
1916
+ {
1917
+ "data-slot": "command-empty",
1918
+ className: cn("py-6 text-center text-sm", className),
1919
+ ...props
1920
+ }
1921
+ );
1922
+ }
1923
+ function CommandItem({
1924
+ className,
1925
+ children,
1926
+ ...props
1927
+ }) {
1928
+ return /* @__PURE__ */ jsxs(
1929
+ Command$1.Item,
1930
+ {
1931
+ "data-slot": "command-item",
1932
+ className: cn(
1933
+ "group/command-item relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none in-data-[slot=dialog-content]:rounded-lg! data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50 data-selected:bg-muted data-selected:text-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 data-selected:*:[svg]:text-foreground",
1934
+ className
1935
+ ),
1936
+ ...props,
1937
+ children: [
1938
+ children,
1939
+ /* @__PURE__ */ jsx(CheckIcon, { className: "ml-auto opacity-0 group-has-data-[slot=command-shortcut]/command-item:hidden group-data-[checked=true]/command-item:opacity-100" })
1940
+ ]
1941
+ }
1942
+ );
1943
+ }
1944
+ function Skeleton({ className, ...props }) {
1945
+ return /* @__PURE__ */ jsx(
1946
+ "div",
1947
+ {
1948
+ "data-slot": "skeleton",
1949
+ className: cn("animate-pulse rounded-md bg-muted", className),
1950
+ ...props
1951
+ }
1952
+ );
1953
+ }
1954
+ function ExternalField({ value, onChange, fetchList, readOnly, label }) {
1955
+ const [query, setQuery] = React18.useState("");
1956
+ const [results, setResults] = React18.useState([]);
1957
+ const [loading, setLoading] = React18.useState(false);
1958
+ React18.useEffect(() => {
1959
+ if (!fetchList) return;
1960
+ setLoading(true);
1961
+ const timer = setTimeout(async () => {
1962
+ try {
1963
+ const data = await fetchList(query);
1964
+ setResults(data);
1965
+ } catch {
1966
+ setResults([]);
1967
+ } finally {
1968
+ setLoading(false);
1969
+ }
1970
+ }, 300);
1971
+ return () => clearTimeout(timer);
1972
+ }, [query, fetchList]);
1973
+ const selectedLabel = results.find((r) => r.value === value)?.label ?? String(value ?? "");
1974
+ return /* @__PURE__ */ jsx(FieldLabel, { label: label ?? "", readOnly, children: /* @__PURE__ */ jsxs(Command, { className: "rounded-md border border-input", children: [
1975
+ /* @__PURE__ */ jsx(
1976
+ CommandInput,
1977
+ {
1978
+ placeholder: "Search...",
1979
+ value: query,
1980
+ onValueChange: setQuery,
1981
+ disabled: readOnly
1982
+ }
1983
+ ),
1984
+ /* @__PURE__ */ jsx(CommandList, { children: loading ? /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1.5 p-2", children: [
1985
+ /* @__PURE__ */ jsx(Skeleton, { className: "h-6 w-full" }),
1986
+ /* @__PURE__ */ jsx(Skeleton, { className: "h-6 w-3/4" }),
1987
+ /* @__PURE__ */ jsx(Skeleton, { className: "h-6 w-1/2" })
1988
+ ] }) : results.length === 0 ? /* @__PURE__ */ jsx(CommandEmpty, { children: "No results found." }) : results.map((r, i) => /* @__PURE__ */ jsx(
1989
+ CommandItem,
1990
+ {
1991
+ "data-checked": r.value === value,
1992
+ onSelect: () => onChange(r.value),
1993
+ children: r.label
1994
+ },
1995
+ i
1996
+ )) }),
1997
+ value != null && /* @__PURE__ */ jsxs("div", { className: "border-t px-3 py-1.5 text-xs text-muted-foreground truncate", children: [
1998
+ "Selected: ",
1999
+ /* @__PURE__ */ jsx("span", { className: "font-medium text-foreground", children: selectedLabel })
2000
+ ] })
2001
+ ] }) });
2002
+ }
2003
+
2004
+ // src/components/overrides/fields/FieldTypesRegistry.ts
2005
+ var fieldTypesRegistry = {
2006
+ text: TextField,
2007
+ textarea: TextareaField,
2008
+ number: NumberField,
2009
+ select: SelectField,
2010
+ radio: RadioField,
2011
+ array: ArrayField,
2012
+ object: ObjectField,
2013
+ slot: SlotField,
2014
+ custom: CustomField,
2015
+ richtext: RichtextField,
2016
+ external: ExternalField
2017
+ };
2018
+ function PuckRoot({ children }) {
2019
+ return /* @__PURE__ */ jsx(Fragment, { children });
2020
+ }
2021
+ var puckOverrides = {
2022
+ header: EditorHeader,
2023
+ headerActions: EditorHeaderActions,
2024
+ drawer: EditorDrawer,
2025
+ components: EditorComponents,
2026
+ drawerItem: DrawerItem,
2027
+ componentItem: DrawerItem,
2028
+ outline: EditorOutline,
2029
+ iframe: CanvasIframe,
2030
+ preview: CanvasPreview,
2031
+ componentOverlay: ComponentOverlay,
2032
+ actionBar: ActionBar,
2033
+ fields: FieldWrapper,
2034
+ fieldTypes: fieldTypesRegistry,
2035
+ puck: PuckRoot
2036
+ };
2037
+ var DEFAULT_BOUNDS_OFFSET = {
2038
+ top: 0,
2039
+ left: 0,
2040
+ width: 0,
2041
+ height: 0
2042
+ };
2043
+ var HighlightContext = React18.createContext(void 0);
2044
+ function useHighlight() {
2045
+ const context = React18.useContext(HighlightContext);
2046
+ if (!context) {
2047
+ throw new Error("useHighlight must be used within a HighlightProvider");
2048
+ }
2049
+ return context;
2050
+ }
2051
+ function Highlight({
2052
+ ref,
2053
+ ...props
2054
+ }) {
2055
+ const {
2056
+ as: Component = "div",
2057
+ children,
2058
+ value,
2059
+ defaultValue,
2060
+ onValueChange,
2061
+ className,
2062
+ style,
2063
+ transition = { type: "spring", stiffness: 350, damping: 35 },
2064
+ hover = false,
2065
+ click = true,
2066
+ enabled = true,
2067
+ controlledItems,
2068
+ disabled = false,
2069
+ exitDelay = 200,
2070
+ mode = "children"
2071
+ } = props;
2072
+ const localRef = React18.useRef(null);
2073
+ React18.useImperativeHandle(ref, () => localRef.current);
2074
+ const propsBoundsOffset = props?.boundsOffset;
2075
+ const boundsOffset = propsBoundsOffset ?? DEFAULT_BOUNDS_OFFSET;
2076
+ const boundsOffsetTop = boundsOffset.top ?? 0;
2077
+ const boundsOffsetLeft = boundsOffset.left ?? 0;
2078
+ const boundsOffsetWidth = boundsOffset.width ?? 0;
2079
+ const boundsOffsetHeight = boundsOffset.height ?? 0;
2080
+ const boundsOffsetRef = React18.useRef({
2081
+ top: boundsOffsetTop,
2082
+ left: boundsOffsetLeft,
2083
+ width: boundsOffsetWidth,
2084
+ height: boundsOffsetHeight
2085
+ });
2086
+ React18.useEffect(() => {
2087
+ boundsOffsetRef.current = {
2088
+ top: boundsOffsetTop,
2089
+ left: boundsOffsetLeft,
2090
+ width: boundsOffsetWidth,
2091
+ height: boundsOffsetHeight
2092
+ };
2093
+ }, [
2094
+ boundsOffsetTop,
2095
+ boundsOffsetLeft,
2096
+ boundsOffsetWidth,
2097
+ boundsOffsetHeight
2098
+ ]);
2099
+ const [activeValue, setActiveValue] = React18.useState(
2100
+ value ?? defaultValue ?? null
2101
+ );
2102
+ const [boundsState, setBoundsState] = React18.useState(null);
2103
+ const [activeClassNameState, setActiveClassNameState] = React18.useState("");
2104
+ const safeSetActiveValue = (id2) => {
2105
+ setActiveValue((prev) => {
2106
+ if (prev !== id2) {
2107
+ onValueChange?.(id2);
2108
+ return id2;
2109
+ }
2110
+ return prev;
2111
+ });
2112
+ };
2113
+ const safeSetBoundsRef = React18.useRef(void 0);
2114
+ React18.useEffect(() => {
2115
+ safeSetBoundsRef.current = (bounds) => {
2116
+ if (!localRef.current) return;
2117
+ const containerRect = localRef.current.getBoundingClientRect();
2118
+ const offset = boundsOffsetRef.current;
2119
+ const newBounds = {
2120
+ top: bounds.top - containerRect.top + offset.top,
2121
+ left: bounds.left - containerRect.left + offset.left,
2122
+ width: bounds.width + offset.width,
2123
+ height: bounds.height + offset.height
2124
+ };
2125
+ setBoundsState((prev) => {
2126
+ if (prev && prev.top === newBounds.top && prev.left === newBounds.left && prev.width === newBounds.width && prev.height === newBounds.height) {
2127
+ return prev;
2128
+ }
2129
+ return newBounds;
2130
+ });
2131
+ };
2132
+ });
2133
+ const safeSetBounds = (bounds) => {
2134
+ safeSetBoundsRef.current?.(bounds);
2135
+ };
2136
+ const clearBounds = React18.useCallback(() => {
2137
+ setBoundsState((prev) => prev === null ? prev : null);
2138
+ }, []);
2139
+ React18.useEffect(() => {
2140
+ if (value !== void 0) setActiveValue(value);
2141
+ else if (defaultValue !== void 0) setActiveValue(defaultValue);
2142
+ }, [value, defaultValue]);
2143
+ const id = React18.useId();
2144
+ React18.useEffect(() => {
2145
+ if (mode !== "parent") return;
2146
+ const container = localRef.current;
2147
+ if (!container) return;
2148
+ const onScroll = () => {
2149
+ if (!activeValue) return;
2150
+ const activeEl = container.querySelector(
2151
+ `[data-value="${activeValue}"][data-highlight="true"]`
2152
+ );
2153
+ if (activeEl)
2154
+ safeSetBoundsRef.current?.(activeEl.getBoundingClientRect());
2155
+ };
2156
+ container.addEventListener("scroll", onScroll, { passive: true });
2157
+ return () => container.removeEventListener("scroll", onScroll);
2158
+ }, [mode, activeValue]);
2159
+ const render = (children2) => {
2160
+ if (mode === "parent") {
2161
+ return /* @__PURE__ */ jsxs(
2162
+ Component,
2163
+ {
2164
+ ref: localRef,
2165
+ "data-slot": "motion-highlight-container",
2166
+ style: { position: "relative", zIndex: 1 },
2167
+ className: props?.containerClassName,
2168
+ children: [
2169
+ /* @__PURE__ */ jsx(AnimatePresence, { initial: false, mode: "wait", children: boundsState && /* @__PURE__ */ jsx(
2170
+ motion.div,
2171
+ {
2172
+ "data-slot": "motion-highlight",
2173
+ animate: {
2174
+ top: boundsState.top,
2175
+ left: boundsState.left,
2176
+ width: boundsState.width,
2177
+ height: boundsState.height,
2178
+ opacity: 1
2179
+ },
2180
+ initial: {
2181
+ top: boundsState.top,
2182
+ left: boundsState.left,
2183
+ width: boundsState.width,
2184
+ height: boundsState.height,
2185
+ opacity: 0
2186
+ },
2187
+ exit: {
2188
+ opacity: 0,
2189
+ transition: {
2190
+ ...transition,
2191
+ delay: (transition?.delay ?? 0) + (exitDelay ?? 0) / 1e3
2192
+ }
2193
+ },
2194
+ transition,
2195
+ style: { position: "absolute", zIndex: 0, ...style },
2196
+ className: cn(className, activeClassNameState)
2197
+ }
2198
+ ) }),
2199
+ children2
2200
+ ]
2201
+ }
2202
+ );
2203
+ }
2204
+ return children2;
2205
+ };
2206
+ return /* @__PURE__ */ jsx(
2207
+ HighlightContext.Provider,
2208
+ {
2209
+ value: {
2210
+ mode,
2211
+ activeValue,
2212
+ setActiveValue: safeSetActiveValue,
2213
+ id,
2214
+ hover,
2215
+ click,
2216
+ className,
2217
+ style,
2218
+ transition,
2219
+ disabled,
2220
+ enabled,
2221
+ exitDelay,
2222
+ setBounds: safeSetBounds,
2223
+ clearBounds,
2224
+ activeClassName: activeClassNameState,
2225
+ setActiveClassName: setActiveClassNameState,
2226
+ forceUpdateBounds: props?.forceUpdateBounds
2227
+ },
2228
+ children: enabled ? controlledItems ? render(children) : render(
2229
+ React18.Children.map(children, (child, index) => /* @__PURE__ */ jsx(HighlightItem, { className: props?.itemsClassName, children: child }, index))
2230
+ ) : children
2231
+ }
2232
+ );
2233
+ }
2234
+ function getNonOverridingDataAttributes(element, dataAttributes) {
2235
+ return Object.keys(dataAttributes).reduce(
2236
+ (acc, key) => {
2237
+ if (element.props[key] === void 0) {
2238
+ acc[key] = dataAttributes[key];
2239
+ }
2240
+ return acc;
2241
+ },
2242
+ {}
2243
+ );
2244
+ }
2245
+ function HighlightItem({
2246
+ ref,
2247
+ as,
2248
+ children,
2249
+ id,
2250
+ value,
2251
+ className,
2252
+ style,
2253
+ transition,
2254
+ disabled = false,
2255
+ activeClassName,
2256
+ exitDelay,
2257
+ asChild = false,
2258
+ forceUpdateBounds,
2259
+ ...props
2260
+ }) {
2261
+ const itemId = React18.useId();
2262
+ const {
2263
+ activeValue,
2264
+ setActiveValue,
2265
+ mode,
2266
+ setBounds,
2267
+ clearBounds,
2268
+ hover,
2269
+ click,
2270
+ enabled,
2271
+ className: contextClassName,
2272
+ style: contextStyle,
2273
+ transition: contextTransition,
2274
+ id: contextId,
2275
+ disabled: contextDisabled,
2276
+ exitDelay: contextExitDelay,
2277
+ forceUpdateBounds: contextForceUpdateBounds,
2278
+ setActiveClassName
2279
+ } = useHighlight();
2280
+ const Component = as ?? "div";
2281
+ const element = children;
2282
+ const childValue = id ?? value ?? element.props?.["data-value"] ?? element.props?.id ?? itemId;
2283
+ const isActive = activeValue === childValue;
2284
+ const isDisabled = disabled === void 0 ? contextDisabled : disabled;
2285
+ const itemTransition = transition ?? contextTransition;
2286
+ const localRef = React18.useRef(null);
2287
+ React18.useImperativeHandle(ref, () => localRef.current);
2288
+ const refCallback = React18.useCallback((node) => {
2289
+ localRef.current = node;
2290
+ }, []);
2291
+ React18.useEffect(() => {
2292
+ if (mode !== "parent") return;
2293
+ let rafId;
2294
+ let previousBounds = null;
2295
+ const shouldUpdateBounds = forceUpdateBounds === true || contextForceUpdateBounds && forceUpdateBounds !== false;
2296
+ const updateBounds = () => {
2297
+ if (!localRef.current) return;
2298
+ const bounds = localRef.current.getBoundingClientRect();
2299
+ if (shouldUpdateBounds) {
2300
+ if (previousBounds && previousBounds.top === bounds.top && previousBounds.left === bounds.left && previousBounds.width === bounds.width && previousBounds.height === bounds.height) {
2301
+ rafId = requestAnimationFrame(updateBounds);
2302
+ return;
2303
+ }
2304
+ previousBounds = bounds;
2305
+ rafId = requestAnimationFrame(updateBounds);
2306
+ }
2307
+ setBounds(bounds);
2308
+ };
2309
+ if (isActive) {
2310
+ updateBounds();
2311
+ setActiveClassName(activeClassName ?? "");
2312
+ } else if (!activeValue) clearBounds();
2313
+ if (shouldUpdateBounds) return () => cancelAnimationFrame(rafId);
2314
+ }, [
2315
+ mode,
2316
+ isActive,
2317
+ activeValue,
2318
+ setBounds,
2319
+ clearBounds,
2320
+ activeClassName,
2321
+ setActiveClassName,
2322
+ forceUpdateBounds,
2323
+ contextForceUpdateBounds
2324
+ ]);
2325
+ if (!React18.isValidElement(children)) return children;
2326
+ const dataAttributes = {
2327
+ "data-active": isActive ? "true" : "false",
2328
+ "aria-selected": isActive,
2329
+ "data-disabled": isDisabled,
2330
+ "data-value": childValue,
2331
+ "data-highlight": true
2332
+ };
2333
+ const commonHandlers = hover ? {
2334
+ onMouseEnter: (e) => {
2335
+ setActiveValue(childValue);
2336
+ element.props.onMouseEnter?.(e);
2337
+ },
2338
+ onMouseLeave: (e) => {
2339
+ setActiveValue(null);
2340
+ element.props.onMouseLeave?.(e);
2341
+ }
2342
+ } : click ? {
2343
+ onClick: (e) => {
2344
+ setActiveValue(childValue);
2345
+ element.props.onClick?.(e);
2346
+ }
2347
+ } : {};
2348
+ if (asChild) {
2349
+ if (mode === "children") {
2350
+ return React18.cloneElement(
2351
+ element,
2352
+ {
2353
+ key: childValue,
2354
+ ref: refCallback,
2355
+ className: cn("relative", element.props.className),
2356
+ ...getNonOverridingDataAttributes(element, {
2357
+ ...dataAttributes,
2358
+ "data-slot": "motion-highlight-item-container"
2359
+ }),
2360
+ ...commonHandlers,
2361
+ ...props
2362
+ },
2363
+ /* @__PURE__ */ jsxs(Fragment, { children: [
2364
+ /* @__PURE__ */ jsx(AnimatePresence, { initial: false, mode: "wait", children: isActive && !isDisabled && /* @__PURE__ */ jsx(
2365
+ motion.div,
2366
+ {
2367
+ layoutId: `transition-background-${contextId}`,
2368
+ "data-slot": "motion-highlight",
2369
+ style: {
2370
+ position: "absolute",
2371
+ zIndex: 0,
2372
+ ...contextStyle,
2373
+ ...style
2374
+ },
2375
+ className: cn(contextClassName, activeClassName),
2376
+ transition: itemTransition,
2377
+ initial: { opacity: 0 },
2378
+ animate: { opacity: 1 },
2379
+ exit: {
2380
+ opacity: 0,
2381
+ transition: {
2382
+ ...itemTransition,
2383
+ delay: (itemTransition?.delay ?? 0) + (exitDelay ?? contextExitDelay ?? 0) / 1e3
2384
+ }
2385
+ },
2386
+ ...dataAttributes
2387
+ }
2388
+ ) }),
2389
+ /* @__PURE__ */ jsx(
2390
+ Component,
2391
+ {
2392
+ "data-slot": "motion-highlight-item",
2393
+ style: { position: "relative", zIndex: 1 },
2394
+ className,
2395
+ ...dataAttributes,
2396
+ children
2397
+ }
2398
+ )
2399
+ ] })
2400
+ );
2401
+ }
2402
+ return React18.cloneElement(element, {
2403
+ ref: refCallback,
2404
+ ...getNonOverridingDataAttributes(element, {
2405
+ ...dataAttributes,
2406
+ "data-slot": "motion-highlight-item"
2407
+ }),
2408
+ ...commonHandlers
2409
+ });
2410
+ }
2411
+ return enabled ? /* @__PURE__ */ jsxs(
2412
+ Component,
2413
+ {
2414
+ ref: localRef,
2415
+ "data-slot": "motion-highlight-item-container",
2416
+ className: cn(mode === "children" && "relative", className),
2417
+ ...dataAttributes,
2418
+ ...props,
2419
+ ...commonHandlers,
2420
+ children: [
2421
+ mode === "children" && /* @__PURE__ */ jsx(AnimatePresence, { initial: false, mode: "wait", children: isActive && !isDisabled && /* @__PURE__ */ jsx(
2422
+ motion.div,
2423
+ {
2424
+ layoutId: `transition-background-${contextId}`,
2425
+ "data-slot": "motion-highlight",
2426
+ style: {
2427
+ position: "absolute",
2428
+ zIndex: 0,
2429
+ ...contextStyle,
2430
+ ...style
2431
+ },
2432
+ className: cn(contextClassName, activeClassName),
2433
+ transition: itemTransition,
2434
+ initial: { opacity: 0 },
2435
+ animate: { opacity: 1 },
2436
+ exit: {
2437
+ opacity: 0,
2438
+ transition: {
2439
+ ...itemTransition,
2440
+ delay: (itemTransition?.delay ?? 0) + (exitDelay ?? contextExitDelay ?? 0) / 1e3
2441
+ }
2442
+ },
2443
+ ...dataAttributes
2444
+ }
2445
+ ) }),
2446
+ React18.cloneElement(element, {
2447
+ style: { position: "relative", zIndex: 1 },
2448
+ className: element.props.className,
2449
+ ...getNonOverridingDataAttributes(element, {
2450
+ ...dataAttributes,
2451
+ "data-slot": "motion-highlight-item"
2452
+ })
2453
+ })
2454
+ ]
2455
+ },
2456
+ childValue
2457
+ ) : children;
2458
+ }
2459
+ function mergeRefs(...refs) {
2460
+ return (node) => {
2461
+ refs.forEach((ref) => {
2462
+ if (!ref) return;
2463
+ if (typeof ref === "function") {
2464
+ ref(node);
2465
+ } else {
2466
+ ref.current = node;
2467
+ }
2468
+ });
2469
+ };
2470
+ }
2471
+ function mergeProps4(childProps, slotProps) {
2472
+ const merged = { ...childProps, ...slotProps };
2473
+ if (childProps.className || slotProps.className) {
2474
+ merged.className = cn(
2475
+ childProps.className,
2476
+ slotProps.className
2477
+ );
2478
+ }
2479
+ if (childProps.style || slotProps.style) {
2480
+ merged.style = {
2481
+ ...childProps.style,
2482
+ ...slotProps.style
2483
+ };
2484
+ }
2485
+ return merged;
2486
+ }
2487
+ function Slot({
2488
+ children,
2489
+ ref,
2490
+ ...props
2491
+ }) {
2492
+ const isAlreadyMotion = typeof children.type === "object" && children.type !== null && isMotionComponent(children.type);
2493
+ const Base = React18.useMemo(
2494
+ () => isAlreadyMotion ? children.type : motion.create(children.type),
2495
+ [isAlreadyMotion, children.type]
2496
+ );
2497
+ if (!React18.isValidElement(children)) return null;
2498
+ const { ref: childRef, ...childProps } = children.props;
2499
+ const mergedProps = mergeProps4(childProps, props);
2500
+ return /* @__PURE__ */ jsx(Base, { ...mergedProps, ref: mergeRefs(childRef, ref) });
2501
+ }
2502
+ var [TabsProvider, useTabs] = getStrictContext("TabsContext");
2503
+ function Tabs(props) {
2504
+ const [value, setValue] = useControlledState({
2505
+ value: props.value,
2506
+ defaultValue: props.defaultValue,
2507
+ onChange: props.onValueChange
2508
+ });
2509
+ return /* @__PURE__ */ jsx(TabsProvider, { value: { value, setValue }, children: /* @__PURE__ */ jsx(
2510
+ Tabs$1.Root,
2511
+ {
2512
+ "data-slot": "tabs",
2513
+ ...props,
2514
+ onValueChange: setValue
2515
+ }
2516
+ ) });
2517
+ }
2518
+ function TabsHighlight({
2519
+ transition = { type: "spring", stiffness: 200, damping: 25 },
2520
+ ...props
2521
+ }) {
2522
+ const { value } = useTabs();
2523
+ return /* @__PURE__ */ jsx(
2524
+ Highlight,
2525
+ {
2526
+ "data-slot": "tabs-highlight",
2527
+ controlledItems: true,
2528
+ value,
2529
+ transition,
2530
+ click: false,
2531
+ ...props
2532
+ }
2533
+ );
2534
+ }
2535
+ function TabsList(props) {
2536
+ return /* @__PURE__ */ jsx(Tabs$1.List, { "data-slot": "tabs-list", ...props });
2537
+ }
2538
+ function TabsHighlightItem(props) {
2539
+ return /* @__PURE__ */ jsx(HighlightItem, { "data-slot": "tabs-highlight-item", ...props });
2540
+ }
2541
+ function TabsTab(props) {
2542
+ return /* @__PURE__ */ jsx(Tabs$1.Tab, { "data-slot": "tabs-tab", ...props });
2543
+ }
2544
+ function Tabs2({ className, ...props }) {
2545
+ return /* @__PURE__ */ jsx(
2546
+ Tabs,
2547
+ {
2548
+ className: cn("flex flex-col gap-2", className),
2549
+ ...props
2550
+ }
2551
+ );
2552
+ }
2553
+ function TabsList2({ className, ...props }) {
2554
+ return /* @__PURE__ */ jsx(TabsHighlight, { className: "absolute z-0 inset-0 border border-transparent rounded-md bg-background dark:border-input dark:bg-input/30 shadow-sm", children: /* @__PURE__ */ jsx(
2555
+ TabsList,
2556
+ {
2557
+ className: cn(
2558
+ "bg-muted text-muted-foreground inline-flex h-9 w-fit items-center justify-center rounded-lg p-[3px]",
2559
+ className
2560
+ ),
2561
+ ...props
2562
+ }
2563
+ ) });
2564
+ }
2565
+ function TabsTab2({ className, ...props }) {
2566
+ return /* @__PURE__ */ jsx(TabsHighlightItem, { value: props.value, className: "flex-1", children: /* @__PURE__ */ jsx(
2567
+ TabsTab,
2568
+ {
2569
+ className: cn(
2570
+ "data-[selected]:text-foreground focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:outline-ring text-muted-foreground inline-flex h-[calc(100%-1px)] flex-1 items-center justify-center gap-1.5 rounded-md w-full px-2 py-1 text-sm font-medium whitespace-nowrap transition-colors duration-500 ease-in-out focus-visible:ring-[3px] focus-visible:outline-1 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
2571
+ className
2572
+ ),
2573
+ ...props
2574
+ }
2575
+ ) });
2576
+ }
2577
+ function Aside() {
2578
+ const activeTab = useActiveTab();
2579
+ const setActiveTab = useSetActiveTab();
2580
+ const tabs = [
2581
+ { value: "insert", icon: /* @__PURE__ */ jsx(Plus, { className: "h-4 w-4" }), label: useMsg("aside.insert") },
2582
+ { value: "layer", icon: /* @__PURE__ */ jsx(Layers2, { className: "h-4 w-4" }), label: useMsg("aside.layer") },
2583
+ { value: "image", icon: /* @__PURE__ */ jsx(Image, { className: "h-4 w-4" }), label: useMsg("aside.image") },
2584
+ { value: "text", icon: /* @__PURE__ */ jsx(Type, { className: "h-4 w-4" }), label: useMsg("aside.text") },
2585
+ { value: "copilot", icon: /* @__PURE__ */ jsx(Bot, { className: "h-4 w-4" }), label: useMsg("aside.copilot") }
2586
+ ];
2587
+ return /* @__PURE__ */ jsx("div", { className: "w-18 h-full py-4 border-r border-neutral-200 dark:border-neutral-800/80", children: /* @__PURE__ */ jsx(TooltipProvider, { children: /* @__PURE__ */ jsx(Tabs2, { value: activeTab, onValueChange: setActiveTab, className: "w-full flex items-center justify-center", children: /* @__PURE__ */ jsx(TabsList2, { className: "flex-col h-auto bg-transparent gap-2", children: tabs.map(({ value, icon, label }) => /* @__PURE__ */ jsxs(Tooltip, { children: [
2588
+ /* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx("span", { children: /* @__PURE__ */ jsx(TabsTab2, { value, className: "p-2", children: icon }) }) }),
2589
+ /* @__PURE__ */ jsx(TooltipContent, { side: "right", children: label })
2590
+ ] }, value)) }) }) }) });
2591
+ }
2592
+ function AnimatedAsana({
2593
+ trigger = false,
2594
+ useTrigger = true,
2595
+ // Enable hover trigger by default
2596
+ animationDuration = 400,
2597
+ // Default animation duration
2598
+ springConfig = {
2599
+ stiffness: 180,
2600
+ damping: 15,
2601
+ mass: 1
2602
+ },
2603
+ priority = false,
2604
+ // Default non-priority
2605
+ ...props
2606
+ }) {
2607
+ const [isHovered, setIsHovered] = useState(false);
2608
+ const [animationPhase, setAnimationPhase] = useState("three-circles");
2609
+ const animationTimerRef = useRef(null);
2610
+ const animationStartedRef = useRef(false);
2611
+ const effectiveTrigger = useMemo(
2612
+ () => trigger || useTrigger && isHovered,
2613
+ [trigger, useTrigger, isHovered]
2614
+ );
2615
+ const springTransition = useMemo(
2616
+ () => ({
2617
+ type: "spring",
2618
+ stiffness: springConfig.stiffness,
2619
+ damping: springConfig.damping,
2620
+ mass: springConfig.mass
2621
+ }),
2622
+ [springConfig.stiffness, springConfig.damping, springConfig.mass]
2623
+ );
2624
+ const handleMouseEnter = useCallback(() => {
2625
+ if (useTrigger) {
2626
+ setIsHovered(true);
2627
+ }
2628
+ }, [useTrigger]);
2629
+ const handleMouseLeave = useCallback(() => {
2630
+ if (useTrigger) {
2631
+ setIsHovered(false);
2632
+ }
2633
+ }, [useTrigger]);
2634
+ useEffect(() => {
2635
+ if (animationTimerRef.current) {
2636
+ clearTimeout(animationTimerRef.current);
2637
+ animationTimerRef.current = null;
2638
+ }
2639
+ if (effectiveTrigger) {
2640
+ animationStartedRef.current = true;
2641
+ setAnimationPhase("one-circle");
2642
+ animationTimerRef.current = setTimeout(() => {
2643
+ setAnimationPhase("three-circles");
2644
+ animationTimerRef.current = null;
2645
+ }, animationDuration);
2646
+ } else if (animationStartedRef.current) {
2647
+ setAnimationPhase("three-circles");
2648
+ animationStartedRef.current = false;
2649
+ }
2650
+ return () => {
2651
+ if (animationTimerRef.current) {
2652
+ clearTimeout(animationTimerRef.current);
2653
+ animationTimerRef.current = null;
2654
+ }
2655
+ };
2656
+ }, [effectiveTrigger, animationDuration]);
2657
+ const centerCircleAnimation = useMemo(
2658
+ () => ({
2659
+ opacity: animationPhase === "one-circle" ? 1 : 0,
2660
+ scale: animationPhase === "one-circle" ? 1 : 0
2661
+ }),
2662
+ [animationPhase]
2663
+ );
2664
+ const topCircleAnimation = useMemo(
2665
+ () => ({
2666
+ opacity: animationPhase === "three-circles" ? 1 : 0,
2667
+ y: animationPhase === "three-circles" ? 0 : 4.5,
2668
+ scale: animationPhase === "three-circles" ? 1 : 0
2669
+ }),
2670
+ [animationPhase]
2671
+ );
2672
+ const leftBottomCircleAnimation = useMemo(
2673
+ () => ({
2674
+ opacity: animationPhase === "three-circles" ? 1 : 0,
2675
+ x: animationPhase === "three-circles" ? 0 : 5,
2676
+ y: animationPhase === "three-circles" ? 0 : -4.5,
2677
+ scale: animationPhase === "three-circles" ? 1 : 0
2678
+ }),
2679
+ [animationPhase]
2680
+ );
2681
+ const rightBottomCircleAnimation = useMemo(
2682
+ () => ({
2683
+ opacity: animationPhase === "three-circles" ? 1 : 0,
2684
+ x: animationPhase === "three-circles" ? 0 : -5,
2685
+ y: animationPhase === "three-circles" ? 0 : -4.5,
2686
+ scale: animationPhase === "three-circles" ? 1 : 0
2687
+ }),
2688
+ [animationPhase]
2689
+ );
2690
+ return /* @__PURE__ */ jsxs(
2691
+ "svg",
2692
+ {
2693
+ width: "1em",
2694
+ height: "1em",
2695
+ strokeWidth: "1.5",
2696
+ viewBox: "0 0 24 24",
2697
+ fill: "none",
2698
+ xmlns: "http://www.w3.org/2000/svg",
2699
+ color: "currentColor",
2700
+ onMouseEnter: handleMouseEnter,
2701
+ onMouseLeave: handleMouseLeave,
2702
+ role: "img",
2703
+ "aria-label": "Asana logo",
2704
+ style: { touchAction: "none" },
2705
+ "data-testid": "asana-icon",
2706
+ ...props,
2707
+ children: [
2708
+ /* @__PURE__ */ jsx(
2709
+ motion.circle,
2710
+ {
2711
+ cx: 12,
2712
+ cy: 12,
2713
+ r: 6,
2714
+ stroke: "currentColor",
2715
+ strokeWidth: "1.5",
2716
+ strokeLinecap: "round",
2717
+ strokeLinejoin: "round",
2718
+ fill: "none",
2719
+ initial: { opacity: 0, scale: 0 },
2720
+ animate: centerCircleAnimation,
2721
+ transition: {
2722
+ opacity: { duration: 0.2 },
2723
+ scale: { ...springTransition, duration: 0.3 }
2724
+ }
2725
+ }
2726
+ ),
2727
+ /* @__PURE__ */ jsx(
2728
+ motion.path,
2729
+ {
2730
+ d: "M12 11.5C14.2091 11.5 16 9.70914 16 7.5C16 5.29086 14.2091 3.5 12 3.5C9.79086 3.5 8 5.29086 8 7.5C8 9.70914 9.79086 11.5 12 11.5Z",
2731
+ stroke: "currentColor",
2732
+ strokeWidth: "1.5",
2733
+ strokeLinecap: "round",
2734
+ strokeLinejoin: "round",
2735
+ fill: "none",
2736
+ initial: { opacity: 1, y: 0, scale: 1 },
2737
+ animate: topCircleAnimation,
2738
+ transition: {
2739
+ ...springTransition,
2740
+ delay: animationPhase === "three-circles" ? 0.1 : 0
2741
+ }
2742
+ }
2743
+ ),
2744
+ /* @__PURE__ */ jsx(
2745
+ motion.path,
2746
+ {
2747
+ d: "M7 20.5C9.20914 20.5 11 18.7091 11 16.5C11 14.2909 9.20914 12.5 7 12.5C4.79086 12.5 3 14.2909 3 16.5C3 18.7091 4.79086 20.5 7 20.5Z",
2748
+ stroke: "currentColor",
2749
+ strokeWidth: "1.5",
2750
+ strokeLinecap: "round",
2751
+ strokeLinejoin: "round",
2752
+ fill: "none",
2753
+ initial: { opacity: 1, x: 0, y: 0, scale: 1 },
2754
+ animate: leftBottomCircleAnimation,
2755
+ transition: {
2756
+ ...springTransition,
2757
+ delay: animationPhase === "three-circles" ? 0.15 : 0.05
2758
+ }
2759
+ }
2760
+ ),
2761
+ /* @__PURE__ */ jsx(
2762
+ motion.path,
2763
+ {
2764
+ d: "M17 20.5C19.2091 20.5 21 18.7091 21 16.5C21 14.2909 19.2091 12.5 17 12.5C14.7909 12.5 13 14.2909 13 16.5C13 18.7091 14.7909 20.5 17 20.5Z",
2765
+ stroke: "currentColor",
2766
+ strokeWidth: "1.5",
2767
+ strokeLinecap: "round",
2768
+ strokeLinejoin: "round",
2769
+ fill: "none",
2770
+ initial: { opacity: 1, x: 0, y: 0, scale: 1 },
2771
+ animate: rightBottomCircleAnimation,
2772
+ transition: {
2773
+ ...springTransition,
2774
+ delay: animationPhase === "three-circles" ? 0.2 : 0.1
2775
+ }
2776
+ }
2777
+ )
2778
+ ]
2779
+ }
2780
+ );
2781
+ }
2782
+ React18__default.memo(function TriggerableAsana2(props) {
2783
+ return /* @__PURE__ */ jsx(AnimatedAsana, { useTrigger: true, ...props });
2784
+ });
2785
+ React18__default.memo(function ControlledAsana2(props) {
2786
+ return /* @__PURE__ */ jsx(AnimatedAsana, { useTrigger: false, ...props });
2787
+ });
2788
+ var [GlobalTooltipProvider, useGlobalTooltip] = getStrictContext("GlobalTooltipProvider");
2789
+ var [LocalTooltipProvider, useTooltip] = getStrictContext(
2790
+ "LocalTooltipProvider"
2791
+ );
2792
+ function getResolvedSide(placement) {
2793
+ if (placement.includes("-")) {
2794
+ return placement.split("-")[0];
2795
+ }
2796
+ return placement;
2797
+ }
2798
+ function initialFromSide(side) {
2799
+ if (side === "top") return { y: 15 };
2800
+ if (side === "bottom") return { y: -15 };
2801
+ if (side === "left") return { x: 15 };
2802
+ return { x: -15 };
2803
+ }
2804
+ function TooltipProvider2({
2805
+ children,
2806
+ id,
2807
+ openDelay = 700,
2808
+ closeDelay = 300,
2809
+ transition = { type: "spring", stiffness: 300, damping: 35 }
2810
+ }) {
2811
+ const globalId = React18.useId();
2812
+ const [currentTooltip, setCurrentTooltip] = React18.useState(null);
2813
+ const timeoutRef = React18.useRef(null);
2814
+ const lastCloseTimeRef = React18.useRef(0);
2815
+ const referenceElRef = React18.useRef(null);
2816
+ const showTooltip = React18.useCallback(
2817
+ (data) => {
2818
+ if (timeoutRef.current) clearTimeout(timeoutRef.current);
2819
+ if (currentTooltip !== null) {
2820
+ setCurrentTooltip(data);
2821
+ return;
2822
+ }
2823
+ const now = Date.now();
2824
+ const delay = now - lastCloseTimeRef.current < closeDelay ? 0 : openDelay;
2825
+ timeoutRef.current = window.setTimeout(
2826
+ () => setCurrentTooltip(data),
2827
+ delay
2828
+ );
2829
+ },
2830
+ [openDelay, closeDelay, currentTooltip]
2831
+ );
2832
+ const hideTooltip = React18.useCallback(() => {
2833
+ if (timeoutRef.current) clearTimeout(timeoutRef.current);
2834
+ timeoutRef.current = window.setTimeout(() => {
2835
+ setCurrentTooltip(null);
2836
+ lastCloseTimeRef.current = Date.now();
2837
+ }, closeDelay);
2838
+ }, [closeDelay]);
2839
+ const hideImmediate = React18.useCallback(() => {
2840
+ if (timeoutRef.current) clearTimeout(timeoutRef.current);
2841
+ setCurrentTooltip(null);
2842
+ lastCloseTimeRef.current = Date.now();
2843
+ }, []);
2844
+ const setReferenceEl = React18.useCallback((el) => {
2845
+ referenceElRef.current = el;
2846
+ }, []);
2847
+ React18.useEffect(() => {
2848
+ const onKeyDown = (e) => {
2849
+ if (e.key === "Escape") hideImmediate();
2850
+ };
2851
+ window.addEventListener("keydown", onKeyDown, true);
2852
+ window.addEventListener("scroll", hideImmediate, true);
2853
+ window.addEventListener("resize", hideImmediate, true);
2854
+ return () => {
2855
+ window.removeEventListener("keydown", onKeyDown, true);
2856
+ window.removeEventListener("scroll", hideImmediate, true);
2857
+ window.removeEventListener("resize", hideImmediate, true);
2858
+ };
2859
+ }, [hideImmediate]);
2860
+ return /* @__PURE__ */ jsxs(
2861
+ GlobalTooltipProvider,
2862
+ {
2863
+ value: {
2864
+ showTooltip,
2865
+ hideTooltip,
2866
+ hideImmediate,
2867
+ currentTooltip,
2868
+ transition,
2869
+ globalId: id ?? globalId,
2870
+ setReferenceEl,
2871
+ referenceElRef
2872
+ },
2873
+ children: [
2874
+ /* @__PURE__ */ jsx(LayoutGroup, { children }),
2875
+ /* @__PURE__ */ jsx(TooltipOverlay, {})
2876
+ ]
2877
+ }
2878
+ );
2879
+ }
2880
+ var [RenderedTooltipProvider, useRenderedTooltip] = getStrictContext("RenderedTooltipContext");
2881
+ var [FloatingProvider, useFloatingContext] = getStrictContext("FloatingContext");
2882
+ var MotionTooltipArrow = motion.create(FloatingArrow);
2883
+ function TooltipArrow({
2884
+ ref,
2885
+ withTransition = true,
2886
+ ...props
2887
+ }) {
2888
+ const { side, align, open } = useRenderedTooltip();
2889
+ const { context, arrowRef } = useFloatingContext();
2890
+ const { transition, globalId } = useGlobalTooltip();
2891
+ React18.useImperativeHandle(ref, () => arrowRef.current);
2892
+ const deg = { top: 0, right: 90, bottom: 180, left: -90 }[side];
2893
+ return /* @__PURE__ */ jsx(
2894
+ MotionTooltipArrow,
2895
+ {
2896
+ ref: arrowRef,
2897
+ context,
2898
+ "data-state": open ? "open" : "closed",
2899
+ "data-side": side,
2900
+ "data-align": align,
2901
+ "data-slot": "tooltip-arrow",
2902
+ style: { rotate: deg },
2903
+ layoutId: withTransition ? `tooltip-arrow-${globalId}` : void 0,
2904
+ transition: withTransition ? transition : void 0,
2905
+ ...props
2906
+ }
2907
+ );
2908
+ }
2909
+ function TooltipPortal(props) {
2910
+ return /* @__PURE__ */ jsx(FloatingPortal, { ...props });
2911
+ }
2912
+ function TooltipOverlay() {
2913
+ const { currentTooltip, transition, globalId, referenceElRef } = useGlobalTooltip();
2914
+ const [rendered, setRendered] = React18.useState({ data: null, open: false });
2915
+ const arrowRef = React18.useRef(null);
2916
+ const side = rendered.data?.side ?? "top";
2917
+ const align = rendered.data?.align ?? "center";
2918
+ const { refs, x, y, strategy, context, update } = useFloating({
2919
+ placement: align === "center" ? side : `${side}-${align}`,
2920
+ whileElementsMounted: autoUpdate,
2921
+ middleware: [
2922
+ offset({
2923
+ mainAxis: rendered.data?.sideOffset ?? 0,
2924
+ crossAxis: rendered.data?.alignOffset ?? 0
2925
+ }),
2926
+ flip(),
2927
+ shift({ padding: 8 }),
2928
+ arrow({ element: arrowRef })
2929
+ ]
2930
+ });
2931
+ React18.useEffect(() => {
2932
+ if (currentTooltip) {
2933
+ setRendered({ data: currentTooltip, open: true });
2934
+ } else {
2935
+ setRendered((p) => p.data ? { ...p, open: false } : p);
2936
+ }
2937
+ }, [currentTooltip]);
2938
+ React18.useLayoutEffect(() => {
2939
+ if (referenceElRef.current) {
2940
+ refs.setReference(referenceElRef.current);
2941
+ update();
2942
+ }
2943
+ }, [referenceElRef, refs, update, rendered.data]);
2944
+ const ready = x != null && y != null;
2945
+ const Component = rendered.data?.contentAsChild ? Slot : motion.div;
2946
+ const resolvedSide = getResolvedSide(context.placement);
2947
+ return /* @__PURE__ */ jsx(AnimatePresence, { mode: "wait", children: rendered.data && ready && /* @__PURE__ */ jsx(TooltipPortal, { children: /* @__PURE__ */ jsx(
2948
+ "div",
2949
+ {
2950
+ ref: refs.setFloating,
2951
+ "data-slot": "tooltip-overlay",
2952
+ "data-side": resolvedSide,
2953
+ "data-align": rendered.data.align,
2954
+ "data-state": rendered.open ? "open" : "closed",
2955
+ style: {
2956
+ position: strategy,
2957
+ top: 0,
2958
+ left: 0,
2959
+ zIndex: 50,
2960
+ transform: `translate3d(${x}px, ${y}px, 0)`
2961
+ },
2962
+ children: /* @__PURE__ */ jsx(FloatingProvider, { value: { context, arrowRef }, children: /* @__PURE__ */ jsx(
2963
+ RenderedTooltipProvider,
2964
+ {
2965
+ value: {
2966
+ side: resolvedSide,
2967
+ align: rendered.data.align,
2968
+ open: rendered.open
2969
+ },
2970
+ children: /* @__PURE__ */ jsx(
2971
+ Component,
2972
+ {
2973
+ "data-slot": "tooltip-content",
2974
+ "data-side": resolvedSide,
2975
+ "data-align": rendered.data.align,
2976
+ "data-state": rendered.open ? "open" : "closed",
2977
+ layoutId: `tooltip-content-${globalId}`,
2978
+ initial: {
2979
+ opacity: 0,
2980
+ scale: 0,
2981
+ ...initialFromSide(rendered.data.side)
2982
+ },
2983
+ animate: rendered.open ? { opacity: 1, scale: 1, x: 0, y: 0 } : {
2984
+ opacity: 0,
2985
+ scale: 0,
2986
+ ...initialFromSide(rendered.data.side)
2987
+ },
2988
+ exit: {
2989
+ opacity: 0,
2990
+ scale: 0,
2991
+ ...initialFromSide(rendered.data.side)
2992
+ },
2993
+ onAnimationComplete: () => {
2994
+ if (!rendered.open)
2995
+ setRendered({ data: null, open: false });
2996
+ },
2997
+ transition,
2998
+ ...rendered.data.contentProps,
2999
+ style: {
3000
+ position: "relative",
3001
+ ...rendered.data.contentProps?.style || {}
3002
+ }
3003
+ }
3004
+ )
3005
+ }
3006
+ ) })
3007
+ }
3008
+ ) }) });
3009
+ }
3010
+ function Tooltip2({
3011
+ children,
3012
+ side = "top",
3013
+ sideOffset = 0,
3014
+ align = "center",
3015
+ alignOffset = 0
3016
+ }) {
3017
+ const id = React18.useId();
3018
+ const [props, setProps] = React18.useState({});
3019
+ const [asChild, setAsChild] = React18.useState(false);
3020
+ return /* @__PURE__ */ jsx(
3021
+ LocalTooltipProvider,
3022
+ {
3023
+ value: {
3024
+ props,
3025
+ setProps,
3026
+ asChild,
3027
+ setAsChild,
3028
+ side,
3029
+ sideOffset,
3030
+ align,
3031
+ alignOffset,
3032
+ id
3033
+ },
3034
+ children
3035
+ }
3036
+ );
3037
+ }
3038
+ function shallowEqualWithoutChildren(a, b) {
3039
+ if (a === b) return true;
3040
+ if (!a || !b) return false;
3041
+ const keysA = Object.keys(a).filter((k) => k !== "children");
3042
+ const keysB = Object.keys(b).filter((k) => k !== "children");
3043
+ if (keysA.length !== keysB.length) return false;
3044
+ for (const k of keysA) {
3045
+ if (a[k] !== b[k]) return false;
3046
+ }
3047
+ return true;
3048
+ }
3049
+ function TooltipContent2({ asChild = false, ...props }) {
3050
+ const { setProps, setAsChild } = useTooltip();
3051
+ const lastPropsRef = React18.useRef(
3052
+ void 0
3053
+ );
3054
+ React18.useEffect(() => {
3055
+ if (!shallowEqualWithoutChildren(lastPropsRef.current, props)) {
3056
+ lastPropsRef.current = props;
3057
+ setProps(props);
3058
+ }
3059
+ }, [props, setProps]);
3060
+ React18.useEffect(() => {
3061
+ setAsChild(asChild);
3062
+ }, [asChild, setAsChild]);
3063
+ return null;
3064
+ }
3065
+ function TooltipTrigger2({
3066
+ ref,
3067
+ onMouseEnter,
3068
+ onMouseLeave,
3069
+ onFocus,
3070
+ onBlur,
3071
+ onPointerDown,
3072
+ asChild = false,
3073
+ ...props
3074
+ }) {
3075
+ const {
3076
+ props: contentProps,
3077
+ asChild: contentAsChild,
3078
+ side,
3079
+ sideOffset,
3080
+ align,
3081
+ alignOffset,
3082
+ id
3083
+ } = useTooltip();
3084
+ const {
3085
+ showTooltip,
3086
+ hideTooltip,
3087
+ hideImmediate,
3088
+ currentTooltip,
3089
+ setReferenceEl
3090
+ } = useGlobalTooltip();
3091
+ const triggerRef = React18.useRef(null);
3092
+ React18.useImperativeHandle(ref, () => triggerRef.current);
3093
+ const suppressNextFocusRef = React18.useRef(false);
3094
+ const handleOpen = React18.useCallback(() => {
3095
+ if (!triggerRef.current) return;
3096
+ setReferenceEl(triggerRef.current);
3097
+ const rect = triggerRef.current.getBoundingClientRect();
3098
+ showTooltip({
3099
+ contentProps,
3100
+ contentAsChild,
3101
+ rect,
3102
+ side,
3103
+ sideOffset,
3104
+ align,
3105
+ alignOffset,
3106
+ id
3107
+ });
3108
+ }, [
3109
+ showTooltip,
3110
+ setReferenceEl,
3111
+ contentProps,
3112
+ contentAsChild,
3113
+ side,
3114
+ sideOffset,
3115
+ align,
3116
+ alignOffset,
3117
+ id
3118
+ ]);
3119
+ const handlePointerDown = React18.useCallback(
3120
+ (e) => {
3121
+ onPointerDown?.(e);
3122
+ if (currentTooltip?.id === id) {
3123
+ suppressNextFocusRef.current = true;
3124
+ hideImmediate();
3125
+ Promise.resolve().then(() => {
3126
+ suppressNextFocusRef.current = false;
3127
+ });
3128
+ }
3129
+ },
3130
+ [onPointerDown, currentTooltip?.id, id, hideImmediate]
3131
+ );
3132
+ const handleMouseEnter = React18.useCallback(
3133
+ (e) => {
3134
+ onMouseEnter?.(e);
3135
+ handleOpen();
3136
+ },
3137
+ [handleOpen, onMouseEnter]
3138
+ );
3139
+ const handleMouseLeave = React18.useCallback(
3140
+ (e) => {
3141
+ onMouseLeave?.(e);
3142
+ hideTooltip();
3143
+ },
3144
+ [hideTooltip, onMouseLeave]
3145
+ );
3146
+ const handleFocus = React18.useCallback(
3147
+ (e) => {
3148
+ onFocus?.(e);
3149
+ if (suppressNextFocusRef.current) return;
3150
+ handleOpen();
3151
+ },
3152
+ [handleOpen, onFocus]
3153
+ );
3154
+ const handleBlur = React18.useCallback(
3155
+ (e) => {
3156
+ onBlur?.(e);
3157
+ hideTooltip();
3158
+ },
3159
+ [hideTooltip, onBlur]
3160
+ );
3161
+ const Component = asChild ? Slot : motion.div;
3162
+ return /* @__PURE__ */ jsx(
3163
+ Component,
3164
+ {
3165
+ ref: triggerRef,
3166
+ onPointerDown: handlePointerDown,
3167
+ onMouseEnter: handleMouseEnter,
3168
+ onMouseLeave: handleMouseLeave,
3169
+ onFocus: handleFocus,
3170
+ onBlur: handleBlur,
3171
+ "data-slot": "tooltip-trigger",
3172
+ "data-side": side,
3173
+ "data-align": align,
3174
+ "data-state": currentTooltip?.id === id ? "open" : "closed",
3175
+ ...props
3176
+ }
3177
+ );
3178
+ }
3179
+ function AvatarContainer({
3180
+ zIndex,
3181
+ translate,
3182
+ side,
3183
+ sideOffset,
3184
+ align,
3185
+ alignOffset,
3186
+ ...props
3187
+ }) {
3188
+ return /* @__PURE__ */ jsx(
3189
+ Tooltip2,
3190
+ {
3191
+ side,
3192
+ sideOffset,
3193
+ align,
3194
+ alignOffset,
3195
+ children: /* @__PURE__ */ jsx(TooltipTrigger2, { asChild: true, children: /* @__PURE__ */ jsx(
3196
+ motion.div,
3197
+ {
3198
+ "data-slot": "avatar-container",
3199
+ initial: "initial",
3200
+ whileHover: "hover",
3201
+ whileTap: "hover",
3202
+ style: { position: "relative", zIndex },
3203
+ children: /* @__PURE__ */ jsx(
3204
+ motion.div,
3205
+ {
3206
+ variants: {
3207
+ initial: { y: 0 },
3208
+ hover: { y: translate }
3209
+ },
3210
+ ...props
3211
+ }
3212
+ )
3213
+ }
3214
+ ) })
3215
+ }
3216
+ );
3217
+ }
3218
+ function AvatarGroup({
3219
+ ref,
3220
+ children,
3221
+ id,
3222
+ transition = { type: "spring", stiffness: 300, damping: 17 },
3223
+ invertOverlap = false,
3224
+ translate = "-30%",
3225
+ openDelay = 0,
3226
+ closeDelay = 0,
3227
+ side = "top",
3228
+ sideOffset = 25,
3229
+ align = "center",
3230
+ alignOffset = 0,
3231
+ tooltipTransition = { type: "spring", stiffness: 300, damping: 35 },
3232
+ style,
3233
+ ...props
3234
+ }) {
3235
+ return /* @__PURE__ */ jsx(
3236
+ TooltipProvider2,
3237
+ {
3238
+ id,
3239
+ openDelay,
3240
+ closeDelay,
3241
+ transition: tooltipTransition,
3242
+ children: /* @__PURE__ */ jsx(
3243
+ "div",
3244
+ {
3245
+ ref,
3246
+ "data-slot": "avatar-group",
3247
+ style: {
3248
+ display: "flex",
3249
+ alignItems: "center",
3250
+ ...style
3251
+ },
3252
+ ...props,
3253
+ children: children?.map((child, index) => /* @__PURE__ */ jsx(
3254
+ AvatarContainer,
3255
+ {
3256
+ zIndex: invertOverlap ? React18.Children.count(children) - index : index,
3257
+ transition,
3258
+ translate,
3259
+ side,
3260
+ sideOffset,
3261
+ align,
3262
+ alignOffset,
3263
+ children: child
3264
+ },
3265
+ index
3266
+ ))
3267
+ }
3268
+ )
3269
+ }
3270
+ );
3271
+ }
3272
+ function AvatarGroupTooltip(props) {
3273
+ return /* @__PURE__ */ jsx(TooltipContent2, { ...props });
3274
+ }
3275
+ function AvatarGroupTooltipArrow(props) {
3276
+ return /* @__PURE__ */ jsx(TooltipArrow, { ...props });
3277
+ }
3278
+ function AvatarGroup2({
3279
+ className,
3280
+ invertOverlap = true,
3281
+ ...props
3282
+ }) {
3283
+ return /* @__PURE__ */ jsx(
3284
+ AvatarGroup,
3285
+ {
3286
+ className: cn("h-12 -space-x-3", className),
3287
+ invertOverlap,
3288
+ ...props
3289
+ }
3290
+ );
3291
+ }
3292
+ function AvatarGroupTooltip2({
3293
+ className,
3294
+ children,
3295
+ layout = "preserve-aspect",
3296
+ ...props
3297
+ }) {
3298
+ return /* @__PURE__ */ jsxs(
3299
+ AvatarGroupTooltip,
3300
+ {
3301
+ className: cn(
3302
+ "bg-primary text-primary-foreground z-50 w-fit rounded-md px-3 py-1.5 text-xs text-balance",
3303
+ className
3304
+ ),
3305
+ ...props,
3306
+ children: [
3307
+ /* @__PURE__ */ jsx(motion9.div, { layout, className: "overflow-hidden", children }),
3308
+ /* @__PURE__ */ jsx(
3309
+ AvatarGroupTooltipArrow,
3310
+ {
3311
+ className: "fill-primary size-3 data-[side='bottom']:translate-y-[1px] data-[side='right']:translate-x-[1px] data-[side='left']:translate-x-[-1px] data-[side='top']:translate-y-[-1px]",
3312
+ tipRadius: 2
3313
+ }
3314
+ )
3315
+ ]
3316
+ }
3317
+ );
3318
+ }
3319
+ function Avatar({
3320
+ className,
3321
+ size = "default",
3322
+ ...props
3323
+ }) {
3324
+ return /* @__PURE__ */ jsx(
3325
+ Avatar$1.Root,
3326
+ {
3327
+ "data-slot": "avatar",
3328
+ "data-size": size,
3329
+ className: cn(
3330
+ "size-8 rounded-full after:rounded-full data-[size=lg]:size-10 data-[size=sm]:size-6 group/avatar relative flex shrink-0 select-none after:absolute after:inset-0 after:border after:border-border after:mix-blend-darken dark:after:mix-blend-lighten",
3331
+ className
3332
+ ),
3333
+ ...props
3334
+ }
3335
+ );
3336
+ }
3337
+ function AvatarImage({ className, ...props }) {
3338
+ return /* @__PURE__ */ jsx(
3339
+ Avatar$1.Image,
3340
+ {
3341
+ "data-slot": "avatar-image",
3342
+ className: cn(
3343
+ "rounded-full aspect-square size-full object-cover",
3344
+ className
3345
+ ),
3346
+ ...props
3347
+ }
3348
+ );
3349
+ }
3350
+ function AvatarFallback({
3351
+ className,
3352
+ ...props
3353
+ }) {
3354
+ return /* @__PURE__ */ jsx(
3355
+ Avatar$1.Fallback,
3356
+ {
3357
+ "data-slot": "avatar-fallback",
3358
+ className: cn(
3359
+ "bg-muted text-muted-foreground rounded-full flex size-full items-center justify-center text-sm group-data-[size=sm]/avatar:text-xs",
3360
+ className
3361
+ ),
3362
+ ...props
3363
+ }
3364
+ );
3365
+ }
3366
+ var AVATARS = [
3367
+ {
3368
+ src: "https://pbs.twimg.com/profile_images/1534700564810018816/anAuSfkp_400x400.jpg",
3369
+ fallback: "JH",
3370
+ tooltip: "Jhey"
3371
+ },
3372
+ {
3373
+ src: "https://pbs.twimg.com/profile_images/1927474594102784000/Al0g-I6o_400x400.jpg",
3374
+ fallback: "DH",
3375
+ tooltip: "David Haz"
3376
+ }
3377
+ ];
3378
+ var Collaborators = () => {
3379
+ return /* @__PURE__ */ jsx(AvatarGroup2, { children: AVATARS.map((avatar, index) => /* @__PURE__ */ jsxs(Avatar, { className: "size-8 border-3 border-background", children: [
3380
+ /* @__PURE__ */ jsx(AvatarImage, { src: avatar.src }),
3381
+ /* @__PURE__ */ jsx(AvatarFallback, { children: avatar.fallback }),
3382
+ /* @__PURE__ */ jsx(AvatarGroupTooltip2, { children: avatar.tooltip })
3383
+ ] }, index)) });
3384
+ };
3385
+ var Share = () => {
3386
+ const [access, setAccess] = React18.useState("private");
3387
+ const [copied, setCopied] = React18.useState(false);
3388
+ const shareUrl = typeof window !== "undefined" ? window.location.href : "";
3389
+ const shareButton = useMsg("share.button");
3390
+ const shareTitle = useMsg("share.title");
3391
+ const peopleWithAccess = useMsg("share.people-with-access");
3392
+ const generalAccess = useMsg("share.general-access");
3393
+ const copyLabel = useMsg("share.copy");
3394
+ const copiedLabel = useMsg("share.copied");
3395
+ const ACCESS_OPTIONS = [
3396
+ {
3397
+ value: "private",
3398
+ label: useMsg("share.access.private.label"),
3399
+ description: useMsg("share.access.private.description"),
3400
+ icon: /* @__PURE__ */ jsx(Lock, { className: "w-4 h-4" })
3401
+ },
3402
+ {
3403
+ value: "anyone-view",
3404
+ label: useMsg("share.access.anyone-view.label"),
3405
+ description: useMsg("share.access.anyone-view.description"),
3406
+ icon: /* @__PURE__ */ jsx(Globe, { className: "w-4 h-4" })
3407
+ },
3408
+ {
3409
+ value: "anyone-edit",
3410
+ label: useMsg("share.access.anyone-edit.label"),
3411
+ description: useMsg("share.access.anyone-edit.description"),
3412
+ icon: /* @__PURE__ */ jsx(Users, { className: "w-4 h-4" })
3413
+ }
3414
+ ];
3415
+ const handleCopy = () => {
3416
+ navigator.clipboard.writeText(shareUrl).then(() => {
3417
+ setCopied(true);
3418
+ setTimeout(() => setCopied(false), 2e3);
3419
+ });
3420
+ };
3421
+ return /* @__PURE__ */ jsxs(Popover2, { children: [
3422
+ /* @__PURE__ */ jsxs(PopoverTrigger2, { render: /* @__PURE__ */ jsx(Button, { size: "sm", variant: "outline" }), children: [
3423
+ /* @__PURE__ */ jsx(Link2, { className: "h-4 w-4" }),
3424
+ shareButton
3425
+ ] }),
3426
+ /* @__PURE__ */ jsxs(PopoverPanel, { side: "bottom", align: "end", className: "w-80 flex flex-col gap-3", children: [
3427
+ /* @__PURE__ */ jsx(PopoverTitle2, { className: "text-base font-medium", children: shareTitle }),
3428
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
3429
+ /* @__PURE__ */ jsx("span", { className: "text-sm text-muted-foreground", children: peopleWithAccess }),
3430
+ /* @__PURE__ */ jsx(Collaborators, {})
3431
+ ] }),
3432
+ /* @__PURE__ */ jsx(Separator, {}),
3433
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1", children: [
3434
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-medium", children: generalAccess }),
3435
+ ACCESS_OPTIONS.map((opt) => /* @__PURE__ */ jsxs(
3436
+ "button",
3437
+ {
3438
+ onClick: () => setAccess(opt.value),
3439
+ className: "flex items-center gap-3 rounded-lg px-3 py-2 text-left transition-colors hover:bg-muted",
3440
+ children: [
3441
+ /* @__PURE__ */ jsx("span", { className: "flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-muted text-muted-foreground", children: opt.icon }),
3442
+ /* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
3443
+ /* @__PURE__ */ jsx("p", { className: "text-sm font-medium leading-none", children: opt.label }),
3444
+ /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground mt-0.5", children: opt.description })
3445
+ ] }),
3446
+ access === opt.value && /* @__PURE__ */ jsx(Check, { className: "w-4 h-4 text-primary shrink-0" })
3447
+ ]
3448
+ },
3449
+ opt.value
3450
+ ))
3451
+ ] }),
3452
+ /* @__PURE__ */ jsx(Separator, {}),
3453
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
3454
+ /* @__PURE__ */ jsx(Input, { readOnly: true, value: shareUrl, className: "flex-1 text-xs" }),
3455
+ /* @__PURE__ */ jsxs(Button, { size: "sm", variant: "outline", onClick: handleCopy, className: "shrink-0", children: [
3456
+ copied ? /* @__PURE__ */ jsx(Check, { className: "h-4 w-4" }) : /* @__PURE__ */ jsx(Link2, { className: "h-4 w-4" }),
3457
+ copied ? copiedLabel : copyLabel
3458
+ ] })
3459
+ ] })
3460
+ ] })
3461
+ ] });
3462
+ };
3463
+ var AVATARS2 = [
3464
+ {
3465
+ src: "https://pbs.twimg.com/profile_images/1534700564810018816/anAuSfkp_400x400.jpg",
3466
+ fallback: "JH",
3467
+ name: "Jhey",
3468
+ role: "Editor (you)"
3469
+ },
3470
+ {
3471
+ src: "https://pbs.twimg.com/profile_images/1927474594102784000/Al0g-I6o_400x400.jpg",
3472
+ fallback: "DH",
3473
+ name: "David Haz",
3474
+ role: "Editor"
3475
+ }
3476
+ ];
3477
+ var [currentUser, ...others] = AVATARS2;
3478
+ var CollaboratorsPopover = () => {
3479
+ const collaboratorsTitle = useMsg("collaborators.title");
3480
+ return /* @__PURE__ */ jsxs(Popover2, { children: [
3481
+ /* @__PURE__ */ jsx(PopoverTrigger2, { render: /* @__PURE__ */ jsx("div", {}), nativeButton: false, children: /* @__PURE__ */ jsx("button", { className: "rounded-full ring-2 ring-background focus:outline-none focus-visible:ring-primary", children: /* @__PURE__ */ jsxs(Avatar, { className: "size-6", children: [
3482
+ /* @__PURE__ */ jsx(AvatarImage, { src: currentUser.src }),
3483
+ /* @__PURE__ */ jsx(AvatarFallback, { children: currentUser.fallback })
3484
+ ] }) }) }),
3485
+ /* @__PURE__ */ jsxs(PopoverPanel, { side: "bottom", align: "end", className: "w-64 flex flex-col gap-3", children: [
3486
+ /* @__PURE__ */ jsx(PopoverTitle2, { className: "text-sm font-medium", children: collaboratorsTitle }),
3487
+ /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-2", children: [currentUser, ...others].map((user) => /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
3488
+ /* @__PURE__ */ jsxs(Avatar, { className: "size-8 shrink-0", children: [
3489
+ /* @__PURE__ */ jsx(AvatarImage, { src: user.src }),
3490
+ /* @__PURE__ */ jsx(AvatarFallback, { children: user.fallback })
3491
+ ] }),
3492
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
3493
+ /* @__PURE__ */ jsx("p", { className: "text-sm font-medium leading-none truncate", children: user.name }),
3494
+ /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground mt-0.5", children: user.role })
3495
+ ] })
3496
+ ] }, user.fallback)) })
3497
+ ] })
3498
+ ] });
3499
+ };
3500
+ var Header = () => {
3501
+ const { history, appState } = usePuck();
3502
+ const publish = useMsg("header.publish");
3503
+ const undo = useMsg("header.undo");
3504
+ const undoTooltip = useMsg("header.undo.tooltip");
3505
+ const redo = useMsg("header.redo");
3506
+ const redoTooltip = useMsg("header.redo.tooltip");
3507
+ const exportLabel = useMsg("header.export");
3508
+ const exportJson = useMsg("header.export.json");
3509
+ const theme = useTheme();
3510
+ const toggleTheme = useToggleTheme();
3511
+ const themeLightLabel = useMsg("header.theme.light");
3512
+ const themeDarkLabel = useMsg("header.theme.dark");
3513
+ const themeLabel = theme === "dark" ? themeLightLabel : themeDarkLabel;
3514
+ React18.useEffect(() => {
3515
+ document.documentElement.classList.toggle("dark", theme === "dark");
3516
+ }, []);
3517
+ return /* @__PURE__ */ jsx(TooltipProvider, { children: /* @__PURE__ */ jsxs("header", { className: "flex overflow-hidden transition-all duration-300 backdrop-blur-xl w-full border-b border-neutral-200 dark:border-neutral-800 items-center h-[54px] py-0", children: [
3518
+ /* @__PURE__ */ jsx("div", { className: "w-18 flex items-center justify-center h-full border-r border-neutral-200 dark:border-neutral-800/80", children: /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center w-10 h-10 bg-primary rounded-full", children: /* @__PURE__ */ jsx(AnimatedAsana, { className: "text-white text-2xl" }) }) }),
3519
+ /* @__PURE__ */ jsxs("div", { className: "relative flex flex-1 w-full px-2", children: [
3520
+ /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "icon", children: /* @__PURE__ */ jsx(ArrowLeft, { className: "h-4 w-4" }) }),
3521
+ /* @__PURE__ */ jsx("div", { className: "absolute inset-0 flex items-center justify-center pointer-events-none text-sm font-medium", children: appState?.data?.root?.props?.title || "" }),
3522
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-center gap-2 h-full ml-auto", children: [
3523
+ /* @__PURE__ */ jsxs(Tooltip, { children: [
3524
+ /* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
3525
+ Button,
3526
+ {
3527
+ variant: "ghost",
3528
+ size: "icon",
3529
+ disabled: !history.hasPast,
3530
+ onClick: () => history.back(),
3531
+ "aria-label": undo,
3532
+ children: /* @__PURE__ */ jsx(Undo2, { className: "h-4 w-4" })
3533
+ }
3534
+ ) }),
3535
+ /* @__PURE__ */ jsx(TooltipContent, { children: undoTooltip })
3536
+ ] }),
3537
+ /* @__PURE__ */ jsxs(Tooltip, { children: [
3538
+ /* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
3539
+ Button,
3540
+ {
3541
+ variant: "ghost",
3542
+ size: "icon",
3543
+ disabled: !history.hasFuture,
3544
+ onClick: () => history.forward(),
3545
+ "aria-label": redo,
3546
+ children: /* @__PURE__ */ jsx(Redo2, { className: "h-4 w-4" })
3547
+ }
3548
+ ) }),
3549
+ /* @__PURE__ */ jsx(TooltipContent, { children: redoTooltip })
3550
+ ] }),
3551
+ /* @__PURE__ */ jsx(Separator, { orientation: "vertical", className: "h-5 mx-1" }),
3552
+ /* @__PURE__ */ jsx(CollaboratorsPopover, {}),
3553
+ /* @__PURE__ */ jsx(Separator, { orientation: "vertical", className: "h-5 mx-1" }),
3554
+ /* @__PURE__ */ jsx(Share, {}),
3555
+ /* @__PURE__ */ jsx(Separator, { orientation: "vertical", className: "h-5 mx-1" }),
3556
+ /* @__PURE__ */ jsxs(DropdownMenu, { children: [
3557
+ /* @__PURE__ */ jsxs(Tooltip, { children: [
3558
+ /* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx(DropdownMenuTrigger, { render: /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "icon", "aria-label": exportLabel }), children: /* @__PURE__ */ jsx(Download, { className: "h-4 w-4" }) }) }),
3559
+ /* @__PURE__ */ jsx(TooltipContent, { children: exportLabel })
3560
+ ] }),
3561
+ /* @__PURE__ */ jsx(DropdownMenuContent, { align: "end", children: /* @__PURE__ */ jsxs(
3562
+ DropdownMenuItem,
3563
+ {
3564
+ onClick: () => {
3565
+ const json = JSON.stringify(appState.data, null, 2);
3566
+ const blob = new Blob([json], { type: "application/json" });
3567
+ const url = URL.createObjectURL(blob);
3568
+ const a = document.createElement("a");
3569
+ a.href = url;
3570
+ a.download = `${appState.data.root?.props?.title || "page"}.json`;
3571
+ a.click();
3572
+ URL.revokeObjectURL(url);
3573
+ },
3574
+ children: [
3575
+ /* @__PURE__ */ jsx(FileDown, { className: "h-4 w-4" }),
3576
+ " ",
3577
+ exportJson
3578
+ ]
3579
+ }
3580
+ ) })
3581
+ ] }),
3582
+ /* @__PURE__ */ jsx(Separator, { orientation: "vertical", className: "h-5 mx-1" }),
3583
+ /* @__PURE__ */ jsxs(Tooltip, { children: [
3584
+ /* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "icon", onClick: toggleTheme, "aria-label": themeLabel, children: theme === "dark" ? /* @__PURE__ */ jsx(Sun, { className: "h-4 w-4" }) : /* @__PURE__ */ jsx(Moon, { className: "h-4 w-4" }) }) }),
3585
+ /* @__PURE__ */ jsx(TooltipContent, { children: themeLabel })
3586
+ ] }),
3587
+ /* @__PURE__ */ jsx(Separator, { orientation: "vertical", className: "h-5 mx-1" }),
3588
+ /* @__PURE__ */ jsxs(Tooltip, { children: [
3589
+ /* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(Button, { size: "sm", "aria-label": publish, children: [
3590
+ /* @__PURE__ */ jsx(Send, { className: "h-4 w-4" }),
3591
+ publish
3592
+ ] }) }),
3593
+ /* @__PURE__ */ jsx(TooltipContent, { children: publish })
3594
+ ] })
3595
+ ] })
3596
+ ] })
3597
+ ] }) });
3598
+ };
3599
+ var DEFAULT_SEEDS = [
3600
+ "forest",
3601
+ "ocean",
3602
+ "mountain",
3603
+ "city",
3604
+ "food",
3605
+ "travel",
3606
+ "abstract",
3607
+ "people",
3608
+ "architecture",
3609
+ "animals",
3610
+ "technology",
3611
+ "minimal"
3612
+ ];
3613
+ function picsumUrl(seed, w = 960, h = 960) {
3614
+ return `https://picsum.photos/seed/${encodeURIComponent(seed)}/${w}/${h}`;
3615
+ }
3616
+ function getDefaultImages(seeds) {
3617
+ return seeds.map((seed, i) => ({
3618
+ src: picsumUrl(seed),
3619
+ alt: seed,
3620
+ id: `${seed}-${i}`
3621
+ }));
3622
+ }
3623
+ function getSearchImages(query) {
3624
+ return Array.from({ length: 12 }, (_, i) => ({
3625
+ src: picsumUrl(`${query}-${i}`),
3626
+ alt: `${query} ${i + 1}`,
3627
+ id: `${query}-${i}`
3628
+ }));
3629
+ }
3630
+ var ghostEl = null;
3631
+ function createGhost(src) {
3632
+ const el = document.createElement("div");
3633
+ el.style.cssText = `
3634
+ position: fixed; top: -9999px; left: -9999px; z-index: 99999;
3635
+ width: 80px; height: 60px; border-radius: 6px; overflow: hidden;
3636
+ box-shadow: 0 4px 12px rgba(0,0,0,0.3); pointer-events: none;
3637
+ `;
3638
+ const img = document.createElement("img");
3639
+ img.src = src;
3640
+ img.style.cssText = "width:100%;height:100%;object-fit:cover;";
3641
+ el.appendChild(img);
3642
+ document.body.appendChild(el);
3643
+ return el;
3644
+ }
3645
+ function moveGhost(x, y) {
3646
+ if (!ghostEl) return;
3647
+ ghostEl.style.left = `${x + 12}px`;
3648
+ ghostEl.style.top = `${y + 12}px`;
3649
+ }
3650
+ function removeGhost() {
3651
+ ghostEl?.remove();
3652
+ ghostEl = null;
3653
+ }
3654
+ function ImageLibrary({
3655
+ seeds,
3656
+ items: customImages
3657
+ } = {}) {
3658
+ const effectiveSeeds = seeds ?? DEFAULT_SEEDS;
3659
+ const [query, setQuery] = React18.useState("");
3660
+ const [committed, setCommitted] = React18.useState("");
3661
+ const [items, setItems] = React18.useState(
3662
+ () => customImages ?? getDefaultImages(effectiveSeeds)
3663
+ );
3664
+ const libraryTitle = useMsg("image-library.title");
3665
+ const searchPlaceholder = useMsg("image-library.search.placeholder");
3666
+ React18.useEffect(() => {
3667
+ const t = setTimeout(() => setCommitted(query.trim()), 400);
3668
+ return () => clearTimeout(t);
3669
+ }, [query]);
3670
+ React18.useEffect(() => {
3671
+ if (customImages) return;
3672
+ setItems(
3673
+ committed ? getSearchImages(committed) : getDefaultImages(effectiveSeeds)
3674
+ );
3675
+ }, [committed, customImages, effectiveSeeds]);
3676
+ function handlePointerDown(e, src) {
3677
+ e.stopPropagation();
3678
+ e.currentTarget.setPointerCapture(e.pointerId);
3679
+ ghostEl = createGhost(src);
3680
+ moveGhost(e.clientX, e.clientY);
3681
+ window.dispatchEvent(
3682
+ new CustomEvent("anvilkit:librarydragstart", {
3683
+ detail: { type: "image" }
3684
+ })
3685
+ );
3686
+ function onMove(ev) {
3687
+ moveGhost(ev.clientX, ev.clientY);
3688
+ }
3689
+ function onUp(ev) {
3690
+ removeGhost();
3691
+ window.removeEventListener("pointermove", onMove);
3692
+ window.removeEventListener("pointerup", onUp);
3693
+ window.dispatchEvent(
3694
+ new CustomEvent("anvilkit:imagedrop", {
3695
+ detail: { src, clientX: ev.clientX, clientY: ev.clientY }
3696
+ })
3697
+ );
3698
+ }
3699
+ window.addEventListener("pointermove", onMove);
3700
+ window.addEventListener("pointerup", onUp);
3701
+ }
3702
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-col h-full", children: [
3703
+ /* @__PURE__ */ jsx("div", { className: "px-3 pt-3 pb-1 text-xs font-semibold text-muted-foreground uppercase tracking-wider", children: libraryTitle }),
3704
+ /* @__PURE__ */ jsx("div", { className: "p-2", children: /* @__PURE__ */ jsxs("div", { className: "relative", children: [
3705
+ /* @__PURE__ */ jsx(Search, { className: "absolute left-2 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" }),
3706
+ /* @__PURE__ */ jsx(
3707
+ Input,
3708
+ {
3709
+ className: "pl-8",
3710
+ placeholder: searchPlaceholder,
3711
+ value: query,
3712
+ onChange: (e) => setQuery(e.target.value)
3713
+ }
3714
+ )
3715
+ ] }) }),
3716
+ /* @__PURE__ */ jsx(ScrollArea, { className: "flex-1", children: /* @__PURE__ */ jsx("div", { className: "p-2 grid grid-cols-2 gap-2", children: items.map((item, i) => /* @__PURE__ */ jsxs(
3717
+ "div",
3718
+ {
3719
+ onPointerDown: (e) => handlePointerDown(e, item.src),
3720
+ className: "rounded-md overflow-hidden border border-border bg-muted/40 cursor-grab select-none hover:ring-2 hover:ring-primary/50 active:cursor-grabbing transition-all",
3721
+ children: [
3722
+ /* @__PURE__ */ jsx(
3723
+ "img",
3724
+ {
3725
+ src: item.src,
3726
+ alt: item.alt,
3727
+ className: "w-full h-20 object-cover pointer-events-none",
3728
+ loading: "lazy"
3729
+ }
3730
+ ),
3731
+ /* @__PURE__ */ jsx("div", { className: "px-1.5 py-1 text-xs text-muted-foreground truncate capitalize", children: item.alt })
3732
+ ]
3733
+ },
3734
+ `${item.src}-${i}`
3735
+ )) }) })
3736
+ ] });
3737
+ }
3738
+ var ghostEl2 = null;
3739
+ function createGhost2(text) {
3740
+ const el = document.createElement("div");
3741
+ el.style.cssText = `
3742
+ position: fixed; top: -9999px; left: -9999px; z-index: 99999;
3743
+ max-width: 200px; padding: 6px 10px; border-radius: 6px;
3744
+ background: #1e1e2e; color: #fff; font-size: 12px; line-height: 1.4;
3745
+ box-shadow: 0 4px 12px rgba(0,0,0,0.3); pointer-events: none;
3746
+ white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
3747
+ `;
3748
+ el.textContent = text.length > 40 ? text.slice(0, 40) + "\u2026" : text;
3749
+ document.body.appendChild(el);
3750
+ return el;
3751
+ }
3752
+ function moveGhost2(x, y) {
3753
+ if (!ghostEl2) return;
3754
+ ghostEl2.style.left = `${x + 12}px`;
3755
+ ghostEl2.style.top = `${y + 12}px`;
3756
+ }
3757
+ function removeGhost2() {
3758
+ ghostEl2?.remove();
3759
+ ghostEl2 = null;
3760
+ }
3761
+ var DEFAULT_SNIPPETS = [
3762
+ // Headlines
3763
+ {
3764
+ category: "Headlines",
3765
+ label: "Bold statement",
3766
+ text: "The Future Starts Here"
3767
+ },
3768
+ {
3769
+ category: "Headlines",
3770
+ label: "Question hook",
3771
+ text: "Ready to Transform Your Business?"
3772
+ },
3773
+ {
3774
+ category: "Headlines",
3775
+ label: "Value prop",
3776
+ text: "Simple, Powerful, Built for Teams"
3777
+ },
3778
+ {
3779
+ category: "Headlines",
3780
+ label: "Action-led",
3781
+ text: "Ship Faster. Break Less. Sleep Better."
3782
+ },
3783
+ // Subheadings
3784
+ {
3785
+ category: "Subheadings",
3786
+ label: "Feature intro",
3787
+ text: "Everything you need, nothing you don't."
3788
+ },
3789
+ {
3790
+ category: "Subheadings",
3791
+ label: "Social proof",
3792
+ text: "Trusted by over 10,000 teams worldwide."
3793
+ },
3794
+ {
3795
+ category: "Subheadings",
3796
+ label: "CTA support",
3797
+ text: "Get started in minutes \u2014 no credit card required."
3798
+ },
3799
+ // Body
3800
+ {
3801
+ category: "Body",
3802
+ label: "Product description",
3803
+ text: "Our platform helps teams collaborate in real time, ship products faster, and stay aligned across every stage of the process."
3804
+ },
3805
+ {
3806
+ category: "Body",
3807
+ label: "Feature benefit",
3808
+ text: "With built-in analytics and smart automation, you can focus on what matters most \u2014 building great products."
3809
+ },
3810
+ {
3811
+ category: "Body",
3812
+ label: "About us",
3813
+ text: "We're a small team on a big mission: to make software development feel effortless for everyone."
3814
+ },
3815
+ // CTAs
3816
+ { category: "CTAs", label: "Primary", text: "Get Started Free" },
3817
+ { category: "CTAs", label: "Secondary", text: "Learn More" },
3818
+ { category: "CTAs", label: "Soft sell", text: "See How It Works" },
3819
+ { category: "CTAs", label: "Urgency", text: "Start Your Free Trial Today" }
3820
+ ];
3821
+ function CopyLibrary({
3822
+ items
3823
+ } = {}) {
3824
+ const activeSnippets = items ?? DEFAULT_SNIPPETS;
3825
+ const categories = Array.from(new Set(activeSnippets.map((s) => s.category)));
3826
+ const [query, setQuery] = React18.useState("");
3827
+ const libraryTitle = useMsg("copy-library.title");
3828
+ const searchPlaceholder = useMsg("copy-library.search.placeholder");
3829
+ const filtered = query.trim() ? activeSnippets.filter(
3830
+ (s) => s.text.toLowerCase().includes(query.toLowerCase()) || s.label.toLowerCase().includes(query.toLowerCase()) || s.category.toLowerCase().includes(query.toLowerCase())
3831
+ ) : activeSnippets;
3832
+ function handlePointerDown(e, text) {
3833
+ e.stopPropagation();
3834
+ e.currentTarget.setPointerCapture(e.pointerId);
3835
+ ghostEl2 = createGhost2(text);
3836
+ moveGhost2(e.clientX, e.clientY);
3837
+ window.dispatchEvent(
3838
+ new CustomEvent("anvilkit:librarydragstart", {
3839
+ detail: { type: "text" }
3840
+ })
3841
+ );
3842
+ function onMove(ev) {
3843
+ moveGhost2(ev.clientX, ev.clientY);
3844
+ }
3845
+ function onUp(ev) {
3846
+ removeGhost2();
3847
+ window.removeEventListener("pointermove", onMove);
3848
+ window.removeEventListener("pointerup", onUp);
3849
+ window.dispatchEvent(
3850
+ new CustomEvent("anvilkit:textdrop", {
3851
+ detail: { text, clientX: ev.clientX, clientY: ev.clientY }
3852
+ })
3853
+ );
3854
+ }
3855
+ window.addEventListener("pointermove", onMove);
3856
+ window.addEventListener("pointerup", onUp);
3857
+ }
3858
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-col h-full", children: [
3859
+ /* @__PURE__ */ jsx("div", { className: "px-3 pt-3 pb-1 text-xs font-semibold text-muted-foreground uppercase tracking-wider", children: libraryTitle }),
3860
+ /* @__PURE__ */ jsx("div", { className: "p-2", children: /* @__PURE__ */ jsxs("div", { className: "relative", children: [
3861
+ /* @__PURE__ */ jsx(Search, { className: "absolute left-2 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" }),
3862
+ /* @__PURE__ */ jsx(
3863
+ Input,
3864
+ {
3865
+ className: "pl-8",
3866
+ placeholder: searchPlaceholder,
3867
+ value: query,
3868
+ onChange: (e) => setQuery(e.target.value)
3869
+ }
3870
+ )
3871
+ ] }) }),
3872
+ /* @__PURE__ */ jsx(ScrollArea, { className: "flex-1", children: /* @__PURE__ */ jsx("div", { className: "p-2 flex flex-col gap-4", children: (query.trim() ? [null] : categories).map((cat) => {
3873
+ const items2 = cat ? filtered.filter((s) => s.category === cat) : filtered;
3874
+ if (!items2.length) return null;
3875
+ return /* @__PURE__ */ jsxs("div", { children: [
3876
+ cat && /* @__PURE__ */ jsxs("div", { className: "px-1 pb-1 text-xs font-semibold text-muted-foreground uppercase tracking-wider flex items-center gap-1", children: [
3877
+ /* @__PURE__ */ jsx(Type, { className: "h-3 w-3" }),
3878
+ cat
3879
+ ] }),
3880
+ /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-1", children: items2.map((snippet, i) => /* @__PURE__ */ jsxs(
3881
+ "div",
3882
+ {
3883
+ onPointerDown: (e) => handlePointerDown(e, snippet.text),
3884
+ className: "rounded-md border border-border bg-muted/40 px-2.5 py-2 cursor-grab select-none hover:bg-muted hover:ring-1 hover:ring-primary/40 active:cursor-grabbing transition-all",
3885
+ children: [
3886
+ /* @__PURE__ */ jsx("div", { className: "text-xs font-medium text-foreground/70 mb-0.5", children: snippet.label }),
3887
+ /* @__PURE__ */ jsx("div", { className: "text-xs text-foreground leading-snug line-clamp-2", children: snippet.text })
3888
+ ]
3889
+ },
3890
+ i
3891
+ )) })
3892
+ ] }, cat ?? "results");
3893
+ }) }) })
3894
+ ] });
3895
+ }
3896
+ var EditorLayout = ({
3897
+ aiPanel,
3898
+ images,
3899
+ copywritings
3900
+ }) => {
3901
+ const activeTab = useActiveTab();
3902
+ return /* @__PURE__ */ jsxs("div", { className: "max-sm:hidden overflow-hidden h-screen", children: [
3903
+ /* @__PURE__ */ jsx(Header, {}),
3904
+ /* @__PURE__ */ jsxs(motion.div, { className: "relative w-full flex border-r border-neutral-200 dark:border-neutral-800/80 bg-white/60 dark:bg-neutral-950/80 backdrop-blur-md ml-0 h-[calc(100vh-54px)]", children: [
3905
+ /* @__PURE__ */ jsx(Aside, {}),
3906
+ /* @__PURE__ */ jsxs("div", { className: "relative h-full flex flex-col overflow-x-hidden overflow-y-auto border-r border-neutral-200 dark:border-neutral-800/80 w-[240px]", children: [
3907
+ activeTab === "insert" && /* @__PURE__ */ jsx(Puck.Components, {}),
3908
+ activeTab === "layer" && /* @__PURE__ */ jsx(Puck.Outline, {}),
3909
+ activeTab === "image" && /* @__PURE__ */ jsx(ImageLibrary, { ...images }),
3910
+ activeTab === "text" && /* @__PURE__ */ jsx(CopyLibrary, { ...copywritings }),
3911
+ activeTab === "copilot" && aiPanel
3912
+ ] }),
3913
+ /* @__PURE__ */ jsx("div", { className: "flex-1 h-full overflow-hidden", children: /* @__PURE__ */ jsx(Puck.Preview, {}) }),
3914
+ /* @__PURE__ */ jsx("div", { className: "border-l border-neutral-200 dark:border-neutral-800/80 w-[240px]", children: /* @__PURE__ */ jsx(Puck.Fields, {}) })
3915
+ ] })
3916
+ ] });
3917
+ };
3918
+ function createEditorUiStore(storeId) {
3919
+ return createStore()(
3920
+ persist(
3921
+ (set) => ({
3922
+ drawerSearch: "",
3923
+ setDrawerSearch: (q) => set({ drawerSearch: q }),
3924
+ drawerCollapsed: {},
3925
+ toggleDrawerGroup: (group) => set((s) => ({
3926
+ drawerCollapsed: { ...s.drawerCollapsed, [group]: !s.drawerCollapsed[group] }
3927
+ })),
3928
+ activeTab: "insert",
3929
+ setActiveTab: (tab) => set({ activeTab: tab }),
3930
+ outlineExpanded: {},
3931
+ toggleOutlineItem: (id) => set((s) => ({
3932
+ outlineExpanded: { ...s.outlineExpanded, [id]: !s.outlineExpanded[id] }
3933
+ })),
3934
+ theme: "light",
3935
+ toggleTheme: () => set((s) => {
3936
+ const next = s.theme === "light" ? "dark" : "light";
3937
+ document.documentElement.classList.toggle("dark", next === "dark");
3938
+ return { theme: next };
3939
+ })
3940
+ }),
3941
+ {
3942
+ name: `anvilkit-ui-${storeId}`,
3943
+ partialize: (s) => ({
3944
+ activeTab: s.activeTab,
3945
+ drawerCollapsed: s.drawerCollapsed,
3946
+ outlineExpanded: s.outlineExpanded,
3947
+ theme: s.theme
3948
+ // drawerSearch intentionally excluded — transient input
3949
+ })
3950
+ }
3951
+ )
3952
+ );
3953
+ }
3954
+ function createEditorI18nStore(initial) {
3955
+ return createStore()(
3956
+ persist(
3957
+ (set) => ({
3958
+ locale: initial.locale,
3959
+ messages: initial.messages,
3960
+ setLocale: (locale, messages) => set({ locale, messages })
3961
+ }),
3962
+ {
3963
+ name: "anvilkit-i18n",
3964
+ // Only persist locale — messages are re-loaded from props on mount
3965
+ partialize: (s) => ({ locale: s.locale })
3966
+ }
3967
+ )
3968
+ );
3969
+ }
3970
+
3971
+ // src/i18n/zh.ts
3972
+ var defaultMessages = {
3973
+ "header.publish": "\u53D1\u5E03",
3974
+ "header.undo": "\u64A4\u9500",
3975
+ "header.undo.tooltip": "\u64A4\u9500 (Ctrl+Z)",
3976
+ "header.redo": "\u91CD\u505A",
3977
+ "header.redo.tooltip": "\u91CD\u505A (Ctrl+Y)",
3978
+ "header.export": "\u5BFC\u51FA",
3979
+ "header.export.json": "\u5BFC\u51FA JSON",
3980
+ "drawer.title": "\u7EC4\u4EF6\u5E93",
3981
+ "drawer.search.placeholder": "\u641C\u7D22\u7EC4\u4EF6...",
3982
+ "aside.insert": "\u63D2\u5165",
3983
+ "aside.layer": "\u56FE\u5C42",
3984
+ "aside.image": "\u56FE\u7247",
3985
+ "aside.text": "\u6587\u672C",
3986
+ "aside.copilot": "AI \u52A9\u624B",
3987
+ "image-library.title": "\u56FE\u7247\u5E93",
3988
+ "image-library.search.placeholder": "\u641C\u7D22\u56FE\u7247...",
3989
+ "copy-library.title": "\u6587\u6848\u5E93",
3990
+ "copy-library.search.placeholder": "\u641C\u7D22\u6587\u6848...",
3991
+ "share.button": "\u5206\u4EAB",
3992
+ "share.title": "\u5206\u4EAB",
3993
+ "share.people-with-access": "\u6709\u6743\u9650\u7684\u4EBA",
3994
+ "share.general-access": "\u901A\u7528\u8BBF\u95EE",
3995
+ "share.copy": "\u590D\u5236",
3996
+ "share.copied": "\u5DF2\u590D\u5236",
3997
+ "share.access.private.label": "\u79C1\u5BC6",
3998
+ "share.access.private.description": "\u4EC5\u53D7\u9080\u4EBA\u5458\u53EF\u8BBF\u95EE",
3999
+ "share.access.anyone-view.label": "\u4EFB\u4F55\u6709\u94FE\u63A5\u7684\u4EBA\u53EF\u67E5\u770B",
4000
+ "share.access.anyone-view.description": "\u4EFB\u4F55\u6709\u94FE\u63A5\u7684\u4EBA\u5747\u53EF\u67E5\u770B",
4001
+ "share.access.anyone-edit.label": "\u4EFB\u4F55\u6709\u94FE\u63A5\u7684\u4EBA\u53EF\u7F16\u8F91",
4002
+ "share.access.anyone-edit.description": "\u4EFB\u4F55\u6709\u94FE\u63A5\u7684\u4EBA\u5747\u53EF\u7F16\u8F91",
4003
+ "collaborators.title": "\u534F\u4F5C\u8005",
4004
+ "collaborators.role.editor": "\u7F16\u8F91\u8005",
4005
+ "collaborators.role.editor-you": "\u7F16\u8F91\u8005\uFF08\u4F60\uFF09",
4006
+ "richtext.bold": "\u52A0\u7C97",
4007
+ "richtext.italic": "\u659C\u4F53",
4008
+ "richtext.link": "\u94FE\u63A5",
4009
+ "richtext.link.prompt": "URL\uFF1A",
4010
+ "header.theme.light": "\u6D45\u8272\u6A21\u5F0F",
4011
+ "header.theme.dark": "\u6DF1\u8272\u6A21\u5F0F"
4012
+ };
4013
+ function Studio({
4014
+ config,
4015
+ data,
4016
+ onPublish,
4017
+ onChange,
4018
+ overrideExtensions,
4019
+ aiHost,
4020
+ className,
4021
+ images,
4022
+ copywritings,
4023
+ storeId,
4024
+ locale,
4025
+ messages
4026
+ }) {
4027
+ const uiStore2 = React18.useRef(createEditorUiStore(storeId ?? "default")).current;
4028
+ const i18nStore = React18.useRef(
4029
+ createEditorI18nStore({
4030
+ locale: locale ?? "zh",
4031
+ messages: messages ?? defaultMessages
4032
+ })
4033
+ ).current;
4034
+ const aiPlugin = React18.useMemo(
4035
+ () => createAiPlugin({ host: aiHost }),
4036
+ [aiHost]
4037
+ );
4038
+ const mergedOverrides = React18.useMemo(
4039
+ () => ({ ...aiPlugin.overrides, ...puckOverrides, ...overrideExtensions }),
4040
+ [aiPlugin.overrides, overrideExtensions]
4041
+ );
4042
+ return /* @__PURE__ */ jsx(EditorUiStoreProvider, { value: uiStore2, children: /* @__PURE__ */ jsx(EditorI18nStoreProvider, { value: i18nStore, children: /* @__PURE__ */ jsx("div", { className, children: /* @__PURE__ */ jsx(
4043
+ Puck,
4044
+ {
4045
+ config,
4046
+ data,
4047
+ onPublish,
4048
+ onChange,
4049
+ overrides: mergedOverrides,
4050
+ plugins: [aiPlugin],
4051
+ children: /* @__PURE__ */ jsx(
4052
+ EditorLayout,
4053
+ {
4054
+ aiPanel: aiPlugin.render(),
4055
+ images,
4056
+ copywritings
4057
+ }
4058
+ )
4059
+ }
4060
+ ) }) }) });
4061
+ }
4062
+
4063
+ // src/store/index.ts
4064
+ var uiStore = createEditorUiStore("default");
4065
+
4066
+ export { Studio, createEditorI18nStore, createEditorUiStore, puckOverrides, uiStore };