@anvilkit/puck-studio 0.0.1 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,118 +1,50 @@
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';
1
+ import { Search, Plus, Bold, Italic, Link, ChevronRightIcon, Info, Sparkles, ChevronDownIcon, CheckIcon, GripVertical, Copy, Trash2, SearchIcon, ChevronUpIcon, ArrowLeft, Undo2, Redo2, Download, FileDown, Sun, Moon, Send, Layers2, Image, Type, Bot, Lock, Globe, Users, Link2, Check } from 'lucide-react';
9
2
  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';
3
+ import * as React17 from 'react';
4
+ import React17__default, { useState, useRef, useMemo, useCallback, useEffect } from 'react';
5
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
14
6
  import { ScrollArea as ScrollArea$1 } from '@base-ui/react/scroll-area';
7
+ import { clsx } from 'clsx';
8
+ import { twMerge } from 'tailwind-merge';
15
9
  import { Input as Input$1 } from '@base-ui/react/input';
10
+ import { usePuck, Puck, useGetPuck, AutoField } from '@puckeditor/core';
11
+ import { Separator as Separator$1 } from '@base-ui/react/separator';
12
+ import { Tooltip as Tooltip$1 } from '@base-ui/react/tooltip';
16
13
  import { mergeProps } from '@base-ui/react/merge-props';
17
14
  import { useRender } from '@base-ui/react/use-render';
15
+ import { Button as Button$1 } from '@base-ui/react/button';
16
+ import { cva } from 'class-variance-authority';
18
17
  import { Select as Select$1 } from '@base-ui/react/select';
19
18
  import { useSensors, useSensor, PointerSensor, DndContext, closestCenter } from '@dnd-kit/core';
20
19
  import { SortableContext, verticalListSortingStrategy, arrayMove, useSortable } from '@dnd-kit/sortable';
21
20
  import { CSS } from '@dnd-kit/utilities';
22
- import { Popover as Popover$1 } from '@base-ui-components/react/popover';
21
+ import { Popover as Popover$1 } from '@base-ui/react/popover';
23
22
  import { motion, AnimatePresence, LayoutGroup, isMotionComponent } from 'motion/react';
24
23
  import { Toggle as Toggle$1 } from '@base-ui/react/toggle';
25
24
  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
25
  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';
26
+ import { Tabs as Tabs$1 } from '@base-ui/react/tabs';
27
+ import { persist } from 'zustand/middleware';
28
+ import * as motion8 from 'motion/react-client';
32
29
  import { FloatingArrow, useFloating, autoUpdate, offset, flip, shift, arrow, FloatingPortal } from '@floating-ui/react';
33
30
  import { Avatar as Avatar$1 } from '@base-ui/react/avatar';
34
- import { persist } from 'zustand/middleware';
31
+ import { Menu } from '@base-ui/react/menu';
35
32
 
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
- }
33
+ // src/core/overrides/layout/EditorDrawer.tsx
102
34
  function getStrictContext(name) {
103
- const Context = React18.createContext(void 0);
104
- const Provider2 = ({
35
+ const Context = React17.createContext(void 0);
36
+ const Provider = ({
105
37
  value,
106
38
  children
107
39
  }) => /* @__PURE__ */ jsx(Context.Provider, { value, children });
108
40
  const useSafeContext = () => {
109
- const ctx = React18.useContext(Context);
41
+ const ctx = React17.useContext(Context);
110
42
  if (ctx === void 0) {
111
43
  throw new Error(`useContext must be used within ${name ?? "a Provider"}`);
112
44
  }
113
45
  return ctx;
114
46
  };
115
- return [Provider2, useSafeContext];
47
+ return [Provider, useSafeContext];
116
48
  }
117
49
 
118
50
  // src/store/ui-context.ts
@@ -143,137 +75,8 @@ function useToggleTheme() {
143
75
  function useMsg(key) {
144
76
  return useStore(useEditorI18nStoreApi(), (s) => s.messages[key] ?? key);
145
77
  }
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 }) });
78
+ function cn(...inputs) {
79
+ return twMerge(clsx(inputs));
277
80
  }
