@ckc-net/puck-extended 0.3.0 → 0.5.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
@@ -4,28 +4,28 @@ import { useRender } from "@base-ui/react/use-render";
4
4
  import { cva } from "class-variance-authority";
5
5
  import { clsx } from "clsx";
6
6
  import { twMerge } from "tailwind-merge";
7
- import { AlignCenterIcon, AlignJustifyIcon, AlignLeftIcon, AlignRightIcon, BaselineIcon, BoldIcon, BoxIcon, CaseLowerIcon, CaseSensitiveIcon, CaseUpperIcon, CheckIcon, ChevronDownIcon, ChevronLeftIcon, ChevronRightIcon, ChevronUpIcon, ChevronsUpDownIcon, CircleAlertIcon, CircleCheckIcon, DatabaseIcon, FileIcon, FileTextIcon, GripVerticalIcon, InfoIcon, ItalicIcon, LightbulbIcon, LinkIcon, Loader2Icon, LoaderCircleIcon, LockIcon, MinusIcon, MoreHorizontalIcon, PlusIcon, SearchIcon, StrikethroughIcon, Trash2Icon, TriangleAlertIcon, UnderlineIcon, UploadIcon, XIcon } from "lucide-react";
7
+ import { AlignCenterIcon, AlignJustifyIcon, AlignLeftIcon, AlignRightIcon, BaselineIcon, BoldIcon, BoxIcon, CaseLowerIcon, CaseSensitiveIcon, CaseUpperIcon, CheckIcon, ChevronDownIcon, ChevronLeftIcon, ChevronRightIcon, ChevronUpIcon, ChevronsUpDownIcon, CircleAlertIcon, CircleCheckIcon, DatabaseIcon, DownloadIcon, ExternalLinkIcon, FileIcon, FilePlayIcon, FileTextIcon, GripVerticalIcon, InfoIcon, ItalicIcon, LightbulbIcon, LinkIcon, Loader2Icon, LoaderCircleIcon, LockIcon, MinusIcon, MoreHorizontalIcon, PlusIcon, SearchIcon, SlidersHorizontalIcon, SparklesIcon, StrikethroughIcon, Trash2Icon, TriangleAlertIcon, UnderlineIcon, UploadIcon, XIcon, ZoomInIcon } from "lucide-react";
8
8
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
9
- import { Tooltip as Tooltip$1 } from "@base-ui/react/tooltip";
9
+ import { Tooltip as TooltipPrimitive } from "@base-ui/react/tooltip";
10
10
  import { Checkbox as Checkbox$1 } from "@base-ui/react/checkbox";
11
11
  import { Input as Input$1 } from "@base-ui/react/input";
12
12
  import { Select as Select$1 } from "@base-ui/react/select";
13
13
  import { Radio } from "@base-ui/react/radio";
14
14
  import { RadioGroup as RadioGroup$1 } from "@base-ui/react/radio-group";
15
+ import { Switch as Switch$1 } from "@base-ui/react/switch";
15
16
  import { Field } from "@base-ui/react/field";
16
17
  import { AutoField, createUsePuck, walkTree } from "@puckeditor/core";
17
- import { Tabs as Tabs$1 } from "@base-ui/react/tabs";
18
+ import { Tabs as TabsPrimitive } from "@base-ui/react/tabs";
18
19
  import { Accordion as AccordionPrimitive } from "@base-ui/react/accordion";
19
20
  import { Popover as Popover$1 } from "@base-ui/react/popover";
20
21
  import { Dialog as Dialog$1 } from "@base-ui/react/dialog";
21
- import { ScrollArea as ScrollArea$1 } from "@base-ui/react/scroll-area";
22
+ import { ScrollArea as ScrollAreaPrimitive } from "@base-ui/react/scroll-area";
22
23
  import { flexRender, getCoreRowModel, useReactTable } from "@tanstack/react-table";
23
24
  import { Color } from "@tiptap/extension-color";
24
25
  import { TextStyle } from "@tiptap/extension-text-style";
25
26
  import { HexColorPicker } from "react-colorful";
26
- import { Menu as Menu$1 } from "@base-ui/react/menu";
27
+ import { Menu as MenuPrimitive } from "@base-ui/react/menu";
27
28
  import { Separator as Separator$1 } from "@base-ui/react/separator";
28
- import { Switch as Switch$1 } from "@base-ui/react/switch";
29
29
  import { Toast } from "@base-ui/react/toast";
30
30
 
31
31
  //#region src/lib/utils.ts
@@ -59,7 +59,7 @@ const buttonVariants = cva("[&_svg]:-mx-0.5 relative inline-flex shrink-0 cursor
59
59
  "destructive-outline": "border-input bg-transparent not-dark:bg-clip-padding text-destructive-foreground not-disabled:not-active:not-data-pressed:before:shadow-[0_1px_--theme(--color-black/6%)] dark:bg-input/32 dark:not-disabled:before:shadow-[0_-1px_--theme(--color-white/2%)] dark:not-disabled:not-active:not-data-pressed:before:shadow-[0_-1px_--theme(--color-white/6%)] [:disabled,:active,[data-pressed]]:shadow-none [:hover,[data-pressed]]:border-destructive/32 [:hover,[data-pressed]]:bg-destructive/4",
60
60
  ghost: "border-transparent data-pressed:bg-accent [:hover,[data-pressed]]:bg-accent",
61
61
  link: "border-transparent underline-offset-4 [:hover,[data-pressed]]:underline",
62
- outline: "border-input bg-background not-dark:bg-clip-padding not-disabled:not-active:not-data-pressed:before:shadow-[0_1px_--theme(--color-black/6%)] dark:bg-input/32 dark:not-disabled:before:shadow-[0_-1px_--theme(--color-white/2%)] dark:not-disabled:not-active:not-data-pressed:before:shadow-[0_-1px_--theme(--color-white/6%)] [:disabled,:active,[data-pressed]]:shadow-none [:hover,[data-pressed]]:bg-accent/50 dark:[:hover,[data-pressed]]:bg-input/64",
62
+ outline: "border-input bg-background hover:bg-gray-50 not-dark:bg-clip-padding not-disabled:not-active:not-data-pressed:before:shadow-[0_1px_--theme(--color-black/6%)] dark:bg-input/32 dark:not-disabled:before:shadow-[0_-1px_--theme(--color-white/2%)] dark:not-disabled:not-active:not-data-pressed:before:shadow-[0_-1px_--theme(--color-white/6%)] [:disabled,:active,[data-pressed]]:shadow-none",
63
63
  primary: "border-transparent from-primary-600 to-primary-500 hover:from-primary-500 hover:to-primary-600 bg-linear-to-t text-white inset-shadow-[0_2px_0_0] inset-shadow-white/25 hover:text-white",
64
64
  secondary: "border-transparent bg-secondary text-secondary-foreground [:active,[data-pressed]]:bg-secondary/80 [:hover,[data-pressed]]:bg-secondary/90"
65
65
  }