278
81
  function ScrollArea({
279
82
  className,
@@ -334,7 +137,7 @@ function Input({ className, type, ...props }) {
334
137
  type,
335
138
  "data-slot": "input",
336
139
  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",
140
+ "h-9 w-full min-w-0 rounded-md border border-input bg-transparent px-2.5 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 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:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 md:text-sm dark:bg-input/30 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40",
338
141
  className
339
142
  ),
340
143
  ...props
@@ -399,9 +202,9 @@ function DrawerItem({
399
202
  children,
400
203
  name
401
204
  }) {
402
- const { appState } = usePuck();
403
- const componentConfig = appState.config?.components?.[name];
404
- const thumbnail = componentConfig?.metadata?.thumbnail;
205
+ const { config } = usePuck();
206
+ const componentConfig = config.components?.[name];
207
+ const thumbnail = typeof componentConfig?.metadata?.thumbnail === "string" ? componentConfig.metadata.thumbnail : void 0;
405
208
  const src = thumbnail ?? getPlaceholderUrl(name);
406
209
  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
210
  /* @__PURE__ */ jsx("div", { className: "w-full h-16 bg-muted overflow-hidden", children: /* @__PURE__ */ jsx(
@@ -418,6 +221,24 @@ function DrawerItem({
418
221
  /* @__PURE__ */ jsx("div", { className: "px-2 py-1.5 text-xs font-medium truncate", children: name ?? children })
419
222
  ] });
420
223
  }
224
+ function Separator({
225
+ className,
226
+ orientation = "horizontal",
227
+ ...props
228
+ }) {
229
+ return /* @__PURE__ */ jsx(
230
+ Separator$1,
231
+ {
232
+ "data-slot": "separator",
233
+ orientation,
234
+ className: cn(
235
+ "shrink-0 bg-border data-horizontal:h-px data-horizontal:w-full data-vertical:w-px data-vertical:self-stretch",
236
+ className
237
+ ),
238
+ ...props
239
+ }
240
+ );
241
+ }
421
242
  function EditorOutline({
422
243
  children
423
244
  }) {
@@ -428,7 +249,7 @@ function EditorOutline({
428
249
  /* @__PURE__ */ jsxs("div", { className: "px-3 py-1.5 text-xs text-muted-foreground truncate", children: [
429
250
  "Selected:",
430
251
  " ",
431
- /* @__PURE__ */ jsx("span", { className: "font-medium text-foreground", children: selectedItem.type ?? "Component" })
252
+ /* @__PURE__ */ jsx("span", { className: "font-medium text-foreground", children: typeof selectedItem.type === "string" ? selectedItem.type : String(selectedItem.type) })
432
253
  ] }),
433
254
  /* @__PURE__ */ jsx(Separator, {})
434
255
  ] }),
@@ -460,6 +281,61 @@ var CANVAS_CSS = `
460
281
  }
461
282
  body { margin: 0; font-family: system-ui, sans-serif; }
462
283
  `;
284
+ var CANVAS_STYLE_ID = "__anvilkit_styles__";
285
+ function resolveDocument(target) {
286
+ if (target) return target;
287
+ if (typeof document === "undefined") return void 0;
288
+ return document;
289
+ }
290
+ function useThemeSync({
291
+ document: targetDocument,
292
+ injectCanvasCss = false
293
+ } = {}) {
294
+ const theme = useTheme();
295
+ React17.useEffect(() => {
296
+ const resolvedDocument = resolveDocument(targetDocument);
297
+ if (!resolvedDocument || !injectCanvasCss) return;
298
+ const existing = resolvedDocument.getElementById(CANVAS_STYLE_ID);
299
+ if (existing) existing.remove();
300
+ const style = resolvedDocument.createElement("style");
301
+ style.id = CANVAS_STYLE_ID;
302
+ style.textContent = CANVAS_CSS;
303
+ resolvedDocument.head.appendChild(style);
304
+ }, [targetDocument, injectCanvasCss]);
305
+ React17.useEffect(() => {
306
+ const resolvedDocument = resolveDocument(targetDocument);
307
+ if (!resolvedDocument) return;
308
+ resolvedDocument.documentElement.classList.toggle("dark", theme === "dark");
309
+ }, [targetDocument, theme]);
310
+ }
311
+
312
+ // src/features/library-dnd/drop-contract.ts
313
+ var LIBRARY_DRAG_START = "anvilkit:librarydragstart";
314
+ var IMAGE_DROP = "anvilkit:imagedrop";
315
+ var TEXT_DROP = "anvilkit:textdrop";
316
+ function createLibraryDragEvent(type, detail) {
317
+ return new CustomEvent(type, { detail });
318
+ }
319
+ function addLibraryDragEventListener(type, listener, target = window) {
320
+ const wrapped = (event) => {
321
+ listener(event);
322
+ };
323
+ target.addEventListener(type, wrapped);
324
+ return () => {
325
+ target.removeEventListener(type, wrapped);
326
+ };
327
+ }
328
+ function dispatchLibraryDragStart(type) {
329
+ window.dispatchEvent(createLibraryDragEvent(LIBRARY_DRAG_START, { type }));
330
+ }
331
+ function dispatchImageDrop(detail) {
332
+ window.dispatchEvent(createLibraryDragEvent(IMAGE_DROP, detail));
333
+ }
334
+ function dispatchTextDrop(detail) {
335
+ window.dispatchEvent(createLibraryDragEvent(TEXT_DROP, detail));
336
+ }
337
+
338
+ // src/features/library-dnd/replace-props.ts
463
339
  function isImageUrl(val) {
464
340
  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
341
  }
@@ -498,160 +374,187 @@ function replaceTextInProps(props, newText, targetText) {
498
374
  }
499
375
  return { result: props, replaced: false };
500
376
  }
501
- function CanvasIframe({
502
- children,
503
- document: iframeDoc
504
- }) {
377
+
378
+ // src/core/overrides/canvas/drop-targets.ts
379
+ var TEXT_TAGS = /* @__PURE__ */ new Set([
380
+ "P",
381
+ "H1",
382
+ "H2",
383
+ "H3",
384
+ "H4",
385
+ "H5",
386
+ "H6",
387
+ "SPAN",
388
+ "A",
389
+ "LI",
390
+ "BUTTON",
391
+ "LABEL"
392
+ ]);
393
+ function getIframeCoords(iframeEl, clientX, clientY) {
394
+ const rect = iframeEl.getBoundingClientRect();
395
+ const x = clientX - rect.left;
396
+ const y = clientY - rect.top;
397
+ if (x < 0 || y < 0 || x > rect.width || y > rect.height) {
398
+ return null;
399
+ }
400
+ return { x, y };
401
+ }
402
+ function getComponentElAt(iframeDoc, iframeEl, clientX, clientY) {
403
+ const coords = getIframeCoords(iframeEl, clientX, clientY);
404
+ if (!coords) return null;
405
+ const element = iframeDoc.elementFromPoint(coords.x, coords.y);
406
+ if (!element) return null;
407
+ return element.closest("[data-puck-component]");
408
+ }
409
+ function getImageElInComponent(compEl, iframeEl, clientX, clientY) {
410
+ const images = Array.from(compEl.querySelectorAll("img"));
411
+ if (!images.length) return null;
412
+ if (images.length === 1) return images[0];
413
+ const rect = iframeEl.getBoundingClientRect();
414
+ const x = clientX - rect.left;
415
+ const y = clientY - rect.top;
416
+ let closest = null;
417
+ let minDistance = Infinity;
418
+ for (const image of images) {
419
+ const imageRect = image.getBoundingClientRect();
420
+ const centerX = imageRect.left + imageRect.width / 2;
421
+ const centerY = imageRect.top + imageRect.height / 2;
422
+ const distance = Math.hypot(centerX - x, centerY - y);
423
+ if (distance < minDistance) {
424
+ minDistance = distance;
425
+ closest = image;
426
+ }
427
+ }
428
+ return closest;
429
+ }
430
+ function getTextElInComponent(iframeDoc, iframeEl, compEl, clientX, clientY) {
431
+ const coords = getIframeCoords(iframeEl, clientX, clientY);
432
+ if (!coords) return null;
433
+ const element = iframeDoc.elementFromPoint(coords.x, coords.y);
434
+ if (element && compEl.contains(element)) {
435
+ let current = element;
436
+ while (current && current !== iframeDoc.body) {
437
+ if (TEXT_TAGS.has(current.tagName) && current.textContent?.trim()) {
438
+ return current;
439
+ }
440
+ current = current.parentElement;
441
+ }
442
+ }
443
+ for (const tag of TEXT_TAGS) {
444
+ const candidate = compEl.querySelector(tag.toLowerCase());
445
+ if (candidate?.textContent?.trim()) {
446
+ return candidate;
447
+ }
448
+ }
449
+ return null;
450
+ }
451
+
452
+ // src/core/overrides/canvas/highlight.ts
453
+ function createElementHighlighter() {
454
+ let highlightedEl = null;
455
+ function set(el, color) {
456
+ if (highlightedEl && highlightedEl !== el) {
457
+ highlightedEl.style.outline = "";
458
+ highlightedEl.style.outlineOffset = "";
459
+ }
460
+ if (el) {
461
+ el.style.outline = `2px solid ${color}`;
462
+ el.style.outlineOffset = "2px";
463
+ }
464
+ highlightedEl = el;
465
+ }
466
+ function clear() {
467
+ set(null, "");
468
+ }
469
+ return { set, clear };
470
+ }
471
+
472
+ // src/core/overrides/canvas/useLibraryDropBridge.ts
473
+ function useLibraryDropBridge(iframeDoc) {
505
474
  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(() => {
475
+ React17.useEffect(() => {
521
476
  if (!iframeDoc) return;
522
477
  const iframeEl = iframeDoc.defaultView?.frameElement;
523
478
  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;
479
+ const iframeDocument = iframeDoc;
480
+ const frameElement = iframeEl;
481
+ let activeLibrary = null;
482
+ const highlighter = createElementHighlighter();
483
+ function dispatchReplace(componentId, updatedProps) {
484
+ const { dispatch, getItemById, getSelectorForId } = getPuck();
485
+ const item = getItemById(componentId);
486
+ const selector = getSelectorForId(componentId);
487
+ if (!item || !selector) return false;
488
+ dispatch({
489
+ type: "replace",
490
+ destinationIndex: selector.index,
491
+ destinationZone: selector.zone,
492
+ data: {
493
+ ...item,
494
+ props: { ...item.props, ...updatedProps }
571
495
  }
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, "");
496
+ });
497
+ return true;
592
498
  }
593
- let activeLibrary = null;
594
- function onLibraryDragStart(e) {
595
- activeLibrary = e.detail.type;
499
+ function onLibraryDragStart(type) {
500
+ activeLibrary = type;
596
501
  }
597
502
  function onPointerMove(e) {
598
503
  if (!activeLibrary) return;
599
- const compEl = getComponentElAt(e.clientX, e.clientY);
504
+ const compEl = getComponentElAt(iframeDocument, frameElement, e.clientX, e.clientY);
600
505
  if (!compEl) {
601
- clearHighlight();
506
+ highlighter.clear();
602
507
  return;
603
508
  }
604
509
  if (activeLibrary === "image") {
605
- const img = getImgInComponent(compEl, e.clientX, e.clientY);
606
- setHighlight(img, "#6366f1");
510
+ highlighter.set(
511
+ getImageElInComponent(compEl, frameElement, e.clientX, e.clientY),
512
+ "#6366f1"
513
+ );
607
514
  } else {
608
- const textEl = getTextElInComponent(compEl, e.clientX, e.clientY);
609
- setHighlight(textEl, "#f59e0b");
515
+ highlighter.set(
516
+ getTextElInComponent(iframeDocument, frameElement, compEl, e.clientX, e.clientY),
517
+ "#f59e0b"
518
+ );
610
519
  }
611
520
  }
612
521
  function onPointerUp() {
613
522
  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;
523
+ highlighter.clear();
628
524
  }
629
- function onImageDrop(e) {
630
- const { src, clientX, clientY } = e.detail;
631
- clearHighlight();
525
+ function onImageDrop(src, clientX, clientY) {
526
+ highlighter.clear();
632
527
  activeLibrary = null;
633
528
  if (!src) return;
634
- const compEl = getComponentElAt(clientX, clientY);
529
+ const compEl = getComponentElAt(iframeDocument, frameElement, clientX, clientY);
635
530
  if (!compEl) return;
636
- const componentId = compEl.getAttribute("data-puck-component");
637
- const { getItemById } = getPuck();
638
- const item = getItemById(componentId);
531
+ const componentId = compEl.dataset.puckComponent;
532
+ if (!componentId) return;
533
+ const item = getPuck().getItemById(componentId);
639
534
  if (!item) return;
640
- const updatedProps = replaceImageInProps(item.props, src);
535
+ const updatedProps = replaceImageInProps(
536
+ item.props,
537
+ src
538
+ );
641
539
  dispatchReplace(componentId, updatedProps);
642
540
  }
643
- function onTextDrop(e) {
644
- const { text, clientX, clientY } = e.detail;
645
- clearHighlight();
541
+ function onTextDrop(text, clientX, clientY) {
542
+ highlighter.clear();
646
543
  activeLibrary = null;
647
544
  if (!text) return;
648
- const compEl = getComponentElAt(clientX, clientY);
545
+ const compEl = getComponentElAt(iframeDocument, frameElement, clientX, clientY);
649
546
  if (!compEl) return;
650
- const componentId = compEl.getAttribute("data-puck-component");
651
- const textEl = getTextElInComponent(compEl, clientX, clientY);
547
+ const componentId = compEl.dataset.puckComponent;
548
+ if (!componentId) return;
549
+ const textEl = getTextElInComponent(
550
+ iframeDocument,
551
+ frameElement,
552
+ compEl,
553
+ clientX,
554
+ clientY
555
+ );
652
556
  const targetText = textEl?.textContent?.trim() ?? "";
653
- const { getItemById } = getPuck();
654
- const item = getItemById(componentId);
557
+ const item = getPuck().getItemById(componentId);
655
558
  if (!item) return;
656
559
  const { result: updatedProps, replaced } = replaceTextInProps(
657
560
  item.props,
@@ -660,19 +563,38 @@ function CanvasIframe({
660
563
  );
661
564
  if (replaced) dispatchReplace(componentId, updatedProps);
662
565
  }
663
- window.addEventListener("anvilkit:librarydragstart", onLibraryDragStart);
566
+ const removeLibraryDragStart = addLibraryDragEventListener(
567
+ LIBRARY_DRAG_START,
568
+ (event) => {
569
+ onLibraryDragStart(event.detail.type);
570
+ }
571
+ );
664
572
  window.addEventListener("pointermove", onPointerMove);
665
573
  window.addEventListener("pointerup", onPointerUp);
666
- window.addEventListener("anvilkit:imagedrop", onImageDrop);
667
- window.addEventListener("anvilkit:textdrop", onTextDrop);
574
+ const removeImageDrop = addLibraryDragEventListener(IMAGE_DROP, (event) => {
575
+ const { src, clientX, clientY } = event.detail;
576
+ onImageDrop(src, clientX, clientY);
577
+ });
578
+ const removeTextDrop = addLibraryDragEventListener(TEXT_DROP, (event) => {
579
+ const { text, clientX, clientY } = event.detail;
580
+ onTextDrop(text, clientX, clientY);
581
+ });
668
582
  return () => {
669
- window.removeEventListener("anvilkit:librarydragstart", onLibraryDragStart);
583
+ removeLibraryDragStart();
670
584
  window.removeEventListener("pointermove", onPointerMove);
671
585
  window.removeEventListener("pointerup", onPointerUp);
672
- window.removeEventListener("anvilkit:imagedrop", onImageDrop);
673
- window.removeEventListener("anvilkit:textdrop", onTextDrop);
586
+ removeImageDrop();
587
+ removeTextDrop();
588
+ highlighter.clear();
674
589
  };
675
590
  }, [iframeDoc, getPuck]);
591
+ }
592
+ function CanvasIframe({
593
+ children,
594
+ document: iframeDoc
595
+ }) {
596
+ useThemeSync({ document: iframeDoc, injectCanvasCss: true });
597
+ useLibraryDropBridge(iframeDoc);
676
598
  return /* @__PURE__ */ jsx(Fragment, { children });
677
599
  }
678
600
  function CanvasPreview({
@@ -696,6 +618,60 @@ function ComponentOverlay({
696
618
  }
697
619
  );
698
620
  }
621
+ function TooltipProvider({
622
+ delay = 0,
623
+ ...props
624
+ }) {
625
+ return /* @__PURE__ */ jsx(
626
+ Tooltip$1.Provider,
627
+ {
628
+ "data-slot": "tooltip-provider",
629
+ delay,
630
+ ...props
631
+ }
632
+ );
633
+ }
634
+ function Tooltip({ ...props }) {
635
+ return /* @__PURE__ */ jsx(Tooltip$1.Root, { "data-slot": "tooltip", ...props });
636
+ }
637
+ function TooltipTrigger({ ...props }) {
638
+ return /* @__PURE__ */ jsx(Tooltip$1.Trigger, { "data-slot": "tooltip-trigger", ...props });
639
+ }
640
+ function TooltipContent({
641
+ className,
642
+ side = "top",
643
+ sideOffset = 4,
644
+ align = "center",
645
+ alignOffset = 0,
646
+ children,
647
+ ...props
648
+ }) {
649
+ return /* @__PURE__ */ jsx(Tooltip$1.Portal, { children: /* @__PURE__ */ jsx(
650
+ Tooltip$1.Positioner,
651
+ {
652
+ align,
653
+ alignOffset,
654
+ side,
655
+ sideOffset,
656
+ className: "isolate z-50",
657
+ children: /* @__PURE__ */ jsxs(
658
+ Tooltip$1.Popup,
659
+ {
660
+ "data-slot": "tooltip-content",
661
+ className: cn(
662
+ "z-50 inline-flex w-fit max-w-xs origin-(--transform-origin) items-center gap-1.5 rounded-md bg-foreground px-3 py-1.5 text-xs text-background has-data-[slot=kbd]:pr-1.5 data-[side=bottom]:slide-in-from-top-2 data-[side=inline-end]:slide-in-from-left-2 data-[side=inline-start]:slide-in-from-right-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 **:data-[slot=kbd]:relative **:data-[slot=kbd]:isolate **:data-[slot=kbd]:z-50 **:data-[slot=kbd]:rounded-sm data-[state=delayed-open]:animate-in data-[state=delayed-open]:fade-in-0 data-[state=delayed-open]:zoom-in-95 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95",
663
+ className
664
+ ),
665
+ ...props,
666
+ children: [
667
+ children,
668
+ /* @__PURE__ */ jsx(Tooltip$1.Arrow, { className: "z-50 size-2.5 translate-y-[calc(-50%-2px)] rotate-45 rounded-[2px] bg-foreground fill-foreground data-[side=bottom]:top-1 data-[side=inline-end]:top-1/2! data-[side=inline-end]:-left-1 data-[side=inline-end]:-translate-y-1/2 data-[side=inline-start]:top-1/2! data-[side=inline-start]:-right-1 data-[side=inline-start]:-translate-y-1/2 data-[side=left]:top-1/2! data-[side=left]:-right-1 data-[side=left]:-translate-y-1/2 data-[side=right]:top-1/2! data-[side=right]:-left-1 data-[side=right]:-translate-y-1/2 data-[side=top]:-bottom-2.5" })
669
+ ]
670
+ }
671
+ )
672
+ }
673
+ ) });
674
+ }
699
675
  function ActionBar({
700
676
  children,
701
677
  label,
@@ -713,16 +689,13 @@ function ActionBar({
713
689
  children
714
690
  ] }) });
715
691
  }
716
- function cn2(...inputs) {
717
- return twMerge(clsx(inputs));
718
- }
719
692
  function Label({ className, ...props }) {
720
693
  return /* @__PURE__ */ jsx(
721
694
  "label",
722
695
  {
723
696
  "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",
697
+ className: cn(
698
+ "flex items-center gap-2 text-sm leading-none font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50",
726
699
  className
727
700
  ),
728
701
  ...props
@@ -735,7 +708,7 @@ function Breadcrumb({ className, ...props }) {
735
708
  {
736
709
  "aria-label": "breadcrumb",
737
710
  "data-slot": "breadcrumb",
738
- className: cn2(className),
711
+ className: cn(className),
739
712
  ...props
740
713
  }
741
714
  );
@@ -745,8 +718,8 @@ function BreadcrumbList({ className, ...props }) {
745
718
  "ol",
746
719
  {
747
720
  "data-slot": "breadcrumb-list",
748
- className: cn2(
749
- "text-muted-foreground gap-1.5 text-sm flex flex-wrap items-center wrap-break-word",
721
+ className: cn(
722
+ "flex flex-wrap items-center gap-1.5 text-sm wrap-break-word text-muted-foreground sm:gap-2.5",
750
723
  className
751
724
  ),
752
725
  ...props
@@ -758,7 +731,7 @@ function BreadcrumbItem({ className, ...props }) {
758
731
  "li",
759
732
  {
760
733
  "data-slot": "breadcrumb-item",
761
- className: cn2("gap-1 inline-flex items-center", className),
734
+ className: cn("inline-flex items-center gap-1.5", className),
762
735
  ...props
763
736
  }
764
737
  );
@@ -772,7 +745,7 @@ function BreadcrumbLink({
772
745
  defaultTagName: "a",
773
746
  props: mergeProps(
774
747
  {
775
- className: cn2("hover:text-foreground transition-colors", className)
748
+ className: cn("transition-colors hover:text-foreground", className)
776
749
  },
777
750
  props
778
751
  ),
@@ -790,7 +763,7 @@ function BreadcrumbPage({ className, ...props }) {
790
763
  role: "link",
791
764
  "aria-disabled": "true",
792
765
  "aria-current": "page",
793
- className: cn2("text-foreground font-normal", className),
766
+ className: cn("font-normal text-foreground", className),
794
767
  ...props
795
768
  }
796
769
  );
@@ -806,32 +779,39 @@ function BreadcrumbSeparator({
806
779
  "data-slot": "breadcrumb-separator",
807
780
  role: "presentation",
808
781
  "aria-hidden": "true",
809
- className: cn2("[&>svg]:size-3.5", className),
782
+ className: cn("[&>svg]:size-3.5", className),
810
783
  ...props,
811
- children: children ?? /* @__PURE__ */ jsx(ChevronRightIcon, { className: "cn-rtl-flip" })
784
+ children: children ?? /* @__PURE__ */ jsx(ChevronRightIcon, {})
812
785
  }
813
786
  );
814
787
  }
788
+ function getComponentTypeLabel(item) {
789
+ if (!item) return "Component";
790
+ return typeof item.type === "string" ? item.type : String(item.type);
791
+ }
792
+ function getComponentId(item) {
793
+ const id = item?.props?.id;
794
+ return typeof id === "string" ? id : null;
795
+ }
815
796
  function useBreadcrumbs() {
816
- const { appState, dispatch, selectedItem, getParentById, getSelectorForId } = usePuck();
797
+ const { appState, dispatch, selectedItem, getParentById } = usePuck();
817
798
  const { itemSelector } = appState.ui;
818
799
  const selectRoot = () => dispatch({ type: "setUi", ui: { itemSelector: null } });
819
800
  if (!itemSelector || !selectedItem) {
820
801
  return [{ label: "Page" }];
821
802
  }
822
- const selectedType = selectedItem.type ?? "Component";
823
- const parent = getParentById(selectedItem.props?.id ?? "");
803
+ const selectedType = getComponentTypeLabel(selectedItem);
804
+ const parentId = getComponentId(selectedItem);
805
+ const parent = parentId ? getParentById(parentId) : void 0;
824
806
  if (!parent) {
825
807
  return [{ label: "Page", onSelect: selectRoot }, { label: selectedType }];
826
808
  }
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 }];
809
+ const parentType = getComponentTypeLabel(parent);
810
+ return [
811
+ { label: "Page", onSelect: selectRoot },
812
+ { label: parentType },
813
+ { label: selectedType }
814
+ ];
835
815
  }
836
816
  function FieldWrapper({
837
817
  children
@@ -840,7 +820,7 @@ function FieldWrapper({
840
820
  return /* @__PURE__ */ jsx(ScrollArea, { className: "h-full", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-0", children: [
841
821
  /* @__PURE__ */ jsx("div", { className: "px-3 py-2 border-b", children: /* @__PURE__ */ jsx(Breadcrumb, { children: /* @__PURE__ */ jsx(BreadcrumbList, { children: crumbs.map((crumb, i) => {
842
822
  const isLast = i === crumbs.length - 1;
843
- return /* @__PURE__ */ jsxs(React18.Fragment, { children: [
823
+ return /* @__PURE__ */ jsxs(React17.Fragment, { children: [
844
824
  i > 0 && /* @__PURE__ */ jsx(BreadcrumbSeparator, {}),
845
825
  /* @__PURE__ */ jsx(BreadcrumbItem, { children: isLast ? /* @__PURE__ */ jsx(BreadcrumbPage, { children: crumb.label }) : /* @__PURE__ */ jsx(
846
826
  BreadcrumbLink,
@@ -858,20 +838,33 @@ function FieldWrapper({
858
838
  function FieldLabel({
859
839
  children,
860
840
  label,
841
+ icon,
861
842
  labelIcon,
862
843
  el,
863
- type: _type,
864
844
  readOnly,
865
845
  className
866
846
  }) {
867
- console.log("Lable type", labelIcon);
868
847
  const El = el ?? "div";
848
+ const labelAdornment = icon ?? labelIcon;
869
849
  return /* @__PURE__ */ jsx(TooltipProvider, { children: /* @__PURE__ */ jsxs(El, { className: `flex flex-col gap-1.5 ${className ?? ""}`, children: [
870
850
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
871
- labelIcon && /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: labelIcon }),
851
+ labelAdornment && /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: labelAdornment }),
872
852
  /* @__PURE__ */ jsx(Label, { className: "text-xs font-medium text-muted-foreground", children: label }),
873
853
  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" }) }),
854
+ /* @__PURE__ */ jsx(
855
+ TooltipTrigger,
856
+ {
857
+ "aria-label": label,
858
+ render: /* @__PURE__ */ jsx(
859
+ "button",
860
+ {
861
+ type: "button",
862
+ className: "inline-flex cursor-help items-center justify-center text-muted-foreground/60 transition-colors hover:text-muted-foreground"
863
+ }
864
+ ),
865
+ children: /* @__PURE__ */ jsx(Info, { className: "h-3 w-3 text-muted-foreground/60 cursor-help" })
866
+ }
867
+ ),
875
868
  /* @__PURE__ */ jsx(TooltipContent, { side: "right", children: label })
876
869
  ] }),
877
870
  readOnly && /* @__PURE__ */ jsx("span", { className: "ml-auto text-xs text-muted-foreground/50", children: "Read only" })
@@ -879,6 +872,50 @@ function FieldLabel({
879
872
  children
880
873
  ] }) });
881
874
  }
875
+ var buttonVariants = cva(
876
+ "group/button inline-flex shrink-0 items-center justify-center rounded-md 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",
877
+ {
878
+ variants: {
879
+ variant: {
880
+ default: "bg-primary text-primary-foreground hover:bg-primary/80",
881
+ outline: "border-border bg-background shadow-xs 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",
882
+ secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80 aria-expanded:bg-secondary aria-expanded:text-secondary-foreground",
883
+ ghost: "hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:hover:bg-muted/50",
884
+ 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",
885
+ link: "text-primary underline-offset-4 hover:underline"
886
+ },
887
+ size: {
888
+ default: "h-9 gap-1.5 px-2.5 in-data-[slot=button-group]:rounded-md has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2",
889
+ xs: "h-6 gap-1 rounded-[min(var(--radius-md),8px)] px-2 text-xs in-data-[slot=button-group]:rounded-md has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3",
890
+ sm: "h-8 gap-1 rounded-[min(var(--radius-md),10px)] px-2.5 in-data-[slot=button-group]:rounded-md has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5",
891
+ lg: "h-10 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-3 has-data-[icon=inline-start]:pl-3",
892
+ icon: "size-9",
893
+ "icon-xs": "size-6 rounded-[min(var(--radius-md),8px)] in-data-[slot=button-group]:rounded-md [&_svg:not([class*='size-'])]:size-3",
894
+ "icon-sm": "size-8 rounded-[min(var(--radius-md),10px)] in-data-[slot=button-group]:rounded-md",
895
+ "icon-lg": "size-10"
896
+ }
897
+ },
898
+ defaultVariants: {
899
+ variant: "default",
900
+ size: "default"
901
+ }
902
+ }
903
+ );
904
+ function Button({
905
+ className,
906
+ variant = "default",
907
+ size = "default",
908
+ ...props
909
+ }) {
910
+ return /* @__PURE__ */ jsx(
911
+ Button$1,
912
+ {
913
+ "data-slot": "button",
914
+ className: cn(buttonVariants({ variant, size, className })),
915
+ ...props
916
+ }
917
+ );
918
+ }
882
919
  var MOCK_VALUES = {
883
920
  default: ["AI-generated content", "Smart suggestion", "Generated text"]
884
921
  };
@@ -890,7 +927,7 @@ function getMock(instructions) {
890
927
  return pool[Math.floor(Math.random() * pool.length)];
891
928
  }
892
929
  function AiButton({ ai, onGenerate }) {
893
- const [loading, setLoading] = React18.useState(false);
930
+ const [loading, setLoading] = React17.useState(false);
894
931
  const handleClick = () => {
895
932
  setLoading(true);
896
933
  setTimeout(() => {
@@ -921,13 +958,22 @@ function TextField({
921
958
  label,
922
959
  labelIcon
923
960
  }) {
924
- const [local, setLocal] = React18.useState(value ?? "");
925
- React18.useEffect(() => {
961
+ const [local, setLocal] = React17.useState(value ?? "");
962
+ const onChangeRef = React17.useRef(onChange);
963
+ const lastCommittedValueRef = React17.useRef(value ?? "");
964
+ React17.useEffect(() => {
965
+ onChangeRef.current = onChange;
966
+ }, [onChange]);
967
+ React17.useEffect(() => {
968
+ lastCommittedValueRef.current = value ?? "";
926
969
  setLocal(value ?? "");
927
970
  }, [value]);
928
- React18.useEffect(() => {
971
+ React17.useEffect(() => {
929
972
  const timer = setTimeout(() => {
930
- if (local !== value) onChange(local);
973
+ if (local !== lastCommittedValueRef.current) {
974
+ onChangeRef.current(local);
975
+ lastCommittedValueRef.current = local;
976
+ }
931
977
  }, 200);
932
978
  return () => clearTimeout(timer);
933
979
  }, [local]);
@@ -942,10 +988,17 @@ function TextField({
942
988
  className: "h-8 text-sm flex-1"
943
989
  }
944
990
  ),
945
- field?.ai && /* @__PURE__ */ jsx(AiButton, { ai: field.ai, onGenerate: (v) => {
946
- setLocal(v);
947
- onChange(v);
948
- } })
991
+ field?.ai && /* @__PURE__ */ jsx(
992
+ AiButton,
993
+ {
994
+ ai: field.ai,
995
+ onGenerate: (nextValue) => {
996
+ lastCommittedValueRef.current = nextValue;
997
+ setLocal(nextValue);
998
+ onChangeRef.current(nextValue);
999
+ }
1000
+ }
1001
+ )
949
1002
  ] }) });
950
1003
  }
951
1004
  function Textarea({ className, ...props }) {
@@ -954,7 +1007,7 @@ function Textarea({ className, ...props }) {
954
1007
  {
955
1008
  "data-slot": "textarea",
956
1009
  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",
1010
+ "flex field-sizing-content min-h-16 w-full rounded-md border border-input bg-transparent px-2.5 py-2 text-base shadow-xs transition-[color,box-shadow] outline-none placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 md:text-sm dark:bg-input/30 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40",
958
1011
  className
959
1012
  ),
960
1013
  ...props
@@ -962,11 +1015,11 @@ function Textarea({ className, ...props }) {
962
1015
  );
963
1016
  }
964
1017
  function TextareaField({ value, onChange, readOnly, placeholder, label }) {
965
- const [local, setLocal] = React18.useState(value ?? "");
966
- React18.useEffect(() => {
1018
+ const [local, setLocal] = React17.useState(value ?? "");
1019
+ React17.useEffect(() => {
967
1020
  setLocal(value ?? "");
968
1021
  }, [value]);
969
- React18.useEffect(() => {
1022
+ React17.useEffect(() => {
970
1023
  const timer = setTimeout(() => {
971
1024
  if (local !== value) onChange(local);
972
1025
  }, 200);
@@ -984,11 +1037,11 @@ function TextareaField({ value, onChange, readOnly, placeholder, label }) {
984
1037
  ) });
985
1038
  }
986
1039
  function NumberField({ value, onChange, readOnly, min, max, step, label }) {
987
- const [local, setLocal] = React18.useState(String(value ?? ""));
988
- React18.useEffect(() => {
1040
+ const [local, setLocal] = React17.useState(String(value ?? ""));
1041
+ React17.useEffect(() => {
989
1042
  setLocal(String(value ?? ""));
990
1043
  }, [value]);
991
- React18.useEffect(() => {
1044
+ React17.useEffect(() => {
992
1045
  const parsed = parseFloat(local);
993
1046
  if (!isNaN(parsed) && parsed !== value) {
994
1047
  const timer = setTimeout(() => onChange(parsed), 200);
@@ -1042,7 +1095,7 @@ function SelectTrigger({
1042
1095
  "data-slot": "select-trigger",
1043
1096
  "data-size": size,
1044
1097
  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",
1098
+ "flex w-fit items-center justify-between gap-1.5 rounded-md border border-input bg-transparent py-2 pr-2 pl-2.5 text-sm whitespace-nowrap shadow-xs transition-[color,box-shadow] outline-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 data-placeholder:text-muted-foreground data-[size=default]:h-9 data-[size=sm]:h-8 *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-1.5 dark:bg-input/30 dark:hover:bg-input/50 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",
1046
1099
  className
1047
1100
  ),
1048
1101
  ...props,
@@ -1051,7 +1104,7 @@ function SelectTrigger({
1051
1104
  /* @__PURE__ */ jsx(
1052
1105
  Select$1.Icon,
1053
1106
  {
1054
- render: /* @__PURE__ */ jsx(ChevronDownIcon, { className: "text-muted-foreground size-4 pointer-events-none" })
1107
+ render: /* @__PURE__ */ jsx(ChevronDownIcon, { className: "pointer-events-none size-4 text-muted-foreground" })
1055
1108
  }
1056
1109
  )
1057
1110
  ]
@@ -1082,10 +1135,7 @@ function SelectContent({
1082
1135
  {
1083
1136
  "data-slot": "select-content",
1084
1137
  "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
- ),
1138
+ className: cn("relative isolate z-50 max-h-(--available-height) w-(--anchor-width) min-w-36 origin-(--transform-origin) overflow-x-hidden overflow-y-auto rounded-md bg-popover text-popover-foreground shadow-md ring-1 ring-foreground/10 duration-100 data-[align-trigger=true]:animate-none data-[side=bottom]:slide-in-from-top-2 data-[side=inline-end]:slide-in-from-left-2 data-[side=inline-start]:slide-in-from-right-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 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95", className),
1089
1139
  ...props,
1090
1140
  children: [
1091
1141
  /* @__PURE__ */ jsx(SelectScrollUpButton, {}),
@@ -1107,16 +1157,17 @@ function SelectItem({
1107
1157
  {
1108
1158
  "data-slot": "select-item",
1109
1159
  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",
1160
+ "relative flex w-full cursor-default items-center gap-2 rounded-sm py-1.5 pr-8 pl-2 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground not-data-[variant=destructive]:focus:**:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2",
1111
1161
  className
1112
1162
  ),
1113
1163
  ...props,
1114
1164
  children: [
1115
- /* @__PURE__ */ jsx(Select$1.ItemText, { className: "flex flex-1 gap-2 shrink-0 whitespace-nowrap", children }),
1165
+ /* @__PURE__ */ jsx(Select$1.ItemText, { className: "flex flex-1 shrink-0 gap-2 whitespace-nowrap", children }),
1116
1166
  /* @__PURE__ */ jsx(
1117
1167
  Select$1.ItemIndicator,
1118
1168
  {
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" }) })
1169
+ render: /* @__PURE__ */ jsx("span", { className: "pointer-events-none absolute right-2 flex size-4 items-center justify-center" }),
1170
+ children: /* @__PURE__ */ jsx(CheckIcon, { className: "pointer-events-none" })
1120
1171
  }
1121
1172
  )
1122
1173
  ]
@@ -1132,11 +1183,14 @@ function SelectScrollUpButton({
1132
1183
  {
1133
1184
  "data-slot": "select-scroll-up-button",
1134
1185
  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",
1186
+ "top-0 z-10 flex w-full cursor-default items-center justify-center bg-popover py-1 [&_svg:not([class*='size-'])]:size-4",
1136
1187
  className
1137
1188
  ),
1138
1189
  ...props,
1139
- children: /* @__PURE__ */ jsx(ChevronUpIcon, {})
1190
+ children: /* @__PURE__ */ jsx(
1191
+ ChevronUpIcon,
1192
+ {}
1193
+ )
1140
1194
  }
1141
1195
  );
1142
1196
  }
@@ -1149,11 +1203,14 @@ function SelectScrollDownButton({
1149
1203
  {
1150
1204
  "data-slot": "select-scroll-down-button",
1151
1205
  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",
1206
+ "bottom-0 z-10 flex w-full cursor-default items-center justify-center bg-popover py-1 [&_svg:not([class*='size-'])]:size-4",
1153
1207
  className
1154
1208
  ),
1155
1209
  ...props,
1156
- children: /* @__PURE__ */ jsx(ChevronDownIcon, {})
1210
+ children: /* @__PURE__ */ jsx(
1211
+ ChevronDownIcon,
1212
+ {}
1213
+ )
1157
1214
  }
1158
1215
  );
1159
1216
  }
@@ -1175,12 +1232,12 @@ function SelectField({ field, value, onChange, id, readOnly, label }) {
1175
1232
  ) });
1176
1233
  }
1177
1234
  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",
1235
+ "flex w-fit items-stretch *:focus-visible:relative *:focus-visible:z-10 has-[>[data-slot=button-group]]:gap-2 has-[select[aria-hidden=true]:last-child]:[&>[data-slot=select-trigger]:last-of-type]:rounded-r-md [&>[data-slot=select-trigger]:not([class*='w-'])]:w-fit [&>input]:flex-1",
1179
1236
  {
1180
1237
  variants: {
1181
1238
  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"
1239
+ horizontal: "*:data-slot:rounded-r-none [&>[data-slot]:not(:has(~[data-slot]))]:rounded-r-md! [&>[data-slot]~[data-slot]]:rounded-l-none [&>[data-slot]~[data-slot]]:border-l-0",
1240
+ vertical: "flex-col *:data-slot:rounded-b-none [&>[data-slot]:not(:has(~[data-slot]))]:rounded-b-md! [&>[data-slot]~[data-slot]]:rounded-t-none [&>[data-slot]~[data-slot]]:border-t-0"
1184
1241
  }
1185
1242
  },
1186
1243
  defaultVariants: {
@@ -1199,7 +1256,7 @@ function ButtonGroup({
1199
1256
  role: "group",
1200
1257
  "data-slot": "button-group",
1201
1258
  "data-orientation": orientation,
1202
- className: cn2(buttonGroupVariants({ orientation }), className),
1259
+ className: cn(buttonGroupVariants({ orientation }), className),
1203
1260
  ...props
1204
1261
  }
1205
1262
  );
@@ -1229,8 +1286,8 @@ function ItemGroup({ className, ...props }) {
1229
1286
  {
1230
1287
  role: "list",
1231
1288
  "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",
1289
+ className: cn(
1290
+ "group/item-group flex w-full flex-col gap-4 has-data-[size=sm]:gap-2.5 has-data-[size=xs]:gap-2",
1234
1291
  className
1235
1292
  ),
1236
1293
  ...props
@@ -1238,16 +1295,16 @@ function ItemGroup({ className, ...props }) {
1238
1295
  );
1239
1296
  }
1240
1297
  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",
1298
+ "group/item flex w-full flex-wrap items-center rounded-md border text-sm transition-colors duration-100 outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 [a]:transition-colors [a]:hover:bg-muted",
1242
1299
  {
1243
1300
  variants: {
1244
1301
  variant: {
1245
1302
  default: "border-transparent",
1246
1303
  outline: "border-border",
1247
- muted: "bg-muted/50 border-transparent"
1304
+ muted: "border-transparent bg-muted/50"
1248
1305
  },
1249
1306
  size: {
1250
- default: "gap-2.5 px-3 py-2.5",
1307
+ default: "gap-3.5 px-4 py-3.5",
1251
1308
  sm: "gap-2.5 px-3 py-2.5",
1252
1309
  xs: "gap-2 px-2.5 py-2 in-data-[slot=dropdown-menu-content]:p-0"
1253
1310
  }
@@ -1269,7 +1326,7 @@ function Item({
1269
1326
  defaultTagName: "div",
1270
1327
  props: mergeProps(
1271
1328
  {
1272
- className: cn2(itemVariants({ variant, size, className }))
1329
+ className: cn(itemVariants({ variant, size, className }))
1273
1330
  },
1274
1331
  props
1275
1332
  ),
@@ -1282,7 +1339,7 @@ function Item({
1282
1339
  });
1283
1340
  }
1284
1341
  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",
1342
+ "flex shrink-0 items-center justify-center gap-2 group-has-data-[slot=item-description]/item:translate-y-0.5 group-has-data-[slot=item-description]/item:self-start [&_svg]:pointer-events-none",
1286
1343
  {
1287
1344
  variants: {
1288
1345
  variant: {
@@ -1301,8 +1358,8 @@ function ItemContent({ className, ...props }) {
1301
1358
  "div",
1302
1359
  {
1303
1360
  "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",
1361
+ className: cn(
1362
+ "flex flex-1 flex-col gap-1 group-data-[size=xs]/item:gap-0 [&+[data-slot=item-content]]:flex-none",
1306
1363
  className
1307
1364
  ),
1308
1365
  ...props
@@ -1314,8 +1371,8 @@ function ItemTitle({ className, ...props }) {
1314
1371
  "div",
1315
1372
  {
1316
1373
  "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",
1374
+ className: cn(
1375
+ "line-clamp-1 flex w-fit items-center gap-2 text-sm leading-snug font-medium underline-offset-4",
1319
1376
  className
1320
1377
  ),
1321
1378
  ...props
@@ -1327,20 +1384,20 @@ function ItemActions({ className, ...props }) {
1327
1384
  "div",
1328
1385
  {
1329
1386
  "data-slot": "item-actions",
1330
- className: cn2("gap-2 flex items-center", className),
1387
+ className: cn("flex items-center gap-2", className),
1331
1388
  ...props
1332
1389
  }
1333
1390
  );
1334
1391
  }
1335
1392
  function useControlledState(props) {
1336
1393
  const { value, defaultValue, onChange } = props;
1337
- const [state, setInternalState] = React18.useState(
1394
+ const [state, setInternalState] = React17.useState(
1338
1395
  value !== void 0 ? value : defaultValue
1339
1396
  );
1340
- React18.useEffect(() => {
1397
+ React17.useEffect(() => {
1341
1398
  if (value !== void 0) setInternalState(value);
1342
1399
  }, [value]);
1343
- const setState = React18.useCallback(
1400
+ const setState = React17.useCallback(
1344
1401
  (next, ...args) => {
1345
1402
  setInternalState(next);
1346
1403
  onChange?.(next, ...args);
@@ -1453,6 +1510,9 @@ function PopoverPanel({
1453
1510
  function PopoverTitle2(props) {
1454
1511
  return /* @__PURE__ */ jsx(PopoverTitle, { ...props });
1455
1512
  }
1513
+ function getArraySubField(subName, subField) {
1514
+ return { ...subField, label: subField.label ?? subName };
1515
+ }
1456
1516
  function SortableItem({
1457
1517
  id,
1458
1518
  index,
@@ -1539,7 +1599,7 @@ function SortableItem({
1539
1599
  children: Object.entries(field.arrayFields).map(([subName, subField]) => /* @__PURE__ */ jsx(
1540
1600
  AutoField,
1541
1601
  {
1542
- field: { ...subField, label: subField.label ?? subName },
1602
+ field: getArraySubField(subName, subField),
1543
1603
  value: item[subName],
1544
1604
  onChange: (val) => onUpdate(index, subName, val),
1545
1605
  readOnly,
@@ -1559,13 +1619,17 @@ function ArrayField({
1559
1619
  label,
1560
1620
  id = ""
1561
1621
  }) {
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
- );
1622
+ const [openIndex, setOpenIndex] = React17.useState(null);
1623
+ const stableValue = React17.useMemo(() => {
1624
+ const needsId = value.some((item) => !item._id);
1625
+ if (!needsId) return value;
1626
+ return value.map(
1627
+ (item) => item._id ? item : { ...item, _id: `${id}-${Math.random().toString(36).slice(2)}` }
1628
+ );
1629
+ }, [value, id]);
1630
+ const itemIds = stableValue.map((item) => item._id);
1631
+ const atMax = field.max !== void 0 && stableValue.length >= field.max;
1632
+ const atMin = field.min !== void 0 && stableValue.length <= field.min;
1569
1633
  const sensors = useSensors(useSensor(PointerSensor));
1570
1634
  const handleDragEnd = (event) => {
1571
1635
  const { active, over } = event;
@@ -1573,7 +1637,7 @@ function ArrayField({
1573
1637
  const from = itemIds.indexOf(active.id);
1574
1638
  const to = itemIds.indexOf(over.id);
1575
1639
  if (from === -1 || to === -1) return;
1576
- onChange(arrayMove(value, from, to));
1640
+ onChange(arrayMove(stableValue, from, to));
1577
1641
  };
1578
1642
  const defaultItem = () => {
1579
1643
  if (!field.defaultItemProps) return {};
@@ -1581,33 +1645,35 @@ function ArrayField({
1581
1645
  };
1582
1646
  const addItem = () => {
1583
1647
  if (atMax || readOnly) return;
1584
- const newIndex = value.length;
1585
- onChange([...value, defaultItem()]);
1648
+ const newIndex = stableValue.length;
1649
+ onChange([...stableValue, defaultItem()]);
1586
1650
  setOpenIndex(newIndex);
1587
1651
  };
1588
1652
  const removeItem = (i) => {
1589
1653
  if (atMin || readOnly) return;
1590
- const next = [...value];
1654
+ const next = [...stableValue];
1591
1655
  next.splice(i, 1);
1592
1656
  onChange(next);
1593
1657
  };
1594
1658
  const duplicateItem = (i) => {
1595
1659
  if (atMax || readOnly) return;
1596
- const next = [...value];
1597
- next.splice(i + 1, 0, { ...value[i] });
1660
+ const next = [...stableValue];
1661
+ next.splice(i + 1, 0, { ...stableValue[i] });
1598
1662
  onChange(next);
1599
1663
  };
1600
1664
  const updateItem = (i, subName, val) => {
1601
1665
  onChange(
1602
- value.map(
1666
+ stableValue.map(
1603
1667
  (item, idx) => idx === i ? { ...item, [subName]: val } : item
1604
1668
  )
1605
1669
  );
1606
1670
  };
1607
1671
  const getSummary = (item, i) => {
1608
1672
  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}`;
1673
+ const first = Object.entries(item).find(
1674
+ (entry) => entry[0] !== "_id" && typeof entry[1] === "string" && entry[1].length > 0
1675
+ );
1676
+ return first?.[1] ?? `Item ${i + 1}`;
1611
1677
  };
1612
1678
  return /* @__PURE__ */ jsx(FieldLabel, { label: label ?? field.label ?? "", readOnly, el: "div", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1.5", children: [
1613
1679
  /* @__PURE__ */ jsx(
@@ -1621,7 +1687,7 @@ function ArrayField({
1621
1687
  {
1622
1688
  items: itemIds,
1623
1689
  strategy: verticalListSortingStrategy,
1624
- children: /* @__PURE__ */ jsx(ItemGroup, { children: value.map((item, i) => /* @__PURE__ */ jsx(
1690
+ children: /* @__PURE__ */ jsx(ItemGroup, { children: stableValue.map((item, i) => /* @__PURE__ */ jsx(
1625
1691
  SortableItem,
1626
1692
  {
1627
1693
  id: itemIds[i],
@@ -1674,7 +1740,7 @@ function Card({
1674
1740
  "data-slot": "card",
1675
1741
  "data-size": size,
1676
1742
  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",
1743
+ "group/card flex flex-col gap-6 overflow-hidden rounded-xl bg-card py-6 text-sm text-card-foreground shadow-xs ring-1 ring-foreground/10 has-[>img:first-child]:pt-0 data-[size=sm]:gap-4 data-[size=sm]:py-4 *:[img:first-child]:rounded-t-xl *:[img:last-child]:rounded-b-xl",
1678
1744
  className
1679
1745
  ),
1680
1746
  ...props
@@ -1686,7 +1752,7 @@ function CardContent({ className, ...props }) {
1686
1752
  "div",
1687
1753
  {
1688
1754
  "data-slot": "card-content",
1689
- className: cn("px-4 group-data-[size=sm]/card:px-3", className),
1755
+ className: cn("px-6 group-data-[size=sm]/card:px-4", className),
1690
1756
  ...props
1691
1757
  }
1692
1758
  );
@@ -1698,17 +1764,17 @@ function CustomField({ children, label }) {
1698
1764
  return /* @__PURE__ */ jsx(FieldLabel, { label: label ?? "", el: "div", children: /* @__PURE__ */ jsx("div", { className: "rounded-md border border-border/60 p-3", children }) });
1699
1765
  }
1700
1766
  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",
1767
+ "group/toggle inline-flex items-center justify-center gap-1 rounded-md text-sm font-medium whitespace-nowrap transition-[color,box-shadow] 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 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
1702
1768
  {
1703
1769
  variants: {
1704
1770
  variant: {
1705
1771
  default: "bg-transparent",
1706
- outline: "border border-input bg-transparent hover:bg-muted"
1772
+ outline: "border border-input bg-transparent shadow-xs hover:bg-muted"
1707
1773
  },
1708
1774
  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"
1775
+ default: "h-9 min-w-9 px-2",
1776
+ sm: "h-8 min-w-8 px-1.5",
1777
+ lg: "h-10 min-w-10 px-2.5"
1712
1778
  }
1713
1779
  },
1714
1780
  defaultVariants: {
@@ -1733,12 +1799,12 @@ function Toggle({
1733
1799
  );
1734
1800
  }
1735
1801
  function RichtextField({ value, onChange, readOnly, label }) {
1736
- const ref = React18.useRef(null);
1802
+ const ref = React17.useRef(null);
1737
1803
  const boldLabel = useMsg("richtext.bold");
1738
1804
  const italicLabel = useMsg("richtext.italic");
1739
1805
  const linkLabel = useMsg("richtext.link");
1740
1806
  const linkPrompt = useMsg("richtext.link.prompt");
1741
- React18.useEffect(() => {
1807
+ React17.useEffect(() => {
1742
1808
  if (ref.current && ref.current.innerHTML !== value) {
1743
1809
  ref.current.innerHTML = value ?? "";
1744
1810
  }
@@ -1785,17 +1851,7 @@ function InputGroup({ className, ...props }) {
1785
1851
  "data-slot": "input-group",
1786
1852
  role: "group",
1787
1853
  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",
1854
+ "group/input-group relative flex h-9 w-full min-w-0 items-center rounded-md border border-input shadow-xs transition-[color,box-shadow] outline-none in-data-[slot=combobox-content]:focus-within:border-inherit in-data-[slot=combobox-content]:focus-within:ring-0 has-[[data-slot=input-group-control]:focus-visible]:border-ring has-[[data-slot=input-group-control]:focus-visible]:ring-3 has-[[data-slot=input-group-control]:focus-visible]:ring-ring/50 has-[[data-slot][aria-invalid=true]]:border-destructive has-[[data-slot][aria-invalid=true]]:ring-3 has-[[data-slot][aria-invalid=true]]:ring-destructive/20 has-[>[data-align=block-end]]:h-auto has-[>[data-align=block-end]]:flex-col has-[>[data-align=block-start]]:h-auto has-[>[data-align=block-start]]:flex-col has-[>textarea]:h-auto dark:bg-input/30 dark:has-[[data-slot][aria-invalid=true]]:ring-destructive/40 has-[>[data-align=block-end]]:[&>input]:pt-3 has-[>[data-align=block-start]]:[&>input]:pb-3 has-[>[data-align=inline-end]]:[&>input]:pr-1.5 has-[>[data-align=inline-start]]:[&>input]:pl-1.5",
1799
1855
  className
1800
1856
  ),
1801
1857
  ...props
@@ -1803,14 +1859,14 @@ function InputGroup({ className, ...props }) {
1803
1859
  );
1804
1860
  }
1805
1861
  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",
1862
+ "flex h-auto cursor-text items-center justify-center gap-2 py-1.5 text-sm font-medium text-muted-foreground select-none group-data-[disabled=true]/input-group:opacity-50 [&>kbd]:rounded-[calc(var(--radius)-5px)] [&>svg:not([class*='size-'])]:size-4",
1807
1863
  {
1808
1864
  variants: {
1809
1865
  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"
1866
+ "inline-start": "order-first pl-2 has-[>button]:-ml-1 has-[>kbd]:ml-[-0.15rem]",
1867
+ "inline-end": "order-last pr-2 has-[>button]:-mr-1 has-[>kbd]:mr-[-0.15rem]",
1868
+ "block-start": "order-first w-full justify-start px-2.5 pt-2 group-has-[>input]/input-group:pt-2 [.border-b]:pb-2",
1869
+ "block-end": "order-last w-full justify-start px-2.5 pb-2 group-has-[>input]/input-group:pb-2 [.border-t]:pt-2"
1814
1870
  }
1815
1871
  },
1816
1872
  defaultVariants: {
@@ -1845,8 +1901,8 @@ cva(
1845
1901
  {
1846
1902
  variants: {
1847
1903
  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",
1904
+ xs: "h-6 gap-1 rounded-[calc(var(--radius)-5px)] px-1.5 [&>svg:not([class*='size-'])]:size-3.5",
1905
+ sm: "",
1850
1906
  "icon-xs": "size-6 rounded-[calc(var(--radius)-5px)] p-0 has-[>svg]:p-0",
1851
1907
  "icon-sm": "size-8 p-0 has-[>svg]:p-0"
1852
1908
  }
@@ -1930,7 +1986,7 @@ function CommandItem({
1930
1986
  {
1931
1987
  "data-slot": "command-item",
1932
1988
  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",
1989
+ "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
1990
  className
1935
1991
  ),
1936
1992
  ...props,
@@ -1952,10 +2008,10 @@ function Skeleton({ className, ...props }) {
1952
2008
  );
1953
2009
  }
1954
2010
  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(() => {
2011
+ const [query, setQuery] = React17.useState("");
2012
+ const [results, setResults] = React17.useState([]);
2013
+ const [loading, setLoading] = React17.useState(false);
2014
+ React17.useEffect(() => {
1959
2015
  if (!fetchList) return;
1960
2016
  setLoading(true);
1961
2017
  const timer = setTimeout(async () => {
@@ -2001,7 +2057,7 @@ function ExternalField({ value, onChange, fetchList, readOnly, label }) {
2001
2057
  ] }) });
2002
2058
  }
2003
2059
 
2004
- // src/components/overrides/fields/FieldTypesRegistry.ts
2060
+ // src/core/overrides/fields/FieldTypesRegistry.ts
2005
2061
  var fieldTypesRegistry = {
2006
2062
  text: TextField,
2007
2063
  textarea: TextareaField,
@@ -2019,8 +2075,6 @@ function PuckRoot({ children }) {
2019
2075
  return /* @__PURE__ */ jsx(Fragment, { children });
2020
2076
  }
2021
2077
  var puckOverrides = {
2022
- header: EditorHeader,
2023
- headerActions: EditorHeaderActions,
2024
2078
  drawer: EditorDrawer,
2025
2079
  components: EditorComponents,
2026
2080
  drawerItem: DrawerItem,
@@ -2040,9 +2094,9 @@ var DEFAULT_BOUNDS_OFFSET = {
2040
2094
  width: 0,
2041
2095
  height: 0
2042
2096
  };
2043
- var HighlightContext = React18.createContext(void 0);
2097
+ var HighlightContext = React17.createContext(void 0);
2044
2098
  function useHighlight() {
2045
- const context = React18.useContext(HighlightContext);
2099
+ const context = React17.useContext(HighlightContext);
2046
2100
  if (!context) {
2047
2101
  throw new Error("useHighlight must be used within a HighlightProvider");
2048
2102
  }
@@ -2069,21 +2123,21 @@ function Highlight({
2069
2123
  exitDelay = 200,
2070
2124
  mode = "children"
2071
2125
  } = props;
2072
- const localRef = React18.useRef(null);
2073
- React18.useImperativeHandle(ref, () => localRef.current);
2126
+ const localRef = React17.useRef(null);
2127
+ React17.useImperativeHandle(ref, () => localRef.current);
2074
2128
  const propsBoundsOffset = props?.boundsOffset;
2075
2129
  const boundsOffset = propsBoundsOffset ?? DEFAULT_BOUNDS_OFFSET;
2076
2130
  const boundsOffsetTop = boundsOffset.top ?? 0;
2077
2131
  const boundsOffsetLeft = boundsOffset.left ?? 0;
2078
2132
  const boundsOffsetWidth = boundsOffset.width ?? 0;
2079
2133
  const boundsOffsetHeight = boundsOffset.height ?? 0;
2080
- const boundsOffsetRef = React18.useRef({
2134
+ const boundsOffsetRef = React17.useRef({
2081
2135
  top: boundsOffsetTop,
2082
2136
  left: boundsOffsetLeft,
2083
2137
  width: boundsOffsetWidth,
2084
2138
  height: boundsOffsetHeight
2085
2139
  });
2086
- React18.useEffect(() => {
2140
+ React17.useEffect(() => {
2087
2141
  boundsOffsetRef.current = {
2088
2142
  top: boundsOffsetTop,
2089
2143
  left: boundsOffsetLeft,
@@ -2096,11 +2150,11 @@ function Highlight({
2096
2150
  boundsOffsetWidth,
2097
2151
  boundsOffsetHeight
2098
2152
  ]);
2099
- const [activeValue, setActiveValue] = React18.useState(
2153
+ const [activeValue, setActiveValue] = React17.useState(
2100
2154
  value ?? defaultValue ?? null
2101
2155
  );
2102
- const [boundsState, setBoundsState] = React18.useState(null);
2103
- const [activeClassNameState, setActiveClassNameState] = React18.useState("");
2156
+ const [boundsState, setBoundsState] = React17.useState(null);
2157
+ const [activeClassNameState, setActiveClassNameState] = React17.useState("");
2104
2158
  const safeSetActiveValue = (id2) => {
2105
2159
  setActiveValue((prev) => {
2106
2160
  if (prev !== id2) {
@@ -2110,8 +2164,8 @@ function Highlight({
2110
2164
  return prev;
2111
2165
  });
2112
2166
  };
2113
- const safeSetBoundsRef = React18.useRef(void 0);
2114
- React18.useEffect(() => {
2167
+ const safeSetBoundsRef = React17.useRef(void 0);
2168
+ React17.useEffect(() => {
2115
2169
  safeSetBoundsRef.current = (bounds) => {
2116
2170
  if (!localRef.current) return;
2117
2171
  const containerRect = localRef.current.getBoundingClientRect();
@@ -2133,15 +2187,15 @@ function Highlight({
2133
2187
  const safeSetBounds = (bounds) => {
2134
2188
  safeSetBoundsRef.current?.(bounds);
2135
2189
  };
2136
- const clearBounds = React18.useCallback(() => {
2190
+ const clearBounds = React17.useCallback(() => {
2137
2191
  setBoundsState((prev) => prev === null ? prev : null);
2138
2192
  }, []);
2139
- React18.useEffect(() => {
2193
+ React17.useEffect(() => {
2140
2194
  if (value !== void 0) setActiveValue(value);
2141
2195
  else if (defaultValue !== void 0) setActiveValue(defaultValue);
2142
2196
  }, [value, defaultValue]);
2143
- const id = React18.useId();
2144
- React18.useEffect(() => {
2197
+ const id = React17.useId();
2198
+ React17.useEffect(() => {
2145
2199
  if (mode !== "parent") return;
2146
2200
  const container = localRef.current;
2147
2201
  if (!container) return;
@@ -2226,7 +2280,7 @@ function Highlight({
2226
2280
  forceUpdateBounds: props?.forceUpdateBounds
2227
2281
  },
2228
2282
  children: enabled ? controlledItems ? render(children) : render(
2229
- React18.Children.map(children, (child, index) => /* @__PURE__ */ jsx(HighlightItem, { className: props?.itemsClassName, children: child }, index))
2283
+ React17.Children.map(children, (child, index) => /* @__PURE__ */ jsx(HighlightItem, { className: props?.itemsClassName, children: child }, index))
2230
2284
  ) : children
2231
2285
  }
2232
2286
  );
@@ -2258,7 +2312,7 @@ function HighlightItem({
2258
2312
  forceUpdateBounds,
2259
2313
  ...props
2260
2314
  }) {
2261
- const itemId = React18.useId();
2315
+ const itemId = React17.useId();
2262
2316
  const {
2263
2317
  activeValue,
2264
2318
  setActiveValue,
@@ -2283,12 +2337,12 @@ function HighlightItem({
2283
2337
  const isActive = activeValue === childValue;
2284
2338
  const isDisabled = disabled === void 0 ? contextDisabled : disabled;
2285
2339
  const itemTransition = transition ?? contextTransition;
2286
- const localRef = React18.useRef(null);
2287
- React18.useImperativeHandle(ref, () => localRef.current);
2288
- const refCallback = React18.useCallback((node) => {
2340
+ const localRef = React17.useRef(null);
2341
+ React17.useImperativeHandle(ref, () => localRef.current);
2342
+ const refCallback = React17.useCallback((node) => {
2289
2343
  localRef.current = node;
2290
2344
  }, []);
2291
- React18.useEffect(() => {
2345
+ React17.useEffect(() => {
2292
2346
  if (mode !== "parent") return;
2293
2347
  let rafId;
2294
2348
  let previousBounds = null;
@@ -2322,7 +2376,7 @@ function HighlightItem({
2322
2376
  forceUpdateBounds,
2323
2377
  contextForceUpdateBounds
2324
2378
  ]);
2325
- if (!React18.isValidElement(children)) return children;
2379
+ if (!React17.isValidElement(children)) return children;
2326
2380
  const dataAttributes = {
2327
2381
  "data-active": isActive ? "true" : "false",
2328
2382
  "aria-selected": isActive,
@@ -2347,7 +2401,7 @@ function HighlightItem({
2347
2401
  } : {};
2348
2402
  if (asChild) {
2349
2403
  if (mode === "children") {
2350
- return React18.cloneElement(
2404
+ return React17.cloneElement(
2351
2405
  element,
2352
2406
  {
2353
2407
  key: childValue,
@@ -2399,7 +2453,7 @@ function HighlightItem({
2399
2453
  ] })
2400
2454
  );
2401
2455
  }
2402
- return React18.cloneElement(element, {
2456
+ return React17.cloneElement(element, {
2403
2457
  ref: refCallback,
2404
2458
  ...getNonOverridingDataAttributes(element, {
2405
2459
  ...dataAttributes,
@@ -2443,7 +2497,7 @@ function HighlightItem({
2443
2497
  ...dataAttributes
2444
2498
  }
2445
2499
  ) }),
2446
- React18.cloneElement(element, {
2500
+ React17.cloneElement(element, {
2447
2501
  style: { position: "relative", zIndex: 1 },
2448
2502
  className: element.props.className,
2449
2503
  ...getNonOverridingDataAttributes(element, {
@@ -2490,11 +2544,11 @@ function Slot({
2490
2544
  ...props
2491
2545
  }) {
2492
2546
  const isAlreadyMotion = typeof children.type === "object" && children.type !== null && isMotionComponent(children.type);
2493
- const Base = React18.useMemo(
2547
+ const Base = React17.useMemo(
2494
2548
  () => isAlreadyMotion ? children.type : motion.create(children.type),
2495
2549
  [isAlreadyMotion, children.type]
2496
2550
  );
2497
- if (!React18.isValidElement(children)) return null;
2551
+ if (!React17.isValidElement(children)) return null;
2498
2552
  const { ref: childRef, ...childProps } = children.props;
2499
2553
  const mergedProps = mergeProps4(childProps, props);
2500
2554
  return /* @__PURE__ */ jsx(Base, { ...mergedProps, ref: mergeRefs(childRef, ref) });
@@ -2574,6 +2628,52 @@ function TabsTab2({ className, ...props }) {
2574
2628
  }
2575
2629
  ) });
2576
2630
  }
2631
+ var ACTIVE_TABS = [
2632
+ "insert",
2633
+ "layer",
2634
+ "image",
2635
+ "text",
2636
+ "copilot"
2637
+ ];
2638
+ var activeTabSet = new Set(ACTIVE_TABS);
2639
+ function isActiveTab(value) {
2640
+ return activeTabSet.has(value);
2641
+ }
2642
+ function createEditorUiStore(storeId) {
2643
+ return createStore()(
2644
+ persist(
2645
+ (set) => ({
2646
+ drawerSearch: "",
2647
+ setDrawerSearch: (q) => set({ drawerSearch: q }),
2648
+ drawerCollapsed: {},
2649
+ toggleDrawerGroup: (group) => set((s) => ({
2650
+ drawerCollapsed: { ...s.drawerCollapsed, [group]: !s.drawerCollapsed[group] }
2651
+ })),
2652
+ activeTab: "insert",
2653
+ setActiveTab: (tab) => set({ activeTab: tab }),
2654
+ outlineExpanded: {},
2655
+ toggleOutlineItem: (id) => set((s) => ({
2656
+ outlineExpanded: { ...s.outlineExpanded, [id]: !s.outlineExpanded[id] }
2657
+ })),
2658
+ theme: "light",
2659
+ toggleTheme: () => set((s) => {
2660
+ const next = s.theme === "light" ? "dark" : "light";
2661
+ return { theme: next };
2662
+ })
2663
+ }),
2664
+ {
2665
+ name: `anvilkit-ui-${storeId}`,
2666
+ partialize: (s) => ({
2667
+ activeTab: s.activeTab,
2668
+ drawerCollapsed: s.drawerCollapsed,
2669
+ outlineExpanded: s.outlineExpanded,
2670
+ theme: s.theme
2671
+ // drawerSearch intentionally excluded — transient input
2672
+ })
2673
+ }
2674
+ )
2675
+ );
2676
+ }
2577
2677
  function Aside() {
2578
2678
  const activeTab = useActiveTab();
2579
2679
  const setActiveTab = useSetActiveTab();
@@ -2584,10 +2684,35 @@ function Aside() {
2584
2684
  { value: "text", icon: /* @__PURE__ */ jsx(Type, { className: "h-4 w-4" }), label: useMsg("aside.text") },
2585
2685
  { value: "copilot", icon: /* @__PURE__ */ jsx(Bot, { className: "h-4 w-4" }), label: useMsg("aside.copilot") }
2586
2686
  ];
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)) }) }) }) });
2687
+ 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(
2688
+ Tabs2,
2689
+ {
2690
+ value: activeTab,
2691
+ onValueChange: (value) => {
2692
+ if (isActiveTab(value)) {
2693
+ setActiveTab(value);
2694
+ }
2695
+ },
2696
+ className: "w-full flex items-center justify-center",
2697
+ children: /* @__PURE__ */ jsx(TabsList2, { className: "flex-col h-auto bg-transparent gap-2", children: tabs.map(({ value, icon, label }) => /* @__PURE__ */ jsxs(Tooltip, { children: [
2698
+ /* @__PURE__ */ jsx(
2699
+ TooltipTrigger,
2700
+ {
2701
+ render: /* @__PURE__ */ jsx(
2702
+ TabsTab2,
2703
+ {
2704
+ value,
2705
+ className: "p-2",
2706
+ "aria-label": label
2707
+ }
2708
+ ),
2709
+ children: icon
2710
+ }
2711
+ ),
2712
+ /* @__PURE__ */ jsx(TooltipContent, { side: "right", children: label })
2713
+ ] }, value)) })
2714
+ }
2715
+ ) }) });
2591
2716
  }
2592
2717
  function AnimatedAsana({
2593
2718
  trigger = false,
@@ -2779,10 +2904,10 @@ function AnimatedAsana({
2779
2904
  }
2780
2905
  );
2781
2906
  }
2782
- React18__default.memo(function TriggerableAsana2(props) {
2907
+ React17__default.memo(function TriggerableAsana2(props) {
2783
2908
  return /* @__PURE__ */ jsx(AnimatedAsana, { useTrigger: true, ...props });
2784
2909
  });
2785
- React18__default.memo(function ControlledAsana2(props) {
2910
+ React17__default.memo(function ControlledAsana2(props) {
2786
2911
  return /* @__PURE__ */ jsx(AnimatedAsana, { useTrigger: false, ...props });
2787
2912
  });
2788
2913
  var [GlobalTooltipProvider, useGlobalTooltip] = getStrictContext("GlobalTooltipProvider");
@@ -2808,12 +2933,12 @@ function TooltipProvider2({
2808
2933
  closeDelay = 300,
2809
2934
  transition = { type: "spring", stiffness: 300, damping: 35 }
2810
2935
  }) {
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(
2936
+ const globalId = React17.useId();
2937
+ const [currentTooltip, setCurrentTooltip] = React17.useState(null);
2938
+ const timeoutRef = React17.useRef(null);
2939
+ const lastCloseTimeRef = React17.useRef(0);
2940
+ const referenceElRef = React17.useRef(null);
2941
+ const showTooltip = React17.useCallback(
2817
2942
  (data) => {
2818
2943
  if (timeoutRef.current) clearTimeout(timeoutRef.current);
2819
2944
  if (currentTooltip !== null) {
@@ -2829,22 +2954,22 @@ function TooltipProvider2({
2829
2954
  },
2830
2955
  [openDelay, closeDelay, currentTooltip]
2831
2956
  );
2832
- const hideTooltip = React18.useCallback(() => {
2957
+ const hideTooltip = React17.useCallback(() => {
2833
2958
  if (timeoutRef.current) clearTimeout(timeoutRef.current);
2834
2959
  timeoutRef.current = window.setTimeout(() => {
2835
2960
  setCurrentTooltip(null);
2836
2961
  lastCloseTimeRef.current = Date.now();
2837
2962
  }, closeDelay);
2838
2963
  }, [closeDelay]);
2839
- const hideImmediate = React18.useCallback(() => {
2964
+ const hideImmediate = React17.useCallback(() => {
2840
2965
  if (timeoutRef.current) clearTimeout(timeoutRef.current);
2841
2966
  setCurrentTooltip(null);
2842
2967
  lastCloseTimeRef.current = Date.now();
2843
2968
  }, []);
2844
- const setReferenceEl = React18.useCallback((el) => {
2969
+ const setReferenceEl = React17.useCallback((el) => {
2845
2970
  referenceElRef.current = el;
2846
2971
  }, []);
2847
- React18.useEffect(() => {
2972
+ React17.useEffect(() => {
2848
2973
  const onKeyDown = (e) => {
2849
2974
  if (e.key === "Escape") hideImmediate();
2850
2975
  };
@@ -2888,7 +3013,7 @@ function TooltipArrow({
2888
3013
  const { side, align, open } = useRenderedTooltip();
2889
3014
  const { context, arrowRef } = useFloatingContext();
2890
3015
  const { transition, globalId } = useGlobalTooltip();
2891
- React18.useImperativeHandle(ref, () => arrowRef.current);
3016
+ React17.useImperativeHandle(ref, () => arrowRef.current);
2892
3017
  const deg = { top: 0, right: 90, bottom: 180, left: -90 }[side];
2893
3018
  return /* @__PURE__ */ jsx(
2894
3019
  MotionTooltipArrow,
@@ -2911,8 +3036,8 @@ function TooltipPortal(props) {
2911
3036
  }
2912
3037
  function TooltipOverlay() {
2913
3038
  const { currentTooltip, transition, globalId, referenceElRef } = useGlobalTooltip();
2914
- const [rendered, setRendered] = React18.useState({ data: null, open: false });
2915
- const arrowRef = React18.useRef(null);
3039
+ const [rendered, setRendered] = React17.useState({ data: null, open: false });
3040
+ const arrowRef = React17.useRef(null);
2916
3041
  const side = rendered.data?.side ?? "top";
2917
3042
  const align = rendered.data?.align ?? "center";
2918
3043
  const { refs, x, y, strategy, context, update } = useFloating({
@@ -2928,14 +3053,14 @@ function TooltipOverlay() {
2928
3053
  arrow({ element: arrowRef })
2929
3054
  ]
2930
3055
  });
2931
- React18.useEffect(() => {
3056
+ React17.useEffect(() => {
2932
3057
  if (currentTooltip) {
2933
3058
  setRendered({ data: currentTooltip, open: true });
2934
3059
  } else {
2935
3060
  setRendered((p) => p.data ? { ...p, open: false } : p);
2936
3061
  }
2937
3062
  }, [currentTooltip]);
2938
- React18.useLayoutEffect(() => {
3063
+ React17.useLayoutEffect(() => {
2939
3064
  if (referenceElRef.current) {
2940
3065
  refs.setReference(referenceElRef.current);
2941
3066
  update();
@@ -3014,9 +3139,9 @@ function Tooltip2({
3014
3139
  align = "center",
3015
3140
  alignOffset = 0
3016
3141
  }) {
3017
- const id = React18.useId();
3018
- const [props, setProps] = React18.useState({});
3019
- const [asChild, setAsChild] = React18.useState(false);
3142
+ const id = React17.useId();
3143
+ const [props, setProps] = React17.useState({});
3144
+ const [asChild, setAsChild] = React17.useState(false);
3020
3145
  return /* @__PURE__ */ jsx(
3021
3146
  LocalTooltipProvider,
3022
3147
  {
@@ -3048,16 +3173,16 @@ function shallowEqualWithoutChildren(a, b) {
3048
3173
  }
3049
3174
  function TooltipContent2({ asChild = false, ...props }) {
3050
3175
  const { setProps, setAsChild } = useTooltip();
3051
- const lastPropsRef = React18.useRef(
3176
+ const lastPropsRef = React17.useRef(
3052
3177
  void 0
3053
3178
  );
3054
- React18.useEffect(() => {
3179
+ React17.useEffect(() => {
3055
3180
  if (!shallowEqualWithoutChildren(lastPropsRef.current, props)) {
3056
3181
  lastPropsRef.current = props;
3057
3182
  setProps(props);
3058
3183
  }
3059
3184
  }, [props, setProps]);
3060
- React18.useEffect(() => {
3185
+ React17.useEffect(() => {
3061
3186
  setAsChild(asChild);
3062
3187
  }, [asChild, setAsChild]);
3063
3188
  return null;
@@ -3088,10 +3213,10 @@ function TooltipTrigger2({
3088
3213
  currentTooltip,
3089
3214
  setReferenceEl
3090
3215
  } = useGlobalTooltip();
3091
- const triggerRef = React18.useRef(null);
3092
- React18.useImperativeHandle(ref, () => triggerRef.current);
3093
- const suppressNextFocusRef = React18.useRef(false);
3094
- const handleOpen = React18.useCallback(() => {
3216
+ const triggerRef = React17.useRef(null);
3217
+ React17.useImperativeHandle(ref, () => triggerRef.current);
3218
+ const suppressNextFocusRef = React17.useRef(false);
3219
+ const handleOpen = React17.useCallback(() => {
3095
3220
  if (!triggerRef.current) return;
3096
3221
  setReferenceEl(triggerRef.current);
3097
3222
  const rect = triggerRef.current.getBoundingClientRect();
@@ -3116,7 +3241,7 @@ function TooltipTrigger2({
3116
3241
  alignOffset,
3117
3242
  id
3118
3243
  ]);
3119
- const handlePointerDown = React18.useCallback(
3244
+ const handlePointerDown = React17.useCallback(
3120
3245
  (e) => {
3121
3246
  onPointerDown?.(e);
3122
3247
  if (currentTooltip?.id === id) {
@@ -3129,21 +3254,21 @@ function TooltipTrigger2({
3129
3254
  },
3130
3255
  [onPointerDown, currentTooltip?.id, id, hideImmediate]
3131
3256
  );
3132
- const handleMouseEnter = React18.useCallback(
3257
+ const handleMouseEnter = React17.useCallback(
3133
3258
  (e) => {
3134
3259
  onMouseEnter?.(e);
3135
3260
  handleOpen();
3136
3261
  },
3137
3262
  [handleOpen, onMouseEnter]
3138
3263
  );
3139
- const handleMouseLeave = React18.useCallback(
3264
+ const handleMouseLeave = React17.useCallback(
3140
3265
  (e) => {
3141
3266
  onMouseLeave?.(e);
3142
3267
  hideTooltip();
3143
3268
  },
3144
3269
  [hideTooltip, onMouseLeave]
3145
3270
  );
3146
- const handleFocus = React18.useCallback(
3271
+ const handleFocus = React17.useCallback(
3147
3272
  (e) => {
3148
3273
  onFocus?.(e);
3149
3274
  if (suppressNextFocusRef.current) return;
@@ -3151,7 +3276,7 @@ function TooltipTrigger2({
3151
3276
  },
3152
3277
  [handleOpen, onFocus]
3153
3278
  );
3154
- const handleBlur = React18.useCallback(
3279
+ const handleBlur = React17.useCallback(
3155
3280
  (e) => {
3156
3281
  onBlur?.(e);
3157
3282
  hideTooltip();
@@ -3253,7 +3378,7 @@ function AvatarGroup({
3253
3378
  children: children?.map((child, index) => /* @__PURE__ */ jsx(
3254
3379
  AvatarContainer,
3255
3380
  {
3256
- zIndex: invertOverlap ? React18.Children.count(children) - index : index,
3381
+ zIndex: invertOverlap ? React17.Children.count(children) - index : index,
3257
3382
  transition,
3258
3383
  translate,
3259
3384
  side,
@@ -3304,7 +3429,7 @@ function AvatarGroupTooltip2({
3304
3429
  ),
3305
3430
  ...props,
3306
3431
  children: [
3307
- /* @__PURE__ */ jsx(motion9.div, { layout, className: "overflow-hidden", children }),
3432
+ /* @__PURE__ */ jsx(motion8.div, { layout, className: "overflow-hidden", children }),
3308
3433
  /* @__PURE__ */ jsx(
3309
3434
  AvatarGroupTooltipArrow,
3310
3435
  {
@@ -3327,7 +3452,7 @@ function Avatar({
3327
3452
  "data-slot": "avatar",
3328
3453
  "data-size": size,
3329
3454
  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",
3455
+ "group/avatar relative flex size-8 shrink-0 rounded-full select-none after:absolute after:inset-0 after:rounded-full after:border after:border-border after:mix-blend-darken data-[size=lg]:size-10 data-[size=sm]:size-6 dark:after:mix-blend-lighten",
3331
3456
  className
3332
3457
  ),
3333
3458
  ...props
@@ -3340,7 +3465,7 @@ function AvatarImage({ className, ...props }) {
3340
3465
  {
3341
3466
  "data-slot": "avatar-image",
3342
3467
  className: cn(
3343
- "rounded-full aspect-square size-full object-cover",
3468
+ "aspect-square size-full rounded-full object-cover",
3344
3469
  className
3345
3470
  ),
3346
3471
  ...props
@@ -3356,7 +3481,7 @@ function AvatarFallback({
3356
3481
  {
3357
3482
  "data-slot": "avatar-fallback",
3358
3483
  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",
3484
+ "flex size-full items-center justify-center rounded-full bg-muted text-sm text-muted-foreground group-data-[size=sm]/avatar:text-xs",
3360
3485
  className
3361
3486
  ),
3362
3487
  ...props
@@ -3383,8 +3508,8 @@ var Collaborators = () => {
3383
3508
  ] }, index)) });
3384
3509
  };
3385
3510
  var Share = () => {
3386
- const [access, setAccess] = React18.useState("private");
3387
- const [copied, setCopied] = React18.useState(false);
3511
+ const [access, setAccess] = React17.useState("private");
3512
+ const [copied, setCopied] = React17.useState(false);
3388
3513
  const shareUrl = typeof window !== "undefined" ? window.location.href : "";
3389
3514
  const shareButton = useMsg("share.button");
3390
3515
  const shareTitle = useMsg("share.title");
@@ -3497,6 +3622,78 @@ var CollaboratorsPopover = () => {
3497
3622
  ] })
3498
3623
  ] });
3499
3624
  };
3625
+
3626
+ // src/features/export/export-json.ts
3627
+ function getFilename(filenameBase) {
3628
+ const safeBase = filenameBase?.trim() || "page";
3629
+ return `${safeBase}.json`;
3630
+ }
3631
+ function exportDataAsJson(data, { filenameBase, document: targetDocument } = {}) {
3632
+ const resolvedDocument = targetDocument ?? (typeof document !== "undefined" ? document : void 0);
3633
+ if (!resolvedDocument) return;
3634
+ const blob = new Blob([JSON.stringify(data, null, 2)], {
3635
+ type: "application/json"
3636
+ });
3637
+ const url = URL.createObjectURL(blob);
3638
+ const link = resolvedDocument.createElement("a");
3639
+ link.href = url;
3640
+ link.download = getFilename(filenameBase);
3641
+ link.click();
3642
+ URL.revokeObjectURL(url);
3643
+ }
3644
+ function DropdownMenu({ ...props }) {
3645
+ return /* @__PURE__ */ jsx(Menu.Root, { "data-slot": "dropdown-menu", ...props });
3646
+ }
3647
+ function DropdownMenuTrigger({ ...props }) {
3648
+ return /* @__PURE__ */ jsx(Menu.Trigger, { "data-slot": "dropdown-menu-trigger", ...props });
3649
+ }
3650
+ function DropdownMenuContent({
3651
+ align = "start",
3652
+ alignOffset = 0,
3653
+ side = "bottom",
3654
+ sideOffset = 4,
3655
+ className,
3656
+ ...props
3657
+ }) {
3658
+ return /* @__PURE__ */ jsx(Menu.Portal, { children: /* @__PURE__ */ jsx(
3659
+ Menu.Positioner,
3660
+ {
3661
+ className: "isolate z-50 outline-none",
3662
+ align,
3663
+ alignOffset,
3664
+ side,
3665
+ sideOffset,
3666
+ children: /* @__PURE__ */ jsx(
3667
+ Menu.Popup,
3668
+ {
3669
+ "data-slot": "dropdown-menu-content",
3670
+ className: cn("z-50 max-h-(--available-height) w-(--anchor-width) min-w-32 origin-(--transform-origin) overflow-x-hidden overflow-y-auto rounded-md bg-popover p-1 text-popover-foreground shadow-md ring-1 ring-foreground/10 duration-100 outline-none data-[side=bottom]:slide-in-from-top-2 data-[side=inline-end]:slide-in-from-left-2 data-[side=inline-start]:slide-in-from-right-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 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:overflow-hidden data-closed:fade-out-0 data-closed:zoom-out-95", className),
3671
+ ...props
3672
+ }
3673
+ )
3674
+ }
3675
+ ) });
3676
+ }
3677
+ function DropdownMenuItem({
3678
+ className,
3679
+ inset,
3680
+ variant = "default",
3681
+ ...props
3682
+ }) {
3683
+ return /* @__PURE__ */ jsx(
3684
+ Menu.Item,
3685
+ {
3686
+ "data-slot": "dropdown-menu-item",
3687
+ "data-inset": inset,
3688
+ "data-variant": variant,
3689
+ className: cn(
3690
+ "group/dropdown-menu-item relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground not-data-[variant=destructive]:focus:**:text-accent-foreground data-inset:pl-8 data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 data-[variant=destructive]:focus:text-destructive dark:data-[variant=destructive]:focus:bg-destructive/20 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 data-[variant=destructive]:*:[svg]:text-destructive",
3691
+ className
3692
+ ),
3693
+ ...props
3694
+ }
3695
+ );
3696
+ }
3500
3697
  var Header = () => {
3501
3698
  const { history, appState } = usePuck();
3502
3699
  const publish = useMsg("header.publish");
@@ -3511,9 +3708,7 @@ var Header = () => {
3511
3708
  const themeLightLabel = useMsg("header.theme.light");
3512
3709
  const themeDarkLabel = useMsg("header.theme.dark");
3513
3710
  const themeLabel = theme === "dark" ? themeLightLabel : themeDarkLabel;
3514
- React18.useEffect(() => {
3515
- document.documentElement.classList.toggle("dark", theme === "dark");
3516
- }, []);
3711
+ useThemeSync();
3517
3712
  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
3713
  /* @__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
3714
  /* @__PURE__ */ jsxs("div", { className: "relative flex flex-1 w-full px-2", children: [
@@ -3521,31 +3716,41 @@ var Header = () => {
3521
3716
  /* @__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
3717
  /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-center gap-2 h-full ml-auto", children: [
3523
3718
  /* @__PURE__ */ jsxs(Tooltip, { children: [
3524
- /* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
3525
- Button,
3719
+ /* @__PURE__ */ jsx(
3720
+ TooltipTrigger,
3526
3721
  {
3527
- variant: "ghost",
3528
- size: "icon",
3529
- disabled: !history.hasPast,
3530
- onClick: () => history.back(),
3531
- "aria-label": undo,
3722
+ render: /* @__PURE__ */ jsx(
3723
+ Button,
3724
+ {
3725
+ variant: "ghost",
3726
+ size: "icon",
3727
+ disabled: !history.hasPast,
3728
+ onClick: () => history.back(),
3729
+ "aria-label": undo
3730
+ }
3731
+ ),
3532
3732
  children: /* @__PURE__ */ jsx(Undo2, { className: "h-4 w-4" })
3533
3733
  }
3534
- ) }),
3734
+ ),
3535
3735
  /* @__PURE__ */ jsx(TooltipContent, { children: undoTooltip })
3536
3736
  ] }),
3537
3737
  /* @__PURE__ */ jsxs(Tooltip, { children: [
3538
- /* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
3539
- Button,
3738
+ /* @__PURE__ */ jsx(
3739
+ TooltipTrigger,
3540
3740
  {
3541
- variant: "ghost",
3542
- size: "icon",
3543
- disabled: !history.hasFuture,
3544
- onClick: () => history.forward(),
3545
- "aria-label": redo,
3741
+ render: /* @__PURE__ */ jsx(
3742
+ Button,
3743
+ {
3744
+ variant: "ghost",
3745
+ size: "icon",
3746
+ disabled: !history.hasFuture,
3747
+ onClick: () => history.forward(),
3748
+ "aria-label": redo
3749
+ }
3750
+ ),
3546
3751
  children: /* @__PURE__ */ jsx(Redo2, { className: "h-4 w-4" })
3547
3752
  }
3548
- ) }),
3753
+ ),
3549
3754
  /* @__PURE__ */ jsx(TooltipContent, { children: redoTooltip })
3550
3755
  ] }),
3551
3756
  /* @__PURE__ */ jsx(Separator, { orientation: "vertical", className: "h-5 mx-1" }),
@@ -3555,21 +3760,34 @@ var Header = () => {
3555
3760
  /* @__PURE__ */ jsx(Separator, { orientation: "vertical", className: "h-5 mx-1" }),
3556
3761
  /* @__PURE__ */ jsxs(DropdownMenu, { children: [
3557
3762
  /* @__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" }) }) }),
3763
+ /* @__PURE__ */ jsx(
3764
+ TooltipTrigger,
3765
+ {
3766
+ render: /* @__PURE__ */ jsx(
3767
+ DropdownMenuTrigger,
3768
+ {
3769
+ render: /* @__PURE__ */ jsx(
3770
+ Button,
3771
+ {
3772
+ variant: "ghost",
3773
+ size: "icon",
3774
+ "aria-label": exportLabel
3775
+ }
3776
+ )
3777
+ }
3778
+ ),
3779
+ children: /* @__PURE__ */ jsx(Download, { className: "h-4 w-4" })
3780
+ }
3781
+ ),
3559
3782
  /* @__PURE__ */ jsx(TooltipContent, { children: exportLabel })
3560
3783
  ] }),
3561
3784
  /* @__PURE__ */ jsx(DropdownMenuContent, { align: "end", children: /* @__PURE__ */ jsxs(
3562
3785
  DropdownMenuItem,
3563
3786
  {
3564
3787
  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);
3788
+ exportDataAsJson(appState.data, {
3789
+ filenameBase: appState.data.root?.props?.title
3790
+ });
3573
3791
  },
3574
3792
  children: [
3575
3793
  /* @__PURE__ */ jsx(FileDown, { className: "h-4 w-4" }),
@@ -3581,21 +3799,79 @@ var Header = () => {
3581
3799
  ] }),
3582
3800
  /* @__PURE__ */ jsx(Separator, { orientation: "vertical", className: "h-5 mx-1" }),
3583
3801
  /* @__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" }) }) }),
3802
+ /* @__PURE__ */ jsx(
3803
+ TooltipTrigger,
3804
+ {
3805
+ render: /* @__PURE__ */ jsx(
3806
+ Button,
3807
+ {
3808
+ variant: "ghost",
3809
+ size: "icon",
3810
+ onClick: toggleTheme,
3811
+ "aria-label": themeLabel
3812
+ }
3813
+ ),
3814
+ children: theme === "dark" ? /* @__PURE__ */ jsx(Sun, { className: "h-4 w-4" }) : /* @__PURE__ */ jsx(Moon, { className: "h-4 w-4" })
3815
+ }
3816
+ ),
3585
3817
  /* @__PURE__ */ jsx(TooltipContent, { children: themeLabel })
3586
3818
  ] }),
3587
3819
  /* @__PURE__ */ jsx(Separator, { orientation: "vertical", className: "h-5 mx-1" }),
3588
3820
  /* @__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
- ] }) }),
3821
+ /* @__PURE__ */ jsxs(
3822
+ TooltipTrigger,
3823
+ {
3824
+ render: /* @__PURE__ */ jsx(Button, { size: "sm", "aria-label": publish }),
3825
+ children: [
3826
+ /* @__PURE__ */ jsx(Send, { className: "h-4 w-4" }),
3827
+ publish
3828
+ ]
3829
+ }
3830
+ ),
3593
3831
  /* @__PURE__ */ jsx(TooltipContent, { children: publish })
3594
3832
  ] })
3595
3833
  ] })
3596
3834
  ] })
3597
3835
  ] }) });
3598
3836
  };
3837
+ function useGhostDrag({ createGhostEl }) {
3838
+ const ghostRef = React17.useRef(null);
3839
+ function moveGhost(x, y) {
3840
+ if (!ghostRef.current) return;
3841
+ ghostRef.current.style.left = `${x + 12}px`;
3842
+ ghostRef.current.style.top = `${y + 12}px`;
3843
+ }
3844
+ function removeGhost() {
3845
+ ghostRef.current?.remove();
3846
+ ghostRef.current = null;
3847
+ }
3848
+ const startDrag = React17.useCallback(
3849
+ (e, type, payload) => {
3850
+ e.stopPropagation();
3851
+ e.currentTarget.setPointerCapture(e.pointerId);
3852
+ ghostRef.current = createGhostEl(payload);
3853
+ moveGhost(e.clientX, e.clientY);
3854
+ dispatchLibraryDragStart(type);
3855
+ function onMove(ev) {
3856
+ moveGhost(ev.clientX, ev.clientY);
3857
+ }
3858
+ function onUp(ev) {
3859
+ removeGhost();
3860
+ window.removeEventListener("pointermove", onMove);
3861
+ window.removeEventListener("pointerup", onUp);
3862
+ if (type === "image") {
3863
+ dispatchImageDrop({ src: payload, clientX: ev.clientX, clientY: ev.clientY });
3864
+ } else {
3865
+ dispatchTextDrop({ text: payload, clientX: ev.clientX, clientY: ev.clientY });
3866
+ }
3867
+ }
3868
+ window.addEventListener("pointermove", onMove);
3869
+ window.addEventListener("pointerup", onUp);
3870
+ },
3871
+ [createGhostEl]
3872
+ );
3873
+ return { startDrag };
3874
+ }
3599
3875
  var DEFAULT_SEEDS = [
3600
3876
  "forest",
3601
3877
  "ocean",
@@ -3627,8 +3903,7 @@ function getSearchImages(query) {
3627
3903
  id: `${query}-${i}`
3628
3904
  }));
3629
3905
  }
3630
- var ghostEl = null;
3631
- function createGhost(src) {
3906
+ function createImageGhost(src) {
3632
3907
  const el = document.createElement("div");
3633
3908
  el.style.cssText = `
3634
3909
  position: fixed; top: -9999px; left: -9999px; z-index: 99999;
@@ -3642,63 +3917,31 @@ function createGhost(src) {
3642
3917
  document.body.appendChild(el);
3643
3918
  return el;
3644
3919
  }
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
3920
  function ImageLibrary({
3655
3921
  seeds,
3656
3922
  items: customImages
3657
3923
  } = {}) {
3658
3924
  const effectiveSeeds = seeds ?? DEFAULT_SEEDS;
3659
- const [query, setQuery] = React18.useState("");
3660
- const [committed, setCommitted] = React18.useState("");
3661
- const [items, setItems] = React18.useState(
3925
+ const [query, setQuery] = React17.useState("");
3926
+ const [committed, setCommitted] = React17.useState("");
3927
+ const [items, setItems] = React17.useState(
3662
3928
  () => customImages ?? getDefaultImages(effectiveSeeds)
3663
3929
  );
3664
3930
  const libraryTitle = useMsg("image-library.title");
3665
3931
  const searchPlaceholder = useMsg("image-library.search.placeholder");
3666
- React18.useEffect(() => {
3932
+ const { startDrag } = useGhostDrag({
3933
+ createGhostEl: createImageGhost
3934
+ });
3935
+ React17.useEffect(() => {
3667
3936
  const t = setTimeout(() => setCommitted(query.trim()), 400);
3668
3937
  return () => clearTimeout(t);
3669
3938
  }, [query]);
3670
- React18.useEffect(() => {
3939
+ React17.useEffect(() => {
3671
3940
  if (customImages) return;
3672
3941
  setItems(
3673
3942
  committed ? getSearchImages(committed) : getDefaultImages(effectiveSeeds)
3674
3943
  );
3675
3944
  }, [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
3945
  return /* @__PURE__ */ jsxs("div", { className: "flex flex-col h-full", children: [
3703
3946
  /* @__PURE__ */ jsx("div", { className: "px-3 pt-3 pb-1 text-xs font-semibold text-muted-foreground uppercase tracking-wider", children: libraryTitle }),
3704
3947
  /* @__PURE__ */ jsx("div", { className: "p-2", children: /* @__PURE__ */ jsxs("div", { className: "relative", children: [
@@ -3713,10 +3956,10 @@ function ImageLibrary({
3713
3956
  }
3714
3957
  )
3715
3958
  ] }) }),
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(
3959
+ /* @__PURE__ */ jsx(ScrollArea, { className: "flex-1", children: /* @__PURE__ */ jsx("div", { className: "p-2 grid grid-cols-2 gap-2", children: items.map((item) => /* @__PURE__ */ jsxs(
3717
3960
  "div",
3718
3961
  {
3719
- onPointerDown: (e) => handlePointerDown(e, item.src),
3962
+ onPointerDown: (e) => startDrag(e, "image", item.src),
3720
3963
  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
3964
  children: [
3722
3965
  /* @__PURE__ */ jsx(
@@ -3731,12 +3974,27 @@ function ImageLibrary({
3731
3974
  /* @__PURE__ */ jsx("div", { className: "px-1.5 py-1 text-xs text-muted-foreground truncate capitalize", children: item.alt })
3732
3975
  ]
3733
3976
  },
3734
- `${item.src}-${i}`
3977
+ item.id
3735
3978
  )) }) })
3736
3979
  ] });
3737
3980
  }
3738
- var ghostEl2 = null;
3739
- function createGhost2(text) {
3981
+ var DEFAULT_SNIPPETS = [
3982
+ { category: "Headlines", label: "Bold statement", text: "The Future Starts Here" },
3983
+ { category: "Headlines", label: "Question hook", text: "Ready to Transform Your Business?" },
3984
+ { category: "Headlines", label: "Value prop", text: "Simple, Powerful, Built for Teams" },
3985
+ { category: "Headlines", label: "Action-led", text: "Ship Faster. Break Less. Sleep Better." },
3986
+ { category: "Subheadings", label: "Feature intro", text: "Everything you need, nothing you don't." },
3987
+ { category: "Subheadings", label: "Social proof", text: "Trusted by over 10,000 teams worldwide." },
3988
+ { category: "Subheadings", label: "CTA support", text: "Get started in minutes \u2014 no credit card required." },
3989
+ { category: "Body", label: "Product description", text: "Our platform helps teams collaborate in real time, ship products faster, and stay aligned across every stage of the process." },
3990
+ { category: "Body", label: "Feature benefit", text: "With built-in analytics and smart automation, you can focus on what matters most \u2014 building great products." },
3991
+ { category: "Body", label: "About us", text: "We're a small team on a big mission: to make software development feel effortless for everyone." },
3992
+ { category: "CTAs", label: "Primary", text: "Get Started Free" },
3993
+ { category: "CTAs", label: "Secondary", text: "Learn More" },
3994
+ { category: "CTAs", label: "Soft sell", text: "See How It Works" },
3995
+ { category: "CTAs", label: "Urgency", text: "Start Your Free Trial Today" }
3996
+ ];
3997
+ function createTextGhost(text) {
3740
3998
  const el = document.createElement("div");
3741
3999
  el.style.cssText = `
3742
4000
  position: fixed; top: -9999px; left: -9999px; z-index: 99999;
@@ -3749,112 +4007,16 @@ function createGhost2(text) {
3749
4007
  document.body.appendChild(el);
3750
4008
  return el;
3751
4009
  }
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
- } = {}) {
4010
+ function CopyLibrary({ items } = {}) {
3824
4011
  const activeSnippets = items ?? DEFAULT_SNIPPETS;
3825
4012
  const categories = Array.from(new Set(activeSnippets.map((s) => s.category)));
3826
- const [query, setQuery] = React18.useState("");
4013
+ const [query, setQuery] = React17.useState("");
3827
4014
  const libraryTitle = useMsg("copy-library.title");
3828
4015
  const searchPlaceholder = useMsg("copy-library.search.placeholder");
4016
+ const { startDrag } = useGhostDrag({ createGhostEl: createTextGhost });
3829
4017
  const filtered = query.trim() ? activeSnippets.filter(
3830
4018
  (s) => s.text.toLowerCase().includes(query.toLowerCase()) || s.label.toLowerCase().includes(query.toLowerCase()) || s.category.toLowerCase().includes(query.toLowerCase())
3831
4019
  ) : 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
4020
  return /* @__PURE__ */ jsxs("div", { className: "flex flex-col h-full", children: [
3859
4021
  /* @__PURE__ */ jsx("div", { className: "px-3 pt-3 pb-1 text-xs font-semibold text-muted-foreground uppercase tracking-wider", children: libraryTitle }),
3860
4022
  /* @__PURE__ */ jsx("div", { className: "p-2", children: /* @__PURE__ */ jsxs("div", { className: "relative", children: [
@@ -3870,24 +4032,24 @@ function CopyLibrary({
3870
4032
  )
3871
4033
  ] }) }),
3872
4034
  /* @__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;
4035
+ const snippets = cat ? filtered.filter((s) => s.category === cat) : filtered;
4036
+ if (!snippets.length) return null;
3875
4037
  return /* @__PURE__ */ jsxs("div", { children: [
3876
4038
  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
4039
  /* @__PURE__ */ jsx(Type, { className: "h-3 w-3" }),
3878
4040
  cat
3879
4041
  ] }),
3880
- /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-1", children: items2.map((snippet, i) => /* @__PURE__ */ jsxs(
4042
+ /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-1", children: snippets.map((snippet) => /* @__PURE__ */ jsxs(
3881
4043
  "div",
3882
4044
  {
3883
- onPointerDown: (e) => handlePointerDown(e, snippet.text),
4045
+ onPointerDown: (e) => startDrag(e, "text", snippet.text),
3884
4046
  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
4047
  children: [
3886
4048
  /* @__PURE__ */ jsx("div", { className: "text-xs font-medium text-foreground/70 mb-0.5", children: snippet.label }),
3887
4049
  /* @__PURE__ */ jsx("div", { className: "text-xs text-foreground leading-snug line-clamp-2", children: snippet.text })
3888
4050
  ]
3889
4051
  },
3890
- i
4052
+ `${snippet.category}-${snippet.label}`
3891
4053
  )) })
3892
4054
  ] }, cat ?? "results");
3893
4055
  }) }) })
@@ -3915,42 +4077,6 @@ var EditorLayout = ({
3915
4077
  ] })
3916
4078
  ] });
3917
4079
  };
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
4080
  function createEditorI18nStore(initial) {
3955
4081
  return createStore()(
3956
4082
  persist(
@@ -4024,20 +4150,33 @@ function Studio({
4024
4150
  locale,
4025
4151
  messages
4026
4152
  }) {
4027
- const uiStore2 = React18.useRef(createEditorUiStore(storeId ?? "default")).current;
4028
- const i18nStore = React18.useRef(
4153
+ const uiStore2 = React17.useRef(createEditorUiStore(storeId ?? "default")).current;
4154
+ const i18nStore = React17.useRef(
4029
4155
  createEditorI18nStore({
4030
4156
  locale: locale ?? "zh",
4031
4157
  messages: messages ?? defaultMessages
4032
4158
  })
4033
4159
  ).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]
4160
+ React17.useEffect(() => {
4161
+ i18nStore.getState().setLocale(locale ?? "zh", messages ?? defaultMessages);
4162
+ }, [locale, messages, i18nStore]);
4163
+ const [aiPlugin, setAiPlugin] = React17.useState(null);
4164
+ React17.useEffect(() => {
4165
+ if (!aiHost) return;
4166
+ let cancelled = false;
4167
+ import('@puckeditor/plugin-ai').then(({ createAiPlugin }) => {
4168
+ if (cancelled) return;
4169
+ import('@puckeditor/plugin-ai/styles.css').catch(() => {
4170
+ });
4171
+ setAiPlugin(createAiPlugin({ host: aiHost }));
4172
+ });
4173
+ return () => {
4174
+ cancelled = true;
4175
+ };
4176
+ }, [aiHost]);
4177
+ const mergedOverrides = React17.useMemo(
4178
+ () => ({ ...aiPlugin?.overrides ?? {}, ...puckOverrides, ...overrideExtensions }),
4179
+ [aiPlugin, overrideExtensions]
4041
4180
  );
4042
4181
  return /* @__PURE__ */ jsx(EditorUiStoreProvider, { value: uiStore2, children: /* @__PURE__ */ jsx(EditorI18nStoreProvider, { value: i18nStore, children: /* @__PURE__ */ jsx("div", { className, children: /* @__PURE__ */ jsx(
4043
4182
  Puck,
@@ -4047,11 +4186,11 @@ function Studio({
4047
4186
  onPublish,
4048
4187
  onChange,
4049
4188
  overrides: mergedOverrides,
4050
- plugins: [aiPlugin],
4189
+ plugins: aiPlugin ? [aiPlugin] : [],
4051
4190
  children: /* @__PURE__ */ jsx(
4052
4191
  EditorLayout,
4053
4192
  {
4054
- aiPanel: aiPlugin.render(),
4193
+ aiPanel: aiPlugin?.render(),
4055
4194
  images,
4056
4195
  copywritings
4057
4196
  }
@@ -4061,6 +4200,6 @@ function Studio({
4061
4200
  }
4062
4201
 
4063
4202
  // src/store/index.ts
4064
- var uiStore = createEditorUiStore("default");
4203
+ createEditorUiStore("default");
4065
4204
 
4066
- export { Studio, createEditorI18nStore, createEditorUiStore, puckOverrides, uiStore };
4205
+ export { EditorI18nStoreProvider, EditorUiStoreProvider, Studio, createEditorI18nStore, createEditorUiStore, defaultMessages, puckOverrides, useEditorI18nStoreApi, useEditorUiStoreApi };