@@ -96,192 +96,61 @@ function Label$2({ className, readOnly, children, ...props }) {
96
96
 
97
97
  //#endregion
98
98
  //#region src/components/ui/tooltip.tsx
99
- const cssAnimationPresets = {
100
- none: "transition-none",
101
- scale: [`[transition-property:scale,opacity]`, `data-starting-style:scale-80 data-starting-style:opacity-0 data-ending-style:opacity-0 data-ending-style:scale-80`],
102
- fade: [`[transition-property:opacity,scale]`, `data-starting-style:scale-98 data-starting-style:opacity-0 data-ending-style:opacity-0 data-ending-style:scale-98`],
103
- slideOutside: [
104
- `[transition-property:translate,opacity]`,
105
- `data-[side=bottom]:data-starting-style:opacity-0 data-[side=bottom]:data-starting-style:translate-y-[10px] data-[side=bottom]:data-ending-style:translate-y-[10px] data-[side=bottom]:data-ending-style:opacity-0`,
106
- `data-[side=top]:data-starting-style:opacity-0 data-[side=top]:data-starting-style:translate-y-[-10px] data-[side=top]:data-ending-style:translate-y-[-10px] data-[side=top]:data-ending-style:opacity-0`,
107
- `data-[side=left]:data-starting-style:opacity-0 data-[side=left]:data-starting-style:translate-x-[-10px] data-[side=left]:data-ending-style:translate-x-[-10px] data-[side=left]:data-ending-style:opacity-0`,
108
- `data-[side=right]:data-starting-style:opacity-0 data-[side=right]:data-starting-style:translate-x-[10px] data-[side=right]:data-ending-style:translate-x-[10px] data-[side=right]:data-ending-style:opacity-0`,
109
- `data-[side=inline-start]:data-starting-style:opacity-0 data-[side=inline-start]:data-starting-style:translate-x-[-10px] data-[side=inline-start]:data-ending-style:translate-x-[-10px] data-[side=inline-start]:data-ending-style:opacity-0`,
110
- `data-[side=inline-end]:data-starting-style:opacity-0 data-[side=inline-end]:data-starting-style:translate-x-[10px] data-[side=inline-end]:data-ending-style:translate-x-[10px] data-[side=inline-end]:data-ending-style:opacity-0`
111
- ],
112
- slideInside: [
113
- `[transition-property:translate,opacity]`,
114
- `data-[side=bottom]:data-starting-style:opacity-0 data-[side=bottom]:data-starting-style:translate-y-[-10px] data-[side=bottom]:data-ending-style:translate-y-[-10px] data-[side=bottom]:data-ending-style:opacity-0`,
115
- `data-[side=top]:data-starting-style:opacity-0 data-[side=top]:data-starting-style:translate-y-[10px] data-[side=top]:data-ending-style:translate-y-[10px] data-[side=top]:data-ending-style:opacity-0`,
116
- `data-[side=left]:data-starting-style:opacity-0 data-[side=left]:data-starting-style:translate-x-[10px] data-[side=left]:data-ending-style:translate-x-[10px] data-[side=left]:data-ending-style:opacity-0`,
117
- `data-[side=right]:data-starting-style:opacity-0 data-[side=right]:data-starting-style:translate-x-[-10px] data-[side=right]:data-ending-style:translate-x-[-10px] data-[side=right]:data-ending-style:opacity-0`,
118
- `data-[side=inline-start]:data-starting-style:opacity-0 data-[side=inline-start]:data-starting-style:translate-x-[10px] data-[side=inline-start]:data-ending-style:translate-x-[10px] data-[side=inline-start]:data-ending-style:opacity-0`,
119
- `data-[side=inline-end]:data-starting-style:opacity-0 data-[side=inline-end]:data-starting-style:translate-x-[-10px] data-[side=inline-end]:data-ending-style:translate-x-[-10px] data-[side=inline-end]:data-ending-style:opacity-0`
120
- ],
121
- wipe: [
122
- `[transition-property:clip-path] [will-change:clip-path]`,
123
- `[clip-path:inset(0_0_0_0_round_var(--radius))] [-webkit-clip-path:inset(0_0_0_0_round_var(--radius))]`,
124
- `data-[side=bottom]:data-starting-style:[clip-path:inset(0_0_100%_0_round_var(--radius))] data-[side=bottom]:data-ending-style:[clip-path:inset(0_0_100%_0_round_var(--radius))]`,
125
- `data-[side=top]:data-starting-style:[clip-path:inset(100%_0_0_0_round_var(--radius))] data-[side=top]:data-ending-style:[clip-path:inset(100%_0_0_0_round_var(--radius))]`,
126
- `data-[side=left]:data-starting-style:[clip-path:inset(0_0_0_100%_round_var(--radius))] data-[side=left]:data-ending-style:[clip-path:inset(0_0_0_100%_round_var(--radius))]`,
127
- `data-[side=right]:data-starting-style:[clip-path:inset(0_100%_0_0_round_var(--radius))] data-[side=right]:data-ending-style:[clip-path:inset(0_100%_0_0_round_var(--radius))]`,
128
- `data-[side=inline-start]:data-starting-style:[clip-path:inset(0_0_0_100%_round_var(--radius))] data-[side=inline-start]:data-ending-style:[clip-path:inset(0_0_0_100%_round_var(--radius))]`,
129
- `data-[side=inline-end]:data-starting-style:[clip-path:inset(0_100%_0_0_round_var(--radius))] data-[side=inline-end]:data-ending-style:[clip-path:inset(0_100%_0_0_round_var(--radius))]`
130
- ],
131
- wipeScale: [
132
- `[transition-property:clip-path,scale] [will-change:clip-path,scale]`,
133
- `[clip-path:inset(0_0_0_0_round_var(--radius))] [-webkit-clip-path:inset(0_0_0_0_round_var(--radius))]`,
134
- `data-starting-style:scale-80 data-ending-style:scale-80`,
135
- `data-[side=bottom]:data-starting-style:[clip-path:inset(0_0_100%_0_round_var(--radius))] data-[side=bottom]:data-ending-style:[clip-path:inset(0_0_100%_0_round_var(--radius))]`,
136
- `data-[side=top]:data-starting-style:[clip-path:inset(100%_0_0_0_round_var(--radius))] data-[side=top]:data-ending-style:[clip-path:inset(100%_0_0_0_round_var(--radius))]`,
137
- `data-[side=left]:data-starting-style:[clip-path:inset(0_0_0_100%_round_var(--radius))] data-[side=left]:data-ending-style:[clip-path:inset(0_0_0_100%_round_var(--radius))]`,
138
- `data-[side=right]:data-starting-style:[clip-path:inset(0_100%_0_0_round_var(--radius))] data-[side=right]:data-ending-style:[clip-path:inset(0_100%_0_0_round_var(--radius))]`,
139
- `data-[side=inline-start]:data-starting-style:[clip-path:inset(0_0_0_100%_round_var(--radius))] data-[side=inline-start]:data-ending-style:[clip-path:inset(0_0_0_100%_round_var(--radius))]`,
140
- `data-[side=inline-end]:data-starting-style:[clip-path:inset(0_100%_0_0_round_var(--radius))] data-[side=inline-end]:data-ending-style:[clip-path:inset(0_100%_0_0_round_var(--radius))]`
141
- ],
142
- motion: [
143
- `[transition-property:translate,scale,opacity,rotateX,rotateY,transform] [will-change:translate,scale,opacity,rotateX,rotateY,transform]`,
144
- `[transform:perspective(1000px)]`,
145
- `data-[side=bottom]:data-starting-style:translate-y-[7px] data-[side=bottom]:data-starting-style:opacity-0 data-[side=bottom]:data-starting-style:scale-[0.26] data-[side=bottom]:data-starting-style:rotate-x-[70deg] data-[side=bottom]:data-ending-style:translate-y-[7px] data-[side=bottom]:data-ending-style:opacity-0 data-[side=bottom]:data-ending-style:scale-[0.26] data-[side=bottom]:data-ending-style:rotate-x-[70deg]`,
146
- `data-[side=top]:data-starting-style:translate-y-[7px] data-[side=top]:data-starting-style:opacity-0 data-[side=top]:data-starting-style:scale-[0.26] data-[side=top]:data-starting-style:rotate-x-[70deg] data-[side=top]:data-ending-style:translate-y-[7px] data-[side=top]:data-ending-style:opacity-0 data-[side=top]:data-ending-style:scale-[0.26] data-[side=top]:data-ending-style:rotate-x-[70deg]`,
147
- `data-[side=left]:data-starting-style:translate-x-[-7px] data-[side=left]:data-starting-style:opacity-0 data-[side=left]:data-starting-style:scale-[0.26] data-[side=left]:data-starting-style:rotate-y-[-40deg] data-[side=left]:data-ending-style:translate-x-[-7px] data-[side=left]:data-ending-style:opacity-0 data-[side=left]:data-ending-style:scale-[0.26] data-[side=left]:data-ending-style:rotate-y-[-40deg]`,
148
- `data-[side=right]:data-starting-style:translate-x-[7px] data-[side=right]:data-starting-style:opacity-0 data-[side=right]:data-starting-style:scale-[0.26] data-[side=right]:data-starting-style:rotate-y-[40deg] data-[side=right]:data-ending-style:translate-x-[7px] data-[side=right]:data-ending-style:opacity-0 data-[side=right]:data-ending-style:scale-[0.26] data-[side=right]:data-ending-style:rotate-y-[40deg]`,
149
- `data-[side=inline-start]:data-starting-style:translate-x-[-7px] data-[side=inline-start]:data-starting-style:opacity-0 data-[side=inline-start]:data-starting-style:scale-[0.26] data-[side=inline-start]:data-starting-style:rotate-y-[-40deg] data-[side=inline-start]:data-ending-style:translate-x-[-7px] data-[side=inline-start]:data-ending-style:opacity-0 data-[side=inline-start]:data-ending-style:scale-[0.26] data-[side=inline-start]:data-ending-style:rotate-y-[-40deg]`,
150
- `data-[side=inline-end]:data-starting-style:translate-x-[7px] data-[side=inline-end]:data-starting-style:opacity-0 data-[side=inline-end]:data-starting-style:scale-[0.26] data-[side=inline-end]:data-starting-style:rotate-y-[40deg] data-[side=inline-end]:data-ending-style:translate-x-[7px] data-[side=inline-end]:data-ending-style:opacity-0 data-[side=inline-end]:data-ending-style:scale-[0.26] data-[side=inline-end]:data-ending-style:rotate-y-[40deg]`
151
- ],
152
- motionBlur: [
153
- `[transition-property:translate,scale,opacity,rotateX,rotateY,transform,filter] [will-change:translate,scale,opacity,rotateX,rotateY,transform,filter]`,
154
- `[transform:perspective(1000px)]`,
155
- `data-starting-style:blur-[9px] data-ending-style:blur-[9px]`,
156
- `data-[side=bottom]:data-starting-style:translate-y-[7px] data-[side=bottom]:data-starting-style:opacity-0 data-[side=bottom]:data-starting-style:scale-[0.26] data-[side=bottom]:data-starting-style:rotate-x-[70deg] data-[side=bottom]:data-ending-style:translate-y-[7px] data-[side=bottom]:data-ending-style:opacity-0 data-[side=bottom]:data-ending-style:scale-[0.26] data-[side=bottom]:data-ending-style:rotate-x-[70deg]`,
157
- `data-[side=top]:data-starting-style:translate-y-[7px] data-[side=top]:data-starting-style:opacity-0 data-[side=top]:data-starting-style:scale-[0.26] data-[side=top]:data-starting-style:rotate-x-[70deg] data-[side=top]:data-ending-style:translate-y-[7px] data-[side=top]:data-ending-style:opacity-0 data-[side=top]:data-ending-style:scale-[0.26] data-[side=top]:data-ending-style:rotate-x-[70deg]`,
158
- `data-[side=left]:data-starting-style:translate-x-[-7px] data-[side=left]:data-starting-style:opacity-0 data-[side=left]:data-starting-style:scale-[0.26] data-[side=left]:data-starting-style:rotate-y-[-40deg] data-[side=left]:data-ending-style:translate-x-[-7px] data-[side=left]:data-ending-style:opacity-0 data-[side=left]:data-ending-style:scale-[0.26] data-[side=left]:data-ending-style:rotate-y-[-40deg]`,
159
- `data-[side=right]:data-starting-style:translate-x-[7px] data-[side=right]:data-starting-style:opacity-0 data-[side=right]:data-starting-style:scale-[0.26] data-[side=right]:data-starting-style:rotate-y-[40deg] data-[side=right]:data-ending-style:translate-x-[7px] data-[side=right]:data-ending-style:opacity-0 data-[side=right]:data-ending-style:scale-[0.26] data-[side=right]:data-ending-style:rotate-y-[40deg]`,
160
- `data-[side=inline-start]:data-starting-style:translate-x-[-7px] data-[side=inline-start]:data-starting-style:opacity-0 data-[side=inline-start]:data-starting-style:scale-[0.26] data-[side=inline-start]:data-starting-style:rotate-y-[-40deg] data-[side=inline-start]:data-ending-style:translate-x-[-7px] data-[side=inline-start]:data-ending-style:opacity-0 data-[side=inline-start]:data-ending-style:scale-[0.26] data-[side=inline-start]:data-ending-style:rotate-y-[-40deg]`,
161
- `data-[side=inline-end]:data-starting-style:translate-x-[7px] data-[side=inline-end]:data-starting-style:opacity-0 data-[side=inline-end]:data-starting-style:scale-[0.26] data-[side=inline-end]:data-starting-style:rotate-y-[40deg] data-[side=inline-end]:data-ending-style:translate-x-[7px] data-[side=inline-end]:data-ending-style:opacity-0 data-[side=inline-end]:data-ending-style:scale-[0.26] data-[side=inline-end]:data-ending-style:rotate-y-[40deg]`
162
- ]
163
- };
164
- const cssTransitionPresets = {
165
- inExpo: `duration-[0.25s] ease-[cubic-bezier(0.95,0.05,0.795,0.035)]`,
166
- outExpo: `duration-[0.25s] ease-[cubic-bezier(0.19,1,0.22,1)]`,
167
- inOutExpo: `duration-[0.25s] ease-[cubic-bezier(1,0,0,1)]`,
168
- anticipate: `duration-[0.25s] ease-[cubic-bezier(1,-0.4,0.35,0.95)]`,
169
- quickOut: `duration-[0.25s] ease-out`,
170
- overshootOut: `duration-[0.25s] ease-[cubic-bezier(0.175,0.885,0.32,1.275)]`,
171
- swiftOut: `duration-[0.25s] ease-[cubic-bezier(0.175,0.885,0.32,1.1)]`,
172
- snappyOut: `duration-[0.25s] ease-[cubic-bezier(0.19,1,0.22,1)]`,
173
- in: `duration-[0.25s] ease-[cubic-bezier(0.42,0,1,1)]`,
174
- out: `duration-[0.25s] ease-[cubic-bezier(0,0,0.58,1)]`,
175
- inOut: `duration-[0.25s] ease-[cubic-bezier(0.42,0,0.58,1)]`,
176
- outIn: `duration-[0.25s] ease-[cubic-bezier(0.1,0.7,0.9,0.5)]`,
177
- inQuad: `duration-[0.25s] ease-[cubic-bezier(0.55,0.085,0.68,0.53)]`,
178
- outQuad: `duration-[0.25s] ease-[cubic-bezier(0.25,0.46,0.45,0.94)]`,
179
- inOutQuad: `duration-[0.32s] ease-[cubic-bezier(0.455,0.03,0.515,0.955)]`,
180
- inCubic: `duration-[0.25s] ease-[cubic-bezier(0.55,0.055,0.675,0.19)]`,
181
- outCubic: `duration-[0.25s] ease-[cubic-bezier(0.215,0.61,0.355,1)]`,
182
- inOutCubic: `duration-[0.25s] ease-[cubic-bezier(0.645,0.045,0.355,1)]`,
183
- inQuart: `duration-[0.25s] ease-[cubic-bezier(0.895,0.03,0.685,0.22)]`,
184
- outQuart: `duration-[0.25s] ease-[cubic-bezier(0.165,0.84,0.44,1)]`,
185
- inOutQuart: `duration-[0.25s] ease-[cubic-bezier(0.77,0,0.175,1)]`,
186
- inQuint: `duration-[0.25s] ease-[cubic-bezier(0.755,0.05,0.855,0.06)]`,
187
- outQuint: `duration-[0.25s] ease-[cubic-bezier(0.23,1,0.32,1)]`,
188
- inOutQuint: `duration-[0.25s] ease-[cubic-bezier(0.86,0,0.07,1)]`,
189
- inCirc: `duration-[0.25s] ease-[cubic-bezier(0.6,0.04,0.98,0.335)]`,
190
- outCirc: `duration-[0.25s] ease-[cubic-bezier(0.075,0.82,0.165,1)]`,
191
- inOutCirc: `duration-[0.25s] ease-[cubic-bezier(0.785,0.135,0.15,0.86)]`,
192
- inOutBase: `duration-[0.25s] ease-[cubic-bezier(0.25,0.1,0.25,1)]`,
193
- none: `duration-0 ease-none`
194
- };
195
- function TooltipProvider({ delay = 300, ...props }) {
196
- return /* @__PURE__ */ jsx(Tooltip$1.Provider, {
197
- "data-slot": "tooltip-provider",
198
- delay,
199
- ...props
200
- });
201
- }
202
- function Tooltip({ ...props }) {
203
- return /* @__PURE__ */ jsx(Tooltip$1.Root, {
204
- "data-slot": "tooltip",
205
- ...props
206
- });
207
- }
99
+ const TooltipCreateHandle = TooltipPrimitive.createHandle;
100
+ const TooltipProvider = TooltipPrimitive.Provider;
101
+ const Tooltip = TooltipPrimitive.Root;
208
102
  function TooltipTrigger(props) {
209
- return /* @__PURE__ */ jsx(Tooltip$1.Trigger, {
103
+ return /* @__PURE__ */ jsx(TooltipPrimitive.Trigger, {
210
104
  "data-slot": "tooltip-trigger",
211
105
  ...props
212
106
  });
213
107
  }
214
- function TooltipPortal(props) {
215
- return /* @__PURE__ */ jsx(Tooltip$1.Portal, {
216
- "data-slot": "tooltip-portal",
217
- ...props
218
- });
219
- }
220
- function TooltipPositioner({ className, side = "top", ...rest }) {
221
- return /* @__PURE__ */ jsx(TooltipPortal, { children: /* @__PURE__ */ jsx(Tooltip$1.Positioner, {
222
- side,
223
- "data-slot": "tooltip-positioner",
224
- className: cn("z-100", (side === "inline-end" || side === "inline-start") && "**:data-[slot=tooltip-arrow]:hidden", className),
225
- ...rest
226
- }) });
227
- }
228
- function TooltipPopup({ className, animationPreset = "scale", transitionPreset = "outQuint", reduceMotion = false, showArrow = false, side = "top", sideOffset = 4, align = "center", alignOffset = 0, ...rest }) {
229
- const cssAnimationConfig = useMemo(() => {
230
- if (reduceMotion) return "none";
231
- if (animationPreset) return cssAnimationPresets[animationPreset];
232
- return cssAnimationPresets.scale;
233
- }, [
234
- animationPreset,
235
- reduceMotion,
236
- side
237
- ]);
238
- const cssTransitionConfig = useMemo(() => {
239
- if (reduceMotion) return "none";
240
- if (transitionPreset) return cssTransitionPresets[transitionPreset];
241
- return cssTransitionPresets.snappyOut;
242
- }, [
243
- transitionPreset,
244
- reduceMotion,
245
- side
246
- ]);
247
- return /* @__PURE__ */ jsx(TooltipPositioner, {
248
- side,
249
- sideOffset,
250
- align,
251
- alignOffset,
252
- children: /* @__PURE__ */ jsx(Tooltip$1.Popup, {
253
- "data-slot": "tooltip-popup",
254
- className: cn("[--radius:10px]", "bg-popover border-border pointer-events-auto w-fit origin-(--transform-origin) rounded-(--radius) border px-2 py-1 text-[13px] text-balance shadow-xs data-instant:duration-0!", className, cssAnimationConfig, cssTransitionConfig, showArrow && [
255
- `before: before:bg-popover z-[-1] before:absolute before:h-2 before:w-2 before:rotate-45 before:content-['']`,
256
- side === "top" && `before:border-border before:-bottom-[4.7px] before:left-1/2 before:-translate-x-1/2 before:border-r before:border-b`,
257
- side === "right" && `before:border-border before:top-1/2 before:-left-[4.07px] before:-translate-y-1/2 before:border-b before:border-l`,
258
- side === "bottom" && `before:border-border before:-top-[4.7px] before:left-1/2 before:-translate-x-1/2 before:border-t before:border-l`,
259
- side === "left" && `before:border-border before:top-1/2 before:-right-[4.07px] before:-translate-y-1/2 before:border-t before:border-r`,
260
- side === "inline-start" && `before:border-border before:top-1/2 before:-right-[4.07px] before:-translate-y-1/2 before:border-t before:border-r`,
261
- side === "inline-end" && `before:border-border before:top-1/2 before:-left-[4.07px] before:-translate-y-1/2 before:border-b before:border-l`
262
- ]),
263
- ...rest
108
+ function TooltipPopup({ className, align = "center", sideOffset = 4, side = "top", anchor, children, portalProps, ...props }) {
109
+ return /* @__PURE__ */ jsx(TooltipPrimitive.Portal, {
110
+ ...portalProps,
111
+ children: /* @__PURE__ */ jsx(TooltipPrimitive.Positioner, {
112
+ align,
113
+ anchor,
114
+ className: "z-50 h-(--positioner-height) w-(--positioner-width) max-w-(--available-width) transition-[top,left,right,bottom,transform] data-instant:transition-none",
115
+ "data-slot": "tooltip-positioner",
116
+ side,
117
+ sideOffset,
118
+ children: /* @__PURE__ */ jsx(TooltipPrimitive.Popup, {
119
+ className: cn("relative flex h-(--popup-height,auto) w-(--popup-width,auto) origin-(--transform-origin) text-balance rounded-md border bg-popover not-dark:bg-clip-padding text-popover-foreground text-xs shadow-md/5 transition-[width,height,scale,opacity] before:pointer-events-none before:absolute before:inset-0 before:rounded-[calc(var(--radius-md)-1px)] before:shadow-[0_1px_--theme(--color-black/4%)] data-ending-style:scale-98 data-starting-style:scale-98 data-ending-style:opacity-0 data-starting-style:opacity-0 data-instant:duration-0 dark:before:shadow-[0_-1px_--theme(--color-white/6%)]", className),
120
+ "data-slot": "tooltip-popup",
121
+ ...props,
122
+ children: /* @__PURE__ */ jsx(TooltipPrimitive.Viewport, {
123
+ className: "relative size-full overflow-clip px-(--viewport-inline-padding) py-1 [--viewport-inline-padding:--spacing(2)] data-instant:transition-none **:data-current:data-ending-style:opacity-0 **:data-current:data-starting-style:opacity-0 **:data-previous:data-ending-style:opacity-0 **:data-previous:data-starting-style:opacity-0 **:data-current:w-[calc(var(--popup-width)-2*var(--viewport-inline-padding)-2px)] **:data-previous:w-[calc(var(--popup-width)-2*var(--viewport-inline-padding)-2px)] **:data-previous:truncate **:data-current:opacity-100 **:data-previous:opacity-100 **:data-current:transition-opacity **:data-previous:transition-opacity",
124
+ "data-slot": "tooltip-viewport",
125
+ children
126
+ })
127
+ })
264
128
  })
265
129
  });
266
130
  }
267
131
 
268
132
  //#endregion
269
133
  //#region src/components/Fields/Label/index.tsx
270
- const Label$1 = ({ label, readOnly, tooltip }) => /* @__PURE__ */ jsxs(Label$2, {
134
+ const Label$1 = ({ label, readOnly, tooltip, action, className }) => /* @__PURE__ */ jsxs(Label$2, {
271
135
  readOnly,
272
- children: [label, tooltip && /* @__PURE__ */ jsxs(Tooltip, { children: [/* @__PURE__ */ jsx(TooltipTrigger, {
273
- delay: 200,
274
- children: /* @__PURE__ */ jsx(Button, {
275
- variant: "secondary",
276
- className: "!text-xs !rounded-full !p-0 !size-5 flex items-center justify-center",
277
- children: "?"
278
- })
279
- }), /* @__PURE__ */ jsx(TooltipPopup, {
280
- side: "top",
281
- align: "end",
282
- className: "max-w-64 text-xs",
283
- children: tooltip
284
- })] })]
136
+ className,
137
+ children: [label, (action || tooltip) && /* @__PURE__ */ jsxs("div", {
138
+ className: "flex items-center gap-x-1.5",
139
+ children: [action, tooltip && /* @__PURE__ */ jsxs(Tooltip, { children: [/* @__PURE__ */ jsx(TooltipTrigger, {
140
+ delay: 200,
141
+ render: /* @__PURE__ */ jsx("span", {}),
142
+ children: /* @__PURE__ */ jsx(Button, {
143
+ variant: "secondary",
144
+ className: "!text-xs !rounded-full !p-0 !size-5 flex items-center justify-center",
145
+ children: "?"
146
+ })
147
+ }), /* @__PURE__ */ jsx(TooltipPopup, {
148
+ side: "top",
149
+ align: "end",
150
+ className: "max-w-64 text-xs",
151
+ children: tooltip
152
+ })] })]
153
+ })]
285
154
  });
286
155
  var Label_default = Label$1;
287
156
 
@@ -696,6 +565,49 @@ const FieldRadio = ({ onChange, value, readOnly, field, label }) => {
696
565
  };
697
566
  var Radio_default = FieldRadio;
698
567
 
568
+ //#endregion
569
+ //#region src/components/ui/switch.tsx
570
+ function Switch({ className, ...props }) {
571
+ return /* @__PURE__ */ jsx(Switch$1.Root, {
572
+ className: cn("inline-flex h-[calc(var(--thumb-size)+2px)] w-[calc(var(--thumb-size)*2-2px)] shrink-0 items-center rounded-full p-px outline-none transition-[background-color,box-shadow] duration-200 [--thumb-size:--spacing(5)] focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:ring-offset-background data-checked:bg-primary data-unchecked:bg-input data-disabled:opacity-64 sm:[--thumb-size:--spacing(4)] cursor-pointer", className),
573
+ "data-slot": "switch",
574
+ ...props,
575
+ children: /* @__PURE__ */ jsx(Switch$1.Thumb, {
576
+ className: cn("pointer-events-none block aspect-square h-full origin-left in-[[role=switch]:active,[data-slot=label]:active,[data-slot=field-label]:active]:not-data-disabled:scale-x-110 in-[[role=switch]:active,[data-slot=label]:active,[data-slot=field-label]:active]:rounded-[var(--thumb-size)/calc(var(--thumb-size)*1.1)] rounded-(--thumb-size) bg-background shadow-sm/5 will-change-transform [transition:translate_.15s,border-radius_.15s,scale_.1s_.1s,transform-origin_.15s] data-checked:origin-[var(--thumb-size)_50%] data-checked:translate-x-[calc(var(--thumb-size)-4px)]"),
577
+ "data-slot": "switch-thumb"
578
+ })
579
+ });
580
+ }
581
+
582
+ //#endregion
583
+ //#region src/components/Fields/Switch/index.tsx
584
+ const FieldSwitch = ({ onChange, value, readOnly, field, label }) => {
585
+ const layout = field.layout || "stacked";
586
+ const handleCheckedChange = (checked) => {
587
+ if (!readOnly) onChange(checked);
588
+ };
589
+ const switchEl = /* @__PURE__ */ jsx(Switch, {
590
+ checked: !!value,
591
+ onCheckedChange: handleCheckedChange,
592
+ disabled: field.readOnly || readOnly
593
+ });
594
+ if (layout === "stacked") return /* @__PURE__ */ jsxs(Fragment, { children: [label && /* @__PURE__ */ jsx(Label_default, {
595
+ label,
596
+ readOnly: field.readOnly || readOnly,
597
+ tooltip: field.tooltip
598
+ }), switchEl] });
599
+ return /* @__PURE__ */ jsxs("div", {
600
+ className: "flex items-center gap-x-3",
601
+ children: [switchEl, label && /* @__PURE__ */ jsx(Label_default, {
602
+ label,
603
+ readOnly: field.readOnly || readOnly,
604
+ tooltip: field.tooltip,
605
+ className: "mb-0"
606
+ })]
607
+ });
608
+ };
609
+ var Switch_default = FieldSwitch;
610
+
699
611
  //#endregion
700
612
  //#region src/components/ui/textarea.tsx
701
613
  function Textarea({ className, size = "default", unstyled = false, ...props }) {
@@ -763,32 +675,32 @@ var ActionBar_default = ActionBar;
763
675
  //#endregion
764
676
  //#region src/components/ui/tabs.tsx
765
677
  function Tabs({ className, ...props }) {
766
- return /* @__PURE__ */ jsx(Tabs$1.Root, {
678
+ return /* @__PURE__ */ jsx(TabsPrimitive.Root, {
767
679
  className: cn("flex flex-col gap-2 data-[orientation=vertical]:flex-row", className),
768
680
  "data-slot": "tabs",
769
681
  ...props
770
682
  });
771
683
  }
772
684
  function TabsList({ variant = "default", className, children, ...props }) {
773
- return /* @__PURE__ */ jsxs(Tabs$1.List, {
774
- className: cn("text-muted-foreground relative z-0 flex w-fit items-center justify-center gap-x-0.5", "data-[orientation=vertical]:flex-col", variant === "default" ? "bg-muted text-muted-foreground/72 rounded-lg p-0.5" : "*:data-[slot=tabs-tab]:hover:bg-accent data-[orientation=horizontal]:py-1 data-[orientation=vertical]:px-1", className),
685
+ return /* @__PURE__ */ jsxs(TabsPrimitive.List, {
686
+ className: cn("relative z-0 flex w-fit items-center justify-center gap-x-0.5 text-muted-foreground", "data-[orientation=vertical]:flex-col", variant === "default" ? "rounded-lg bg-muted p-0.5 text-muted-foreground/72" : "data-[orientation=vertical]:px-1 data-[orientation=horizontal]:py-1 *:data-[slot=tabs-tab]:hover:bg-accent", className),
775
687
  "data-slot": "tabs-list",
776
688
  ...props,
777
- children: [children, /* @__PURE__ */ jsx(Tabs$1.Indicator, {
778
- className: cn("absolute bottom-0 left-0 h-(--active-tab-height) w-(--active-tab-width) translate-x-(--active-tab-left) -translate-y-(--active-tab-bottom) transition-[width,translate] duration-200 ease-in-out", variant === "underline" ? "bg-primary z-10 data-[orientation=horizontal]:h-0.5 data-[orientation=horizontal]:translate-y-px data-[orientation=vertical]:w-0.5 data-[orientation=vertical]:-translate-x-px" : "bg-background dark:bg-input -z-1 rounded-md shadow-sm/5"),
689
+ children: [children, /* @__PURE__ */ jsx(TabsPrimitive.Indicator, {
690
+ className: cn("absolute bottom-0 left-0 h-(--active-tab-height) w-(--active-tab-width) translate-x-(--active-tab-left) -translate-y-(--active-tab-bottom) transition-[width,translate] duration-200 ease-in-out", variant === "underline" ? "z-10 bg-primary data-[orientation=horizontal]:h-0.5 data-[orientation=vertical]:w-0.5 data-[orientation=vertical]:-translate-x-px data-[orientation=horizontal]:translate-y-px" : "-z-1 rounded-md bg-background shadow-sm/5 dark:bg-input"),
779
691
  "data-slot": "tab-indicator"
780
692
  })]
781
693
  });
782
694
  }
783
695
  function TabsTab({ className, ...props }) {
784
- return /* @__PURE__ */ jsx(Tabs$1.Tab, {
785
- className: cn("hover:text-muted-foreground focus-visible:ring-ring data-active:text-foreground flex h-9 shrink-0 grow cursor-pointer items-center justify-center gap-1.5 rounded-md border border-transparent px-[calc(--spacing(2.5)-1px)] text-base font-medium whitespace-nowrap transition-[color,background-color,box-shadow] outline-none focus-visible:ring-2 data-disabled:pointer-events-none data-disabled:opacity-64 data-[orientation=vertical]:w-full data-[orientation=vertical]:justify-start sm:h-8 sm:text-sm [&_svg]:pointer-events-none [&_svg]:-mx-0.5 [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4.5 sm:[&_svg:not([class*='size-'])]:size-4", className),
696
+ return /* @__PURE__ */ jsx(TabsPrimitive.Tab, {
697
+ className: cn("relative flex h-9 shrink-0 grow cursor-pointer items-center justify-center gap-1.5 whitespace-nowrap rounded-md border border-transparent px-[calc(--spacing(2.5)-1px)] font-medium text-base outline-none transition-[color,background-color,box-shadow] hover:text-muted-foreground focus-visible:ring-2 focus-visible:ring-ring data-disabled:pointer-events-none data-[orientation=vertical]:w-full data-[orientation=vertical]:justify-start data-active:text-foreground data-disabled:opacity-64 sm:h-8 sm:text-sm [&_svg:not([class*='size-'])]:size-4.5 sm:[&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:-mx-0.5 [&_svg]:shrink-0", className),
786
698
  "data-slot": "tabs-tab",
787
699
  ...props
788
700
  });
789
701
  }
790
702
  function TabsPanel({ className, ...props }) {
791
- return /* @__PURE__ */ jsx(Tabs$1.Panel, {
703
+ return /* @__PURE__ */ jsx(TabsPrimitive.Panel, {
792
704
  className: cn("flex-1 outline-none", className),
793
705
  "data-slot": "tabs-content",
794
706
  ...props
@@ -1039,7 +951,7 @@ const DrawerItem = ({ name, icon }) => {
1039
951
  children: [/* @__PURE__ */ jsxs("div", {
1040
952
  className: "flex items-center gap-2 truncate",
1041
953
  children: [/* @__PURE__ */ jsx("div", {
1042
- className: "border-border group-hover:border-muted rounded-sm border bg-white p-3",
954
+ className: "border-border group-hover:border-muted rounded-md border bg-white p-3",
1043
955
  children: displayIcon
1044
956
  }), /* @__PURE__ */ jsx("span", {
1045
957
  className: "truncate text-sm font-medium",
@@ -1081,13 +993,14 @@ const createPuckOverridesPlugin = () => {
1081
993
  fields: FieldGroups_default,
1082
994
  fieldLabel: ({ children, label, field, tooltip }) => /* @__PURE__ */ jsxs(Fragment, { children: [label && /* @__PURE__ */ jsx(Label_default, {
1083
995
  label,
1084
- tooltip
996
+ tooltip: tooltip ?? field?.tooltip
1085
997
  }), children] }),
1086
998
  fieldTypes: {
1087
999
  checkbox: Checkbox_default,
1088
1000
  numberUnit: NumberUnit_default,
1089
1001
  radio: Radio_default,
1090
1002
  select: Select_default,
1003
+ switch: Switch_default,
1091
1004
  text: Input_default,
1092
1005
  textarea: Textarea_default
1093
1006
  }
@@ -1189,28 +1102,28 @@ function Badge({ className, variant, size, render, ...props }) {
1189
1102
  //#endregion
1190
1103
  //#region src/components/ui/scroll-area.tsx
1191
1104
  function ScrollArea({ className, children, scrollFade = false, scrollbarGutter = false, ...props }) {
1192
- return /* @__PURE__ */ jsxs(ScrollArea$1.Root, {
1105
+ return /* @__PURE__ */ jsxs(ScrollAreaPrimitive.Root, {
1193
1106
  className: cn("size-full min-h-0", className),
1194
1107
  ...props,
1195
1108
  children: [
1196
- /* @__PURE__ */ jsx(ScrollArea$1.Viewport, {
1197
- className: cn("h-full rounded-[inherit] outline-none transition-shadows focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:ring-offset-background data-has-overflow-x:overscroll-x-contain", scrollFade && "mask-t-from-[calc(100%-min(var(--fade-size),var(--scroll-area-overflow-y-start)))] mask-b-from-[calc(100%-min(var(--fade-size),var(--scroll-area-overflow-y-end)))] mask-l-from-[calc(100%-min(var(--fade-size),var(--scroll-area-overflow-x-start)))] mask-r-from-[calc(100%-min(var(--fade-size),var(--scroll-area-overflow-x-end)))] [--fade-size:1.5rem]", scrollbarGutter && "data-has-overflow-y:pe-2.5 data-has-overflow-x:pb-2.5"),
1109
+ /* @__PURE__ */ jsx(ScrollAreaPrimitive.Viewport, {
1110
+ className: cn("h-full rounded-[inherit] outline-none transition-shadows focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:ring-offset-background data-has-overflow-y:overscroll-y-contain data-has-overflow-x:overscroll-x-contain", scrollFade && "mask-t-from-[calc(100%-min(var(--fade-size),var(--scroll-area-overflow-y-start)))] mask-b-from-[calc(100%-min(var(--fade-size),var(--scroll-area-overflow-y-end)))] mask-l-from-[calc(100%-min(var(--fade-size),var(--scroll-area-overflow-x-start)))] mask-r-from-[calc(100%-min(var(--fade-size),var(--scroll-area-overflow-x-end)))] [--fade-size:1.5rem]", scrollbarGutter && "data-has-overflow-y:pe-2.5 data-has-overflow-x:pb-2.5"),
1198
1111
  "data-slot": "scroll-area-viewport",
1199
1112
  children
1200
1113
  }),
1201
1114
  /* @__PURE__ */ jsx(ScrollBar, { orientation: "vertical" }),
1202
1115
  /* @__PURE__ */ jsx(ScrollBar, { orientation: "horizontal" }),
1203
- /* @__PURE__ */ jsx(ScrollArea$1.Corner, { "data-slot": "scroll-area-corner" })
1116
+ /* @__PURE__ */ jsx(ScrollAreaPrimitive.Corner, { "data-slot": "scroll-area-corner" })
1204
1117
  ]
1205
1118
  });
1206
1119
  }
1207
1120
  function ScrollBar({ className, orientation = "vertical", ...props }) {
1208
- return /* @__PURE__ */ jsx(ScrollArea$1.Scrollbar, {
1121
+ return /* @__PURE__ */ jsx(ScrollAreaPrimitive.Scrollbar, {
1209
1122
  className: cn("m-1 flex opacity-0 transition-opacity delay-300 data-[orientation=horizontal]:h-1.5 data-[orientation=vertical]:w-1.5 data-[orientation=horizontal]:flex-col data-hovering:opacity-100 data-scrolling:opacity-100 data-hovering:delay-0 data-scrolling:delay-0 data-hovering:duration-100 data-scrolling:duration-100", className),
1210
1123
  "data-slot": "scroll-area-scrollbar",
1211
1124
  orientation,
1212
1125
  ...props,
1213
- children: /* @__PURE__ */ jsx(ScrollArea$1.Thumb, {
1126
+ children: /* @__PURE__ */ jsx(ScrollAreaPrimitive.Thumb, {
1214
1127
  className: "relative flex-1 rounded-full bg-foreground/20",
1215
1128
  "data-slot": "scroll-area-thumb"
1216
1129
  })
@@ -1246,7 +1159,7 @@ function DialogPopup({ className, children, showCloseButton = true, bottomStickO
1246
1159
  return /* @__PURE__ */ jsxs(DialogPortal, { children: [/* @__PURE__ */ jsx(DialogBackdrop, {}), /* @__PURE__ */ jsx(DialogViewport, {
1247
1160
  className: cn(bottomStickOnMobile && "max-sm:grid-rows-[1fr_auto] max-sm:p-0 max-sm:pt-12"),
1248
1161
  children: /* @__PURE__ */ jsxs(Dialog$1.Popup, {
1249
- className: cn("-translate-y-[calc(1.25rem*var(--nested-dialogs))] relative row-start-2 flex max-h-full min-h-0 w-full min-w-0 max-w-lg scale-[calc(1-0.1*var(--nested-dialogs))] flex-col rounded-2xl border bg-popover not-dark:bg-clip-padding text-popover-foreground opacity-[calc(1-0.1*var(--nested-dialogs))] shadow-lg/5 transition-[scale,opacity,translate] duration-200 ease-in-out will-change-transform before:pointer-events-none before:absolute before:inset-0 before:rounded-[calc(var(--radius-2xl)-1px)] before:shadow-[0_1px_--theme(--color-black/6%)] data-nested:data-ending-style:translate-y-8 data-nested:data-starting-style:translate-y-8 data-nested-dialog-open:origin-top data-ending-style:scale-98 data-starting-style:scale-98 data-ending-style:opacity-0 data-starting-style:opacity-0 dark:before:shadow-[0_-1px_--theme(--color-white/6%)]", bottomStickOnMobile && "max-sm:max-w-none max-sm:rounded-none max-sm:border-x-0 max-sm:border-t max-sm:border-b-0 max-sm:opacity-[calc(1-min(var(--nested-dialogs),1))] max-sm:data-ending-style:translate-y-4 max-sm:data-starting-style:translate-y-4 max-sm:before:hidden max-sm:before:rounded-none", className),
1162
+ className: cn("-translate-y-[calc(1.25rem*var(--nested-dialogs))] relative row-start-2 flex max-h-full min-h-0 w-full min-w-0 max-w-lg scale-[calc(1-0.1*var(--nested-dialogs))] flex-col rounded-2xl border bg-popover not-dark:bg-clip-padding text-popover-foreground opacity-[calc(1-0.1*var(--nested-dialogs))] shadow-lg/5 transition-[scale,opacity,translate] duration-200 ease-in-out will-change-transform before:pointer-events-none before:absolute before:inset-0 before:rounded-[calc(var(--radius-2xl)-1px)] before:shadow-[0_1px_--theme(--color-black/6%)] data-nested:data-ending-style:translate-y-8 data-nested:data-starting-style:translate-y-8 data-nested-dialog-open:origin-top data-ending-style:scale-98 data-starting-style:scale-98 data-ending-style:opacity-0 data-starting-style:opacity-0 dark:before:shadow-[0_-1px_--theme(--color-white/6%)] overflow-hidden", bottomStickOnMobile && "max-sm:max-w-none max-sm:rounded-none max-sm:border-x-0 max-sm:border-t max-sm:border-b-0 max-sm:opacity-[calc(1-min(var(--nested-dialogs),1))] max-sm:data-ending-style:translate-y-4 max-sm:data-starting-style:translate-y-4 max-sm:before:hidden max-sm:before:rounded-none", className),
1250
1163
  "data-slot": "dialog-popup",
1251
1164
  ...props,
1252
1165
  children: [children, showCloseButton && /* @__PURE__ */ jsx(Dialog$1.Close, {
@@ -1579,7 +1492,7 @@ function PickerModal({ title, searchQuery, onSearch, searchPlaceholder = "Search
1579
1492
  25,
1580
1493
  50,
1581
1494
  100
1582
- ], emptyIcon, emptyMessage = "No results found", footer }) {
1495
+ ], emptyIcon, emptyMessage = "No results found", footer, filterContent, tableContent, overlay }) {
1583
1496
  const table = useReactTable({
1584
1497
  data,
1585
1498
  columns,
@@ -1597,16 +1510,35 @@ function PickerModal({ title, searchQuery, onSearch, searchPlaceholder = "Search
1597
1510
  });
1598
1511
  const pageSize = table.getState().pagination.pageSize;
1599
1512
  return /* @__PURE__ */ jsxs(DialogPopup, {
1600
- className: "max-w-4xl",
1513
+ className: "relative max-w-4xl",
1601
1514
  children: [
1602
1515
  /* @__PURE__ */ jsx(DialogHeader, { children: /* @__PURE__ */ jsx(DialogTitle, { children: title }) }),
1603
1516
  /* @__PURE__ */ jsxs(DialogPanel, {
1604
1517
  className: "flex flex-col gap-4",
1605
- children: [/* @__PURE__ */ jsxs(InputGroup, { children: [/* @__PURE__ */ jsx(InputGroupInput, {
1606
- placeholder: searchPlaceholder,
1607
- value: searchQuery,
1608
- onChange: (e) => onSearch(e.target.value)
1609
- }), /* @__PURE__ */ jsx(InputGroupAddon, { children: /* @__PURE__ */ jsx(SearchIcon, { className: "size-4" }) })] }), loading ? /* @__PURE__ */ jsx("div", {
1518
+ scrollFade: false,
1519
+ children: [/* @__PURE__ */ jsxs("div", {
1520
+ className: "flex items-center gap-2",
1521
+ children: [/* @__PURE__ */ jsxs(InputGroup, {
1522
+ className: "flex-1",
1523
+ children: [/* @__PURE__ */ jsx(InputGroupInput, {
1524
+ placeholder: searchPlaceholder,
1525
+ value: searchQuery,
1526
+ onChange: (e) => onSearch(e.target.value)
1527
+ }), /* @__PURE__ */ jsx(InputGroupAddon, {
1528
+ align: "inline-end",
1529
+ children: searchQuery ? /* @__PURE__ */ jsx(Button, {
1530
+ "aria-label": "Clear search",
1531
+ onClick: () => onSearch(""),
1532
+ size: "icon-xs",
1533
+ variant: "ghost",
1534
+ children: /* @__PURE__ */ jsx(XIcon, { "aria-hidden": "true" })
1535
+ }) : /* @__PURE__ */ jsx(SearchIcon, { className: "size-4" })
1536
+ })]
1537
+ }), filterContent && /* @__PURE__ */ jsx("div", {
1538
+ className: "flex items-center gap-2",
1539
+ children: filterContent
1540
+ })]
1541
+ }), tableContent ? tableContent : loading ? /* @__PURE__ */ jsx("div", {
1610
1542
  className: "flex items-center justify-center py-12",
1611
1543
  children: /* @__PURE__ */ jsx(Loader2Icon, { className: "text-muted-foreground size-8 animate-spin" })
1612
1544
  }) : /* @__PURE__ */ jsxs(Frame, {
@@ -1777,7 +1709,8 @@ function PickerModal({ title, searchQuery, onSearch, searchPlaceholder = "Search
1777
1709
  })]
1778
1710
  })]
1779
1711
  }),
1780
- footer && /* @__PURE__ */ jsx(DialogFooter, { children: footer })
1712
+ footer && /* @__PURE__ */ jsx(DialogFooter, { children: footer }),
1713
+ overlay
1781
1714
  ]
1782
1715
  });
1783
1716
  }
@@ -2360,6 +2293,486 @@ function RichTextMenuColorPicker({ editor, ...props }) {
2360
2293
  });
2361
2294
  }
2362
2295
 
2296
+ //#endregion
2297
+ //#region src/plugins/unsplash/utils/unsplashClient.ts
2298
+ const DEFAULT_SEARCH_ENDPOINT = "/admin/puck/unsplash/search";
2299
+ const DEFAULT_DOWNLOAD_ENDPOINT = "/admin/puck/unsplash/download";
2300
+ const DEFAULT_UPLOAD_ENDPOINT = "/admin/puck/unsplash/upload";
2301
+ /** Terme utilisé pour afficher les photos "à la une" quand aucune recherche n'est saisie. */
2302
+ const DEFAULT_FEATURED_QUERY = "editorial";
2303
+ const TIMEOUT_MS$1 = 2e4;
2304
+ async function searchUnsplashPhotos({ endpoint = DEFAULT_SEARCH_ENDPOINT, query, page = 1, portrait = false, landscape = false }) {
2305
+ const controller = new AbortController();
2306
+ const timeoutId = setTimeout(() => controller.abort(), TIMEOUT_MS$1);
2307
+ const params = new URLSearchParams();
2308
+ params.set("query", query);
2309
+ if (page > 1) params.set("page", String(page));
2310
+ if (portrait) params.set("portrait", "1");
2311
+ if (landscape) params.set("landscape", "1");
2312
+ try {
2313
+ const response = await fetch(`${endpoint}?${params.toString()}`, {
2314
+ method: "GET",
2315
+ headers: { Accept: "application/json" },
2316
+ credentials: "same-origin",
2317
+ signal: controller.signal
2318
+ });
2319
+ if (!response.ok) {
2320
+ const body = await response.json().catch(() => ({}));
2321
+ throw new Error(body.error || `Erreur serveur (${response.status})`);
2322
+ }
2323
+ const data = await response.json();
2324
+ const items = (Array.isArray(data.items) ? data.items : Array.isArray(data.results) ? data.results : []).map((item) => ({
2325
+ id: String(item.id),
2326
+ description: item.description ?? null,
2327
+ altDescription: item.alt_description ?? item.altDescription ?? null,
2328
+ thumbUrl: item.thumbUrl ?? item.urls?.thumb ?? item.urls?.small ?? "",
2329
+ regularUrl: item.regularUrl ?? item.urls?.regular ?? item.urls?.small ?? "",
2330
+ htmlUrl: item.htmlUrl ?? item.links?.html ?? "",
2331
+ width: item.width,
2332
+ height: item.height,
2333
+ authorName: item.authorName ?? item.user?.name
2334
+ }));
2335
+ return {
2336
+ items,
2337
+ total: Number(data.total ?? data.total_results ?? items.length) || 0
2338
+ };
2339
+ } catch (error) {
2340
+ if (error.name === "AbortError") throw new Error("La requête Unsplash a expiré. Réessayez.");
2341
+ throw error;
2342
+ } finally {
2343
+ clearTimeout(timeoutId);
2344
+ }
2345
+ }
2346
+ async function downloadUnsplashPhoto({ endpoint = DEFAULT_DOWNLOAD_ENDPOINT, uploadEndpoint = DEFAULT_UPLOAD_ENDPOINT, id }) {
2347
+ const params = new URLSearchParams();
2348
+ params.set("id", id);
2349
+ const response = await fetch(`${endpoint}?${params.toString()}`, {
2350
+ method: "GET",
2351
+ headers: { Accept: "application/json" },
2352
+ credentials: "same-origin"
2353
+ });
2354
+ if (!response.ok) {
2355
+ const body = await response.json().catch(() => ({}));
2356
+ throw new Error(body.error || `Erreur serveur (${response.status})`);
2357
+ }
2358
+ const data = await response.json().catch(() => ({}));
2359
+ const downloadUrl = typeof data.url === "string" ? data.url.trim() : "";
2360
+ if (!downloadUrl) throw new Error("La réponse de download ne contient pas d'URL exploitable.");
2361
+ await uploadUnsplashPhoto({
2362
+ endpoint: uploadEndpoint,
2363
+ url: downloadUrl
2364
+ });
2365
+ }
2366
+ async function uploadUnsplashPhoto({ endpoint = DEFAULT_UPLOAD_ENDPOINT, url }) {
2367
+ const token = getCsrfToken();
2368
+ const response = await fetch(endpoint, {
2369
+ method: "POST",
2370
+ headers: {
2371
+ Accept: "application/json",
2372
+ "Content-Type": "application/json",
2373
+ ...token ? { "X-CSRF-TOKEN": token } : {}
2374
+ },
2375
+ credentials: "same-origin",
2376
+ body: JSON.stringify({ url })
2377
+ });
2378
+ if (!response.ok) {
2379
+ const body = await response.json().catch(() => ({}));
2380
+ throw new Error(body.error || `Erreur serveur (${response.status})`);
2381
+ }
2382
+ }
2383
+
2384
+ //#endregion
2385
+ //#region src/plugins/unsplash/hooks/useUnsplashSearch.ts
2386
+ function useUnsplashSearch({ endpoint, featuredQuery = DEFAULT_FEATURED_QUERY, initialQuery = "", perPage = 12, debounceMs = 300, enabled = true }) {
2387
+ const [query, setQuery] = useState(initialQuery);
2388
+ const [page, setPage] = useState(1);
2389
+ const [portrait, setPortrait] = useState(false);
2390
+ const [landscape, setLandscape] = useState(false);
2391
+ const [items, setItems] = useState([]);
2392
+ const [total, setTotal] = useState(0);
2393
+ const [loading, setLoading] = useState(false);
2394
+ const [error, setError] = useState(null);
2395
+ const [refreshIndex, setRefreshIndex] = useState(0);
2396
+ const [hasMore, setHasMore] = useState(false);
2397
+ const [isFeatured, setIsFeatured] = useState(false);
2398
+ const totalPages = useMemo(() => Math.max(1, Math.ceil(total / perPage)), [total, perPage]);
2399
+ useEffect(() => {
2400
+ if (!enabled) return;
2401
+ const isFeaturedMode = !query.trim();
2402
+ const effectiveQuery = isFeaturedMode ? featuredQuery : query.trim();
2403
+ setIsFeatured(isFeaturedMode);
2404
+ const timer = setTimeout(async () => {
2405
+ setLoading(true);
2406
+ setError(null);
2407
+ try {
2408
+ const result = await searchUnsplashPhotos({
2409
+ endpoint,
2410
+ query: effectiveQuery,
2411
+ page,
2412
+ portrait,
2413
+ landscape
2414
+ });
2415
+ setItems((prev) => page > 1 ? [...prev, ...result.items] : result.items);
2416
+ setTotal(result.total);
2417
+ setHasMore(result.items.length > 0);
2418
+ } catch (err) {
2419
+ setItems([]);
2420
+ setTotal(0);
2421
+ setHasMore(false);
2422
+ setError(err?.message || "Impossible de charger les photos Unsplash.");
2423
+ } finally {
2424
+ setLoading(false);
2425
+ }
2426
+ }, debounceMs);
2427
+ return () => {
2428
+ clearTimeout(timer);
2429
+ };
2430
+ }, [
2431
+ enabled,
2432
+ endpoint,
2433
+ featuredQuery,
2434
+ query,
2435
+ page,
2436
+ perPage,
2437
+ debounceMs,
2438
+ refreshIndex,
2439
+ portrait,
2440
+ landscape
2441
+ ]);
2442
+ return {
2443
+ query,
2444
+ page,
2445
+ portrait,
2446
+ landscape,
2447
+ totalPages,
2448
+ hasMore,
2449
+ isFeatured,
2450
+ items,
2451
+ total,
2452
+ loading,
2453
+ error,
2454
+ onSearchChange: useCallback((value) => {
2455
+ setQuery(value);
2456
+ setPage(1);
2457
+ setHasMore(false);
2458
+ }, []),
2459
+ onPageChange: useCallback((nextPage) => {
2460
+ setPage(Math.max(1, nextPage));
2461
+ }, []),
2462
+ onPortraitChange: useCallback((value) => {
2463
+ setPortrait(value);
2464
+ setPage(1);
2465
+ }, []),
2466
+ onLandscapeChange: useCallback((value) => {
2467
+ setLandscape(value);
2468
+ setPage(1);
2469
+ }, []),
2470
+ refresh: useCallback(() => {
2471
+ setRefreshIndex((value) => value + 1);
2472
+ }, [])
2473
+ };
2474
+ }
2475
+
2476
+ //#endregion
2477
+ //#region src/plugins/unsplash/hooks/useUnsplashModalController.ts
2478
+ function useUnsplashModalController({ downloadEndpoint, uploadEndpoint, onDownloadPhoto, onPhotoRegistered, items, refresh }) {
2479
+ const [selectedPhotoId, setSelectedPhotoId] = useState(null);
2480
+ const [saveError, setSaveError] = useState(null);
2481
+ const [saving, setSaving] = useState(false);
2482
+ const selectedPhoto = useMemo(() => items.find((item) => item.id === selectedPhotoId) ?? null, [items, selectedPhotoId]);
2483
+ const onSelectPhoto = useCallback((photoId) => {
2484
+ setSelectedPhotoId(photoId);
2485
+ }, []);
2486
+ const savePhoto = useCallback(async (photo) => {
2487
+ if (saving) return;
2488
+ setSaving(true);
2489
+ setSaveError(null);
2490
+ try {
2491
+ if (onDownloadPhoto) await onDownloadPhoto(photo);
2492
+ else await downloadUnsplashPhoto({
2493
+ endpoint: downloadEndpoint,
2494
+ uploadEndpoint,
2495
+ id: photo.id
2496
+ });
2497
+ onPhotoRegistered?.();
2498
+ toastManager.add({
2499
+ title: "Download successful",
2500
+ type: "success"
2501
+ });
2502
+ refresh();
2503
+ } catch (err) {
2504
+ setSaveError(err?.message || "An error occurred while downloading the photo.");
2505
+ toastManager.add({
2506
+ title: "Download failed",
2507
+ description: err?.message || "An error occurred while downloading the photo.",
2508
+ type: "error"
2509
+ });
2510
+ } finally {
2511
+ setSaving(false);
2512
+ }
2513
+ }, [
2514
+ downloadEndpoint,
2515
+ onDownloadPhoto,
2516
+ onPhotoRegistered,
2517
+ refresh,
2518
+ saving,
2519
+ uploadEndpoint
2520
+ ]);
2521
+ return {
2522
+ selectedPhotoId,
2523
+ selectedPhoto,
2524
+ saveError,
2525
+ saving,
2526
+ onSelectPhoto,
2527
+ onSavePhoto: useCallback(async (photoId) => {
2528
+ const photo = items.find((item) => item.id === photoId);
2529
+ if (!photo) return;
2530
+ await savePhoto(photo);
2531
+ }, [items, savePhoto]),
2532
+ onSave: useCallback(async () => {
2533
+ if (!selectedPhoto) return;
2534
+ await savePhoto(selectedPhoto);
2535
+ }, [savePhoto, selectedPhoto])
2536
+ };
2537
+ }
2538
+
2539
+ //#endregion
2540
+ //#region src/plugins/unsplash/components/UnsplashPhotoGrid.tsx
2541
+ function PhotoPreviewOverlay({ photo, onClose, onDownload }) {
2542
+ useEffect(() => {
2543
+ const handleKey = (e) => {
2544
+ if (e.key === "Escape") onClose();
2545
+ };
2546
+ document.addEventListener("keydown", handleKey);
2547
+ return () => document.removeEventListener("keydown", handleKey);
2548
+ }, [onClose]);
2549
+ return /* @__PURE__ */ jsx("div", {
2550
+ className: "fixed inset-0 z-[9999] flex items-center justify-center bg-black/80 backdrop-blur-sm p-4",
2551
+ onClick: onClose,
2552
+ children: /* @__PURE__ */ jsxs("div", {
2553
+ className: "relative flex max-h-full max-w-5xl flex-col overflow-hidden rounded-xl shadow-2xl",
2554
+ onClick: (e) => e.stopPropagation(),
2555
+ children: [
2556
+ /* @__PURE__ */ jsxs("div", {
2557
+ className: "absolute right-2 top-2 z-10 flex gap-1.5",
2558
+ children: [photo.htmlUrl && /* @__PURE__ */ jsxs(Tooltip, { children: [/* @__PURE__ */ jsx(TooltipTrigger, { render: /* @__PURE__ */ jsx(Button, {
2559
+ type: "button",
2560
+ size: "icon",
2561
+ variant: "outline",
2562
+ className: "bg-white/90 backdrop-blur-sm hover:bg-white",
2563
+ onClick: () => window.open(photo.htmlUrl, "_blank"),
2564
+ children: /* @__PURE__ */ jsx(ExternalLinkIcon, { className: "size-4" })
2565
+ }) }), /* @__PURE__ */ jsx(TooltipPopup, {
2566
+ className: "text-xs",
2567
+ children: "View on Unsplash"
2568
+ })] }), /* @__PURE__ */ jsx(Button, {
2569
+ type: "button",
2570
+ size: "icon",
2571
+ variant: "outline",
2572
+ onClick: onClose,
2573
+ children: /* @__PURE__ */ jsx(XIcon, { className: "size-4" })
2574
+ })]
2575
+ }),
2576
+ /* @__PURE__ */ jsx("div", {
2577
+ className: "overflow-hidden",
2578
+ children: /* @__PURE__ */ jsx("img", {
2579
+ src: photo.regularUrl,
2580
+ alt: photo.description,
2581
+ className: "h-auto w-full object-contain"
2582
+ })
2583
+ }),
2584
+ /* @__PURE__ */ jsxs("div", {
2585
+ className: "flex items-center justify-between gap-4 bg-white px-4 py-3",
2586
+ children: [/* @__PURE__ */ jsxs("div", {
2587
+ className: "min-w-0",
2588
+ children: [photo.authorName && /* @__PURE__ */ jsx("p", {
2589
+ className: "text-sm font-medium text-gray-900 truncate",
2590
+ children: photo.authorName
2591
+ }), photo.description && /* @__PURE__ */ jsx("p", {
2592
+ className: "text-xs text-gray-500 truncate",
2593
+ children: photo.description
2594
+ })]
2595
+ }), /* @__PURE__ */ jsx(Button, {
2596
+ type: "button",
2597
+ size: "sm",
2598
+ onClick: () => {
2599
+ onDownload(photo.id);
2600
+ onClose();
2601
+ },
2602
+ children: "Download"
2603
+ })]
2604
+ })
2605
+ ]
2606
+ })
2607
+ });
2608
+ }
2609
+ function UnsplashPhotoGrid({ items, selectedPhotoId, onSelectPhoto, onDownloadPhoto }) {
2610
+ const [previewPhoto, setPreviewPhoto] = useState(null);
2611
+ const openPreview = useCallback((photo, description) => {
2612
+ setPreviewPhoto({
2613
+ ...photo,
2614
+ description
2615
+ });
2616
+ }, []);
2617
+ const closePreview = useCallback(() => {
2618
+ setPreviewPhoto(null);
2619
+ }, []);
2620
+ return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("div", {
2621
+ className: "columns-2 gap-3 sm:columns-3",
2622
+ children: items.map((photo) => {
2623
+ const rawDescription = photo.altDescription || photo.description || "Photo Unsplash";
2624
+ const description = rawDescription.charAt(0).toUpperCase() + rawDescription.slice(1);
2625
+ return /* @__PURE__ */ jsxs("div", {
2626
+ onClick: () => onSelectPhoto(photo.id),
2627
+ className: "group relative mb-3 w-full overflow-hidden rounded-lg border text-left transition break-inside-avoid cursor-pointer",
2628
+ children: [
2629
+ /* @__PURE__ */ jsx("img", {
2630
+ src: photo.thumbUrl,
2631
+ alt: description,
2632
+ className: "h-auto w-full object-cover"
2633
+ }),
2634
+ selectedPhotoId === photo.id && /* @__PURE__ */ jsx("div", { className: "absolute inset-0 rounded-lg ring-2 ring-primary ring-offset-1 pointer-events-none" }),
2635
+ /* @__PURE__ */ jsx("div", {
2636
+ className: "absolute inset-0 bg-gradient-to-t from-black/75 via-black/45 to-transparent opacity-0 transition-all duration-300 ease-out group-hover:opacity-100",
2637
+ children: /* @__PURE__ */ jsxs("div", {
2638
+ className: "absolute inset-x-0 bottom-0 translate-y-3 p-3 transition-transform duration-300 ease-out group-hover:translate-y-0 flex gap-x-3 justify-between items-end",
2639
+ children: [/* @__PURE__ */ jsxs("div", {
2640
+ className: "flex flex-col gap-y-1.5 text-white min-w-0",
2641
+ children: [photo.authorName && /* @__PURE__ */ jsx("div", {
2642
+ className: "text-xs font-medium truncate",
2643
+ children: photo.authorName
2644
+ }), /* @__PURE__ */ jsx("div", {
2645
+ className: "line-clamp-2 text-xs text-white/80",
2646
+ children: description
2647
+ })]
2648
+ }), /* @__PURE__ */ jsxs("div", {
2649
+ className: "flex shrink-0 gap-1.5",
2650
+ children: [/* @__PURE__ */ jsxs(Tooltip, { children: [/* @__PURE__ */ jsx(TooltipTrigger, {
2651
+ delay: 50,
2652
+ render: /* @__PURE__ */ jsx(Button, {
2653
+ type: "button",
2654
+ variant: "outline",
2655
+ className: "hover:bg-stone-50!",
2656
+ size: "icon-lg",
2657
+ onClick: (e) => {
2658
+ e.stopPropagation();
2659
+ openPreview(photo, description);
2660
+ },
2661
+ children: /* @__PURE__ */ jsx(ZoomInIcon, {
2662
+ "aria-hidden": "true",
2663
+ className: "size-4"
2664
+ })
2665
+ })
2666
+ }), /* @__PURE__ */ jsx(TooltipPopup, {
2667
+ className: "text-xs",
2668
+ children: "Preview"
2669
+ })] }), /* @__PURE__ */ jsxs(Tooltip, { children: [/* @__PURE__ */ jsx(TooltipTrigger, {
2670
+ delay: 50,
2671
+ render: /* @__PURE__ */ jsx(Button, {
2672
+ type: "button",
2673
+ variant: "outline",
2674
+ className: "hover:bg-stone-50!",
2675
+ size: "icon-lg",
2676
+ onClick: (e) => {
2677
+ e.stopPropagation();
2678
+ onSelectPhoto(photo.id);
2679
+ onDownloadPhoto(photo.id);
2680
+ },
2681
+ children: /* @__PURE__ */ jsx(DownloadIcon, {
2682
+ "aria-hidden": "true",
2683
+ className: "size-4"
2684
+ })
2685
+ })
2686
+ }), /* @__PURE__ */ jsx(TooltipPopup, {
2687
+ className: "text-xs",
2688
+ children: "Download"
2689
+ })] })]
2690
+ })]
2691
+ })
2692
+ })
2693
+ ]
2694
+ }, photo.id);
2695
+ })
2696
+ }), previewPhoto && /* @__PURE__ */ jsx(PhotoPreviewOverlay, {
2697
+ photo: previewPhoto,
2698
+ onClose: closePreview,
2699
+ onDownload: (id) => {
2700
+ onSelectPhoto(id);
2701
+ return onDownloadPhoto(id);
2702
+ }
2703
+ })] });
2704
+ }
2705
+
2706
+ //#endregion
2707
+ //#region src/components/ui/alert.tsx
2708
+ const alertVariants = cva("relative grid w-full items-start gap-x-2 gap-y-0.5 rounded-lg border text-card-foreground text-sm has-[>svg]:has-data-[slot=alert-action]:grid-cols-[calc(var(--spacing)*4)_1fr_auto] has-[>svg]:grid-cols-[calc(var(--spacing)*4)_1fr] has-data-[slot=alert-action]:grid-cols-[1fr_auto] has-[>svg]:gap-x-2 [&>svg]:h-lh [&>svg]:w-4", {
2709
+ defaultVariants: {
2710
+ size: "default",
2711
+ variant: "default"
2712
+ },
2713
+ variants: {
2714
+ size: {
2715
+ default: "px-3.5 py-3",
2716
+ sm: "px-2.5 py-2 text-xs"
2717
+ },
2718
+ variant: {
2719
+ default: "bg-transparent dark:bg-input/32 [&>svg]:text-muted-foreground",
2720
+ error: "border-destructive/32 bg-destructive/4 [&>svg]:text-destructive",
2721
+ info: "border-info/32 bg-info/4 [&>svg]:text-info",
2722
+ success: "border-success/32 bg-success/4 [&>svg]:text-success",
2723
+ warning: "border-warning/32 bg-warning/4 [&>svg]:text-warning"
2724
+ }
2725
+ }
2726
+ });
2727
+ function Alert({ className, size, variant, ...props }) {
2728
+ return /* @__PURE__ */ jsx("div", {
2729
+ className: cn(alertVariants({
2730
+ size,
2731
+ variant
2732
+ }), className),
2733
+ "data-slot": "alert",
2734
+ role: "alert",
2735
+ ...props
2736
+ });
2737
+ }
2738
+ function AlertTitle({ className, ...props }) {
2739
+ return /* @__PURE__ */ jsx("div", {
2740
+ className: cn("font-medium [svg~&]:col-start-2", className),
2741
+ "data-slot": "alert-title",
2742
+ ...props
2743
+ });
2744
+ }
2745
+ function AlertDescription({ className, ...props }) {
2746
+ return /* @__PURE__ */ jsx("div", {
2747
+ className: cn("flex flex-col gap-2.5 text-muted-foreground [svg~&]:col-start-2", className),
2748
+ "data-slot": "alert-description",
2749
+ ...props
2750
+ });
2751
+ }
2752
+
2753
+ //#endregion
2754
+ //#region src/plugins/unsplash/components/UnsplashStatus.tsx
2755
+ function UnsplashStatus({ loading = false, error = null, saveError = null, noResults = false }) {
2756
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
2757
+ error ? /* @__PURE__ */ jsxs(Alert, {
2758
+ variant: "error",
2759
+ children: [/* @__PURE__ */ jsx(TriangleAlertIcon, {}), /* @__PURE__ */ jsx(AlertDescription, { children: error })]
2760
+ }) : null,
2761
+ saveError ? /* @__PURE__ */ jsxs(Alert, {
2762
+ variant: "error",
2763
+ children: [/* @__PURE__ */ jsx(TriangleAlertIcon, {}), /* @__PURE__ */ jsx(AlertDescription, { children: saveError })]
2764
+ }) : null,
2765
+ noResults ? /* @__PURE__ */ jsxs(Alert, {
2766
+ variant: "info",
2767
+ children: [/* @__PURE__ */ jsx(InfoIcon, {}), /* @__PURE__ */ jsx(AlertDescription, { children: "No photos found for your search." })]
2768
+ }) : null,
2769
+ loading ? /* @__PURE__ */ jsx("div", {
2770
+ className: "flex items-center justify-center py-8",
2771
+ children: /* @__PURE__ */ jsx(Loader2Icon, { className: "text-muted-foreground size-8 animate-spin" })
2772
+ }) : null
2773
+ ] });
2774
+ }
2775
+
2363
2776
  //#endregion
2364
2777
  //#region src/components/MediaPicker/index.tsx
2365
2778
  const getEditorConfig = () => {
@@ -2422,7 +2835,7 @@ const MediaUploadZone = ({ onUploadSuccess, acceptedTypes = "image/*,video/*,app
2422
2835
  return new Promise((resolve, reject) => {
2423
2836
  try {
2424
2837
  const { uploadUrl, langId } = getEditorConfig();
2425
- const csrfToken = document.querySelector("meta[name=\"csrf-token\"]");
2838
+ const csrfToken = getCsrfToken();
2426
2839
  const formData = new FormData();
2427
2840
  formData.append("file", file);
2428
2841
  if (langId) formData.append("lang_id", langId);
@@ -2431,17 +2844,20 @@ const MediaUploadZone = ({ onUploadSuccess, acceptedTypes = "image/*,video/*,app
2431
2844
  if (e.lengthComputable) setUploadProgress(e.loaded / e.total * 100);
2432
2845
  });
2433
2846
  xhr.addEventListener("load", () => {
2434
- if (xhr.status === 200) {
2847
+ try {
2435
2848
  const response = JSON.parse(xhr.responseText);
2436
- if (response.success && response.media) resolve(response.media);
2437
- else reject(/* @__PURE__ */ new Error("Upload failed"));
2438
- } else reject(/* @__PURE__ */ new Error("Error during upload"));
2849
+ if (xhr.status === 200 && response.success && response.media) resolve(response.media);
2850
+ else reject(new Error(response.message || response.error || `Upload failed (${xhr.status})`));
2851
+ } catch {
2852
+ reject(/* @__PURE__ */ new Error(`Server returned an unexpected response (status ${xhr.status}). The server may not accept this file type.`));
2853
+ }
2439
2854
  });
2440
2855
  xhr.addEventListener("error", () => {
2441
2856
  reject(/* @__PURE__ */ new Error("Error during upload"));
2442
2857
  });
2443
2858
  xhr.open("POST", uploadUrl);
2444
- if (csrfToken) xhr.setRequestHeader("X-CSRF-TOKEN", csrfToken.content);
2859
+ xhr.setRequestHeader("Accept", "application/json");
2860
+ if (csrfToken) xhr.setRequestHeader("X-CSRF-TOKEN", csrfToken);
2445
2861
  xhr.send(formData);
2446
2862
  } catch (error) {
2447
2863
  reject(error);
@@ -2559,24 +2975,86 @@ const MediaUploadZone = ({ onUploadSuccess, acceptedTypes = "image/*,video/*,app
2559
2975
  });
2560
2976
  };
2561
2977
  const MediaPreview = ({ media, className }) => {
2562
- if (media.type.toLowerCase().startsWith("video/")) return /* @__PURE__ */ jsx("video", {
2563
- src: media.url,
2564
- className: cn("h-auto w-full aspect-square border border-border rounded-md object-cover", className),
2565
- controls: true,
2566
- preload: "metadata"
2978
+ const type = media.type.toLowerCase();
2979
+ const isVideo = type.startsWith("video/") || type === "video";
2980
+ const isImage = type.startsWith("image/") || type === "picture";
2981
+ const isFile = !isVideo && !isImage;
2982
+ if (isVideo) return /* @__PURE__ */ jsx("div", {
2983
+ className: cn("flex items-center justify-center aspect-square border border-border rounded-md bg-muted", className),
2984
+ children: /* @__PURE__ */ jsx(FilePlayIcon, { className: "text-muted-foreground size-5" })
2985
+ });
2986
+ if (isFile) return /* @__PURE__ */ jsx("div", {
2987
+ className: cn("flex items-center justify-center aspect-square border border-border rounded-md bg-muted", className),
2988
+ children: /* @__PURE__ */ jsx(FileTextIcon, { className: "text-muted-foreground size-5" })
2567
2989
  });
2568
2990
  return /* @__PURE__ */ jsx("img", {
2569
- src: media.thumbnail,
2991
+ src: media.url,
2570
2992
  alt: media.name,
2571
2993
  className: cn("h-auto w-full aspect-square border border-border rounded-md object-cover", className)
2572
2994
  });
2573
2995
  };
2996
+ function isMediaPicture(type) {
2997
+ const t = type.toLowerCase();
2998
+ return t.startsWith("image/") || t === "picture";
2999
+ }
3000
+ function MediaPreviewOverlay({ media, onClose }) {
3001
+ useEffect(() => {
3002
+ const handleKey = (e) => {
3003
+ if (e.key === "Escape") onClose();
3004
+ };
3005
+ document.addEventListener("keydown", handleKey);
3006
+ return () => document.removeEventListener("keydown", handleKey);
3007
+ }, [onClose]);
3008
+ return /* @__PURE__ */ jsx("div", {
3009
+ className: "absolute inset-0 z-10 flex items-center justify-center bg-black/80 backdrop-blur-sm p-4",
3010
+ onClick: onClose,
3011
+ children: /* @__PURE__ */ jsxs("div", {
3012
+ className: "relative flex max-h-full max-w-5xl flex-col overflow-hidden rounded-xl shadow-2xl",
3013
+ onClick: (e) => e.stopPropagation(),
3014
+ children: [
3015
+ /* @__PURE__ */ jsx("div", {
3016
+ className: "absolute right-2 top-2 z-10 flex gap-1.5",
3017
+ children: /* @__PURE__ */ jsx(Button, {
3018
+ type: "button",
3019
+ size: "icon",
3020
+ variant: "outline",
3021
+ onClick: onClose,
3022
+ children: /* @__PURE__ */ jsx(XIcon, { className: "size-4" })
3023
+ })
3024
+ }),
3025
+ /* @__PURE__ */ jsx("div", {
3026
+ className: "overflow-hidden",
3027
+ children: /* @__PURE__ */ jsx("img", {
3028
+ src: media.url,
3029
+ alt: media.name,
3030
+ className: "h-auto w-full object-contain"
3031
+ })
3032
+ }),
3033
+ /* @__PURE__ */ jsx("div", {
3034
+ className: "flex items-center justify-between gap-4 bg-white px-4 py-3",
3035
+ children: /* @__PURE__ */ jsxs("div", {
3036
+ className: "min-w-0",
3037
+ children: [/* @__PURE__ */ jsx("p", {
3038
+ className: "text-sm font-medium text-gray-900 truncate",
3039
+ children: media.name
3040
+ }), /* @__PURE__ */ jsx("p", {
3041
+ className: "text-xs text-gray-500",
3042
+ children: media.type
3043
+ })]
3044
+ })
3045
+ })
3046
+ ]
3047
+ })
3048
+ });
3049
+ }
2574
3050
  const DEFAULT_VALUE = {
2575
3051
  type: "media",
2576
3052
  media: null
2577
3053
  };
2578
3054
  const PAGE_SIZE = 10;
2579
- const MediaLibraryModal = ({ value, onChange, mediaType, open, onOpenChange, acceptedTypes, maxSize }) => {
3055
+ const MediaLibraryModal = ({ value, onChange, mediaType, open, onOpenChange, acceptedTypes, maxSize, unsplashSearchEndpoint, unsplashFeaturedQuery, unsplashDownloadEndpoint, unsplashUploadEndpoint, onUnsplashDownloadPhoto }) => {
3056
+ const [source, setSource] = useState("media-library");
3057
+ const [previewMedia, setPreviewMedia] = useState(null);
2580
3058
  const [medias, setMedias] = useState([]);
2581
3059
  const [loading, setLoading] = useState(false);
2582
3060
  const [totalItems, setTotalItems] = useState(0);
@@ -2607,25 +3085,15 @@ const MediaLibraryModal = ({ value, onChange, mediaType, open, onOpenChange, acc
2607
3085
  }
2608
3086
  }, [mediaType]);
2609
3087
  useEffect(() => {
2610
- if (open) loadMedias(pagination.pageIndex, pagination.pageSize, searchQuery, sorting);
3088
+ if (open && source === "media-library") loadMedias(pagination.pageIndex, pagination.pageSize, searchQuery, sorting);
2611
3089
  }, [
2612
3090
  open,
3091
+ source,
2613
3092
  pagination,
2614
3093
  sorting,
2615
3094
  searchQuery,
2616
3095
  loadMedias
2617
3096
  ]);
2618
- const handleSearch = useCallback((query) => {
2619
- setSearchQuery(query);
2620
- setPagination((prev) => ({
2621
- ...prev,
2622
- pageIndex: 0
2623
- }));
2624
- }, []);
2625
- const handleRowClick = useCallback((media) => {
2626
- onChange(media);
2627
- onOpenChange(false);
2628
- }, [onChange, onOpenChange]);
2629
3097
  const handleUploadSuccess = useCallback(async (_media) => {
2630
3098
  await loadMedias(0, pagination.pageSize, searchQuery, sorting);
2631
3099
  setPagination((prev) => ({
@@ -2638,42 +3106,204 @@ const MediaLibraryModal = ({ value, onChange, mediaType, open, onOpenChange, acc
2638
3106
  searchQuery,
2639
3107
  sorting
2640
3108
  ]);
3109
+ const handleRowClick = useCallback((media) => {
3110
+ onChange(media);
3111
+ onOpenChange(false);
3112
+ }, [onChange, onOpenChange]);
3113
+ const { query: unsplashQuery, page: unsplashPage, portrait, landscape, isFeatured: unsplashIsFeatured, items: unsplashItems, hasMore, loading: unsplashLoading, error: unsplashError, onSearchChange: onUnsplashSearchChange, onPageChange: onUnsplashPageChange, onPortraitChange, onLandscapeChange, refresh: refreshUnsplash } = useUnsplashSearch({
3114
+ endpoint: unsplashSearchEndpoint,
3115
+ featuredQuery: unsplashFeaturedQuery,
3116
+ enabled: source === "unsplash"
3117
+ });
3118
+ const { selectedPhotoId, saveError, onSelectPhoto, onSavePhoto } = useUnsplashModalController({
3119
+ downloadEndpoint: unsplashDownloadEndpoint,
3120
+ uploadEndpoint: unsplashUploadEndpoint,
3121
+ onDownloadPhoto: onUnsplashDownloadPhoto,
3122
+ onPhotoRegistered: useCallback(() => {
3123
+ setSource("media-library");
3124
+ loadMedias(0, pagination.pageSize, searchQuery, sorting);
3125
+ setPagination((prev) => ({
3126
+ ...prev,
3127
+ pageIndex: 0
3128
+ }));
3129
+ }, [
3130
+ loadMedias,
3131
+ pagination.pageSize,
3132
+ searchQuery,
3133
+ sorting
3134
+ ]),
3135
+ items: unsplashItems,
3136
+ refresh: refreshUnsplash
3137
+ });
3138
+ const activeSearchQuery = source === "unsplash" ? unsplashQuery : searchQuery;
3139
+ const handleSearch = useCallback((query) => {
3140
+ if (source === "unsplash") onUnsplashSearchChange(query);
3141
+ else {
3142
+ setSearchQuery(query);
3143
+ setPagination((prev) => ({
3144
+ ...prev,
3145
+ pageIndex: 0
3146
+ }));
3147
+ }
3148
+ }, [source, onUnsplashSearchChange]);
3149
+ const handleSourceChange = useCallback((newSource) => {
3150
+ setSource(newSource);
3151
+ if (newSource === "media-library") setPagination((prev) => ({
3152
+ ...prev,
3153
+ pageIndex: 0
3154
+ }));
3155
+ }, []);
3156
+ const columns = useMemo(() => [
3157
+ {
3158
+ id: "thumbnail",
3159
+ header: "Preview",
3160
+ size: 80,
3161
+ enableSorting: false,
3162
+ cell: ({ row }) => /* @__PURE__ */ jsx(MediaPreview, {
3163
+ media: row.original,
3164
+ className: "size-12"
3165
+ })
3166
+ },
3167
+ {
3168
+ accessorKey: "name",
3169
+ header: "Name",
3170
+ cell: ({ row }) => /* @__PURE__ */ jsx("div", {
3171
+ className: "font-medium",
3172
+ children: row.getValue("name")
3173
+ })
3174
+ },
3175
+ {
3176
+ accessorKey: "type",
3177
+ header: "Type",
3178
+ size: 140,
3179
+ cell: ({ row }) => /* @__PURE__ */ jsx(Badge, {
3180
+ variant: "outline",
3181
+ children: row.getValue("type")
3182
+ })
3183
+ },
3184
+ {
3185
+ id: "actions",
3186
+ header: "",
3187
+ size: 60,
3188
+ enableSorting: false,
3189
+ cell: ({ row }) => {
3190
+ const media = row.original;
3191
+ if (!isMediaPicture(media.type)) return null;
3192
+ return /* @__PURE__ */ jsx("div", {
3193
+ className: "flex justify-end",
3194
+ children: /* @__PURE__ */ jsxs(Tooltip, { children: [/* @__PURE__ */ jsx(TooltipTrigger, {
3195
+ delay: 50,
3196
+ render: /* @__PURE__ */ jsx(Button, {
3197
+ type: "button",
3198
+ variant: "outline",
3199
+ size: "icon-lg",
3200
+ onClick: (e) => {
3201
+ e.stopPropagation();
3202
+ setPreviewMedia(media);
3203
+ },
3204
+ "aria-label": "Preview image",
3205
+ children: /* @__PURE__ */ jsx(ZoomInIcon, { className: "size-4" })
3206
+ })
3207
+ }), /* @__PURE__ */ jsx(TooltipPopup, {
3208
+ className: "text-xs",
3209
+ children: "Preview"
3210
+ })] })
3211
+ });
3212
+ }
3213
+ }
3214
+ ], [setPreviewMedia]);
3215
+ const filterContent = /* @__PURE__ */ jsxs(Fragment, { children: [source === "unsplash" && /* @__PURE__ */ jsxs(Popover, { children: [/* @__PURE__ */ jsx(PopoverTrigger, { render: /* @__PURE__ */ jsx(Button, {
3216
+ size: "icon",
3217
+ variant: portrait || landscape ? "secondary" : "outline",
3218
+ "aria-label": "Unsplash filters",
3219
+ children: /* @__PURE__ */ jsx(SlidersHorizontalIcon, { className: "size-4" })
3220
+ }) }), /* @__PURE__ */ jsxs(PopoverPopup, {
3221
+ align: "end",
3222
+ className: "min-w-44",
3223
+ children: [/* @__PURE__ */ jsx("p", {
3224
+ className: "mb-3 text-sm font-medium",
3225
+ children: "Filters"
3226
+ }), /* @__PURE__ */ jsxs("div", {
3227
+ className: "flex flex-col gap-2",
3228
+ children: [/* @__PURE__ */ jsxs("div", {
3229
+ className: "flex items-center gap-2",
3230
+ children: [/* @__PURE__ */ jsx(Checkbox, {
3231
+ id: "unsplash-portrait",
3232
+ checked: portrait,
3233
+ onCheckedChange: onPortraitChange,
3234
+ layout: "inline"
3235
+ }), /* @__PURE__ */ jsx("label", {
3236
+ htmlFor: "unsplash-portrait",
3237
+ className: "cursor-pointer text-sm font-medium leading-none",
3238
+ children: "Portrait"
3239
+ })]
3240
+ }), /* @__PURE__ */ jsxs("div", {
3241
+ className: "flex items-center gap-2",
3242
+ children: [/* @__PURE__ */ jsx(Checkbox, {
3243
+ id: "unsplash-landscape",
3244
+ checked: landscape,
3245
+ onCheckedChange: onLandscapeChange,
3246
+ layout: "inline"
3247
+ }), /* @__PURE__ */ jsx("label", {
3248
+ htmlFor: "unsplash-landscape",
3249
+ className: "cursor-pointer text-sm font-medium leading-none",
3250
+ children: "Landscape"
3251
+ })]
3252
+ })]
3253
+ })]
3254
+ })] }), /* @__PURE__ */ jsxs(Select, {
3255
+ value: source,
3256
+ onValueChange: (val) => handleSourceChange(val),
3257
+ children: [/* @__PURE__ */ jsx(SelectTrigger, { children: /* @__PURE__ */ jsx(SelectValue, { children: source === "media-library" ? "Media Library" : "Unsplash" }) }), /* @__PURE__ */ jsxs(SelectPopup, { children: [/* @__PURE__ */ jsx(SelectItem, {
3258
+ value: "media-library",
3259
+ children: "Media Library"
3260
+ }), /* @__PURE__ */ jsx(SelectItem, {
3261
+ value: "unsplash",
3262
+ children: "Unsplash"
3263
+ })] })]
3264
+ })] });
3265
+ const unsplashContent = source === "unsplash" ? /* @__PURE__ */ jsxs("div", {
3266
+ className: "flex flex-col gap-4",
3267
+ children: [
3268
+ /* @__PURE__ */ jsx(UnsplashStatus, {
3269
+ error: unsplashError,
3270
+ saveError
3271
+ }),
3272
+ unsplashItems.length > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(UnsplashPhotoGrid, {
3273
+ items: unsplashItems,
3274
+ selectedPhotoId,
3275
+ onSelectPhoto,
3276
+ onDownloadPhoto: onSavePhoto
3277
+ }), hasMore && /* @__PURE__ */ jsx("div", {
3278
+ className: "flex justify-center",
3279
+ children: /* @__PURE__ */ jsx(Button, {
3280
+ type: "button",
3281
+ variant: "outline",
3282
+ disabled: unsplashLoading,
3283
+ onClick: () => onUnsplashPageChange(unsplashPage + 1),
3284
+ children: "Load more"
3285
+ })
3286
+ })] }),
3287
+ /* @__PURE__ */ jsx(UnsplashStatus, {
3288
+ loading: unsplashLoading,
3289
+ noResults: !unsplashLoading && unsplashItems.length === 0 && !unsplashIsFeatured
3290
+ })
3291
+ ]
3292
+ }) : void 0;
3293
+ const footer = source === "media-library" ? /* @__PURE__ */ jsx(MediaUploadZone, {
3294
+ onUploadSuccess: handleUploadSuccess,
3295
+ acceptedTypes,
3296
+ maxSize
3297
+ }) : void 0;
2641
3298
  return /* @__PURE__ */ jsx(PickerModal, {
2642
3299
  title: "Select Media",
2643
- searchQuery,
3300
+ searchQuery: activeSearchQuery,
2644
3301
  onSearch: handleSearch,
2645
- columns: useMemo(() => [
2646
- {
2647
- id: "thumbnail",
2648
- header: "Preview",
2649
- size: 80,
2650
- enableSorting: false,
2651
- cell: ({ row }) => /* @__PURE__ */ jsx(MediaPreview, {
2652
- media: row.original,
2653
- className: "size-12"
2654
- })
2655
- },
2656
- {
2657
- accessorKey: "name",
2658
- header: "Name",
2659
- cell: ({ row }) => /* @__PURE__ */ jsx("div", {
2660
- className: "font-medium",
2661
- children: row.getValue("name")
2662
- })
2663
- },
2664
- {
2665
- accessorKey: "type",
2666
- header: "Type",
2667
- size: 140,
2668
- cell: ({ row }) => /* @__PURE__ */ jsx(Badge, {
2669
- variant: "outline",
2670
- children: row.getValue("type")
2671
- })
2672
- }
2673
- ], []),
2674
- data: medias,
2675
- loading,
2676
- totalItems,
3302
+ searchPlaceholder: source === "unsplash" ? "Search Unsplash photos…" : "Search media…",
3303
+ columns,
3304
+ data: source === "media-library" ? medias : [],
3305
+ loading: source === "media-library" ? loading : false,
3306
+ totalItems: source === "media-library" ? totalItems : 0,
2677
3307
  selectedId: value?.id,
2678
3308
  onRowClick: handleRowClick,
2679
3309
  pagination,
@@ -2682,16 +3312,19 @@ const MediaLibraryModal = ({ value, onChange, mediaType, open, onOpenChange, acc
2682
3312
  onSortingChange: setSorting,
2683
3313
  emptyIcon: /* @__PURE__ */ jsx(FileIcon, { className: "mb-2 size-12 opacity-20" }),
2684
3314
  emptyMessage: "No media found",
2685
- footer: /* @__PURE__ */ jsx(MediaUploadZone, {
2686
- onUploadSuccess: handleUploadSuccess,
2687
- acceptedTypes,
2688
- maxSize
2689
- })
3315
+ filterContent,
3316
+ tableContent: unsplashContent,
3317
+ footer,
3318
+ overlay: previewMedia ? /* @__PURE__ */ jsx(MediaPreviewOverlay, {
3319
+ media: previewMedia,
3320
+ onClose: () => setPreviewMedia(null)
3321
+ }) : void 0
2690
3322
  });
2691
3323
  };
2692
- const MediaPicker = ({ value, onChange, mediaType, acceptedTypes = "image/*,video/*,application/pdf", maxSize = 10 }) => {
3324
+ const MediaPicker = ({ value, onChange, mediaType, acceptedTypes = "image/*,video/*,application/pdf", maxSize = 10, unsplashSearchEndpoint, unsplashFeaturedQuery, unsplashDownloadEndpoint, unsplashUploadEndpoint, onUnsplashDownloadPhoto }) => {
2693
3325
  const [open, setOpen] = useState(false);
2694
3326
  const currentValue = value || DEFAULT_VALUE;
3327
+ const { url: previewUrl } = useMediaUrl(currentValue);
2695
3328
  const displayValue = currentValue.type === "media" && currentValue.media ? currentValue.media.name : currentValue.url || "";
2696
3329
  const hasValue = Boolean(currentValue.type === "media" && currentValue.media || currentValue.type === "url" && currentValue.url);
2697
3330
  const handleInputChange = (e) => {
@@ -2705,8 +3338,7 @@ const MediaPicker = ({ value, onChange, mediaType, acceptedTypes = "image/*,vide
2705
3338
  type: "media",
2706
3339
  media: {
2707
3340
  id: media.id,
2708
- name: media.name,
2709
- thumbnail: media.thumbnail
3341
+ name: media.name
2710
3342
  }
2711
3343
  });
2712
3344
  };
@@ -2721,12 +3353,12 @@ const MediaPicker = ({ value, onChange, mediaType, acceptedTypes = "image/*,vide
2721
3353
  children: [/* @__PURE__ */ jsx("div", {
2722
3354
  className: "flex items-center gap-2",
2723
3355
  children: /* @__PURE__ */ jsxs(InputGroup, { children: [
2724
- currentValue.type === "media" && currentValue.media?.thumbnail && /* @__PURE__ */ jsx(InputGroupAddon, {
3356
+ previewUrl && /* @__PURE__ */ jsx(InputGroupAddon, {
2725
3357
  align: "inline-start",
2726
3358
  className: "pl-1.5",
2727
3359
  children: /* @__PURE__ */ jsx("img", {
2728
- src: currentValue.media.thumbnail,
2729
- alt: currentValue.media.name,
3360
+ src: previewUrl,
3361
+ alt: currentValue.media?.name ?? "",
2730
3362
  className: cn("max-h-12 my-1.5 aspect-square border border-border rounded-md object-cover")
2731
3363
  })
2732
3364
  }),
@@ -2757,20 +3389,17 @@ const MediaPicker = ({ value, onChange, mediaType, acceptedTypes = "image/*,vide
2757
3389
  open,
2758
3390
  onOpenChange: setOpen,
2759
3391
  acceptedTypes,
2760
- maxSize
3392
+ maxSize,
3393
+ unsplashSearchEndpoint,
3394
+ unsplashFeaturedQuery,
3395
+ unsplashDownloadEndpoint,
3396
+ unsplashUploadEndpoint,
3397
+ onUnsplashDownloadPhoto
2761
3398
  })]
2762
3399
  })
2763
3400
  });
2764
3401
  };
2765
3402
  /**
2766
- * Retourne l'URL directe si le type est "url".
2767
- * Pour les références média (`type: "media"`), utiliser `useMediaUrl` à la place.
2768
- */
2769
- const getMediaUrl = (value) => {
2770
- if (!value) return void 0;
2771
- if (value.type === "url") return value.url;
2772
- };
2773
- /**
2774
3403
  * Hook React qui résout dynamiquement l'URL courante d'un `MediaPickerValue`.
2775
3404
  *
2776
3405
  * Pour les références média (`type: "media"`), l'URL est récupérée depuis
@@ -2815,43 +3444,49 @@ const useMediaUrl = (value) => {
2815
3444
 
2816
3445
  //#endregion
2817
3446
  //#region src/components/ui/menu.tsx
2818
- const MenuCreateHandle = Menu$1.createHandle;
2819
- const Menu = Menu$1.Root;
2820
- const MenuPortal = Menu$1.Portal;
2821
- function MenuTrigger(props) {
2822
- return /* @__PURE__ */ jsx(Menu$1.Trigger, {
3447
+ const MenuCreateHandle = MenuPrimitive.createHandle;
3448
+ const Menu = MenuPrimitive.Root;
3449
+ const MenuPortal = MenuPrimitive.Portal;
3450
+ function MenuTrigger({ className, children, ...props }) {
3451
+ return /* @__PURE__ */ jsx(MenuPrimitive.Trigger, {
3452
+ className,
2823
3453
  "data-slot": "menu-trigger",
2824
- ...props
3454
+ ...props,
3455
+ children
2825
3456
  });
2826
3457
  }
2827
- function MenuPopup({ children, className, sideOffset = 4, align = "center", alignOffset, side = "bottom", ...props }) {
2828
- return /* @__PURE__ */ jsx(Menu$1.Portal, { children: /* @__PURE__ */ jsx(Menu$1.Positioner, {
2829
- align,
2830
- alignOffset,
2831
- className: "z-50",
2832
- "data-slot": "menu-positioner",
2833
- side,
2834
- sideOffset,
2835
- children: /* @__PURE__ */ jsx(Menu$1.Popup, {
2836
- className: cn("relative flex not-[class*='w-']:min-w-32 origin-(--transform-origin) rounded-lg border bg-popover not-dark:bg-clip-padding shadow-lg/5 outline-none before:pointer-events-none before:absolute before:inset-0 before:rounded-[calc(var(--radius-lg)-1px)] before:shadow-[0_1px_--theme(--color-black/6%)] focus:outline-none dark:before:shadow-[0_-1px_--theme(--color-white/6%)]", className),
2837
- "data-slot": "menu-popup",
2838
- ...props,
2839
- children: /* @__PURE__ */ jsx("div", {
2840
- className: "max-h-(--available-height) w-full overflow-y-auto p-1",
2841
- children
3458
+ function MenuPopup({ children, className, sideOffset = 4, align = "center", alignOffset, side = "bottom", anchor, portalProps, ...props }) {
3459
+ return /* @__PURE__ */ jsx(MenuPortal, {
3460
+ ...portalProps,
3461
+ children: /* @__PURE__ */ jsx(MenuPrimitive.Positioner, {
3462
+ align,
3463
+ alignOffset,
3464
+ anchor,
3465
+ className: "z-50",
3466
+ "data-slot": "menu-positioner",
3467
+ side,
3468
+ sideOffset,
3469
+ children: /* @__PURE__ */ jsx(MenuPrimitive.Popup, {
3470
+ className: cn("relative flex not-[class*='w-']:min-w-32 origin-(--transform-origin) rounded-lg border bg-popover not-dark:bg-clip-padding shadow-lg/5 outline-none before:pointer-events-none before:absolute before:inset-0 before:rounded-[calc(var(--radius-lg)-1px)] before:shadow-[0_1px_--theme(--color-black/4%)] focus:outline-none dark:before:shadow-[0_-1px_--theme(--color-white/6%)]", className),
3471
+ "data-slot": "menu-popup",
3472
+ ...props,
3473
+ children: /* @__PURE__ */ jsx("div", {
3474
+ className: "max-h-(--available-height) w-full overflow-y-auto p-1",
3475
+ children
3476
+ })
2842
3477
  })
2843
3478
  })
2844
- }) });
3479
+ });
2845
3480
  }
2846
3481
  function MenuGroup(props) {
2847
- return /* @__PURE__ */ jsx(Menu$1.Group, {
3482
+ return /* @__PURE__ */ jsx(MenuPrimitive.Group, {
2848
3483
  "data-slot": "menu-group",
2849
3484
  ...props
2850
3485
  });
2851
3486
  }
2852
3487
  function MenuItem({ className, inset, variant = "default", ...props }) {
2853
- return /* @__PURE__ */ jsx(Menu$1.Item, {
2854
- className: cn("[&>svg]:-mx-0.5 flex min-h-8 cursor-default select-none items-center gap-2 rounded-sm px-2 py-1 text-base text-foreground outline-none data-disabled:pointer-events-none data-highlighted:bg-accent data-inset:ps-8 data-[variant=destructive]:text-destructive-foreground data-highlighted:text-accent-foreground data-disabled:opacity-64 sm:min-h-7 sm:text-sm [&>svg:not([class*='opacity-'])]:opacity-80 [&>svg:not([class*='size-'])]:size-4.5 sm:[&>svg:not([class*='size-'])]:size-4 [&>svg]:pointer-events-none [&>svg]:shrink-0", className),
3488
+ return /* @__PURE__ */ jsx(MenuPrimitive.Item, {
3489
+ className: cn("flex min-h-8 cursor-default select-none items-center gap-2 rounded-sm px-2 py-1 text-base text-foreground outline-none data-disabled:pointer-events-none data-highlighted:bg-accent data-inset:ps-8 data-[variant=destructive]:text-destructive-foreground data-highlighted:text-accent-foreground data-disabled:opacity-64 sm:min-h-7 sm:text-sm [&>svg:not([class*='opacity-'])]:opacity-80 [&>svg:not([class*='size-'])]:size-4.5 sm:[&>svg:not([class*='size-'])]:size-4 [&>svg]:pointer-events-none [&>svg]:-mx-0.5 [&>svg]:shrink-0", className),
2855
3490
  "data-inset": inset,
2856
3491
  "data-slot": "menu-item",
2857
3492
  "data-variant": variant,
@@ -2859,21 +3494,22 @@ function MenuItem({ className, inset, variant = "default", ...props }) {
2859
3494
  });
2860
3495
  }
2861
3496
  function MenuCheckboxItem({ className, children, checked, variant = "default", ...props }) {
2862
- return /* @__PURE__ */ jsx(Menu$1.CheckboxItem, {
3497
+ return /* @__PURE__ */ jsx(MenuPrimitive.CheckboxItem, {
2863
3498
  checked,
2864
- className: cn("grid min-h-8 in-data-[side=none]:min-w-[calc(var(--anchor-width)+1.25rem)] cursor-default items-center gap-2 rounded-sm py-1 ps-2 text-base text-foreground outline-none data-disabled:pointer-events-none data-highlighted:bg-accent data-highlighted:text-accent-foreground data-disabled:opacity-64 sm:min-h-7 sm:text-sm [&_svg:not([class*='size-'])]:size-4.5 sm:[&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0", variant === "switch" ? "grid-cols-[1fr_auto] gap-4 pe-1.5" : "grid-cols-[1rem_1fr] pe-4", className),
3499
+ className: cn("grid min-h-8 in-data-[side=none]:min-w-[calc(var(--anchor-width)+1.25rem)] cursor-default items-center gap-2 rounded-sm py-1 ps-2 text-base text-foreground outline-none data-disabled:pointer-events-none data-highlighted:bg-accent data-highlighted:text-accent-foreground data-disabled:opacity-64 sm:min-h-7 sm:text-sm [&_svg:not([class*='size-'])]:size-4.5 sm:[&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0", variant === "switch" ? "grid-cols-[1fr_auto] gap-4 pe-1.5" : "grid-cols-[.75rem_1fr] pe-4", className),
2865
3500
  "data-slot": "menu-checkbox-item",
2866
3501
  ...props,
2867
3502
  children: variant === "switch" ? /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("span", {
2868
3503
  className: "col-start-1",
2869
3504
  children
2870
- }), /* @__PURE__ */ jsx(Menu$1.CheckboxItemIndicator, {
2871
- className: "inset-shadow-[0_1px_--theme(--color-black/6%)] inline-flex h-[calc(var(--thumb-size)+2px)] w-[calc(var(--thumb-size)*2-2px)] shrink-0 items-center rounded-full p-px outline-none transition-[background-color,box-shadow] duration-200 [--thumb-size:--spacing(4)] focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:ring-offset-background data-checked:bg-primary data-unchecked:bg-input data-disabled:opacity-64 sm:[--thumb-size:--spacing(3)]",
3505
+ }), /* @__PURE__ */ jsx(MenuPrimitive.CheckboxItemIndicator, {
3506
+ className: "inset-shadow-[0_1px_--theme(--color-black/4%)] inline-flex h-[calc(var(--thumb-size)+2px)] w-[calc(var(--thumb-size)*2-2px)] shrink-0 items-center rounded-full p-px outline-none transition-[background-color,box-shadow] duration-200 [--thumb-size:--spacing(4)] focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:ring-offset-background data-checked:bg-primary data-unchecked:bg-input data-disabled:opacity-64 sm:[--thumb-size:--spacing(3)]",
2872
3507
  keepMounted: true,
2873
3508
  children: /* @__PURE__ */ jsx("span", { className: "pointer-events-none block aspect-square h-full in-[[data-slot=menu-checkbox-item][data-checked]]:origin-[var(--thumb-size)_50%] origin-left in-[[data-slot=menu-checkbox-item][data-checked]]:translate-x-[calc(var(--thumb-size)-4px)] in-[[data-slot=menu-checkbox-item]:active]:not-data-disabled:scale-x-110 in-[[data-slot=menu-checkbox-item]:active]:rounded-[var(--thumb-size)/calc(var(--thumb-size)*1.10)] rounded-(--thumb-size) bg-background shadow-sm/5 will-change-transform [transition:translate_.15s,border-radius_.15s,scale_.1s_.1s,transform-origin_.15s]" })
2874
- })] }) : /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(Menu$1.CheckboxItemIndicator, {
2875
- className: "col-start-1",
3509
+ })] }) : /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(MenuPrimitive.CheckboxItemIndicator, {
3510
+ className: "col-start-1 -ms-0.5",
2876
3511
  children: /* @__PURE__ */ jsx("svg", {
3512
+ "aria-hidden": "true",
2877
3513
  fill: "none",
2878
3514
  height: "24",
2879
3515
  stroke: "currentColor",
@@ -2892,19 +3528,20 @@ function MenuCheckboxItem({ className, children, checked, variant = "default", .
2892
3528
  });
2893
3529
  }
2894
3530
  function MenuRadioGroup(props) {
2895
- return /* @__PURE__ */ jsx(Menu$1.RadioGroup, {
3531
+ return /* @__PURE__ */ jsx(MenuPrimitive.RadioGroup, {
2896
3532
  "data-slot": "menu-radio-group",
2897
3533
  ...props
2898
3534
  });
2899
3535
  }
2900
3536
  function MenuRadioItem({ className, children, ...props }) {
2901
- return /* @__PURE__ */ jsxs(Menu$1.RadioItem, {
2902
- className: cn("grid min-h-8 in-data-[side=none]:min-w-[calc(var(--anchor-width)+1.25rem)] cursor-default grid-cols-[1rem_1fr] items-center gap-2 rounded-sm py-1 ps-2 pe-4 text-base text-foreground outline-none data-disabled:pointer-events-none data-highlighted:bg-accent data-highlighted:text-accent-foreground data-disabled:opacity-64 sm:min-h-7 sm:text-sm [&_svg:not([class*='size-'])]:size-4.5 sm:[&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0", className),
3537
+ return /* @__PURE__ */ jsxs(MenuPrimitive.RadioItem, {
3538
+ className: cn("grid min-h-8 in-data-[side=none]:min-w-[calc(var(--anchor-width)+1.25rem)] cursor-default grid-cols-[.75rem_1fr] items-center gap-2 rounded-sm py-1 ps-2 pe-4 text-base text-foreground outline-none data-disabled:pointer-events-none data-highlighted:bg-accent data-highlighted:text-accent-foreground data-disabled:opacity-64 sm:min-h-7 sm:text-sm [&_svg:not([class*='size-'])]:size-4.5 sm:[&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0", className),
2903
3539
  "data-slot": "menu-radio-item",
2904
3540
  ...props,
2905
- children: [/* @__PURE__ */ jsx(Menu$1.RadioItemIndicator, {
2906
- className: "col-start-1",
3541
+ children: [/* @__PURE__ */ jsx(MenuPrimitive.RadioItemIndicator, {
3542
+ className: "col-start-1 -ms-0.5",
2907
3543
  children: /* @__PURE__ */ jsx("svg", {
3544
+ "aria-hidden": "true",
2908
3545
  fill: "none",
2909
3546
  height: "24",
2910
3547
  stroke: "currentColor",
@@ -2923,7 +3560,7 @@ function MenuRadioItem({ className, children, ...props }) {
2923
3560
  });
2924
3561
  }
2925
3562
  function MenuGroupLabel({ className, inset, ...props }) {
2926
- return /* @__PURE__ */ jsx(Menu$1.GroupLabel, {
3563
+ return /* @__PURE__ */ jsx(MenuPrimitive.GroupLabel, {
2927
3564
  className: cn("px-2 py-1.5 font-medium text-muted-foreground text-xs data-inset:ps-9 sm:data-inset:ps-8", className),
2928
3565
  "data-inset": inset,
2929
3566
  "data-slot": "menu-label",
@@ -2931,32 +3568,25 @@ function MenuGroupLabel({ className, inset, ...props }) {
2931
3568
  });
2932
3569
  }
2933
3570
  function MenuSeparator({ className, ...props }) {
2934
- return /* @__PURE__ */ jsx(Menu$1.Separator, {
3571
+ return /* @__PURE__ */ jsx(MenuPrimitive.Separator, {
2935
3572
  className: cn("mx-2 my-1 h-px bg-border", className),
2936
3573
  "data-slot": "menu-separator",
2937
3574
  ...props
2938
3575
  });
2939
3576
  }
2940
- function MenuShortcut({ className, ...props }) {
2941
- return /* @__PURE__ */ jsx("kbd", {
2942
- className: cn("ms-auto font-medium font-sans text-muted-foreground/72 text-xs tracking-widest", className),
2943
- "data-slot": "menu-shortcut",
2944
- ...props
2945
- });
2946
- }
2947
3577
  function MenuSub(props) {
2948
- return /* @__PURE__ */ jsx(Menu$1.SubmenuRoot, {
3578
+ return /* @__PURE__ */ jsx(MenuPrimitive.SubmenuRoot, {
2949
3579
  "data-slot": "menu-sub",
2950
3580
  ...props
2951
3581
  });
2952
3582
  }
2953
3583
  function MenuSubTrigger({ className, inset, children, ...props }) {
2954
- return /* @__PURE__ */ jsxs(Menu$1.SubmenuTrigger, {
2955
- className: cn("flex min-h-8 items-center gap-2 rounded-sm px-2 py-1 text-base text-foreground outline-none data-disabled:pointer-events-none data-highlighted:bg-accent data-popup-open:bg-accent data-inset:ps-8 data-highlighted:text-accent-foreground data-popup-open:text-accent-foreground data-disabled:opacity-64 sm:min-h-7 sm:text-sm [&_svg:not([class*='size-'])]:size-4.5 sm:[&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none", className),
3584
+ return /* @__PURE__ */ jsxs(MenuPrimitive.SubmenuTrigger, {
3585
+ className: cn("flex min-h-8 items-center gap-2 rounded-sm px-2 py-1 text-base text-foreground outline-none data-disabled:pointer-events-none data-highlighted:bg-accent data-popup-open:bg-accent data-inset:ps-8 data-highlighted:text-accent-foreground data-popup-open:text-accent-foreground data-disabled:opacity-64 sm:min-h-7 sm:text-sm [&>svg:not(:last-child)]:-mx-0.5 [&_svg:not([class*='size-'])]:size-4.5 sm:[&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none", className),
2956
3586
  "data-inset": inset,
2957
3587
  "data-slot": "menu-sub-trigger",
2958
3588
  ...props,
2959
- children: [children, /* @__PURE__ */ jsx(ChevronRightIcon, { className: "-me-0.5 ms-auto opacity-80" })]
3589
+ children: [children, /* @__PURE__ */ jsx(ChevronRightIcon, { className: "ms-auto -me-0.5 opacity-80" })]
2960
3590
  });
2961
3591
  }
2962
3592
  function MenuSubPopup({ className, sideOffset = 0, alignOffset, align = "start", ...props }) {
@@ -2982,20 +3612,6 @@ function Separator({ className, orientation = "horizontal", ...props }) {
2982
3612
  });
2983
3613
  }
2984
3614
 
2985
- //#endregion
2986
- //#region src/components/ui/switch.tsx
2987
- function Switch({ className, ...props }) {
2988
- return /* @__PURE__ */ jsx(Switch$1.Root, {
2989
- className: cn("inline-flex h-[calc(var(--thumb-size)+2px)] w-[calc(var(--thumb-size)*2-2px)] shrink-0 items-center rounded-full p-px outline-none transition-[background-color,box-shadow] duration-200 [--thumb-size:--spacing(5)] focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:ring-offset-background data-checked:bg-primary data-unchecked:bg-input data-disabled:opacity-64 sm:[--thumb-size:--spacing(4)]", className),
2990
- "data-slot": "switch",
2991
- ...props,
2992
- children: /* @__PURE__ */ jsx(Switch$1.Thumb, {
2993
- className: cn("pointer-events-none block aspect-square h-full origin-left in-[[role=switch]:active,[data-slot=label]:active,[data-slot=field-label]:active]:not-data-disabled:scale-x-110 in-[[role=switch]:active,[data-slot=label]:active,[data-slot=field-label]:active]:rounded-[var(--thumb-size)/calc(var(--thumb-size)*1.1)] rounded-(--thumb-size) bg-background shadow-sm/5 will-change-transform [transition:translate_.15s,border-radius_.15s,scale_.1s_.1s,transform-origin_.15s] data-checked:origin-[var(--thumb-size)_50%] data-checked:translate-x-[calc(var(--thumb-size)-4px)]"),
2994
- "data-slot": "switch-thumb"
2995
- })
2996
- });
2997
- }
2998
-
2999
3615
  //#endregion
3000
3616
  //#region src/components/ui/toast.tsx
3001
3617
  const toastManager = Toast.createToastManager();
@@ -4268,7 +4884,7 @@ const sizeDefaultProps = {
4268
4884
 
4269
4885
  //#endregion
4270
4886
  //#region src/utils/spacing.tsx
4271
- const spacingFields = {
4887
+ const spacingField = {
4272
4888
  ...marginField,
4273
4889
  ...paddingField
4274
4890
  };
@@ -4559,7 +5175,7 @@ const textTransformDefaultProps = { textTransform: "none" };
4559
5175
 
4560
5176
  //#endregion
4561
5177
  //#region src/utils/typography.tsx
4562
- const typographyFields = {
5178
+ const typographyField = {
4563
5179
  ...textAlignField,
4564
5180
  ...textTransformField,
4565
5181
  ...textDecorationField,
@@ -4598,6 +5214,20 @@ const typographyFieldNames = [
4598
5214
  "fontWeight"
4599
5215
  ];
4600
5216
 
5217
+ //#endregion
5218
+ //#region src/utils/csrf.ts
5219
+ /**
5220
+ * Récupère le jeton CSRF à partir d'une balise meta dans le DOM.
5221
+ * Standard Laravel : <meta name="csrf-token" content="...">
5222
+ *
5223
+ * @returns Le jeton CSRF sous forme de chaîne, ou null s'il n'est pas trouvé ou si l'exécution n'est pas côté client.
5224
+ */
5225
+ function getCsrfToken() {
5226
+ if (typeof document === "undefined") return null;
5227
+ const meta = document.querySelector("meta[name=\"csrf-token\"]");
5228
+ return meta ? meta.getAttribute("content") : null;
5229
+ }
5230
+
4601
5231
  //#endregion
4602
5232
  //#region src/hooks/useOptimizedImage.tsx
4603
5233
  const BREAKPOINTS = [
@@ -4695,5 +5325,336 @@ function useOptimizedImage(src) {
4695
5325
  }
4696
5326
 
4697
5327
  //#endregion
4698
- export { Accordion, AccordionItem, AccordionPanel, AccordionTrigger, ActionBar_default as ActionBar, AnchoredToastProvider, Badge, Button, Checkbox, ColorPicker, ColorPickerContent, Dialog, DialogDescription, DialogFooter, DialogHeader, DialogPanel, DialogPopup, DialogTitle, DialogTrigger, DrawerItem_default as DrawerItem, Checkbox_default as FieldCheckbox, FieldGroups_default as FieldGroups, Input_default as FieldInput, NumberUnit_default as FieldNumberUnit, Radio_default as FieldRadio, Select_default as FieldSelect, Textarea_default as FieldTextarea, Frame, FrameDescription, FrameFooter, FrameHeader, FramePanel, FrameTitle, GRAY_COLORS, Input, InputGroup, InputGroupAddon, InputGroupInput, InputGroupText, InputGroupTextarea, Label_default as Label, MediaPicker, Menu, MenuCheckboxItem, MenuCreateHandle, MenuGroup, MenuGroupLabel, MenuItem, MenuPopup, MenuPortal, MenuRadioGroup, MenuRadioItem, MenuSeparator, MenuShortcut, MenuSub, MenuSubPopup, MenuSubTrigger, MenuTrigger, PREDEFINED_COLORS, PagePicker, Pagination, PaginationContent, PaginationEllipsis, PaginationItem, PaginationLink, PaginationNext, PaginationPrevious, PickerModal, Popover, PopoverClose, PopoverPopup as PopoverContent, PopoverPopup, PopoverCreateHandle, PopoverDescription, PopoverTitle, PopoverTrigger, RadioGroup, RadioGroupItem, RichTextMenuColorPicker, RichTextMenuLink, ScrollArea, ScrollBar, Select, SelectGroup, SelectGroupLabel, SelectItem, SelectPopup, SelectSeparator, SelectTrigger, SelectValue, Separator, Switch, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableRow, Tabs, TabsPanel as TabsContent, TabsPanel, TabsList, TabsTab, TabsTab as TabsTrigger, Textarea, ToastProvider, Tooltip, TooltipPopup, TooltipProvider, TooltipTrigger, anchoredToastManager, animationDefaultProps, animationField, animationToAttributes, badgeVariants, buttonVariants, plugin_default as createPuckOverridesPlugin, createTemplateBlock, customClassesDefaultProps, customClassesField, customClassesToClasses, displayDefaultProps, displayField, displayToClasses, fontWeightDefaultProps, fontWeightField, fontWeightToClasses, getMediaUrl, lineHeightDefaultProps, lineHeightField, lineHeightToClasses, marginDefaultProps, marginField, marginToClasses, paddingDefaultProps, paddingField, paddingToClasses, pageField, positionDefaultProps, positionField, positionToClasses, richTextMenuColorPickerExtension, sizeDefaultProps, sizeField, sizeToClasses, spacingDefaultProps, spacingFieldNames, spacingFields, spacingOptions, spacingToClasses, textAlignDefaultProps, textAlignField, textAlignToClasses, textColorDefaultProps, textColorField, textColorToClasses, textDecorationDefaultProps, textDecorationField, textDecorationToClasses, textSizeDefaultProps, textSizeField, textSizeToClasses, textTransformDefaultProps, textTransformField, textTransformToClasses, toastManager, typographyDefaultProps, typographyFieldNames, typographyFields, typographyToClasses, useMediaUrl, useOptimizedImage, usePageUrl };
5328
+ //#region src/plugins/redactor-assistant/utils/constants.ts
5329
+ const QUICK_ACTIONS = [
5330
+ {
5331
+ label: "Improve",
5332
+ task: "rewrite",
5333
+ instructions: "Improve this text to make it clearer and more engaging."
5334
+ },
5335
+ {
5336
+ label: "Shorten",
5337
+ task: "rewrite",
5338
+ instructions: "Shorten this text while keeping the essential ideas."
5339
+ },
5340
+ {
5341
+ label: "Lengthen",
5342
+ task: "rewrite",
5343
+ instructions: "Expand this text with more details."
5344
+ },
5345
+ {
5346
+ label: "Pro tone",
5347
+ task: "rewrite",
5348
+ instructions: "Rewrite this text with a professional and formal tone."
5349
+ },
5350
+ {
5351
+ label: "SEO",
5352
+ task: "seo",
5353
+ instructions: "Optimize this text for search engines."
5354
+ },
5355
+ {
5356
+ label: "Summarize",
5357
+ task: "summarize",
5358
+ instructions: "Summarize this text in a few concise sentences, capturing the key points."
5359
+ }
5360
+ ];
5361
+
5362
+ //#endregion
5363
+ //#region src/plugins/redactor-assistant/components/RedactorMenuButton.tsx
5364
+ function RedactorMenuButton({ onAction, onGenerate, loading = false }) {
5365
+ return /* @__PURE__ */ jsxs(Menu, { children: [/* @__PURE__ */ jsx(MenuTrigger, {
5366
+ openOnHover: true,
5367
+ render: /* @__PURE__ */ jsx(Button, {
5368
+ size: "icon-sm",
5369
+ title: "AI writing assistance",
5370
+ disabled: loading,
5371
+ children: /* @__PURE__ */ jsx(SparklesIcon, { size: 10 })
5372
+ })
5373
+ }), /* @__PURE__ */ jsxs(MenuPopup, {
5374
+ align: "end",
5375
+ className: "min-w-0!",
5376
+ children: [
5377
+ /* @__PURE__ */ jsx(MenuGroup, { children: /* @__PURE__ */ jsx(MenuItem, {
5378
+ className: "cursor-pointer",
5379
+ onClick: () => onGenerate?.(),
5380
+ children: "Generate"
5381
+ }) }),
5382
+ /* @__PURE__ */ jsx(MenuSeparator, {}),
5383
+ /* @__PURE__ */ jsx(MenuGroup, { children: QUICK_ACTIONS.map((action) => /* @__PURE__ */ jsx(MenuItem, {
5384
+ className: "cursor-pointer",
5385
+ onClick: () => onAction(action.task, action.instructions),
5386
+ children: action.label
5387
+ }, action.label)) })
5388
+ ]
5389
+ })] });
5390
+ }
5391
+
5392
+ //#endregion
5393
+ //#region src/plugins/redactor-assistant/utils/redactorClient.ts
5394
+ const AI_ENDPOINT = "/admin/puck/redactor";
5395
+ const TIMEOUT_MS = 35e3;
5396
+ /**
5397
+ * Appelle l'endpoint Redactor backend. Ne jamais exposer de clé API côté client.
5398
+ */
5399
+ async function callRedactor(request) {
5400
+ const token = getCsrfToken();
5401
+ const controller = new AbortController();
5402
+ const timeoutId = setTimeout(() => controller.abort(), TIMEOUT_MS);
5403
+ try {
5404
+ const response = await fetch(AI_ENDPOINT, {
5405
+ method: "POST",
5406
+ headers: {
5407
+ "Content-Type": "application/json",
5408
+ Accept: "application/json",
5409
+ ...token ? { "X-CSRF-TOKEN": token } : {}
5410
+ },
5411
+ body: JSON.stringify(request),
5412
+ credentials: "same-origin",
5413
+ signal: controller.signal
5414
+ });
5415
+ if (!response.ok) {
5416
+ const body = await response.json().catch(() => ({}));
5417
+ throw new Error(body.error || `Erreur serveur (${response.status})`);
5418
+ }
5419
+ const data = await response.json();
5420
+ if (!data.text || typeof data.text !== "string") throw new Error("Réponse Redactor invalide.");
5421
+ return { text: data.text };
5422
+ } catch (error) {
5423
+ if (error.name === "AbortError") throw new Error("La requête Redactor a expiré. Réessayez.");
5424
+ throw error;
5425
+ } finally {
5426
+ clearTimeout(timeoutId);
5427
+ }
5428
+ }
5429
+
5430
+ //#endregion
5431
+ //#region src/plugins/redactor-assistant/utils/buildContext.ts
5432
+ /**
5433
+ * Construit un résumé textuel de la page à partir des données Puck.
5434
+ */
5435
+ function buildPageSummary(data) {
5436
+ if (!data?.content?.length) return "Page vide.";
5437
+ return data.content.map((block, i) => {
5438
+ const textProps = extractTextProps(block.props);
5439
+ const propsStr = textProps.length > 0 ? `: ${textProps.join(", ")}` : "";
5440
+ return `[${i + 1}] ${block.type}${propsStr}`;
5441
+ }).join("\n");
5442
+ }
5443
+ /**
5444
+ * Extrait les propriétés textuelles d'un bloc pour le résumé.
5445
+ */
5446
+ function extractTextProps(props) {
5447
+ const textFields = [];
5448
+ for (const [key, value] of Object.entries(props)) {
5449
+ if (key === "id") continue;
5450
+ if (typeof value === "string" && value.length > 0 && value.length <= 200) {
5451
+ const truncated = value.length > 80 ? value.substring(0, 80) + "…" : value;
5452
+ textFields.push(`${key}="${truncated}"`);
5453
+ }
5454
+ }
5455
+ return textFields;
5456
+ }
5457
+ /**
5458
+ * Construit le contexte AI complet à partir des données Puck et du bloc sélectionné.
5459
+ */
5460
+ function buildContext(data, selectedBlock, brandVoice) {
5461
+ return {
5462
+ pageSummary: buildPageSummary(data),
5463
+ currentBlock: selectedBlock ? {
5464
+ type: selectedBlock.type,
5465
+ props: selectedBlock.props
5466
+ } : null,
5467
+ brandVoice: {
5468
+ tone: brandVoice?.tone ?? "professionnel",
5469
+ language: brandVoice?.language ?? "français"
5470
+ }
5471
+ };
5472
+ }
5473
+
5474
+ //#endregion
5475
+ //#region src/plugins/redactor-assistant/hooks/useRedactor.ts
5476
+ /**
5477
+ * Custom hook to manage the Redactor logic (API calls, loading, error, result).
5478
+ * Encapsulates the core logic for both the sidebar plugin and the richtext button.
5479
+ */
5480
+ function useRedactor() {
5481
+ const [instructions, setInstructions] = useState("");
5482
+ const [loading, setLoading] = useState(false);
5483
+ const [error, setError] = useState(null);
5484
+ const [result, setResult] = useState(null);
5485
+ const [previousValue, setPreviousValue] = useState(null);
5486
+ /**
5487
+ * Call the redactor API to generate or rewrite text.
5488
+ */
5489
+ const generate = async ({ task, taskInstructions, input, appData, currentBlock, outputFormat }) => {
5490
+ setLoading(true);
5491
+ setError(null);
5492
+ setResult(null);
5493
+ try {
5494
+ const context = buildContext(appData, currentBlock);
5495
+ const response = await callRedactor({
5496
+ task,
5497
+ instructions: taskInstructions || instructions,
5498
+ input,
5499
+ context,
5500
+ outputFormat
5501
+ });
5502
+ setResult(response.text);
5503
+ setPreviousValue(input);
5504
+ return response.text;
5505
+ } catch (err) {
5506
+ setError(err.message || "Unknown error.");
5507
+ throw err;
5508
+ } finally {
5509
+ setLoading(false);
5510
+ }
5511
+ };
5512
+ return {
5513
+ instructions,
5514
+ setInstructions,
5515
+ loading,
5516
+ error,
5517
+ setError,
5518
+ result,
5519
+ setResult,
5520
+ previousValue,
5521
+ setPreviousValue,
5522
+ generate
5523
+ };
5524
+ }
5525
+
5526
+ //#endregion
5527
+ //#region src/plugins/redactor-assistant/components/RedactorRichTextButton.tsx
5528
+ const useTypedPuck = createUsePuck();
5529
+ function RedactorRichTextButton({ value, onChange, children, label, field }) {
5530
+ const appState = useTypedPuck((s) => s.appState);
5531
+ const [mode, setMode] = useState(null);
5532
+ const { instructions, setInstructions, loading, error, result, setResult, generate } = useRedactor();
5533
+ const textValue = typeof value === "string" ? value : "";
5534
+ const selectedItem = appState.ui.itemSelector ? appState.data.content?.[appState.ui.itemSelector.index] : null;
5535
+ const handleGenerate = async (task, taskInstructions) => {
5536
+ await generate({
5537
+ task,
5538
+ taskInstructions,
5539
+ input: textValue,
5540
+ appData: appState.data,
5541
+ currentBlock: selectedItem,
5542
+ outputFormat: "html"
5543
+ }).catch(() => {});
5544
+ };
5545
+ const applyResult = () => {
5546
+ if (result === null) return;
5547
+ onChange(result);
5548
+ setResult(null);
5549
+ setMode(null);
5550
+ };
5551
+ const showPanel = loading || mode === "generate" || result !== null || !!error;
5552
+ return /* @__PURE__ */ jsxs("div", { children: [
5553
+ label && /* @__PURE__ */ jsx(Label_default, {
5554
+ label,
5555
+ tooltip: field?.tooltip,
5556
+ action: /* @__PURE__ */ jsx(RedactorMenuButton, {
5557
+ onAction: handleGenerate,
5558
+ onGenerate: () => setMode("generate"),
5559
+ loading
5560
+ })
5561
+ }),
5562
+ /* @__PURE__ */ jsx("div", {
5563
+ className: loading ? "pointer-events-none opacity-60" : "",
5564
+ children
5565
+ }),
5566
+ showPanel && /* @__PURE__ */ jsxs("div", {
5567
+ className: "mt-3 border border-gray-200 rounded-lg bg-muted p-3 flex flex-col gap-y-3",
5568
+ children: [
5569
+ error && /* @__PURE__ */ jsx(Alert, {
5570
+ variant: "error",
5571
+ size: "sm",
5572
+ children: /* @__PURE__ */ jsx(AlertTitle, { children: error })
5573
+ }),
5574
+ loading && /* @__PURE__ */ jsxs("div", {
5575
+ className: "flex items-center justify-center gap-1.5 text-muted-foreground",
5576
+ children: [/* @__PURE__ */ jsx(Loader2Icon, {
5577
+ size: 14,
5578
+ className: "animate-spin"
5579
+ }), /* @__PURE__ */ jsx("span", {
5580
+ className: "text-sm",
5581
+ children: "Generating…"
5582
+ })]
5583
+ }),
5584
+ mode === "generate" && !loading && result === null && /* @__PURE__ */ jsxs("div", {
5585
+ className: "flex flex-col",
5586
+ children: [
5587
+ /* @__PURE__ */ jsx(Label_default, {
5588
+ label: "AI writing assistance",
5589
+ tooltip: "The generated text will fully replace your current content. Your original text will not be preserved."
5590
+ }),
5591
+ /* @__PURE__ */ jsx(Textarea, {
5592
+ value: instructions,
5593
+ onChange: (e) => setInstructions(e.target.value),
5594
+ placeholder: "Ex: Write a short introduction about our services, in a professional tone…",
5595
+ disabled: loading
5596
+ }),
5597
+ /* @__PURE__ */ jsxs("div", {
5598
+ className: "flex justify-end gap-x-2 mt-3",
5599
+ children: [/* @__PURE__ */ jsx(Button, {
5600
+ variant: "outline",
5601
+ onClick: () => setMode(null),
5602
+ disabled: loading,
5603
+ children: "Cancel"
5604
+ }), /* @__PURE__ */ jsx(Button, {
5605
+ variant: "default",
5606
+ onClick: () => handleGenerate(textValue ? "rewrite" : "generate", instructions),
5607
+ disabled: loading,
5608
+ children: "Generate"
5609
+ })]
5610
+ })
5611
+ ]
5612
+ }),
5613
+ result !== null && !loading && /* @__PURE__ */ jsxs("div", {
5614
+ className: "flex flex-col",
5615
+ children: [/* @__PURE__ */ jsx(Alert, {
5616
+ variant: "success",
5617
+ size: "sm",
5618
+ children: /* @__PURE__ */ jsx(AlertDescription, {
5619
+ className: "max-h-[150px] overflow-auto",
5620
+ dangerouslySetInnerHTML: { __html: result }
5621
+ })
5622
+ }), /* @__PURE__ */ jsxs("div", {
5623
+ className: "flex justify-end gap-x-2 mt-3",
5624
+ children: [/* @__PURE__ */ jsx(Button, {
5625
+ variant: "outline",
5626
+ onClick: () => setResult(null),
5627
+ children: "Ignore"
5628
+ }), /* @__PURE__ */ jsx(Button, {
5629
+ variant: "default",
5630
+ onClick: applyResult,
5631
+ children: "Apply"
5632
+ })]
5633
+ })]
5634
+ })
5635
+ ]
5636
+ })
5637
+ ] });
5638
+ }
5639
+
5640
+ //#endregion
5641
+ //#region src/plugins/redactor-assistant/index.tsx
5642
+ createUsePuck();
5643
+ function createRedactorPlugin() {
5644
+ return {
5645
+ name: "redactor-assistant",
5646
+ overrides: { fieldTypes: { richtext: ({ children, onChange, value, field, label }) => {
5647
+ return /* @__PURE__ */ jsx(RedactorRichTextButton, {
5648
+ value,
5649
+ onChange,
5650
+ label,
5651
+ field,
5652
+ children: React.cloneElement(children, { Label: ({ children: c }) => /* @__PURE__ */ jsx(Fragment, { children: c }) })
5653
+ });
5654
+ } } }
5655
+ };
5656
+ }
5657
+
5658
+ //#endregion
5659
+ export { Accordion, AccordionItem, AccordionPanel, AccordionTrigger, ActionBar_default as ActionBar, AnchoredToastProvider, Badge, Button, Checkbox, ColorPicker, ColorPickerContent, Dialog, DialogDescription, DialogFooter, DialogHeader, DialogPanel, DialogPopup, DialogTitle, DialogTrigger, DrawerItem_default as DrawerItem, Checkbox_default as FieldCheckbox, FieldGroups_default as FieldGroups, Input_default as FieldInput, NumberUnit_default as FieldNumberUnit, Radio_default as FieldRadio, Select_default as FieldSelect, Switch_default as FieldSwitch, Textarea_default as FieldTextarea, Frame, FrameDescription, FrameFooter, FrameHeader, FramePanel, FrameTitle, GRAY_COLORS, Input, InputGroup, InputGroupAddon, InputGroupInput, InputGroupText, InputGroupTextarea, Label_default as Label, MediaPicker, Menu, MenuCheckboxItem, MenuGroup, MenuGroupLabel, MenuItem, MenuPopup, MenuRadioGroup, MenuRadioItem, MenuSeparator, MenuSub, MenuSubPopup, MenuSubTrigger, MenuTrigger, PREDEFINED_COLORS, PagePicker, Pagination, PaginationContent, PaginationEllipsis, PaginationItem, PaginationLink, PaginationNext, PaginationPrevious, PickerModal, Popover, PopoverClose, PopoverPopup as PopoverContent, PopoverPopup, PopoverCreateHandle, PopoverDescription, PopoverTitle, PopoverTrigger, RadioGroup, RadioGroupItem, RichTextMenuColorPicker, RichTextMenuLink, ScrollArea, ScrollBar, Select, SelectGroup, SelectGroupLabel, SelectItem, SelectPopup, SelectSeparator, SelectTrigger, SelectValue, Separator, Switch, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableRow, Tabs, TabsList, TabsPanel, TabsTab, Textarea, ToastProvider, Tooltip, TooltipPopup, TooltipProvider, TooltipTrigger, anchoredToastManager, animationDefaultProps, animationField, animationToAttributes, badgeVariants, buttonVariants, plugin_default as createPuckOverridesPlugin, createRedactorPlugin, createTemplateBlock, customClassesDefaultProps, customClassesField, customClassesToClasses, displayDefaultProps, displayField, displayToClasses, fontWeightDefaultProps, fontWeightField, fontWeightToClasses, getCsrfToken, lineHeightDefaultProps, lineHeightField, lineHeightToClasses, marginDefaultProps, marginField, marginToClasses, paddingDefaultProps, paddingField, paddingToClasses, pageField, positionDefaultProps, positionField, positionToClasses, richTextMenuColorPickerExtension, sizeDefaultProps, sizeField, sizeToClasses, spacingDefaultProps, spacingField, spacingFieldNames, spacingOptions, spacingToClasses, textAlignDefaultProps, textAlignField, textAlignToClasses, textColorDefaultProps, textColorField, textColorToClasses, textDecorationDefaultProps, textDecorationField, textDecorationToClasses, textSizeDefaultProps, textSizeField, textSizeToClasses, textTransformDefaultProps, textTransformField, textTransformToClasses, toastManager, typographyDefaultProps, typographyField, typographyFieldNames, typographyToClasses, useMediaUrl, useOptimizedImage, usePageUrl };
4699
5660
  //# sourceMappingURL=index.js.map