@frontlit/page-builder 0.1.0-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (98) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +97 -0
  3. package/dist/alignment-dZDzN96G.d.ts +5 -0
  4. package/dist/blocks/index.d.ts +255 -0
  5. package/dist/blocks/index.js +6 -0
  6. package/dist/blocks/index.js.map +1 -0
  7. package/dist/builder/index.d.ts +240 -0
  8. package/dist/builder/index.js +8 -0
  9. package/dist/builder/index.js.map +1 -0
  10. package/dist/button-C3VrMJkr.d.ts +20 -0
  11. package/dist/chunk-7KIX4WAO.js +4229 -0
  12. package/dist/chunk-7KIX4WAO.js.map +1 -0
  13. package/dist/chunk-7W6Q4WA7.js +515 -0
  14. package/dist/chunk-7W6Q4WA7.js.map +1 -0
  15. package/dist/chunk-GTY75HW2.js +22 -0
  16. package/dist/chunk-GTY75HW2.js.map +1 -0
  17. package/dist/chunk-MO76KZY2.js +3580 -0
  18. package/dist/chunk-MO76KZY2.js.map +1 -0
  19. package/dist/chunk-RBJBODQP.js +2011 -0
  20. package/dist/chunk-RBJBODQP.js.map +1 -0
  21. package/dist/chunk-RUYOL7JZ.js +4 -0
  22. package/dist/chunk-RUYOL7JZ.js.map +1 -0
  23. package/dist/chunk-SSQWYMQR.js +393 -0
  24. package/dist/chunk-SSQWYMQR.js.map +1 -0
  25. package/dist/color-mode-DIEEt9WP.d.ts +3 -0
  26. package/dist/components/index.d.ts +36 -0
  27. package/dist/components/index.js +5 -0
  28. package/dist/components/index.js.map +1 -0
  29. package/dist/data-slots.d.ts +25 -0
  30. package/dist/data-slots.js +3 -0
  31. package/dist/data-slots.js.map +1 -0
  32. package/dist/fonts/alegreya/alegreya-latin-variable.woff2 +0 -0
  33. package/dist/fonts/bebas-neue/bebas-neue-latin-400-normal.woff2 +0 -0
  34. package/dist/fonts/inter/inter-latin-variable.woff2 +0 -0
  35. package/dist/fonts/lato/lato-latin-100-normal.woff2 +0 -0
  36. package/dist/fonts/lato/lato-latin-300-normal.woff2 +0 -0
  37. package/dist/fonts/lato/lato-latin-400-normal.woff2 +0 -0
  38. package/dist/fonts/lato/lato-latin-700-normal.woff2 +0 -0
  39. package/dist/fonts/lato/lato-latin-900-normal.woff2 +0 -0
  40. package/dist/fonts/merriweather/merriweather-latin-variable.woff2 +0 -0
  41. package/dist/fonts/montserrat/montserrat-latin-100-normal.woff2 +0 -0
  42. package/dist/fonts/montserrat/montserrat-latin-200-normal.woff2 +0 -0
  43. package/dist/fonts/montserrat/montserrat-latin-300-normal.woff2 +0 -0
  44. package/dist/fonts/montserrat/montserrat-latin-400-normal.woff2 +0 -0
  45. package/dist/fonts/montserrat/montserrat-latin-500-normal.woff2 +0 -0
  46. package/dist/fonts/montserrat/montserrat-latin-600-normal.woff2 +0 -0
  47. package/dist/fonts/montserrat/montserrat-latin-700-normal.woff2 +0 -0
  48. package/dist/fonts/montserrat/montserrat-latin-800-normal.woff2 +0 -0
  49. package/dist/fonts/montserrat/montserrat-latin-900-normal.woff2 +0 -0
  50. package/dist/fonts/montserrat/montserrat-latin-variable.woff2 +0 -0
  51. package/dist/fonts/mulish/mulish-latin-variable.woff2 +0 -0
  52. package/dist/fonts/noto-sans/noto-sans-latin-variable.woff2 +0 -0
  53. package/dist/fonts/nunito/nunito-latin-variable.woff2 +0 -0
  54. package/dist/fonts/open-sans/open-sans-latin-variable.woff2 +0 -0
  55. package/dist/fonts/oswald/oswald-latin-variable.woff2 +0 -0
  56. package/dist/fonts/playfair-display/playfair-display-latin-variable.woff2 +0 -0
  57. package/dist/fonts/poppins/poppins-latin-100-normal.woff2 +0 -0
  58. package/dist/fonts/poppins/poppins-latin-200-normal.woff2 +0 -0
  59. package/dist/fonts/poppins/poppins-latin-300-normal.woff2 +0 -0
  60. package/dist/fonts/poppins/poppins-latin-400-normal.woff2 +0 -0
  61. package/dist/fonts/poppins/poppins-latin-500-normal.woff2 +0 -0
  62. package/dist/fonts/poppins/poppins-latin-600-normal.woff2 +0 -0
  63. package/dist/fonts/poppins/poppins-latin-700-normal.woff2 +0 -0
  64. package/dist/fonts/poppins/poppins-latin-800-normal.woff2 +0 -0
  65. package/dist/fonts/poppins/poppins-latin-900-normal.woff2 +0 -0
  66. package/dist/fonts/pt-sans/pt-sans-latin-400-normal.woff2 +0 -0
  67. package/dist/fonts/pt-sans/pt-sans-latin-700-normal.woff2 +0 -0
  68. package/dist/fonts/quicksand/quicksand-latin-variable.woff2 +0 -0
  69. package/dist/fonts/raleway/raleway-latin-variable.woff2 +0 -0
  70. package/dist/fonts/roboto/roboto-latin-variable.woff2 +0 -0
  71. package/dist/fonts/roboto-slab/roboto-slab-latin-variable.woff2 +0 -0
  72. package/dist/fonts/rubik/rubik-latin-variable.woff2 +0 -0
  73. package/dist/fonts/source-sans-3/source-sans-3-latin-400-normal.woff2 +0 -0
  74. package/dist/fonts/source-sans-3/source-sans-3-latin-variable.woff2 +0 -0
  75. package/dist/fonts/source-serif-4/source-serif-4-latin-variable.woff2 +0 -0
  76. package/dist/fonts/work-sans/work-sans-latin-variable.woff2 +0 -0
  77. package/dist/image-D7GlnOio.d.ts +30 -0
  78. package/dist/index.d.ts +59 -0
  79. package/dist/index.js +10 -0
  80. package/dist/index.js.map +1 -0
  81. package/dist/media-Njv378j7.d.ts +13 -0
  82. package/dist/models/index.d.ts +8 -0
  83. package/dist/models/index.js +4 -0
  84. package/dist/models/index.js.map +1 -0
  85. package/dist/nav-link-BfxvQ1s6.d.ts +37 -0
  86. package/dist/page-DfVl_aHX.d.ts +26 -0
  87. package/dist/primitives/index.d.ts +140 -0
  88. package/dist/primitives/index.js +4 -0
  89. package/dist/primitives/index.js.map +1 -0
  90. package/dist/renderer/index.d.ts +81 -0
  91. package/dist/renderer/index.js +7 -0
  92. package/dist/renderer/index.js.map +1 -0
  93. package/dist/section-background-BgOCp5l3.d.ts +50 -0
  94. package/dist/styles.css +15322 -0
  95. package/dist/theme-style-39nxXQy0.d.ts +141 -0
  96. package/dist/theme.css +242 -0
  97. package/dist/widget--uEOpdq3.d.ts +105 -0
  98. package/package.json +142 -0
@@ -0,0 +1,3580 @@
1
+ "use client";
2
+ import { blockRegistry, getAddableBlocks, getWidget, PageRenderer } from './chunk-SSQWYMQR.js';
3
+ import { generateUniqueId, FieldGroup, TextField, ToggleField } from './chunk-7KIX4WAO.js';
4
+ import { ThemeModeSwitcher } from './chunk-7W6Q4WA7.js';
5
+ import { themes, cn, Button, Switch, Input, PageCard, PageCardContent, PageCardHeader, Link, Button2 } from './chunk-RBJBODQP.js';
6
+ import React2, { useState, useMemo, useCallback, useRef, useEffect } from 'react';
7
+ import * as Icons from 'lucide-react';
8
+ import { Smartphone, Tablet, Monitor, Palette, Globe, Plus, HelpCircle, X, ImageIcon, Sparkles, ExternalLink, Check, ChevronLeft, LayoutTemplate, Layers3, Pin, ChevronDown, FilePenLine, Brush, ChevronRight, Type, SquareMousePointer, Columns3, Pencil, Info } from 'lucide-react';
9
+ import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
10
+ import { createPortal } from 'react-dom';
11
+
12
+ // src/utils/debounce.ts
13
+ function debounce(fn, wait) {
14
+ let timeout;
15
+ return function(...args) {
16
+ clearTimeout(timeout);
17
+ timeout = setTimeout(() => {
18
+ fn(...args);
19
+ }, wait);
20
+ };
21
+ }
22
+
23
+ // src/utils/color-mode.ts
24
+ function resolveColorMode(mode, systemPrefersDark) {
25
+ if (mode === "system") return systemPrefersDark ? "dark" : "light";
26
+ return mode;
27
+ }
28
+ function toggleColorMode(resolved) {
29
+ return resolved === "dark" ? "light" : "dark";
30
+ }
31
+
32
+ // src/starter.ts
33
+ function createWidgetInstance(name, overrides) {
34
+ const definition = blockRegistry[name];
35
+ return {
36
+ widgetId: generateUniqueId(),
37
+ name,
38
+ deletable: !["header", "footer", "data-slot"].includes(name),
39
+ moveable: !["header", "footer"].includes(name),
40
+ shared: ["header", "footer"].includes(name),
41
+ settings: {
42
+ ...definition?.getDefaultSettings?.() ?? {},
43
+ ...overrides ?? {}
44
+ }
45
+ };
46
+ }
47
+ function ensureRequiredSharedWidgets(layout) {
48
+ const header = layout.find((widget) => widget.name === "header") ?? createWidgetInstance("header");
49
+ const footer = layout.find((widget) => widget.name === "footer") ?? createWidgetInstance("footer");
50
+ const body = layout.filter(
51
+ (widget) => !["header", "footer"].includes(widget.name)
52
+ );
53
+ return [
54
+ {
55
+ ...header,
56
+ name: "header",
57
+ deletable: false,
58
+ moveable: false,
59
+ shared: true
60
+ },
61
+ ...body,
62
+ {
63
+ ...footer,
64
+ name: "footer",
65
+ deletable: false,
66
+ moveable: false,
67
+ shared: true
68
+ }
69
+ ];
70
+ }
71
+ function getDefaultTheme() {
72
+ return themes[0];
73
+ }
74
+ function createDefaultLayout() {
75
+ return ensureRequiredSharedWidgets([
76
+ createWidgetInstance("header", { ctaHref: "/builder" }),
77
+ createWidgetInstance("hero", { buttonAction: "/builder" }),
78
+ createWidgetInstance("featured"),
79
+ createWidgetInstance("content"),
80
+ createWidgetInstance("pricing"),
81
+ createWidgetInstance("faq"),
82
+ createWidgetInstance("email-form"),
83
+ createWidgetInstance("footer")
84
+ ]);
85
+ }
86
+ var DEVICES = [
87
+ { key: "mobile", label: "Mobile preview", icon: Smartphone },
88
+ { key: "tablet", label: "Tablet preview", icon: Tablet },
89
+ { key: "desktop", label: "Desktop preview", icon: Monitor }
90
+ ];
91
+ function BuilderTopBar({
92
+ resolvedMode,
93
+ onModeChange,
94
+ device,
95
+ onDeviceChange,
96
+ onOpenTheme,
97
+ onOpenSeo,
98
+ onAddBlock,
99
+ allowThemeEditing = true
100
+ }) {
101
+ return /* @__PURE__ */ jsxs("header", { className: "flex h-14 w-full shrink-0 items-center justify-between border-b bg-background px-4", children: [
102
+ /* @__PURE__ */ jsx("div", { className: "flex items-center gap-3", children: /* @__PURE__ */ jsx("span", { className: "text-sm font-semibold text-foreground", children: "Page builder" }) }),
103
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-4", children: [
104
+ /* @__PURE__ */ jsx(
105
+ "div",
106
+ {
107
+ className: "flex items-center gap-0.5 rounded-md border border-border p-0.5",
108
+ role: "group",
109
+ "aria-label": "Preview device",
110
+ children: DEVICES.map(({ key, label, icon: Icon }) => /* @__PURE__ */ jsx(
111
+ "button",
112
+ {
113
+ type: "button",
114
+ title: label,
115
+ "aria-label": label,
116
+ "aria-pressed": device === key,
117
+ className: cn(
118
+ "inline-flex h-7 w-7 items-center justify-center rounded-sm transition-colors",
119
+ device === key ? "bg-muted text-foreground" : "text-muted-foreground hover:text-foreground"
120
+ ),
121
+ onClick: () => onDeviceChange(key),
122
+ children: /* @__PURE__ */ jsx(Icon, { className: "h-4 w-4" })
123
+ },
124
+ key
125
+ ))
126
+ }
127
+ ),
128
+ /* @__PURE__ */ jsx(
129
+ "div",
130
+ {
131
+ className: "h-6 w-px bg-border",
132
+ role: "separator",
133
+ "aria-orientation": "vertical"
134
+ }
135
+ ),
136
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
137
+ /* @__PURE__ */ jsx(
138
+ ThemeModeSwitcher,
139
+ {
140
+ resolved: resolvedMode,
141
+ onToggle: () => onModeChange(toggleColorMode(resolvedMode))
142
+ }
143
+ ),
144
+ allowThemeEditing && /* @__PURE__ */ jsx(
145
+ Button,
146
+ {
147
+ variant: "outline",
148
+ size: "icon",
149
+ title: "Theme",
150
+ "aria-label": "Edit theme",
151
+ onClick: onOpenTheme,
152
+ children: /* @__PURE__ */ jsx(Palette, { className: "h-4 w-4" })
153
+ }
154
+ ),
155
+ /* @__PURE__ */ jsx(
156
+ Button,
157
+ {
158
+ variant: "outline",
159
+ size: "icon",
160
+ title: "SEO",
161
+ "aria-label": "Edit SEO settings",
162
+ onClick: onOpenSeo,
163
+ children: /* @__PURE__ */ jsx(Globe, { className: "h-4 w-4" })
164
+ }
165
+ )
166
+ ] }),
167
+ /* @__PURE__ */ jsx(
168
+ "div",
169
+ {
170
+ className: "h-6 w-px bg-border",
171
+ role: "separator",
172
+ "aria-orientation": "vertical"
173
+ }
174
+ ),
175
+ /* @__PURE__ */ jsxs(
176
+ Button,
177
+ {
178
+ size: "sm",
179
+ className: "gap-2 whitespace-nowrap",
180
+ title: "Add a new block to the page",
181
+ onClick: onAddBlock,
182
+ children: [
183
+ /* @__PURE__ */ jsx(Plus, { className: "h-4 w-4" }),
184
+ "Add block"
185
+ ]
186
+ }
187
+ )
188
+ ] })
189
+ ] });
190
+ }
191
+ var COPIED_ATTR = "data-pb-preview-style";
192
+ var THEME_FONT_VARS = [
193
+ "--font-sans",
194
+ "--font-mono",
195
+ "--font-inter",
196
+ "--font-open-sans",
197
+ "--font-source-sans-3",
198
+ "--font-noto-sans",
199
+ "--font-roboto",
200
+ "--font-mulish",
201
+ "--font-nunito",
202
+ "--font-work-sans",
203
+ "--font-merriweather",
204
+ "--font-alegreya",
205
+ "--font-playfair-display",
206
+ "--font-roboto-slab",
207
+ "--font-source-serif-4",
208
+ "--font-montserrat",
209
+ "--font-poppins",
210
+ "--font-raleway",
211
+ "--font-rubik",
212
+ "--font-oswald",
213
+ "--font-bebas-neue",
214
+ "--font-lato",
215
+ "--font-pt-sans",
216
+ "--font-quicksand"
217
+ ];
218
+ function readCssVar(sourceDoc, name) {
219
+ const view = sourceDoc.defaultView;
220
+ if (!view) return "";
221
+ const html = view.getComputedStyle(sourceDoc.documentElement).getPropertyValue(name).trim();
222
+ if (html) return html;
223
+ return view.getComputedStyle(sourceDoc.body).getPropertyValue(name).trim();
224
+ }
225
+ function syncFontVariables(sourceDoc, targetDoc) {
226
+ const root = targetDoc.documentElement;
227
+ for (const name of THEME_FONT_VARS) {
228
+ const value = readCssVar(sourceDoc, name);
229
+ if (value) root.style.setProperty(name, value);
230
+ else root.style.removeProperty(name);
231
+ }
232
+ }
233
+ function syncStyles(sourceDoc, targetDoc) {
234
+ targetDoc.head.querySelectorAll(`[${COPIED_ATTR}]`).forEach((node) => node.remove());
235
+ sourceDoc.querySelectorAll('style, link[rel="stylesheet"]').forEach((node) => {
236
+ const clone = node.cloneNode(true);
237
+ clone.setAttribute(COPIED_ATTR, "");
238
+ targetDoc.head.appendChild(clone);
239
+ });
240
+ syncFontVariables(sourceDoc, targetDoc);
241
+ }
242
+ function PreviewFrame({
243
+ className,
244
+ title = "Page preview",
245
+ children
246
+ }) {
247
+ const iframeRef = useRef(null);
248
+ const [mountNode, setMountNode] = useState(null);
249
+ useEffect(() => {
250
+ const iframe = iframeRef.current;
251
+ if (!iframe) return;
252
+ let observer;
253
+ const init = () => {
254
+ const doc = iframe.contentDocument;
255
+ if (!doc) return;
256
+ if (!doc.getElementById("pb-preview-base")) {
257
+ const base = doc.createElement("style");
258
+ base.id = "pb-preview-base";
259
+ base.textContent = "html,body{margin:0;height:100%;}";
260
+ doc.head.appendChild(base);
261
+ }
262
+ syncStyles(document, doc);
263
+ observer?.disconnect();
264
+ observer = new MutationObserver(() => syncStyles(document, doc));
265
+ observer.observe(document.head, {
266
+ childList: true,
267
+ subtree: true,
268
+ characterData: true
269
+ });
270
+ setMountNode(doc.body);
271
+ };
272
+ if (iframe.contentDocument?.readyState === "complete") init();
273
+ iframe.addEventListener("load", init);
274
+ return () => {
275
+ iframe.removeEventListener("load", init);
276
+ observer?.disconnect();
277
+ };
278
+ }, []);
279
+ return /* @__PURE__ */ jsx("iframe", { ref: iframeRef, title, className, children: mountNode ? createPortal(children, mountNode) : null });
280
+ }
281
+ function resolveIcon(name) {
282
+ if (name && name in Icons) {
283
+ return Icons[name];
284
+ }
285
+ return Icons.LayoutTemplate;
286
+ }
287
+ function BlockPicker({
288
+ blocks,
289
+ onPick,
290
+ onClose
291
+ }) {
292
+ return /* @__PURE__ */ jsx(
293
+ "div",
294
+ {
295
+ className: "fixed inset-0 z-50 flex items-center justify-center bg-black/40 p-4",
296
+ onClick: onClose,
297
+ children: /* @__PURE__ */ jsxs(
298
+ "div",
299
+ {
300
+ className: "flex max-h-[80vh] w-full max-w-lg flex-col overflow-hidden rounded-lg border border-border bg-background shadow-xl",
301
+ onClick: (e) => e.stopPropagation(),
302
+ children: [
303
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between border-b border-border px-4 py-3", children: [
304
+ /* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold text-foreground", children: "Add a block" }),
305
+ /* @__PURE__ */ jsx(
306
+ "button",
307
+ {
308
+ type: "button",
309
+ "aria-label": "Close",
310
+ className: "inline-flex h-7 w-7 items-center justify-center rounded text-muted-foreground hover:bg-muted hover:text-foreground",
311
+ onClick: onClose,
312
+ children: /* @__PURE__ */ jsx(Icons.X, { className: "h-4 w-4" })
313
+ }
314
+ )
315
+ ] }),
316
+ /* @__PURE__ */ jsx("div", { className: "grid grid-cols-2 gap-2 overflow-y-auto p-4 sm:grid-cols-3", children: blocks.map((block) => {
317
+ const Icon = resolveIcon(block.metadata.icon);
318
+ return /* @__PURE__ */ jsxs(
319
+ "button",
320
+ {
321
+ type: "button",
322
+ className: "flex flex-col items-center gap-2 rounded-md border border-border p-4 text-center transition-colors hover:border-primary hover:bg-muted",
323
+ onClick: () => onPick(block.metadata.name),
324
+ children: [
325
+ /* @__PURE__ */ jsx(Icon, { className: "h-6 w-6 text-foreground" }),
326
+ /* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-foreground", children: block.metadata.displayName })
327
+ ]
328
+ },
329
+ block.metadata.name
330
+ );
331
+ }) })
332
+ ]
333
+ }
334
+ )
335
+ }
336
+ );
337
+ }
338
+ function PanelHeader({
339
+ title,
340
+ onClose,
341
+ eyebrow = "Settings",
342
+ description,
343
+ icon: Icon = Layers3
344
+ }) {
345
+ return /* @__PURE__ */ jsx("div", { className: "sticky top-0 z-10 border-b border-border/80 bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/80", children: /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-3 px-5 py-4", children: [
346
+ /* @__PURE__ */ jsx("div", { className: "mt-0.5 flex h-9 w-9 shrink-0 items-center justify-center rounded-lg border border-primary/15 bg-primary/10 text-primary", children: /* @__PURE__ */ jsx(Icon, { className: "h-4 w-4", "aria-hidden": "true" }) }),
347
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
348
+ /* @__PURE__ */ jsx("p", { className: "text-[10px] font-semibold uppercase tracking-[0.16em] text-muted-foreground", children: eyebrow }),
349
+ /* @__PURE__ */ jsx("h2", { className: "mt-1 truncate text-base font-semibold tracking-tight text-foreground", children: title }),
350
+ description && /* @__PURE__ */ jsx("p", { className: "mt-1 line-clamp-2 text-xs leading-5 text-muted-foreground", children: description })
351
+ ] }),
352
+ /* @__PURE__ */ jsx(
353
+ Button,
354
+ {
355
+ variant: "ghost",
356
+ size: "icon",
357
+ className: "-mr-2 -mt-1 h-8 w-8 shrink-0 rounded-lg text-muted-foreground hover:bg-muted hover:text-foreground",
358
+ "aria-label": `Close ${title} settings`,
359
+ onClick: onClose,
360
+ children: /* @__PURE__ */ jsx(X, { className: "h-4 w-4" })
361
+ }
362
+ )
363
+ ] }) });
364
+ }
365
+ function SettingsPanel({
366
+ title,
367
+ onClose,
368
+ children,
369
+ eyebrow,
370
+ description,
371
+ icon
372
+ }) {
373
+ return /* @__PURE__ */ jsxs("aside", { className: "flex h-full min-h-0 w-[min(360px,calc(100vw-2rem))] max-w-full shrink-0 flex-col overflow-hidden rounded-2xl border border-border/80 bg-background shadow-lg shadow-foreground/5", children: [
374
+ /* @__PURE__ */ jsx(
375
+ PanelHeader,
376
+ {
377
+ title,
378
+ onClose,
379
+ eyebrow,
380
+ description,
381
+ icon
382
+ }
383
+ ),
384
+ /* @__PURE__ */ jsx("div", { className: "min-h-0 min-w-0 flex-1 overflow-x-hidden overflow-y-auto", children: /* @__PURE__ */ jsx("div", { className: "min-w-0 px-5 py-5", children }) })
385
+ ] });
386
+ }
387
+ function PageBuilderPropertyHeader({
388
+ label,
389
+ tooltip
390
+ }) {
391
+ return /* @__PURE__ */ jsxs("div", { className: "flex grow items-center gap-1.5", children: [
392
+ /* @__PURE__ */ jsx("h2", { className: "text-xs font-semibold text-foreground", children: label }),
393
+ tooltip && /* @__PURE__ */ jsx("span", { title: tooltip, className: "inline-flex cursor-help", children: /* @__PURE__ */ jsx(HelpCircle, { className: "h-3.5 w-3.5 text-muted-foreground" }) })
394
+ ] });
395
+ }
396
+ function PageBuilderSlider({
397
+ title,
398
+ min,
399
+ max,
400
+ value,
401
+ onChange,
402
+ tooltip,
403
+ className,
404
+ allowsReset = false,
405
+ unit = "px"
406
+ }) {
407
+ return /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col gap-2.5", className), children: [
408
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-3", children: [
409
+ /* @__PURE__ */ jsx(PageBuilderPropertyHeader, { label: title, tooltip }),
410
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5", children: [
411
+ /* @__PURE__ */ jsx(
412
+ "input",
413
+ {
414
+ type: "number",
415
+ inputMode: "numeric",
416
+ min,
417
+ max,
418
+ value: Number.isFinite(value) ? value : 0,
419
+ onChange: (e) => {
420
+ const parsed = Number(e.currentTarget.value);
421
+ if (Number.isNaN(parsed)) return;
422
+ onChange(Math.min(Math.max(parsed, min), max));
423
+ },
424
+ className: "h-7 w-12 rounded-md border border-input bg-muted/20 px-1.5 py-1 text-center text-xs font-semibold text-foreground outline-none transition-colors focus:border-ring focus:bg-background focus:ring-2 focus:ring-ring/20"
425
+ }
426
+ ),
427
+ unit && /* @__PURE__ */ jsx("span", { className: "text-[10px] font-medium text-muted-foreground", children: unit }),
428
+ allowsReset && /* @__PURE__ */ jsx(
429
+ "button",
430
+ {
431
+ type: "button",
432
+ title: "Reset",
433
+ className: "inline-flex h-7 w-7 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-muted hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
434
+ onClick: (e) => {
435
+ e.preventDefault();
436
+ onChange();
437
+ },
438
+ children: /* @__PURE__ */ jsx(X, { className: "h-3 w-3 text-muted-foreground hover:text-foreground" })
439
+ }
440
+ )
441
+ ] })
442
+ ] }),
443
+ /* @__PURE__ */ jsx(
444
+ "input",
445
+ {
446
+ type: "range",
447
+ min,
448
+ max,
449
+ step: 1,
450
+ value: Number.isFinite(value) ? value : min,
451
+ onChange: (e) => onChange(Number(e.currentTarget.value)),
452
+ "aria-label": title,
453
+ className: "h-1.5 w-full cursor-pointer accent-primary"
454
+ }
455
+ )
456
+ ] });
457
+ }
458
+ function ColorSelector({
459
+ title,
460
+ value,
461
+ onChange,
462
+ tooltip,
463
+ className,
464
+ allowReset = true,
465
+ description
466
+ }) {
467
+ const [localValue, setLocalValue] = React2.useState(value);
468
+ React2.useEffect(() => {
469
+ setLocalValue(value);
470
+ }, [value]);
471
+ const handleChange = (newValue) => {
472
+ setLocalValue(newValue);
473
+ onChange(newValue);
474
+ };
475
+ const swatchValue = /^#[0-9a-fA-F]{6}$/.test(localValue) ? localValue : "#000000";
476
+ return /* @__PURE__ */ jsxs(
477
+ "div",
478
+ {
479
+ className: cn(
480
+ "flex min-w-0 items-center justify-between gap-2",
481
+ className
482
+ ),
483
+ children: [
484
+ /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-1 flex-col gap-1", children: [
485
+ /* @__PURE__ */ jsx(PageBuilderPropertyHeader, { label: title, tooltip }),
486
+ description && /* @__PURE__ */ jsx("p", { className: "break-words text-xs text-muted-foreground", children: description })
487
+ ] }),
488
+ /* @__PURE__ */ jsxs("div", { className: "flex shrink-0 items-center gap-1.5", children: [
489
+ /* @__PURE__ */ jsx(
490
+ "input",
491
+ {
492
+ type: "color",
493
+ value: swatchValue,
494
+ onChange: (e) => handleChange(e.currentTarget.value),
495
+ className: "h-7 w-9 shrink-0 cursor-pointer rounded-md border border-input bg-background p-0.5",
496
+ "aria-label": `${title} swatch`
497
+ }
498
+ ),
499
+ /* @__PURE__ */ jsx(
500
+ "input",
501
+ {
502
+ type: "text",
503
+ value: localValue ?? "",
504
+ placeholder: "#000000",
505
+ onChange: (e) => handleChange(e.currentTarget.value),
506
+ className: "h-7 w-20 min-w-0 rounded-md border border-input bg-background px-2 py-1 text-xs text-foreground outline-none focus:ring-2 focus:ring-ring/20",
507
+ "aria-label": `${title} hex value`
508
+ }
509
+ ),
510
+ allowReset && /* @__PURE__ */ jsx(
511
+ "button",
512
+ {
513
+ type: "button",
514
+ title: "Reset",
515
+ className: "inline-flex h-6 w-6 shrink-0 items-center justify-center rounded-md hover:bg-muted/50",
516
+ onClick: (e) => {
517
+ e.preventDefault();
518
+ setLocalValue("");
519
+ onChange();
520
+ },
521
+ children: /* @__PURE__ */ jsx(X, { className: "h-3 w-3 text-muted-foreground hover:text-foreground" })
522
+ }
523
+ )
524
+ ] })
525
+ ]
526
+ }
527
+ );
528
+ }
529
+ var MAX_WIDTH_MAP = {
530
+ 1: "max-w-2xl",
531
+ 2: "max-w-3xl",
532
+ 3: "max-w-4xl",
533
+ 4: "max-w-5xl",
534
+ 5: "max-w-6xl"
535
+ };
536
+ function getMaxWidthTWClass(value) {
537
+ const roundedValue = Math.max(1, Math.min(5, Math.round(value)));
538
+ return MAX_WIDTH_MAP[roundedValue] || MAX_WIDTH_MAP[5];
539
+ }
540
+ function MaxWidthSelector({
541
+ value,
542
+ onChange,
543
+ className = "",
544
+ themeDefault
545
+ }) {
546
+ const numericValue = Object.entries(MAX_WIDTH_MAP).find(
547
+ ([_, v]) => v === (value ?? themeDefault)
548
+ )?.[0] || "5";
549
+ const handleChange = (newValue) => {
550
+ if (newValue === void 0) {
551
+ onChange();
552
+ return;
553
+ }
554
+ onChange(getMaxWidthTWClass(newValue));
555
+ };
556
+ return /* @__PURE__ */ jsx(
557
+ PageBuilderSlider,
558
+ {
559
+ title: "Maximum width",
560
+ max: 5,
561
+ min: 1,
562
+ onChange: handleChange,
563
+ value: parseInt(numericValue),
564
+ tooltip: "Maximum width of the content",
565
+ className,
566
+ allowsReset: true,
567
+ unit: ""
568
+ }
569
+ );
570
+ }
571
+ var PADDING_MAP = {
572
+ 0: "py-0",
573
+ 1: "py-4",
574
+ 2: "py-6",
575
+ 3: "py-8",
576
+ 4: "py-10",
577
+ 5: "py-12",
578
+ 6: "py-16",
579
+ 7: "py-20",
580
+ 8: "py-24",
581
+ 9: "py-32",
582
+ 10: "py-40",
583
+ 11: "py-48",
584
+ 12: "py-56",
585
+ 13: "py-64"
586
+ };
587
+ function getVerticalPaddingTWClass(value) {
588
+ const roundedValue = Math.max(0, Math.min(13, Math.round(value)));
589
+ return PADDING_MAP[roundedValue] || PADDING_MAP[5];
590
+ }
591
+ function VerticalPaddingSelector({
592
+ value,
593
+ onChange,
594
+ className = "",
595
+ themeDefault
596
+ }) {
597
+ const numericValue = Object.entries(PADDING_MAP).find(
598
+ ([_, v]) => v === (value ?? themeDefault)
599
+ )?.[0] || "6";
600
+ const handleChange = (newValue) => {
601
+ if (newValue === void 0) {
602
+ onChange();
603
+ return;
604
+ }
605
+ onChange(getVerticalPaddingTWClass(newValue));
606
+ };
607
+ return /* @__PURE__ */ jsx(
608
+ PageBuilderSlider,
609
+ {
610
+ title: "Vertical padding",
611
+ max: 13,
612
+ min: 0,
613
+ onChange: handleChange,
614
+ value: parseInt(numericValue),
615
+ tooltip: "Vertical padding of the section",
616
+ className,
617
+ unit: ""
618
+ }
619
+ );
620
+ }
621
+ function ContentPaddingSelector({
622
+ value,
623
+ onChange,
624
+ className = "",
625
+ variant = "horizontal",
626
+ min,
627
+ max
628
+ }) {
629
+ const tooltip = variant === "horizontal" ? "Width of the actual content. It affects only desktop class displays" : "Space on the top and bottom of the content. It affects only desktop class displays";
630
+ const defaultMax = variant === "horizontal" ? 100 : 200;
631
+ const defaultMin = variant === "horizontal" ? 10 : 16;
632
+ return /* @__PURE__ */ jsx(
633
+ PageBuilderSlider,
634
+ {
635
+ title: variant === "horizontal" ? "Content width" : "Vertical padding",
636
+ max: max || defaultMax,
637
+ min: min || defaultMin,
638
+ onChange,
639
+ value,
640
+ tooltip,
641
+ className
642
+ }
643
+ );
644
+ }
645
+
646
+ // src/builder/controls/pattern-presets.ts
647
+ function svgToDataUri(svg) {
648
+ return `url("data:image/svg+xml,${encodeURIComponent(svg)}")`;
649
+ }
650
+ var light = {
651
+ soft: "rgba(0,0,0,0.06)",
652
+ mid: "rgba(0,0,0,0.09)",
653
+ firm: "rgba(0,0,0,0.12)"};
654
+ var dark = {
655
+ soft: "rgba(255,255,255,0.06)",
656
+ mid: "rgba(255,255,255,0.09)",
657
+ firm: "rgba(255,255,255,0.12)"};
658
+ function dotsSvg(fill, size = 16, r = 1.35) {
659
+ return `<svg xmlns="http://www.w3.org/2000/svg" width="${size}" height="${size}"><circle cx="${size / 4}" cy="${size / 4}" r="${r}" fill="${fill}"/></svg>`;
660
+ }
661
+ function gridSvg(stroke, size = 32) {
662
+ return `<svg xmlns="http://www.w3.org/2000/svg" width="${size}" height="${size}"><path d="M${size} 0H0V${size}" fill="none" stroke="${stroke}" stroke-width="1"/></svg>`;
663
+ }
664
+ function diagonalSvg(stroke, size = 24) {
665
+ return `<svg xmlns="http://www.w3.org/2000/svg" width="${size}" height="${size}"><path d="M0 ${size} ${size} 0M-${size / 4} ${size / 4} ${size / 4}-${size / 4}M${size * 0.75} ${size * 1.25} ${size * 1.25} ${size * 0.75}" stroke="${stroke}" stroke-width="1"/></svg>`;
666
+ }
667
+ function contourSvg(stroke) {
668
+ return `<svg xmlns="http://www.w3.org/2000/svg" width="300" height="150"><g fill="none" stroke="${stroke}" stroke-width="1">
669
+ <path d="M0,15 Q37.5,-5 75,15 T150,15 T225,15 T300,15"/>
670
+ <path d="M0,45 Q37.5,25 75,45 T150,45 T225,45 T300,45"/>
671
+ <path d="M0,75 Q37.5,55 75,75 T150,75 T225,75 T300,75"/>
672
+ <path d="M0,105 Q37.5,85 75,105 T150,105 T225,105 T300,105"/>
673
+ <path d="M0,135 Q37.5,115 75,135 T150,135 T225,135 T300,135"/>
674
+ </g></svg>`;
675
+ }
676
+ function plusSvg(stroke, size = 24) {
677
+ const mid = size / 2;
678
+ const arm = size * 0.22;
679
+ return `<svg xmlns="http://www.w3.org/2000/svg" width="${size}" height="${size}"><path d="M${mid} ${mid - arm}v${arm * 2}M${mid - arm} ${mid}h${arm * 2}" fill="none" stroke="${stroke}" stroke-width="1.25" stroke-linecap="round"/></svg>`;
680
+ }
681
+ function graphPaperSvg(minor, major, size = 80) {
682
+ const step = size / 4;
683
+ const lines = [];
684
+ for (let i = 1; i < 4; i++) {
685
+ lines.push(`M${i * step} 0V${size}`, `M0 ${i * step}H${size}`);
686
+ }
687
+ return `<svg xmlns="http://www.w3.org/2000/svg" width="${size}" height="${size}">
688
+ <path d="${lines.join("")}" fill="none" stroke="${minor}" stroke-width="1"/>
689
+ <path d="M${size} 0H0V${size}" fill="none" stroke="${major}" stroke-width="1"/>
690
+ </svg>`;
691
+ }
692
+ function hexSvg(stroke, size = 56) {
693
+ const w = size;
694
+ const h = Math.round(size * 0.866);
695
+ return `<svg xmlns="http://www.w3.org/2000/svg" width="${w}" height="${h}"><path d="M${w * 0.25} 0 L${w * 0.75} 0 L${w} ${h / 2} L${w * 0.75} ${h} L${w * 0.25} ${h} L0 ${h / 2} Z" fill="none" stroke="${stroke}" stroke-width="1"/></svg>`;
696
+ }
697
+ function chevronSvg(stroke, size = 28) {
698
+ return `<svg xmlns="http://www.w3.org/2000/svg" width="${size}" height="${size}"><path d="M0 ${size * 0.35} L${size / 2} ${size * 0.65} L${size} ${size * 0.35}" fill="none" stroke="${stroke}" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/></svg>`;
699
+ }
700
+ function verticalLinesSvg(stroke, size = 12) {
701
+ return `<svg xmlns="http://www.w3.org/2000/svg" width="${size}" height="${size}"><path d="M1 0V${size}" fill="none" stroke="${stroke}" stroke-width="1"/></svg>`;
702
+ }
703
+ function horizontalLinesSvg(stroke, size = 14) {
704
+ return `<svg xmlns="http://www.w3.org/2000/svg" width="${size}" height="${size}"><path d="M0 1H${size}" fill="none" stroke="${stroke}" stroke-width="1"/></svg>`;
705
+ }
706
+ function crosshatchSvg(stroke, size = 20) {
707
+ return `<svg xmlns="http://www.w3.org/2000/svg" width="${size}" height="${size}"><path d="M0 ${size} ${size} 0M-${size / 4} ${size / 4} ${size / 4}-${size / 4}M${size * 0.75} ${size * 1.25} ${size * 1.25} ${size * 0.75}M0 0 ${size} ${size}M-${size / 4} ${size * 0.75} ${size / 4} ${size * 1.25}M${size * 0.75} ${-size / 4} ${size * 1.25} ${size / 4}" fill="none" stroke="${stroke}" stroke-width="1"/></svg>`;
708
+ }
709
+ function diamondSvg(stroke, size = 28) {
710
+ const m = size / 2;
711
+ return `<svg xmlns="http://www.w3.org/2000/svg" width="${size}" height="${size}"><path d="M${m} 1 L${size - 1} ${m} L${m} ${size - 1} L1 ${m} Z" fill="none" stroke="${stroke}" stroke-width="1"/></svg>`;
712
+ }
713
+ function wavesSvg(stroke) {
714
+ return `<svg xmlns="http://www.w3.org/2000/svg" width="120" height="40"><path d="M0 20 Q15 0 30 20 T60 20 T90 20 T120 20" fill="none" stroke="${stroke}" stroke-width="1.25"/></svg>`;
715
+ }
716
+ function concentricSvg(stroke, size = 64) {
717
+ const c = size / 2;
718
+ return `<svg xmlns="http://www.w3.org/2000/svg" width="${size}" height="${size}">
719
+ <circle cx="${c}" cy="${c}" r="${size * 0.18}" fill="none" stroke="${stroke}" stroke-width="1"/>
720
+ <circle cx="${c}" cy="${c}" r="${size * 0.32}" fill="none" stroke="${stroke}" stroke-width="1"/>
721
+ <circle cx="${c}" cy="${c}" r="${size * 0.46}" fill="none" stroke="${stroke}" stroke-width="1"/>
722
+ </svg>`;
723
+ }
724
+ function isometricSvg(stroke, size = 48) {
725
+ const s = size;
726
+ return `<svg xmlns="http://www.w3.org/2000/svg" width="${s}" height="${s * 0.866}">
727
+ <path d="M${s / 2} 0 L${s} ${s * 0.2165} L${s / 2} ${s * 0.433} L0 ${s * 0.2165} Z M${s / 2} ${s * 0.433} V${s * 0.866} M0 ${s * 0.2165} V${s * 0.65} L${s / 2} ${s * 0.866} L${s} ${s * 0.65} V${s * 0.2165}" fill="none" stroke="${stroke}" stroke-width="1"/>
728
+ </svg>`;
729
+ }
730
+ function trianglesSvg(stroke, size = 36) {
731
+ return `<svg xmlns="http://www.w3.org/2000/svg" width="${size}" height="${Math.round(size * 0.866)}"><path d="M${size / 2} 1 L${size - 1} ${size * 0.866 - 1} L1 ${size * 0.866 - 1} Z" fill="none" stroke="${stroke}" stroke-width="1"/></svg>`;
732
+ }
733
+ function scatterDotsSvg(fill, size = 48) {
734
+ return `<svg xmlns="http://www.w3.org/2000/svg" width="${size}" height="${size}">
735
+ <circle cx="8" cy="10" r="1.2" fill="${fill}"/>
736
+ <circle cx="22" cy="6" r="0.9" fill="${fill}"/>
737
+ <circle cx="38" cy="14" r="1.5" fill="${fill}"/>
738
+ <circle cx="14" cy="28" r="1" fill="${fill}"/>
739
+ <circle cx="30" cy="24" r="0.8" fill="${fill}"/>
740
+ <circle cx="42" cy="36" r="1.1" fill="${fill}"/>
741
+ <circle cx="6" cy="40" r="0.95" fill="${fill}"/>
742
+ <circle cx="24" cy="42" r="1.3" fill="${fill}"/>
743
+ </svg>`;
744
+ }
745
+ function brickSvg(stroke, w = 40, h = 20) {
746
+ return `<svg xmlns="http://www.w3.org/2000/svg" width="${w}" height="${h * 2}">
747
+ <path d="M0 0H${w}V${h}H0Z M0 ${h}H${w / 2}V${h * 2}H0Z M${w / 2} ${h}H${w}V${h * 2}H${w / 2}Z" fill="none" stroke="${stroke}" stroke-width="1"/>
748
+ </svg>`;
749
+ }
750
+ function scalesSvg(stroke, size = 32) {
751
+ return `<svg xmlns="http://www.w3.org/2000/svg" width="${size}" height="${size * 0.6}"><path d="M0 ${size * 0.6} A${size / 2} ${size * 0.55} 0 0 1 ${size} ${size * 0.6}" fill="none" stroke="${stroke}" stroke-width="1"/></svg>`;
752
+ }
753
+ function dashedGridSvg(stroke, size = 28) {
754
+ return `<svg xmlns="http://www.w3.org/2000/svg" width="${size}" height="${size}"><path d="M${size} 0H0V${size}" fill="none" stroke="${stroke}" stroke-width="1" stroke-dasharray="3 4"/></svg>`;
755
+ }
756
+ function circuitSvg(stroke, size = 64) {
757
+ return `<svg xmlns="http://www.w3.org/2000/svg" width="${size}" height="${size}" fill="none" stroke="${stroke}" stroke-width="1" stroke-linecap="round">
758
+ <path d="M8 8H28V28H8Z"/>
759
+ <path d="M36 12H52V20H36Z"/>
760
+ <path d="M12 36H20V52H12Z"/>
761
+ <path d="M28 8V4M8 16H4M28 20H36M20 28V36M44 20V36H36"/>
762
+ <circle cx="36" cy="36" r="3"/>
763
+ <circle cx="52" cy="44" r="2.5"/>
764
+ <path d="M39 36H49M52 41.5V36"/>
765
+ </svg>`;
766
+ }
767
+ function zigZagSvg(stroke, size = 24) {
768
+ return `<svg xmlns="http://www.w3.org/2000/svg" width="${size}" height="${size}"><path d="M0 ${size / 2} L${size / 4} ${size * 0.2} L${size / 2} ${size / 2} L${size * 0.75} ${size * 0.8} L${size} ${size / 2}" fill="none" stroke="${stroke}" stroke-width="1.25" stroke-linejoin="round"/></svg>`;
769
+ }
770
+ function svgPattern(id, label, lightSvg, darkSvg, size, description, extras) {
771
+ return {
772
+ id,
773
+ label,
774
+ category: "tile",
775
+ description,
776
+ apply: {
777
+ backgroundImage: svgToDataUri(lightSvg),
778
+ backgroundImageDark: svgToDataUri(darkSvg),
779
+ gradientBackgroundSize: size,
780
+ gradientBackgroundRepeat: "repeat",
781
+ gradientBackgroundPosition: "0 0",
782
+ gradientMaskImage: void 0,
783
+ ...extras
784
+ }
785
+ };
786
+ }
787
+ function cssPattern(id, label, lightCss, darkCss, description, extras) {
788
+ return {
789
+ id,
790
+ label,
791
+ category: "glow",
792
+ description,
793
+ apply: {
794
+ backgroundImage: lightCss,
795
+ backgroundImageDark: darkCss,
796
+ gradientBackgroundSize: "100% 100%",
797
+ gradientBackgroundRepeat: "no-repeat",
798
+ gradientBackgroundPosition: "center",
799
+ gradientMaskImage: void 0,
800
+ ...extras
801
+ }
802
+ };
803
+ }
804
+ var PATTERN_PRESETS = [
805
+ // ── Classic tiles ────────────────────────────────────────────────────
806
+ svgPattern(
807
+ "dots",
808
+ "Dots",
809
+ dotsSvg(light.firm),
810
+ dotsSvg(dark.firm),
811
+ "16px 16px",
812
+ "Subtle dot grid \u2014 the default SaaS texture"
813
+ ),
814
+ svgPattern(
815
+ "dots-fine",
816
+ "Fine dots",
817
+ dotsSvg(light.mid, 10, 0.9),
818
+ dotsSvg(dark.mid, 10, 0.9),
819
+ "10px 10px",
820
+ "Tighter, quieter polka texture"
821
+ ),
822
+ svgPattern(
823
+ "scatter",
824
+ "Scatter",
825
+ scatterDotsSvg(light.firm),
826
+ scatterDotsSvg(dark.firm),
827
+ "48px 48px",
828
+ "Organic, uneven speckles"
829
+ ),
830
+ svgPattern(
831
+ "grid",
832
+ "Grid",
833
+ gridSvg(light.mid),
834
+ gridSvg(dark.mid),
835
+ "32px 32px",
836
+ "Clean square lattice"
837
+ ),
838
+ svgPattern(
839
+ "graph-paper",
840
+ "Graph paper",
841
+ graphPaperSvg(light.soft, light.firm),
842
+ graphPaperSvg(dark.soft, dark.firm),
843
+ "80px 80px",
844
+ "Notebook-style minor + major grid"
845
+ ),
846
+ svgPattern(
847
+ "dashed-grid",
848
+ "Dashed grid",
849
+ dashedGridSvg(light.mid),
850
+ dashedGridSvg(dark.mid),
851
+ "28px 28px",
852
+ "Soft technical grid with dashed strokes"
853
+ ),
854
+ svgPattern(
855
+ "diagonal",
856
+ "Diagonal lines",
857
+ diagonalSvg(light.mid),
858
+ diagonalSvg(dark.mid),
859
+ "24px 24px",
860
+ "Classic hatch at 45\xB0"
861
+ ),
862
+ svgPattern(
863
+ "crosshatch",
864
+ "Crosshatch",
865
+ crosshatchSvg(light.soft),
866
+ crosshatchSvg(dark.soft),
867
+ "20px 20px",
868
+ "Crossed diagonals for richer grain"
869
+ ),
870
+ svgPattern(
871
+ "plus",
872
+ "Plus marks",
873
+ plusSvg(light.firm),
874
+ plusSvg(dark.firm),
875
+ "24px 24px",
876
+ "Cross / plus marks \u2014 popular on product pages"
877
+ ),
878
+ svgPattern(
879
+ "vertical",
880
+ "Pinstripe",
881
+ verticalLinesSvg(light.mid),
882
+ verticalLinesSvg(dark.mid),
883
+ "12px 12px",
884
+ "Editorial vertical lines"
885
+ ),
886
+ svgPattern(
887
+ "horizontal",
888
+ "Ruled lines",
889
+ horizontalLinesSvg(light.mid),
890
+ horizontalLinesSvg(dark.mid),
891
+ "14px 14px",
892
+ "Horizontal notebook rules"
893
+ ),
894
+ svgPattern(
895
+ "zigzag",
896
+ "Zigzag",
897
+ zigZagSvg(light.mid),
898
+ zigZagSvg(dark.mid),
899
+ "24px 24px",
900
+ "Continuous zigzag band"
901
+ ),
902
+ svgPattern(
903
+ "chevron",
904
+ "Chevron",
905
+ chevronSvg(light.mid),
906
+ chevronSvg(dark.mid),
907
+ "28px 28px",
908
+ "Stacked chevrons for motion"
909
+ ),
910
+ svgPattern(
911
+ "diamond",
912
+ "Diamonds",
913
+ diamondSvg(light.mid),
914
+ diamondSvg(dark.mid),
915
+ "28px 28px",
916
+ "Geometric diamond lattice"
917
+ ),
918
+ svgPattern(
919
+ "triangles",
920
+ "Triangles",
921
+ trianglesSvg(light.mid),
922
+ trianglesSvg(dark.mid),
923
+ "36px 31px",
924
+ "Tessellated triangle outlines"
925
+ ),
926
+ svgPattern(
927
+ "hexagon",
928
+ "Hexagons",
929
+ hexSvg(light.mid),
930
+ hexSvg(dark.mid),
931
+ "56px 48px",
932
+ "Honeycomb / tech hex grid"
933
+ ),
934
+ svgPattern(
935
+ "isometric",
936
+ "Isometric",
937
+ isometricSvg(light.mid),
938
+ isometricSvg(dark.mid),
939
+ "48px 42px",
940
+ "3D cube lattice"
941
+ ),
942
+ svgPattern(
943
+ "waves",
944
+ "Waves",
945
+ wavesSvg(light.mid),
946
+ wavesSvg(dark.mid),
947
+ "120px 40px",
948
+ "Gentle repeating sine waves"
949
+ ),
950
+ svgPattern(
951
+ "contour",
952
+ "Contour",
953
+ contourSvg(light.soft),
954
+ contourSvg(dark.soft),
955
+ "300px 150px",
956
+ "Topographic contour strokes"
957
+ ),
958
+ svgPattern(
959
+ "concentric",
960
+ "Ripples",
961
+ concentricSvg(light.soft),
962
+ concentricSvg(dark.soft),
963
+ "64px 64px",
964
+ "Concentric circle tiles"
965
+ ),
966
+ svgPattern(
967
+ "scales",
968
+ "Scales",
969
+ scalesSvg(light.mid),
970
+ scalesSvg(dark.mid),
971
+ "32px 19px",
972
+ "Fish-scale / arc tile"
973
+ ),
974
+ svgPattern(
975
+ "brick",
976
+ "Brick",
977
+ brickSvg(light.mid),
978
+ brickSvg(dark.mid),
979
+ "40px 40px",
980
+ "Offset brick running bond"
981
+ ),
982
+ svgPattern(
983
+ "circuit",
984
+ "Circuit",
985
+ circuitSvg(light.mid),
986
+ circuitSvg(dark.mid),
987
+ "64px 64px",
988
+ "Tech / PCB-inspired traces"
989
+ ),
990
+ // ── Full-bleed CSS (modern hero / marketing) ─────────────────────────
991
+ cssPattern(
992
+ "soft-mesh",
993
+ "Soft mesh",
994
+ `radial-gradient(at 20% 20%, rgba(99,102,241,0.18) 0px, transparent 50%), radial-gradient(at 80% 10%, rgba(236,72,153,0.14) 0px, transparent 45%), radial-gradient(at 50% 80%, rgba(34,211,238,0.12) 0px, transparent 50%), radial-gradient(at 90% 70%, rgba(250,204,21,0.1) 0px, transparent 40%)`,
995
+ `radial-gradient(at 20% 20%, rgba(99,102,241,0.28) 0px, transparent 50%), radial-gradient(at 80% 10%, rgba(236,72,153,0.22) 0px, transparent 45%), radial-gradient(at 50% 80%, rgba(34,211,238,0.18) 0px, transparent 50%), radial-gradient(at 90% 70%, rgba(250,204,21,0.12) 0px, transparent 40%)`,
996
+ "Colorful soft blobs \u2014 modern marketing hero"
997
+ ),
998
+ cssPattern(
999
+ "aurora",
1000
+ "Aurora",
1001
+ `linear-gradient(135deg, rgba(56,189,248,0.2), transparent 50%), linear-gradient(225deg, rgba(167,139,250,0.22), transparent 50%), linear-gradient(45deg, rgba(52,211,153,0.15), transparent 40%)`,
1002
+ `linear-gradient(135deg, rgba(56,189,248,0.28), transparent 50%), linear-gradient(225deg, rgba(167,139,250,0.3), transparent 50%), linear-gradient(45deg, rgba(52,211,153,0.2), transparent 40%)`,
1003
+ "Layered aurora bands"
1004
+ ),
1005
+ cssPattern(
1006
+ "spotlight",
1007
+ "Spotlight",
1008
+ `radial-gradient(ellipse 80% 60% at 50% -10%, rgba(99,102,241,0.22), transparent 55%)`,
1009
+ `radial-gradient(ellipse 80% 60% at 50% -10%, rgba(129,140,248,0.35), transparent 55%)`,
1010
+ "Top-centered spotlight glow"
1011
+ ),
1012
+ cssPattern(
1013
+ "bottom-glow",
1014
+ "Bottom glow",
1015
+ `radial-gradient(ellipse 70% 50% at 50% 110%, rgba(14,165,233,0.2), transparent 55%)`,
1016
+ `radial-gradient(ellipse 70% 50% at 50% 110%, rgba(56,189,248,0.28), transparent 55%)`,
1017
+ "Soft light rising from the bottom edge"
1018
+ ),
1019
+ cssPattern(
1020
+ "corner-glow",
1021
+ "Corner glow",
1022
+ `radial-gradient(ellipse 60% 50% at 0% 0%, rgba(244,63,94,0.16), transparent 50%), radial-gradient(ellipse 50% 45% at 100% 100%, rgba(59,130,246,0.16), transparent 50%)`,
1023
+ `radial-gradient(ellipse 60% 50% at 0% 0%, rgba(251,113,133,0.25), transparent 50%), radial-gradient(ellipse 50% 45% at 100% 100%, rgba(96,165,250,0.25), transparent 50%)`,
1024
+ "Opposite-corner color accents"
1025
+ ),
1026
+ cssPattern(
1027
+ "horizon",
1028
+ "Horizon fade",
1029
+ `linear-gradient(180deg, transparent 0%, rgba(15,23,42,0.04) 55%, rgba(15,23,42,0.08) 100%)`,
1030
+ `linear-gradient(180deg, transparent 0%, rgba(255,255,255,0.04) 55%, rgba(255,255,255,0.1) 100%)`,
1031
+ "Gentle vertical vignette toward the base"
1032
+ ),
1033
+ cssPattern(
1034
+ "diagonal-fade",
1035
+ "Diagonal fade",
1036
+ `linear-gradient(135deg, rgba(99,102,241,0.12) 0%, transparent 45%, rgba(236,72,153,0.1) 100%)`,
1037
+ `linear-gradient(135deg, rgba(129,140,248,0.2) 0%, transparent 45%, rgba(244,114,182,0.16) 100%)`,
1038
+ "Two-tone diagonal wash"
1039
+ ),
1040
+ svgPattern(
1041
+ "grid-fade",
1042
+ "Fading grid",
1043
+ gridSvg(light.firm, 40),
1044
+ gridSvg(dark.firm, 40),
1045
+ "40px 40px",
1046
+ "Grid that softens toward the edges",
1047
+ {
1048
+ gradientMaskImage: "radial-gradient(ellipse at center, black 25%, transparent 75%)"
1049
+ }
1050
+ )
1051
+ ];
1052
+ var BG_TYPES = [
1053
+ {
1054
+ type: "color",
1055
+ label: "Color",
1056
+ description: "Solid fill",
1057
+ icon: Palette
1058
+ },
1059
+ {
1060
+ type: "image",
1061
+ label: "Image",
1062
+ description: "Photo or asset",
1063
+ icon: ImageIcon
1064
+ },
1065
+ {
1066
+ type: "gradient",
1067
+ label: "Pattern",
1068
+ description: "Texture or glow",
1069
+ icon: Sparkles
1070
+ }
1071
+ ];
1072
+ var BLEND_MODES = [
1073
+ { label: "Normal", value: "normal" },
1074
+ { label: "Multiply", value: "multiply" },
1075
+ { label: "Screen", value: "screen" },
1076
+ { label: "Overlay", value: "overlay" },
1077
+ { label: "Darken", value: "darken" },
1078
+ { label: "Lighten", value: "lighten" },
1079
+ { label: "Color Dodge", value: "color-dodge" },
1080
+ { label: "Color Burn", value: "color-burn" },
1081
+ { label: "Hard Light", value: "hard-light" },
1082
+ { label: "Soft Light", value: "soft-light" },
1083
+ { label: "Difference", value: "difference" },
1084
+ { label: "Exclusion", value: "exclusion" },
1085
+ { label: "Hue", value: "hue" },
1086
+ { label: "Saturation", value: "saturation" },
1087
+ { label: "Color", value: "color" },
1088
+ { label: "Luminosity", value: "luminosity" }
1089
+ ];
1090
+ var BACKGROUND_REPEAT_OPTIONS = [
1091
+ { label: "No repeat", value: "no-repeat" },
1092
+ { label: "Repeat", value: "repeat" },
1093
+ { label: "Repeat X", value: "repeat-x" },
1094
+ { label: "Repeat Y", value: "repeat-y" },
1095
+ { label: "Space", value: "space" },
1096
+ { label: "Round", value: "round" }
1097
+ ];
1098
+ var PATTERN_FILTERS = [
1099
+ { id: "all", label: "All" },
1100
+ { id: "tile", label: "Tiles" },
1101
+ { id: "glow", label: "Glows" }
1102
+ ];
1103
+ var fieldLabelClass = "text-xs font-semibold tracking-tight text-foreground";
1104
+ var fieldHintClass = "text-[11px] leading-relaxed text-muted-foreground break-words";
1105
+ var controlClass = "box-border h-9 w-full min-w-0 max-w-full rounded-lg border border-input bg-muted/20 px-3 text-sm text-foreground outline-none transition-colors placeholder:text-muted-foreground/70 focus:border-ring focus:bg-background focus:ring-2 focus:ring-ring/20";
1106
+ var textareaClass = "box-border min-h-[4.5rem] w-full min-w-0 max-w-full resize-y rounded-lg border border-input bg-muted/20 px-3 py-2 font-mono text-[11px] leading-relaxed text-foreground outline-none transition-colors placeholder:text-muted-foreground/70 focus:border-ring focus:bg-background focus:ring-2 focus:ring-ring/20 break-all";
1107
+ function FieldLabel({
1108
+ htmlFor,
1109
+ children,
1110
+ hint,
1111
+ trailing
1112
+ }) {
1113
+ return /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 items-start justify-between gap-2", children: [
1114
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1 space-y-0.5", children: [
1115
+ /* @__PURE__ */ jsx("label", { htmlFor, className: fieldLabelClass, children }),
1116
+ hint ? /* @__PURE__ */ jsx("p", { className: fieldHintClass, children: hint }) : null
1117
+ ] }),
1118
+ trailing ? /* @__PURE__ */ jsx("div", { className: "shrink-0 pt-0.5", children: trailing }) : null
1119
+ ] });
1120
+ }
1121
+ function PanelInput(props) {
1122
+ return /* @__PURE__ */ jsx("input", { ...props, className: cn(controlClass, props.className) });
1123
+ }
1124
+ function PanelTextarea(props) {
1125
+ return /* @__PURE__ */ jsx(
1126
+ "textarea",
1127
+ {
1128
+ ...props,
1129
+ className: cn(textareaClass, props.className),
1130
+ spellCheck: false
1131
+ }
1132
+ );
1133
+ }
1134
+ function PanelSelect({
1135
+ id,
1136
+ title,
1137
+ hint,
1138
+ value,
1139
+ onChange,
1140
+ options
1141
+ }) {
1142
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
1143
+ /* @__PURE__ */ jsx(FieldLabel, { htmlFor: id, hint, children: title }),
1144
+ /* @__PURE__ */ jsx(
1145
+ "select",
1146
+ {
1147
+ id,
1148
+ className: cn(
1149
+ controlClass,
1150
+ "cursor-pointer appearance-none pr-8"
1151
+ ),
1152
+ value,
1153
+ onChange: (e) => onChange(e.target.value),
1154
+ children: options.map((option) => /* @__PURE__ */ jsx("option", { value: option.value, children: option.label }, option.value))
1155
+ }
1156
+ )
1157
+ ] });
1158
+ }
1159
+ function SectionCard({
1160
+ title,
1161
+ description,
1162
+ children,
1163
+ className
1164
+ }) {
1165
+ return /* @__PURE__ */ jsxs(
1166
+ "div",
1167
+ {
1168
+ className: cn(
1169
+ "min-w-0 max-w-full overflow-hidden rounded-xl border border-border/70 bg-card/40 p-3 shadow-sm",
1170
+ className
1171
+ ),
1172
+ children: [
1173
+ (title || description) && /* @__PURE__ */ jsxs("div", { className: "mb-3 space-y-0.5", children: [
1174
+ title ? /* @__PURE__ */ jsx("p", { className: "text-xs font-semibold text-foreground", children: title }) : null,
1175
+ description ? /* @__PURE__ */ jsx("p", { className: fieldHintClass, children: description }) : null
1176
+ ] }),
1177
+ children
1178
+ ]
1179
+ }
1180
+ );
1181
+ }
1182
+ function CollapsibleCard({
1183
+ title,
1184
+ description,
1185
+ defaultOpen = false,
1186
+ children
1187
+ }) {
1188
+ const [open, setOpen] = useState(defaultOpen);
1189
+ return /* @__PURE__ */ jsxs("div", { className: "min-w-0 max-w-full overflow-hidden rounded-xl border border-border/70 bg-card/40 shadow-sm", children: [
1190
+ /* @__PURE__ */ jsxs(
1191
+ "button",
1192
+ {
1193
+ type: "button",
1194
+ className: "flex w-full min-w-0 items-center gap-3 px-3 py-2.5 text-left transition-colors hover:bg-muted/30",
1195
+ "aria-expanded": open,
1196
+ onClick: () => setOpen((v) => !v),
1197
+ children: [
1198
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1 space-y-0.5", children: [
1199
+ /* @__PURE__ */ jsx("p", { className: "text-xs font-semibold text-foreground", children: title }),
1200
+ description ? /* @__PURE__ */ jsx("p", { className: fieldHintClass, children: description }) : null
1201
+ ] }),
1202
+ /* @__PURE__ */ jsx(
1203
+ ChevronDown,
1204
+ {
1205
+ className: cn(
1206
+ "h-4 w-4 shrink-0 text-muted-foreground transition-transform duration-200",
1207
+ open && "rotate-180"
1208
+ ),
1209
+ "aria-hidden": true
1210
+ }
1211
+ )
1212
+ ]
1213
+ }
1214
+ ),
1215
+ open ? /* @__PURE__ */ jsx("div", { className: "min-w-0 space-y-4 border-t border-border/60 px-3 py-3", children }) : null
1216
+ ] });
1217
+ }
1218
+ function FixedBackgroundToggle({
1219
+ checked,
1220
+ onChange
1221
+ }) {
1222
+ return /* @__PURE__ */ jsx(SectionCard, { children: /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 items-start gap-3", children: [
1223
+ /* @__PURE__ */ jsx(
1224
+ "span",
1225
+ {
1226
+ className: cn(
1227
+ "mt-0.5 inline-flex h-8 w-8 shrink-0 items-center justify-center rounded-lg border",
1228
+ checked ? "border-primary/30 bg-primary/10 text-primary" : "border-border/70 bg-muted/30 text-muted-foreground"
1229
+ ),
1230
+ children: /* @__PURE__ */ jsx(Pin, { className: "h-3.5 w-3.5", "aria-hidden": true })
1231
+ }
1232
+ ),
1233
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1 space-y-1", children: [
1234
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-3", children: [
1235
+ /* @__PURE__ */ jsx(
1236
+ "label",
1237
+ {
1238
+ htmlFor: "bg-fixed-toggle",
1239
+ className: "text-xs font-semibold text-foreground",
1240
+ children: "Fixed background"
1241
+ }
1242
+ ),
1243
+ /* @__PURE__ */ jsx(
1244
+ Switch,
1245
+ {
1246
+ id: "bg-fixed-toggle",
1247
+ checked,
1248
+ onCheckedChange: onChange,
1249
+ "aria-describedby": "bg-fixed-hint"
1250
+ }
1251
+ )
1252
+ ] }),
1253
+ /* @__PURE__ */ jsx("p", { id: "bg-fixed-hint", className: fieldHintClass, children: "Keep the background still while the page scrolls \u2014 content moves over a static surface." })
1254
+ ] })
1255
+ ] }) });
1256
+ }
1257
+ function BackgroundPreview({ value }) {
1258
+ const imageUrl = value.type === "image" && typeof value.media?.file === "string" ? value.media.file : void 0;
1259
+ const previewStyle = (() => {
1260
+ if (value.type === "color") {
1261
+ return {
1262
+ backgroundColor: value.backgroundColor || "var(--muted)"
1263
+ };
1264
+ }
1265
+ if (value.type === "image") {
1266
+ return {
1267
+ backgroundColor: "var(--muted)",
1268
+ backgroundImage: imageUrl ? `url(${imageUrl})` : void 0,
1269
+ backgroundSize: value.backgroundSize || "cover",
1270
+ backgroundPosition: value.backgroundPosition || "center",
1271
+ backgroundRepeat: value.backgroundRepeat || "no-repeat"
1272
+ };
1273
+ }
1274
+ return {
1275
+ backgroundColor: "var(--background)",
1276
+ backgroundImage: value.backgroundImage,
1277
+ backgroundSize: value.gradientBackgroundSize || "cover",
1278
+ backgroundPosition: value.gradientBackgroundPosition || "center",
1279
+ backgroundRepeat: value.gradientBackgroundRepeat || "no-repeat",
1280
+ WebkitMaskImage: value.gradientMaskImage,
1281
+ maskImage: value.gradientMaskImage
1282
+ };
1283
+ })();
1284
+ const typeLabel = BG_TYPES.find((t) => t.type === value.type)?.label ?? "Background";
1285
+ return /* @__PURE__ */ jsxs("div", { className: "min-w-0 max-w-full overflow-hidden rounded-xl border border-border/70 shadow-sm", children: [
1286
+ /* @__PURE__ */ jsxs(
1287
+ "div",
1288
+ {
1289
+ className: "relative h-14 w-full max-w-full",
1290
+ style: previewStyle,
1291
+ children: [
1292
+ value.type === "image" && imageUrl && (value.blur || 0) > 0 ? /* @__PURE__ */ jsx(
1293
+ "div",
1294
+ {
1295
+ className: "absolute inset-0",
1296
+ style: {
1297
+ backdropFilter: `blur(${value.blur}px)`
1298
+ }
1299
+ }
1300
+ ) : null,
1301
+ value.type === "image" && value.overlay?.color ? /* @__PURE__ */ jsx(
1302
+ "div",
1303
+ {
1304
+ className: "absolute inset-0",
1305
+ style: {
1306
+ backgroundColor: value.overlay.color,
1307
+ opacity: (value.overlay.opacity || 0) / 10,
1308
+ mixBlendMode: value.overlay.blendMode || "normal"
1309
+ }
1310
+ }
1311
+ ) : null,
1312
+ value.type === "image" && !imageUrl ? /* @__PURE__ */ jsxs("div", { className: "absolute inset-0 flex items-center justify-center gap-1.5 text-[11px] text-muted-foreground", children: [
1313
+ /* @__PURE__ */ jsx(ImageIcon, { className: "h-3.5 w-3.5", "aria-hidden": true }),
1314
+ "No image yet"
1315
+ ] }) : null,
1316
+ value.type === "gradient" && !value.backgroundImage ? /* @__PURE__ */ jsxs("div", { className: "absolute inset-0 flex items-center justify-center gap-1.5 text-[11px] text-muted-foreground", children: [
1317
+ /* @__PURE__ */ jsx(Sparkles, { className: "h-3.5 w-3.5", "aria-hidden": true }),
1318
+ "Choose a pattern"
1319
+ ] }) : null
1320
+ ]
1321
+ }
1322
+ ),
1323
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2 border-t border-border/60 bg-muted/20 px-3 py-1.5", children: [
1324
+ /* @__PURE__ */ jsx("span", { className: "text-[10px] font-semibold uppercase tracking-[0.14em] text-muted-foreground", children: "Preview" }),
1325
+ /* @__PURE__ */ jsx("span", { className: "text-[11px] font-medium text-foreground", children: typeLabel })
1326
+ ] })
1327
+ ] });
1328
+ }
1329
+ function SectionBackgroundPanel({
1330
+ value = { type: "color" },
1331
+ onChange,
1332
+ className
1333
+ }) {
1334
+ const [patternFilter, setPatternFilter] = useState("all");
1335
+ const handleTypeChange = (type) => {
1336
+ onChange({ ...value, type });
1337
+ };
1338
+ const handleBackgroundChange = (updates) => {
1339
+ onChange({ ...value, ...updates });
1340
+ };
1341
+ const handleOverlayChange = (overlayUpdates) => {
1342
+ onChange({
1343
+ ...value,
1344
+ overlay: {
1345
+ color: "",
1346
+ blendMode: "normal",
1347
+ opacity: 0,
1348
+ ...value.overlay,
1349
+ ...overlayUpdates
1350
+ }
1351
+ });
1352
+ };
1353
+ const filteredPresets = useMemo(
1354
+ () => patternFilter === "all" ? PATTERN_PRESETS : PATTERN_PRESETS.filter((p) => p.category === patternFilter),
1355
+ [patternFilter]
1356
+ );
1357
+ return /* @__PURE__ */ jsxs("div", { className: cn("min-w-0 max-w-full space-y-3", className), children: [
1358
+ /* @__PURE__ */ jsx(
1359
+ "div",
1360
+ {
1361
+ role: "tablist",
1362
+ "aria-label": "Background type",
1363
+ className: "grid min-w-0 grid-cols-3 gap-1.5",
1364
+ children: BG_TYPES.map((tab) => {
1365
+ const Icon = tab.icon;
1366
+ const active = value.type === tab.type;
1367
+ return /* @__PURE__ */ jsxs(
1368
+ "button",
1369
+ {
1370
+ type: "button",
1371
+ role: "tab",
1372
+ "aria-selected": active,
1373
+ className: cn(
1374
+ "group flex min-w-0 flex-col items-start gap-1 rounded-xl border px-2 py-2 text-left transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/40",
1375
+ active ? "border-primary/50 bg-primary/8 shadow-sm" : "border-border/80 bg-muted/15 text-muted-foreground hover:border-primary/30 hover:bg-muted/40 hover:text-foreground"
1376
+ ),
1377
+ onClick: () => handleTypeChange(tab.type),
1378
+ children: [
1379
+ /* @__PURE__ */ jsx(
1380
+ "span",
1381
+ {
1382
+ className: cn(
1383
+ "inline-flex h-6 w-6 items-center justify-center rounded-md border transition-colors",
1384
+ active ? "border-primary/30 bg-primary/15 text-primary" : "border-border/70 bg-background text-muted-foreground group-hover:text-foreground"
1385
+ ),
1386
+ children: /* @__PURE__ */ jsx(Icon, { className: "h-3.5 w-3.5", "aria-hidden": true })
1387
+ }
1388
+ ),
1389
+ /* @__PURE__ */ jsxs("span", { className: "min-w-0 space-y-0.5", children: [
1390
+ /* @__PURE__ */ jsx(
1391
+ "span",
1392
+ {
1393
+ className: cn(
1394
+ "block truncate text-xs font-semibold leading-none",
1395
+ active ? "text-foreground" : "text-inherit"
1396
+ ),
1397
+ children: tab.label
1398
+ }
1399
+ ),
1400
+ /* @__PURE__ */ jsx("span", { className: "block truncate text-[10px] leading-tight text-muted-foreground", children: tab.description })
1401
+ ] })
1402
+ ]
1403
+ },
1404
+ tab.type
1405
+ );
1406
+ })
1407
+ }
1408
+ ),
1409
+ /* @__PURE__ */ jsx(BackgroundPreview, { value }),
1410
+ value.type === "color" && /* @__PURE__ */ jsx(
1411
+ SectionCard,
1412
+ {
1413
+ title: "Fill colors",
1414
+ description: "Solid section background for light and dark appearance.",
1415
+ children: /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
1416
+ /* @__PURE__ */ jsx(
1417
+ ColorSelector,
1418
+ {
1419
+ title: "Light mode",
1420
+ value: value.backgroundColor || "",
1421
+ onChange: (color) => handleBackgroundChange({
1422
+ backgroundColor: color
1423
+ }),
1424
+ allowReset: true
1425
+ }
1426
+ ),
1427
+ /* @__PURE__ */ jsx("div", { className: "border-t border-border/50 pt-4", children: /* @__PURE__ */ jsx(
1428
+ ColorSelector,
1429
+ {
1430
+ title: "Dark mode",
1431
+ value: value.backgroundColorDark || "",
1432
+ onChange: (color) => handleBackgroundChange({
1433
+ backgroundColorDark: color
1434
+ }),
1435
+ allowReset: true,
1436
+ description: "Optional override when the page is in dark mode"
1437
+ }
1438
+ ) })
1439
+ ] })
1440
+ }
1441
+ ),
1442
+ value.type === "image" && /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
1443
+ /* @__PURE__ */ jsx(
1444
+ FixedBackgroundToggle,
1445
+ {
1446
+ checked: value.backgroundAttachment === "fixed",
1447
+ onChange: (fixed) => handleBackgroundChange({
1448
+ backgroundAttachment: fixed ? "fixed" : "scroll"
1449
+ })
1450
+ }
1451
+ ),
1452
+ /* @__PURE__ */ jsx(
1453
+ SectionCard,
1454
+ {
1455
+ title: "Source",
1456
+ description: "Paste a public image URL to use as the section backdrop.",
1457
+ children: /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
1458
+ /* @__PURE__ */ jsx(FieldLabel, { htmlFor: "background-image-url", children: "Image URL" }),
1459
+ /* @__PURE__ */ jsxs("div", { className: "relative", children: [
1460
+ /* @__PURE__ */ jsx(
1461
+ ImageIcon,
1462
+ {
1463
+ className: "pointer-events-none absolute left-3 top-1/2 h-3.5 w-3.5 -translate-y-1/2 text-muted-foreground",
1464
+ "aria-hidden": true
1465
+ }
1466
+ ),
1467
+ /* @__PURE__ */ jsx(
1468
+ PanelInput,
1469
+ {
1470
+ id: "background-image-url",
1471
+ value: value.media?.file || "",
1472
+ onChange: (e) => handleBackgroundChange({
1473
+ media: e.target.value ? { file: e.target.value } : {}
1474
+ }),
1475
+ placeholder: "https://example.com/background.jpg",
1476
+ className: "pl-9"
1477
+ }
1478
+ )
1479
+ ] })
1480
+ ] })
1481
+ }
1482
+ ),
1483
+ /* @__PURE__ */ jsx(SectionCard, { title: "Effects", children: /* @__PURE__ */ jsx(
1484
+ PageBuilderSlider,
1485
+ {
1486
+ title: "Blur",
1487
+ min: 0,
1488
+ max: 20,
1489
+ value: value.blur || 0,
1490
+ onChange: (blur) => handleBackgroundChange({ blur: blur || 0 }),
1491
+ unit: "px",
1492
+ tooltip: "Softens the image behind the content"
1493
+ }
1494
+ ) }),
1495
+ /* @__PURE__ */ jsxs(
1496
+ CollapsibleCard,
1497
+ {
1498
+ title: "Overlay",
1499
+ description: "Tint the image for contrast with text",
1500
+ children: [
1501
+ /* @__PURE__ */ jsx(
1502
+ ColorSelector,
1503
+ {
1504
+ title: "Overlay color",
1505
+ value: value.overlay?.color || "",
1506
+ onChange: (color) => handleOverlayChange({ color: color || "" })
1507
+ }
1508
+ ),
1509
+ /* @__PURE__ */ jsx(
1510
+ ColorSelector,
1511
+ {
1512
+ title: "Dark mode overlay",
1513
+ value: value.overlay?.colorDark || "",
1514
+ onChange: (color) => handleOverlayChange({ colorDark: color }),
1515
+ allowReset: true,
1516
+ description: "Optional overlay when dark mode is active"
1517
+ }
1518
+ ),
1519
+ /* @__PURE__ */ jsx(
1520
+ PageBuilderSlider,
1521
+ {
1522
+ title: "Opacity",
1523
+ min: 0,
1524
+ max: 10,
1525
+ value: value.overlay?.opacity || 0,
1526
+ onChange: (opacity) => handleOverlayChange({
1527
+ opacity: opacity || 0
1528
+ }),
1529
+ unit: "",
1530
+ tooltip: "0 is invisible, 10 is fully solid"
1531
+ }
1532
+ ),
1533
+ /* @__PURE__ */ jsx(
1534
+ PanelSelect,
1535
+ {
1536
+ id: "overlay-blend-mode",
1537
+ title: "Blend mode",
1538
+ value: value.overlay?.blendMode || "normal",
1539
+ onChange: (mode) => handleOverlayChange({ blendMode: mode }),
1540
+ options: BLEND_MODES
1541
+ }
1542
+ )
1543
+ ]
1544
+ }
1545
+ ),
1546
+ /* @__PURE__ */ jsxs(
1547
+ CollapsibleCard,
1548
+ {
1549
+ title: "Advanced",
1550
+ description: "Size, position, repeat, and CSS mask",
1551
+ children: [
1552
+ /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
1553
+ /* @__PURE__ */ jsx(
1554
+ FieldLabel,
1555
+ {
1556
+ htmlFor: "image-background-size",
1557
+ hint: "e.g. cover, contain, 100%",
1558
+ children: "Background size"
1559
+ }
1560
+ ),
1561
+ /* @__PURE__ */ jsx(
1562
+ PanelInput,
1563
+ {
1564
+ id: "image-background-size",
1565
+ value: value.backgroundSize || "",
1566
+ onChange: (e) => handleBackgroundChange({
1567
+ backgroundSize: e.target.value
1568
+ }),
1569
+ placeholder: "cover"
1570
+ }
1571
+ )
1572
+ ] }),
1573
+ /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
1574
+ /* @__PURE__ */ jsx(
1575
+ FieldLabel,
1576
+ {
1577
+ htmlFor: "image-background-position",
1578
+ hint: "e.g. center, top left, 50% 50%",
1579
+ children: "Background position"
1580
+ }
1581
+ ),
1582
+ /* @__PURE__ */ jsx(
1583
+ PanelInput,
1584
+ {
1585
+ id: "image-background-position",
1586
+ value: value.backgroundPosition || "",
1587
+ onChange: (e) => handleBackgroundChange({
1588
+ backgroundPosition: e.target.value
1589
+ }),
1590
+ placeholder: "center"
1591
+ }
1592
+ )
1593
+ ] }),
1594
+ /* @__PURE__ */ jsx(
1595
+ PanelSelect,
1596
+ {
1597
+ id: "image-background-repeat",
1598
+ title: "Background repeat",
1599
+ value: value.backgroundRepeat || "no-repeat",
1600
+ onChange: (repeat) => handleBackgroundChange({
1601
+ backgroundRepeat: repeat
1602
+ }),
1603
+ options: BACKGROUND_REPEAT_OPTIONS
1604
+ }
1605
+ ),
1606
+ /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
1607
+ /* @__PURE__ */ jsx(
1608
+ FieldLabel,
1609
+ {
1610
+ htmlFor: "mask-image",
1611
+ hint: "CSS mask to fade or crop the image",
1612
+ trailing: /* @__PURE__ */ jsxs(
1613
+ "a",
1614
+ {
1615
+ href: "https://developer.mozilla.org/en-US/docs/Web/CSS/mask-image",
1616
+ target: "_blank",
1617
+ rel: "noopener noreferrer",
1618
+ className: "inline-flex items-center gap-1 text-[10px] font-medium text-muted-foreground underline-offset-2 hover:text-foreground hover:underline",
1619
+ children: [
1620
+ "Docs",
1621
+ /* @__PURE__ */ jsx(
1622
+ ExternalLink,
1623
+ {
1624
+ className: "h-3 w-3",
1625
+ "aria-hidden": true
1626
+ }
1627
+ )
1628
+ ]
1629
+ }
1630
+ ),
1631
+ children: "Mask image"
1632
+ }
1633
+ ),
1634
+ /* @__PURE__ */ jsx(
1635
+ PanelTextarea,
1636
+ {
1637
+ id: "mask-image",
1638
+ value: value.maskImage || "",
1639
+ onChange: (e) => handleBackgroundChange({
1640
+ maskImage: e.target.value || void 0
1641
+ }),
1642
+ placeholder: "linear-gradient(black, transparent)",
1643
+ rows: 2
1644
+ }
1645
+ )
1646
+ ] })
1647
+ ]
1648
+ }
1649
+ )
1650
+ ] }),
1651
+ value.type === "gradient" && /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
1652
+ /* @__PURE__ */ jsx(
1653
+ FixedBackgroundToggle,
1654
+ {
1655
+ checked: value.backgroundAttachment === "fixed",
1656
+ onChange: (fixed) => handleBackgroundChange({
1657
+ backgroundAttachment: fixed ? "fixed" : "scroll"
1658
+ })
1659
+ }
1660
+ ),
1661
+ /* @__PURE__ */ jsxs(
1662
+ SectionCard,
1663
+ {
1664
+ title: "Presets",
1665
+ description: "One click fills the CSS fields below \u2014 edit anytime.",
1666
+ children: [
1667
+ /* @__PURE__ */ jsx("div", { className: "mb-3 flex flex-wrap gap-1.5", children: PATTERN_FILTERS.map((filter) => {
1668
+ const active = patternFilter === filter.id;
1669
+ return /* @__PURE__ */ jsx(
1670
+ "button",
1671
+ {
1672
+ type: "button",
1673
+ className: cn(
1674
+ "rounded-full px-2.5 py-1 text-[11px] font-semibold transition-colors",
1675
+ active ? "bg-primary text-primary-foreground shadow-sm" : "bg-muted/50 text-muted-foreground hover:bg-muted hover:text-foreground"
1676
+ ),
1677
+ onClick: () => setPatternFilter(filter.id),
1678
+ children: filter.label
1679
+ },
1680
+ filter.id
1681
+ );
1682
+ }) }),
1683
+ /* @__PURE__ */ jsx("div", { className: "grid max-h-52 min-w-0 grid-cols-3 gap-1.5 overflow-x-hidden overflow-y-auto", children: filteredPresets.map((preset) => {
1684
+ const isSelected = value.backgroundImage === preset.apply.backgroundImage;
1685
+ return /* @__PURE__ */ jsxs(
1686
+ "button",
1687
+ {
1688
+ type: "button",
1689
+ title: preset.description ?? preset.label,
1690
+ "aria-pressed": isSelected,
1691
+ className: cn(
1692
+ "group relative flex min-w-0 flex-col overflow-hidden rounded-lg border text-left transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/40",
1693
+ isSelected ? "border-primary/55 bg-primary/5 shadow-sm" : "border-border/80 bg-muted/10 hover:border-primary/35 hover:bg-muted/30"
1694
+ ),
1695
+ onClick: () => handleBackgroundChange(preset.apply),
1696
+ children: [
1697
+ /* @__PURE__ */ jsx(
1698
+ "span",
1699
+ {
1700
+ className: "relative h-12 w-full max-w-full bg-background",
1701
+ style: {
1702
+ backgroundImage: preset.apply.backgroundImage,
1703
+ backgroundSize: preset.apply.gradientBackgroundSize,
1704
+ backgroundRepeat: preset.apply.gradientBackgroundRepeat,
1705
+ backgroundPosition: preset.apply.gradientBackgroundPosition,
1706
+ WebkitMaskImage: preset.apply.gradientMaskImage,
1707
+ maskImage: preset.apply.gradientMaskImage
1708
+ },
1709
+ "aria-hidden": true,
1710
+ children: isSelected ? /* @__PURE__ */ jsx("span", { className: "absolute right-1 top-1 flex h-3.5 w-3.5 items-center justify-center rounded-full bg-primary text-primary-foreground shadow-sm", children: /* @__PURE__ */ jsx(
1711
+ Check,
1712
+ {
1713
+ className: "h-2.5 w-2.5",
1714
+ "aria-hidden": true
1715
+ }
1716
+ ) }) : null
1717
+ }
1718
+ ),
1719
+ /* @__PURE__ */ jsx("span", { className: "truncate border-t border-border/60 px-1 py-1 text-[10px] font-semibold leading-tight text-foreground", children: preset.label })
1720
+ ]
1721
+ },
1722
+ preset.id
1723
+ );
1724
+ }) })
1725
+ ]
1726
+ }
1727
+ ),
1728
+ /* @__PURE__ */ jsx(
1729
+ SectionCard,
1730
+ {
1731
+ title: "Custom CSS",
1732
+ description: "Override or hand-author background-image values.",
1733
+ children: /* @__PURE__ */ jsxs("div", { className: "min-w-0 space-y-3.5", children: [
1734
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0 space-y-2", children: [
1735
+ /* @__PURE__ */ jsx(
1736
+ FieldLabel,
1737
+ {
1738
+ htmlFor: "gradient-css",
1739
+ hint: "CSS background-image \u2014 gradients, SVG data URIs, etc.",
1740
+ children: "Light mode"
1741
+ }
1742
+ ),
1743
+ /* @__PURE__ */ jsx(
1744
+ PanelTextarea,
1745
+ {
1746
+ id: "gradient-css",
1747
+ value: value.backgroundImage || "",
1748
+ onChange: (e) => handleBackgroundChange({
1749
+ backgroundImage: e.target.value
1750
+ }),
1751
+ placeholder: "linear-gradient(45deg, #ff0000, #0000ff)",
1752
+ rows: 3
1753
+ }
1754
+ )
1755
+ ] }),
1756
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0 space-y-2", children: [
1757
+ /* @__PURE__ */ jsx(
1758
+ FieldLabel,
1759
+ {
1760
+ htmlFor: "gradient-css-dark",
1761
+ hint: "Optional dark appearance override",
1762
+ children: "Dark mode"
1763
+ }
1764
+ ),
1765
+ /* @__PURE__ */ jsx(
1766
+ PanelTextarea,
1767
+ {
1768
+ id: "gradient-css-dark",
1769
+ value: value.backgroundImageDark || "",
1770
+ onChange: (e) => handleBackgroundChange({
1771
+ backgroundImageDark: e.target.value || void 0
1772
+ }),
1773
+ placeholder: "linear-gradient(45deg, #333333, #666666)",
1774
+ rows: 3
1775
+ }
1776
+ )
1777
+ ] })
1778
+ ] })
1779
+ }
1780
+ ),
1781
+ /* @__PURE__ */ jsxs(
1782
+ CollapsibleCard,
1783
+ {
1784
+ title: "Advanced",
1785
+ description: "Tile size, position, repeat, and mask",
1786
+ children: [
1787
+ /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
1788
+ /* @__PURE__ */ jsx(
1789
+ FieldLabel,
1790
+ {
1791
+ htmlFor: "gradient-background-size",
1792
+ hint: "e.g. 24px 24px, cover, 100% 100%",
1793
+ children: "Background size"
1794
+ }
1795
+ ),
1796
+ /* @__PURE__ */ jsx(
1797
+ PanelInput,
1798
+ {
1799
+ id: "gradient-background-size",
1800
+ value: value.gradientBackgroundSize || "",
1801
+ onChange: (e) => handleBackgroundChange({
1802
+ gradientBackgroundSize: e.target.value
1803
+ }),
1804
+ placeholder: "cover"
1805
+ }
1806
+ )
1807
+ ] }),
1808
+ /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
1809
+ /* @__PURE__ */ jsx(
1810
+ FieldLabel,
1811
+ {
1812
+ htmlFor: "gradient-background-position",
1813
+ hint: "e.g. center, 0 0, top left",
1814
+ children: "Background position"
1815
+ }
1816
+ ),
1817
+ /* @__PURE__ */ jsx(
1818
+ PanelInput,
1819
+ {
1820
+ id: "gradient-background-position",
1821
+ value: value.gradientBackgroundPosition || "",
1822
+ onChange: (e) => handleBackgroundChange({
1823
+ gradientBackgroundPosition: e.target.value
1824
+ }),
1825
+ placeholder: "center"
1826
+ }
1827
+ )
1828
+ ] }),
1829
+ /* @__PURE__ */ jsx(
1830
+ PanelSelect,
1831
+ {
1832
+ id: "gradient-background-repeat",
1833
+ title: "Background repeat",
1834
+ value: value.gradientBackgroundRepeat || "no-repeat",
1835
+ onChange: (repeat) => handleBackgroundChange({
1836
+ gradientBackgroundRepeat: repeat
1837
+ }),
1838
+ options: BACKGROUND_REPEAT_OPTIONS
1839
+ }
1840
+ ),
1841
+ /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
1842
+ /* @__PURE__ */ jsx(
1843
+ FieldLabel,
1844
+ {
1845
+ htmlFor: "gradient-mask-image",
1846
+ hint: "CSS mask to fade edges or vignette",
1847
+ trailing: /* @__PURE__ */ jsxs(
1848
+ "a",
1849
+ {
1850
+ href: "https://developer.mozilla.org/en-US/docs/Web/CSS/mask-image",
1851
+ target: "_blank",
1852
+ rel: "noopener noreferrer",
1853
+ className: "inline-flex items-center gap-1 text-[10px] font-medium text-muted-foreground underline-offset-2 hover:text-foreground hover:underline",
1854
+ children: [
1855
+ "Docs",
1856
+ /* @__PURE__ */ jsx(
1857
+ ExternalLink,
1858
+ {
1859
+ className: "h-3 w-3",
1860
+ "aria-hidden": true
1861
+ }
1862
+ )
1863
+ ]
1864
+ }
1865
+ ),
1866
+ children: "Mask image"
1867
+ }
1868
+ ),
1869
+ /* @__PURE__ */ jsx(
1870
+ PanelTextarea,
1871
+ {
1872
+ id: "gradient-mask-image",
1873
+ value: value.gradientMaskImage || "",
1874
+ onChange: (e) => handleBackgroundChange({
1875
+ gradientMaskImage: e.target.value || void 0
1876
+ }),
1877
+ placeholder: "linear-gradient(black, transparent)",
1878
+ rows: 2
1879
+ }
1880
+ )
1881
+ ] })
1882
+ ]
1883
+ }
1884
+ ),
1885
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-2.5 rounded-xl border border-border/60 bg-muted/25 px-3 py-2.5", children: [
1886
+ /* @__PURE__ */ jsx("div", { className: "mt-0.5 flex h-6 w-6 shrink-0 items-center justify-center rounded-md bg-primary/10 text-primary", children: /* @__PURE__ */ jsx(Sparkles, { className: "h-3.5 w-3.5", "aria-hidden": true }) }),
1887
+ /* @__PURE__ */ jsxs("p", { className: "text-[11px] leading-relaxed text-muted-foreground", children: [
1888
+ "Need more inspiration? Browse",
1889
+ " ",
1890
+ /* @__PURE__ */ jsx(
1891
+ "a",
1892
+ {
1893
+ href: "https://patterncraft.fun",
1894
+ target: "_blank",
1895
+ rel: "noopener noreferrer",
1896
+ className: "font-medium text-foreground underline decoration-border underline-offset-2 hover:decoration-foreground",
1897
+ children: "PatternCraft"
1898
+ }
1899
+ ),
1900
+ " ",
1901
+ "or",
1902
+ " ",
1903
+ /* @__PURE__ */ jsx(
1904
+ "a",
1905
+ {
1906
+ href: "https://magicpattern.design/",
1907
+ target: "_blank",
1908
+ rel: "noopener noreferrer",
1909
+ className: "font-medium text-foreground underline decoration-border underline-offset-2 hover:decoration-foreground",
1910
+ children: "MagicPattern"
1911
+ }
1912
+ ),
1913
+ " ",
1914
+ "and paste the CSS into Custom CSS."
1915
+ ] })
1916
+ ] })
1917
+ ] })
1918
+ ] });
1919
+ }
1920
+ function DesignFieldGroup({
1921
+ settings,
1922
+ theme,
1923
+ update
1924
+ }) {
1925
+ return /* @__PURE__ */ jsxs(FieldGroup, { title: "Section styling", children: [
1926
+ /* @__PURE__ */ jsx(
1927
+ MaxWidthSelector,
1928
+ {
1929
+ value: settings.maxWidth,
1930
+ themeDefault: theme?.structure?.page?.width,
1931
+ onChange: (maxWidth) => update({ maxWidth })
1932
+ }
1933
+ ),
1934
+ /* @__PURE__ */ jsx(
1935
+ VerticalPaddingSelector,
1936
+ {
1937
+ value: settings.verticalPadding,
1938
+ themeDefault: theme?.structure?.section?.padding?.y,
1939
+ onChange: (verticalPadding) => update({ verticalPadding })
1940
+ }
1941
+ ),
1942
+ /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 max-w-full flex-col gap-3", children: [
1943
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0 space-y-0.5", children: [
1944
+ /* @__PURE__ */ jsx("p", { className: "text-xs font-semibold text-foreground", children: "Background" }),
1945
+ /* @__PURE__ */ jsx("p", { className: "text-[11px] leading-relaxed text-muted-foreground", children: "Color, image, or pattern for this section surface." })
1946
+ ] }),
1947
+ /* @__PURE__ */ jsx(
1948
+ SectionBackgroundPanel,
1949
+ {
1950
+ value: settings.background,
1951
+ onChange: (background) => update({ background })
1952
+ }
1953
+ )
1954
+ ] })
1955
+ ] });
1956
+ }
1957
+ var TABS = [
1958
+ { key: "data", label: "Data" },
1959
+ { key: "design", label: "Design" }
1960
+ ];
1961
+ function BlockSettings({
1962
+ instance,
1963
+ definition,
1964
+ theme,
1965
+ onChange
1966
+ }) {
1967
+ const [tab, setTab] = useState("data");
1968
+ const Editor = definition.editor;
1969
+ const settings = instance.settings ?? {};
1970
+ return /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 max-w-full flex-col", children: [
1971
+ /* @__PURE__ */ jsx(
1972
+ "div",
1973
+ {
1974
+ role: "tablist",
1975
+ "aria-label": "Block settings sections",
1976
+ className: "grid min-w-0 grid-cols-2 border-b border-border/80",
1977
+ children: TABS.map(({ key, label }) => {
1978
+ const Icon = key === "data" ? FilePenLine : Brush;
1979
+ return /* @__PURE__ */ jsxs(
1980
+ "button",
1981
+ {
1982
+ type: "button",
1983
+ role: "tab",
1984
+ "aria-selected": tab === key,
1985
+ className: cn(
1986
+ "relative inline-flex h-11 min-w-0 items-center justify-center gap-2 text-xs font-semibold transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/40",
1987
+ tab === key ? "text-foreground after:absolute after:inset-x-3 after:-bottom-px after:h-0.5 after:rounded-full after:bg-primary" : "text-muted-foreground hover:text-foreground"
1988
+ ),
1989
+ onClick: () => setTab(key),
1990
+ children: [
1991
+ /* @__PURE__ */ jsx(Icon, { className: "h-3.5 w-3.5", "aria-hidden": "true" }),
1992
+ label
1993
+ ]
1994
+ },
1995
+ key
1996
+ );
1997
+ })
1998
+ }
1999
+ ),
2000
+ /* @__PURE__ */ jsx("div", { className: "pt-5", children: tab === "data" && Editor && /* @__PURE__ */ jsx(
2001
+ Editor,
2002
+ {
2003
+ settings,
2004
+ theme,
2005
+ onChange
2006
+ }
2007
+ ) }),
2008
+ tab === "design" && /* @__PURE__ */ jsxs("div", { className: "min-w-0 max-w-full space-y-5 pt-5", children: [
2009
+ /* @__PURE__ */ jsxs("div", { children: [
2010
+ /* @__PURE__ */ jsx("p", { className: "text-sm font-semibold text-foreground", children: "Refine the look" }),
2011
+ /* @__PURE__ */ jsx("p", { className: "mt-1 text-xs leading-5 text-muted-foreground", children: "Adjust layout and surface styles for this block." })
2012
+ ] }),
2013
+ definition.layouts && definition.layouts.length > 0 && /* @__PURE__ */ jsxs("div", { className: "space-y-3 text-sm", children: [
2014
+ /* @__PURE__ */ jsxs("div", { children: [
2015
+ /* @__PURE__ */ jsx("p", { className: "text-xs font-semibold uppercase tracking-[0.14em] text-muted-foreground", children: "Layout" }),
2016
+ /* @__PURE__ */ jsx("p", { className: "mt-1 text-xs text-muted-foreground", children: "Choose how this block is arranged." })
2017
+ ] }),
2018
+ /* @__PURE__ */ jsx(
2019
+ "div",
2020
+ {
2021
+ className: "grid gap-2.5",
2022
+ style: {
2023
+ gridTemplateColumns: "repeat(auto-fill, minmax(112px, 1fr))"
2024
+ },
2025
+ children: definition.layouts.map((layout, index) => {
2026
+ const active = settings.layout === layout.id || settings.layout == null && index === 0;
2027
+ return /* @__PURE__ */ jsxs(
2028
+ "button",
2029
+ {
2030
+ type: "button",
2031
+ "aria-pressed": active,
2032
+ className: cn(
2033
+ "group relative flex min-h-16 items-center justify-between gap-2 rounded-xl border px-3 py-2.5 text-left text-xs font-semibold transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
2034
+ active ? "border-primary/60 bg-primary/8 text-foreground shadow-sm" : "border-border bg-muted/20 text-muted-foreground hover:border-primary/35 hover:bg-muted/60 hover:text-foreground"
2035
+ ),
2036
+ onClick: () => onChange({
2037
+ ...settings,
2038
+ layout: layout.id
2039
+ }),
2040
+ children: [
2041
+ /* @__PURE__ */ jsx("span", { children: layout.name }),
2042
+ active && /* @__PURE__ */ jsx("span", { className: "flex h-5 w-5 shrink-0 items-center justify-center rounded-full bg-primary text-primary-foreground", children: /* @__PURE__ */ jsx(
2043
+ Check,
2044
+ {
2045
+ className: "h-3 w-3",
2046
+ "aria-hidden": "true"
2047
+ }
2048
+ ) })
2049
+ ]
2050
+ },
2051
+ layout.id
2052
+ );
2053
+ })
2054
+ }
2055
+ )
2056
+ ] }),
2057
+ /* @__PURE__ */ jsx(
2058
+ DesignFieldGroup,
2059
+ {
2060
+ settings,
2061
+ theme: theme.theme,
2062
+ update: (partial) => onChange({ ...settings, ...partial })
2063
+ }
2064
+ )
2065
+ ] })
2066
+ ] });
2067
+ }
2068
+ function CardButton({
2069
+ label,
2070
+ disabled,
2071
+ onClick,
2072
+ children
2073
+ }) {
2074
+ return /* @__PURE__ */ jsx(
2075
+ "button",
2076
+ {
2077
+ type: "button",
2078
+ title: label,
2079
+ "aria-label": label,
2080
+ disabled,
2081
+ className: cn(
2082
+ "inline-flex h-8 w-8 items-center justify-center rounded-md border border-input bg-background shadow-sm transition-colors",
2083
+ disabled ? "cursor-not-allowed opacity-60" : "hover:bg-muted"
2084
+ ),
2085
+ onClick: (e) => {
2086
+ e.stopPropagation();
2087
+ onClick?.();
2088
+ },
2089
+ children
2090
+ }
2091
+ );
2092
+ }
2093
+ function ThemeCard({
2094
+ name,
2095
+ palette,
2096
+ selected = false,
2097
+ active = false,
2098
+ onPreview,
2099
+ onUse,
2100
+ onEdit,
2101
+ className
2102
+ }) {
2103
+ return /* @__PURE__ */ jsxs(
2104
+ "div",
2105
+ {
2106
+ role: "button",
2107
+ tabIndex: 0,
2108
+ "aria-pressed": selected,
2109
+ "aria-label": `Preview ${name}`,
2110
+ className: cn(
2111
+ "flex w-full cursor-pointer flex-col gap-2 rounded-lg bg-background p-2 text-left transition-colors",
2112
+ selected ? "border-2 border-primary shadow-md" : "border border-muted shadow-none hover:bg-muted",
2113
+ className
2114
+ ),
2115
+ onClick: onPreview,
2116
+ onKeyDown: (event) => {
2117
+ if (event.key === "Enter" || event.key === " ") {
2118
+ event.preventDefault();
2119
+ onPreview?.();
2120
+ }
2121
+ },
2122
+ children: [
2123
+ /* @__PURE__ */ jsxs("div", { className: "flex min-h-[32px] items-center justify-between gap-2", children: [
2124
+ /* @__PURE__ */ jsx("span", { className: "truncate text-sm font-semibold text-foreground", children: name }),
2125
+ /* @__PURE__ */ jsxs("div", { className: "flex shrink-0 items-center gap-1", children: [
2126
+ onEdit && /* @__PURE__ */ jsx(CardButton, { label: `Edit ${name}`, onClick: onEdit, children: /* @__PURE__ */ jsx(Pencil, { className: "h-4 w-4" }) }),
2127
+ onUse && /* @__PURE__ */ jsx(
2128
+ CardButton,
2129
+ {
2130
+ label: `Use ${name}`,
2131
+ disabled: active,
2132
+ onClick: onUse,
2133
+ children: /* @__PURE__ */ jsx(Check, { className: "h-4 w-4" })
2134
+ }
2135
+ )
2136
+ ] })
2137
+ ] }),
2138
+ /* @__PURE__ */ jsx("div", { className: "flex gap-2", children: palette.map((color, i) => /* @__PURE__ */ jsx(
2139
+ "span",
2140
+ {
2141
+ className: "h-4 w-4 rounded-full border",
2142
+ style: { backgroundColor: color }
2143
+ },
2144
+ i
2145
+ )) })
2146
+ ]
2147
+ }
2148
+ );
2149
+ }
2150
+ function PanelLabel({
2151
+ htmlFor,
2152
+ children
2153
+ }) {
2154
+ return /* @__PURE__ */ jsx(
2155
+ "label",
2156
+ {
2157
+ htmlFor,
2158
+ className: "text-xs font-medium text-foreground",
2159
+ children
2160
+ }
2161
+ );
2162
+ }
2163
+ function PanelInput2(props) {
2164
+ const { className, ...rest } = props;
2165
+ return /* @__PURE__ */ jsx(
2166
+ "input",
2167
+ {
2168
+ ...rest,
2169
+ className: cn(
2170
+ "h-8 w-full rounded-md border border-input bg-background px-2 text-xs text-foreground outline-none focus:ring-2 focus:ring-ring",
2171
+ className
2172
+ )
2173
+ }
2174
+ );
2175
+ }
2176
+ function PanelSelect2({
2177
+ title,
2178
+ value,
2179
+ onChange,
2180
+ options,
2181
+ placeholder = "Select\u2026"
2182
+ }) {
2183
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
2184
+ /* @__PURE__ */ jsx(PanelLabel, { children: title }),
2185
+ /* @__PURE__ */ jsxs(
2186
+ "select",
2187
+ {
2188
+ className: "h-8 w-full rounded-md border border-input bg-background px-2 text-xs text-foreground outline-none focus:ring-2 focus:ring-ring",
2189
+ value,
2190
+ onChange: (e) => onChange(e.target.value),
2191
+ children: [
2192
+ !value && /* @__PURE__ */ jsx("option", { value: "", disabled: true, children: placeholder }),
2193
+ options.map((option) => /* @__PURE__ */ jsx("option", { value: option.value, children: option.label }, option.value))
2194
+ ]
2195
+ }
2196
+ )
2197
+ ] });
2198
+ }
2199
+ function AccordionSection({
2200
+ title,
2201
+ children,
2202
+ defaultOpen = false
2203
+ }) {
2204
+ return /* @__PURE__ */ jsxs("details", { className: "group rounded-md border", open: defaultOpen, children: [
2205
+ /* @__PURE__ */ jsxs("summary", { className: "flex cursor-pointer select-none items-center justify-between rounded-t-md px-2 py-3 text-xs font-semibold transition-colors hover:bg-muted group-open:border-b [&::-webkit-details-marker]:hidden", children: [
2206
+ title,
2207
+ /* @__PURE__ */ jsx(ChevronDown, { className: "h-4 w-4 text-muted-foreground transition-transform group-open:rotate-180" })
2208
+ ] }),
2209
+ /* @__PURE__ */ jsx("div", { className: "px-2 pb-4 pt-2", children })
2210
+ ] });
2211
+ }
2212
+
2213
+ // src/builder/theme-editor/tailwind-to-human-readable.ts
2214
+ var paddingOptions = {
2215
+ x: [
2216
+ { label: "None", value: "px-0" },
2217
+ { label: "Extra Small", value: "px-4" },
2218
+ { label: "Small", value: "px-6" },
2219
+ { label: "Medium", value: "px-8" },
2220
+ { label: "Large", value: "px-10" },
2221
+ { label: "Extra Large", value: "px-12" },
2222
+ { label: "2X Large", value: "px-16" },
2223
+ { label: "3X Large", value: "px-20" },
2224
+ { label: "4X Large", value: "px-24" },
2225
+ { label: "5X Large", value: "px-32" },
2226
+ { label: "6X Large", value: "px-40" },
2227
+ { label: "7X Large", value: "px-48" },
2228
+ { label: "8X Large", value: "px-56" },
2229
+ { label: "9X Large", value: "px-64" }
2230
+ ],
2231
+ y: [
2232
+ { label: "None", value: "py-0" },
2233
+ { label: "Extra Small", value: "py-4" },
2234
+ { label: "Small", value: "py-6" },
2235
+ { label: "Medium", value: "py-8" },
2236
+ { label: "Large", value: "py-10" },
2237
+ { label: "Extra Large", value: "py-12" },
2238
+ { label: "2X Large", value: "py-16" },
2239
+ { label: "3X Large", value: "py-20" },
2240
+ { label: "4X Large", value: "py-24" },
2241
+ { label: "5X Large", value: "py-32" },
2242
+ { label: "6X Large", value: "py-40" },
2243
+ { label: "7X Large", value: "py-48" },
2244
+ { label: "8X Large", value: "py-56" },
2245
+ { label: "9X Large", value: "py-64" }
2246
+ ]
2247
+ };
2248
+ var borderWidthOptions = [
2249
+ { label: "None", value: "border-0" },
2250
+ { label: "Light", value: "border" },
2251
+ { label: "Medium", value: "border-2" },
2252
+ { label: "Heavy", value: "border-4" },
2253
+ { label: "Extra Heavy", value: "border-8" }
2254
+ ];
2255
+ var borderStyleOptions = [
2256
+ { label: "None", value: "border-none" },
2257
+ { label: "Solid", value: "border-solid" },
2258
+ { label: "Dashed", value: "border-dashed" },
2259
+ { label: "Dotted", value: "border-dotted" },
2260
+ { label: "Double", value: "border-double" },
2261
+ { label: "Hidden", value: "border-hidden" }
2262
+ ];
2263
+ var borderRadiusOptions = [
2264
+ { label: "None", value: "rounded-none" },
2265
+ { label: "Small", value: "rounded-sm" },
2266
+ { label: "Default", value: "rounded" },
2267
+ { label: "Medium", value: "rounded-md" },
2268
+ { label: "Large", value: "rounded-lg" },
2269
+ { label: "Extra Large", value: "rounded-xl" },
2270
+ { label: "2X Large", value: "rounded-2xl" },
2271
+ { label: "3X Large", value: "rounded-3xl" },
2272
+ { label: "Full", value: "rounded-full" }
2273
+ ];
2274
+ var shadowOptions = [
2275
+ { label: "None", value: "shadow-none" },
2276
+ { label: "Extra Small", value: "shadow-2xs" },
2277
+ { label: "Small", value: "shadow-xs" },
2278
+ { label: "Default", value: "shadow-sm" },
2279
+ { label: "Medium", value: "shadow-md" },
2280
+ { label: "Large", value: "shadow-lg" },
2281
+ { label: "Extra Large", value: "shadow-xl" },
2282
+ { label: "2X Large", value: "shadow-2xl" }
2283
+ ];
2284
+ var fontSizeOptions = [
2285
+ { label: "Extra Small", value: "text-xs" },
2286
+ { label: "Small", value: "text-sm" },
2287
+ { label: "Base", value: "text-base" },
2288
+ { label: "Large", value: "text-lg" },
2289
+ { label: "Extra Large", value: "text-xl" },
2290
+ { label: "2X Large", value: "text-2xl" },
2291
+ { label: "3X Large", value: "text-3xl" },
2292
+ { label: "4X Large", value: "text-4xl" },
2293
+ { label: "5X Large", value: "text-5xl" },
2294
+ { label: "6X Large", value: "text-6xl" },
2295
+ { label: "7X Large", value: "text-7xl" },
2296
+ { label: "8X Large", value: "text-8xl" },
2297
+ { label: "9X Large", value: "text-9xl" }
2298
+ ];
2299
+ var fontWeightOptions = [
2300
+ { label: "Thin", value: "font-thin" },
2301
+ { label: "Extra Light", value: "font-extralight" },
2302
+ { label: "Light", value: "font-light" },
2303
+ { label: "Normal", value: "font-normal" },
2304
+ { label: "Medium", value: "font-medium" },
2305
+ { label: "Semi Bold", value: "font-semibold" },
2306
+ { label: "Bold", value: "font-bold" },
2307
+ { label: "Extra Bold", value: "font-extrabold" },
2308
+ { label: "Black", value: "font-black" }
2309
+ ];
2310
+ var lineHeightOptions = [
2311
+ { label: "None", value: "leading-none" },
2312
+ { label: "Tight", value: "leading-tight" },
2313
+ { label: "Snug", value: "leading-snug" },
2314
+ { label: "Normal", value: "leading-normal" },
2315
+ { label: "Relaxed", value: "leading-relaxed" },
2316
+ { label: "Loose", value: "leading-loose" }
2317
+ ];
2318
+ var letterSpacingOptions = [
2319
+ { label: "Tighter", value: "tracking-tighter" },
2320
+ { label: "Tight", value: "tracking-tight" },
2321
+ { label: "Normal", value: "tracking-normal" },
2322
+ { label: "Wide", value: "tracking-wide" },
2323
+ { label: "Wider", value: "tracking-wider" },
2324
+ { label: "Widest", value: "tracking-widest" }
2325
+ ];
2326
+ var textTransformOptions = [
2327
+ { label: "None", value: "normal-case" },
2328
+ { label: "Uppercase", value: "uppercase" },
2329
+ { label: "Lowercase", value: "lowercase" },
2330
+ { label: "Capitalize", value: "capitalize" }
2331
+ ];
2332
+ var fontFamilyOptions = {
2333
+ Sans: [
2334
+ { label: "Inter", value: "font-inter" },
2335
+ { label: "Open Sans", value: "font-open-sans" },
2336
+ { label: "Source Sans 3", value: "font-source-sans-3" },
2337
+ { label: "Noto Sans", value: "font-noto-sans" },
2338
+ { label: "Roboto", value: "font-roboto" },
2339
+ { label: "Mulish", value: "font-mulish" },
2340
+ { label: "Nunito", value: "font-nunito" },
2341
+ { label: "Work Sans", value: "font-work-sans" }
2342
+ ],
2343
+ Serif: [
2344
+ { label: "Merriweather", value: "font-merriweather" },
2345
+ { label: "Alegreya", value: "font-alegreya" },
2346
+ { label: "Playfair Display", value: "font-playfair-display" },
2347
+ { label: "Roboto Slab", value: "font-roboto-slab" },
2348
+ { label: "Source Serif 4", value: "font-source-serif-4" }
2349
+ ],
2350
+ Display: [
2351
+ { label: "Montserrat", value: "font-montserrat" },
2352
+ { label: "Poppins", value: "font-poppins" },
2353
+ { label: "Raleway", value: "font-raleway" },
2354
+ { label: "Rubik", value: "font-rubik" },
2355
+ { label: "Oswald", value: "font-oswald" },
2356
+ { label: "Bebas Neue", value: "font-bebas-neue" }
2357
+ ],
2358
+ Modern: [
2359
+ { label: "Lato", value: "font-lato" },
2360
+ { label: "PT Sans", value: "font-pt-sans" },
2361
+ { label: "Quicksand", value: "font-quicksand" }
2362
+ ]
2363
+ };
2364
+ function TypographySelector({
2365
+ value,
2366
+ onChange
2367
+ }) {
2368
+ const update = (patch) => onChange({ ...value, ...patch });
2369
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-6 p-2", children: [
2370
+ /* @__PURE__ */ jsx("div", { className: "h-[100px] overflow-auto rounded-lg border p-2", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col space-y-2", children: [
2371
+ /* @__PURE__ */ jsx(PanelLabel, { children: "Preview" }),
2372
+ /* @__PURE__ */ jsx(
2373
+ "p",
2374
+ {
2375
+ className: cn(
2376
+ value?.fontFamily || "",
2377
+ value?.fontSize || "",
2378
+ value?.fontWeight || "",
2379
+ value?.lineHeight || "",
2380
+ value?.letterSpacing || "",
2381
+ value?.textTransform || "normal-case"
2382
+ ),
2383
+ children: "The quick brown fox jumps over the lazy dog"
2384
+ }
2385
+ )
2386
+ ] }) }),
2387
+ /* @__PURE__ */ jsxs("div", { className: "space-y-6", children: [
2388
+ /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
2389
+ /* @__PURE__ */ jsx(PanelLabel, { children: "Font Family" }),
2390
+ /* @__PURE__ */ jsxs(
2391
+ "select",
2392
+ {
2393
+ className: "h-8 w-full rounded-md border border-input bg-background px-2 text-xs text-foreground outline-none focus:ring-2 focus:ring-ring",
2394
+ value: value?.fontFamily || "",
2395
+ onChange: (e) => update({ fontFamily: e.target.value }),
2396
+ children: [
2397
+ !value?.fontFamily && /* @__PURE__ */ jsx("option", { value: "", disabled: true, children: "Select font family" }),
2398
+ Object.entries(fontFamilyOptions).map(
2399
+ ([category, fonts]) => /* @__PURE__ */ jsx("optgroup", { label: category, children: fonts.map((font) => /* @__PURE__ */ jsx(
2400
+ "option",
2401
+ {
2402
+ value: font.value,
2403
+ children: font.label
2404
+ },
2405
+ font.value
2406
+ )) }, category)
2407
+ )
2408
+ ]
2409
+ }
2410
+ )
2411
+ ] }),
2412
+ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-4", children: [
2413
+ /* @__PURE__ */ jsx(
2414
+ PanelSelect2,
2415
+ {
2416
+ title: "Font Size",
2417
+ value: value?.fontSize || "",
2418
+ onChange: (v) => update({ fontSize: v }),
2419
+ options: fontSizeOptions,
2420
+ placeholder: "Select size"
2421
+ }
2422
+ ),
2423
+ /* @__PURE__ */ jsx(
2424
+ PanelSelect2,
2425
+ {
2426
+ title: "Font Weight",
2427
+ value: value?.fontWeight || "",
2428
+ onChange: (v) => update({
2429
+ fontWeight: v
2430
+ }),
2431
+ options: fontWeightOptions,
2432
+ placeholder: "Select weight"
2433
+ }
2434
+ )
2435
+ ] }),
2436
+ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-4", children: [
2437
+ /* @__PURE__ */ jsx(
2438
+ PanelSelect2,
2439
+ {
2440
+ title: "Line Height",
2441
+ value: value?.lineHeight || "",
2442
+ onChange: (v) => update({
2443
+ lineHeight: v
2444
+ }),
2445
+ options: lineHeightOptions,
2446
+ placeholder: "Select line height"
2447
+ }
2448
+ ),
2449
+ /* @__PURE__ */ jsx(
2450
+ PanelSelect2,
2451
+ {
2452
+ title: "Letter Spacing",
2453
+ value: value?.letterSpacing || "",
2454
+ onChange: (v) => update({
2455
+ letterSpacing: v
2456
+ }),
2457
+ options: letterSpacingOptions,
2458
+ placeholder: "Select spacing"
2459
+ }
2460
+ )
2461
+ ] }),
2462
+ /* @__PURE__ */ jsx("div", { className: "grid grid-cols-2 gap-4", children: /* @__PURE__ */ jsx(
2463
+ PanelSelect2,
2464
+ {
2465
+ title: "Text Transform",
2466
+ value: value?.textTransform || "normal-case",
2467
+ onChange: (v) => update({
2468
+ textTransform: v
2469
+ }),
2470
+ options: textTransformOptions,
2471
+ placeholder: "Select transform"
2472
+ }
2473
+ ) })
2474
+ ] })
2475
+ ] });
2476
+ }
2477
+ var interactiveDisplayNames = {
2478
+ button: "Button",
2479
+ link: "Link",
2480
+ card: "Card",
2481
+ input: "Input"
2482
+ };
2483
+ function InteractiveSelector({
2484
+ type,
2485
+ theme,
2486
+ onChange
2487
+ }) {
2488
+ const value = theme.interactives[type];
2489
+ const updateInteractive = (patch) => {
2490
+ onChange({
2491
+ ...theme,
2492
+ interactives: {
2493
+ ...theme.interactives,
2494
+ [type]: {
2495
+ ...value,
2496
+ ...patch
2497
+ }
2498
+ }
2499
+ });
2500
+ };
2501
+ const renderDemo = () => {
2502
+ switch (type) {
2503
+ case "button":
2504
+ return /* @__PURE__ */ jsx(Button2, { theme, children: "Preview" });
2505
+ case "link":
2506
+ return /* @__PURE__ */ jsx(Link, { theme, children: "Demo Link" });
2507
+ case "card":
2508
+ return /* @__PURE__ */ jsx(PageCard, { theme, children: /* @__PURE__ */ jsxs(PageCardContent, { theme, children: [
2509
+ /* @__PURE__ */ jsx(PageCardHeader, { theme, children: "Card Title" }),
2510
+ /* @__PURE__ */ jsx("div", { children: "Card content goes here" })
2511
+ ] }) });
2512
+ case "input":
2513
+ return /* @__PURE__ */ jsx(
2514
+ Input,
2515
+ {
2516
+ theme,
2517
+ type: "text",
2518
+ placeholder: "Demo Input"
2519
+ }
2520
+ );
2521
+ default:
2522
+ return null;
2523
+ }
2524
+ };
2525
+ const renderPaddingConfig = (allowedPaddingX, allowedPaddingY) => {
2526
+ const filteredPaddingX = allowedPaddingX ? paddingOptions.x.filter(
2527
+ (option) => allowedPaddingX.includes(option.value)
2528
+ ) : paddingOptions.x;
2529
+ const filteredPaddingY = allowedPaddingY ? paddingOptions.y.filter(
2530
+ (option) => allowedPaddingY.includes(option.value)
2531
+ ) : paddingOptions.y;
2532
+ return /* @__PURE__ */ jsx(AccordionSection, { title: "Padding", children: /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-4", children: [
2533
+ /* @__PURE__ */ jsx(
2534
+ PanelSelect2,
2535
+ {
2536
+ title: "Padding X",
2537
+ value: value?.padding?.x || "",
2538
+ onChange: (newValue) => updateInteractive({
2539
+ padding: {
2540
+ ...value?.padding,
2541
+ x: newValue
2542
+ }
2543
+ }),
2544
+ options: filteredPaddingX,
2545
+ placeholder: "Select padding"
2546
+ }
2547
+ ),
2548
+ /* @__PURE__ */ jsx(
2549
+ PanelSelect2,
2550
+ {
2551
+ title: "Padding Y",
2552
+ value: value?.padding?.y || "",
2553
+ onChange: (newValue) => updateInteractive({
2554
+ padding: {
2555
+ ...value?.padding,
2556
+ y: newValue
2557
+ }
2558
+ }),
2559
+ options: filteredPaddingY,
2560
+ placeholder: "Select padding"
2561
+ }
2562
+ )
2563
+ ] }) });
2564
+ };
2565
+ const renderBorderConfig = () => /* @__PURE__ */ jsx(AccordionSection, { title: "Border", children: /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
2566
+ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-4", children: [
2567
+ /* @__PURE__ */ jsx(
2568
+ PanelSelect2,
2569
+ {
2570
+ title: "Border Radius",
2571
+ value: value?.border?.radius || "",
2572
+ onChange: (newValue) => updateInteractive({
2573
+ border: {
2574
+ ...value?.border,
2575
+ radius: newValue
2576
+ }
2577
+ }),
2578
+ options: borderRadiusOptions,
2579
+ placeholder: "Select radius"
2580
+ }
2581
+ ),
2582
+ /* @__PURE__ */ jsx(
2583
+ PanelSelect2,
2584
+ {
2585
+ title: "Border Style",
2586
+ value: value?.border?.style || "",
2587
+ onChange: (newValue) => updateInteractive({
2588
+ border: {
2589
+ ...value?.border,
2590
+ style: newValue
2591
+ }
2592
+ }),
2593
+ options: borderStyleOptions,
2594
+ placeholder: "Select style"
2595
+ }
2596
+ )
2597
+ ] }),
2598
+ /* @__PURE__ */ jsx("div", { className: "grid grid-cols-2 gap-4", children: /* @__PURE__ */ jsx(
2599
+ PanelSelect2,
2600
+ {
2601
+ title: "Border Width",
2602
+ value: value?.border?.width || "",
2603
+ onChange: (newValue) => updateInteractive({
2604
+ border: {
2605
+ ...value?.border,
2606
+ width: newValue
2607
+ }
2608
+ }),
2609
+ options: borderWidthOptions,
2610
+ placeholder: "Select width"
2611
+ }
2612
+ ) })
2613
+ ] }) });
2614
+ const renderBoxShadowConfig = () => {
2615
+ if (type === "link") {
2616
+ return null;
2617
+ }
2618
+ return /* @__PURE__ */ jsx(AccordionSection, { title: "Shadow", children: /* @__PURE__ */ jsx(
2619
+ PanelSelect2,
2620
+ {
2621
+ title: "Shadow",
2622
+ value: value?.shadow || "",
2623
+ onChange: (newValue) => updateInteractive({ shadow: newValue }),
2624
+ options: shadowOptions,
2625
+ placeholder: "Select shadow"
2626
+ }
2627
+ ) });
2628
+ };
2629
+ const renderTextShadowConfig = () => {
2630
+ if (type !== "link") {
2631
+ return null;
2632
+ }
2633
+ return /* @__PURE__ */ jsx(AccordionSection, { title: "Text Shadow", children: /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
2634
+ /* @__PURE__ */ jsx(PanelLabel, { children: "Text Shadow" }),
2635
+ /* @__PURE__ */ jsx(
2636
+ PanelInput2,
2637
+ {
2638
+ value: value?.textShadow || "",
2639
+ placeholder: "0px 0px 0px 0px rgba(0, 0, 0, 0.1)",
2640
+ onChange: (e) => updateInteractive({ textShadow: e.target.value })
2641
+ }
2642
+ )
2643
+ ] }) });
2644
+ };
2645
+ const renderCustomClassesConfig = () => /* @__PURE__ */ jsx(AccordionSection, { title: "Advanced", children: /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
2646
+ /* @__PURE__ */ jsx(PanelLabel, { children: "Custom classes" }),
2647
+ /* @__PURE__ */ jsx(
2648
+ PanelInput2,
2649
+ {
2650
+ value: value?.custom || "",
2651
+ onChange: (e) => updateInteractive({ custom: e.target.value }),
2652
+ placeholder: "Enter Tailwind classes"
2653
+ }
2654
+ ),
2655
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-xs text-muted-foreground", children: [
2656
+ /* @__PURE__ */ jsx(Info, { className: "h-4 w-4 shrink-0" }),
2657
+ /* @__PURE__ */ jsx("span", { children: "Use any supported Tailwind class here." })
2658
+ ] })
2659
+ ] }) });
2660
+ const renderConfig = () => {
2661
+ switch (type) {
2662
+ case "button":
2663
+ case "input":
2664
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
2665
+ renderPaddingConfig(
2666
+ ["px-0", "px-4", "px-6", "px-8", "px-10"],
2667
+ ["py-0", "py-4", "py-6", "py-8", "py-10"]
2668
+ ),
2669
+ renderBorderConfig(),
2670
+ renderBoxShadowConfig(),
2671
+ renderCustomClassesConfig()
2672
+ ] });
2673
+ case "link":
2674
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
2675
+ renderTextShadowConfig(),
2676
+ renderCustomClassesConfig()
2677
+ ] });
2678
+ case "card":
2679
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
2680
+ renderPaddingConfig([
2681
+ "px-0",
2682
+ "px-4",
2683
+ "px-6",
2684
+ "px-8",
2685
+ "px-10",
2686
+ "px-12",
2687
+ "px-16",
2688
+ "px-20",
2689
+ "px-24",
2690
+ "px-32"
2691
+ ]),
2692
+ renderBorderConfig(),
2693
+ renderBoxShadowConfig(),
2694
+ renderCustomClassesConfig()
2695
+ ] });
2696
+ default:
2697
+ return null;
2698
+ }
2699
+ };
2700
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-6 p-2", children: [
2701
+ /* @__PURE__ */ jsx("div", { className: "min-h-[100px] overflow-auto rounded-lg border p-2", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col space-y-2", children: [
2702
+ /* @__PURE__ */ jsx(PanelLabel, { children: "Preview" }),
2703
+ renderDemo()
2704
+ ] }) }),
2705
+ /* @__PURE__ */ jsx("div", { className: "space-y-4", children: renderConfig() })
2706
+ ] });
2707
+ }
2708
+ var structureDisplayNames = {
2709
+ page: "Page",
2710
+ section: "Section"
2711
+ };
2712
+ function StructureSelector({
2713
+ type,
2714
+ theme,
2715
+ onChange
2716
+ }) {
2717
+ if (type === "page") {
2718
+ const pageValue = theme.structure.page;
2719
+ return /* @__PURE__ */ jsx("div", { className: "space-y-6 p-2", children: /* @__PURE__ */ jsx(
2720
+ MaxWidthSelector,
2721
+ {
2722
+ value: pageValue?.width,
2723
+ onChange: (width) => {
2724
+ if (!width) return;
2725
+ onChange({
2726
+ ...theme,
2727
+ structure: {
2728
+ ...theme.structure,
2729
+ page: { width }
2730
+ }
2731
+ });
2732
+ }
2733
+ }
2734
+ ) });
2735
+ }
2736
+ const sectionValue = theme.structure.section;
2737
+ const updatePadding = (patch) => {
2738
+ onChange({
2739
+ ...theme,
2740
+ structure: {
2741
+ ...theme.structure,
2742
+ section: {
2743
+ ...sectionValue,
2744
+ padding: {
2745
+ ...sectionValue?.padding,
2746
+ ...patch
2747
+ }
2748
+ }
2749
+ }
2750
+ });
2751
+ };
2752
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-6 p-2", children: [
2753
+ /* @__PURE__ */ jsx(
2754
+ PanelSelect2,
2755
+ {
2756
+ title: "Horizontal Padding",
2757
+ value: sectionValue?.padding?.x || "",
2758
+ onChange: (x) => updatePadding({ x }),
2759
+ options: paddingOptions.x,
2760
+ placeholder: "Select padding"
2761
+ }
2762
+ ),
2763
+ /* @__PURE__ */ jsx(
2764
+ VerticalPaddingSelector,
2765
+ {
2766
+ value: sectionValue?.padding?.y,
2767
+ onChange: (y) => {
2768
+ if (!y) return;
2769
+ updatePadding({ y });
2770
+ }
2771
+ }
2772
+ )
2773
+ ] });
2774
+ }
2775
+ var sections = [
2776
+ {
2777
+ id: "colors",
2778
+ label: "Colors",
2779
+ icon: /* @__PURE__ */ jsx(Palette, { className: "h-4 w-4" })
2780
+ },
2781
+ {
2782
+ id: "typography",
2783
+ label: "Typography",
2784
+ icon: /* @__PURE__ */ jsx(Type, { className: "h-4 w-4" })
2785
+ },
2786
+ {
2787
+ id: "interactives",
2788
+ label: "Interactives",
2789
+ icon: /* @__PURE__ */ jsx(SquareMousePointer, { className: "h-4 w-4" })
2790
+ },
2791
+ {
2792
+ id: "structure",
2793
+ label: "Structure",
2794
+ icon: /* @__PURE__ */ jsx(Columns3, { className: "h-4 w-4" })
2795
+ }
2796
+ ];
2797
+ var typographyDisplayNames = {
2798
+ header1: "Header 1",
2799
+ header2: "Header 2",
2800
+ header3: "Header 3",
2801
+ header4: "Header 4",
2802
+ preheader: "Preheader",
2803
+ subheader1: "Subheader 1",
2804
+ subheader2: "Subheader 2",
2805
+ text1: "Text 1",
2806
+ text2: "Text 2",
2807
+ caption: "Caption",
2808
+ link: "Link",
2809
+ button: "Button Text",
2810
+ input: "Input Text"
2811
+ };
2812
+ var typographyCategories = [
2813
+ {
2814
+ name: "Headers",
2815
+ items: ["header1", "header2", "header3", "header4", "preheader"]
2816
+ },
2817
+ {
2818
+ name: "Subheaders",
2819
+ items: ["subheader1", "subheader2"]
2820
+ },
2821
+ {
2822
+ name: "Body Text",
2823
+ items: ["text1", "text2", "caption"]
2824
+ },
2825
+ {
2826
+ name: "Interactive Elements",
2827
+ items: ["link", "button", "input"]
2828
+ }
2829
+ ];
2830
+ var colorCategories = [
2831
+ {
2832
+ key: "primary",
2833
+ label: "Primary Colors",
2834
+ colors: [
2835
+ { name: "primary", displayName: "Primary" },
2836
+ { name: "primaryForeground", displayName: "Primary Foreground" }
2837
+ ]
2838
+ },
2839
+ {
2840
+ key: "secondary",
2841
+ label: "Secondary Colors",
2842
+ colors: [
2843
+ { name: "secondary", displayName: "Secondary" },
2844
+ {
2845
+ name: "secondaryForeground",
2846
+ displayName: "Secondary Foreground"
2847
+ }
2848
+ ]
2849
+ },
2850
+ {
2851
+ key: "accent",
2852
+ label: "Accent Colors",
2853
+ colors: [
2854
+ { name: "accent", displayName: "Accent" },
2855
+ { name: "accentForeground", displayName: "Accent Foreground" }
2856
+ ]
2857
+ },
2858
+ {
2859
+ key: "base",
2860
+ label: "Base Colors",
2861
+ colors: [
2862
+ { name: "background", displayName: "Background" },
2863
+ { name: "foreground", displayName: "Foreground" }
2864
+ ]
2865
+ },
2866
+ {
2867
+ key: "card",
2868
+ label: "Card Colors",
2869
+ colors: [
2870
+ { name: "card", displayName: "Card" },
2871
+ { name: "cardForeground", displayName: "Card Foreground" }
2872
+ ]
2873
+ },
2874
+ {
2875
+ key: "popover",
2876
+ label: "Popover Colors",
2877
+ colors: [
2878
+ { name: "popover", displayName: "Popover" },
2879
+ { name: "popoverForeground", displayName: "Popover Foreground" }
2880
+ ]
2881
+ },
2882
+ {
2883
+ key: "muted",
2884
+ label: "Muted Colors",
2885
+ colors: [
2886
+ { name: "muted", displayName: "Muted" },
2887
+ { name: "mutedForeground", displayName: "Muted Foreground" }
2888
+ ]
2889
+ },
2890
+ {
2891
+ key: "border",
2892
+ label: "Border & Input Colors",
2893
+ colors: [
2894
+ { name: "border", displayName: "Border" },
2895
+ { name: "input", displayName: "Input" },
2896
+ { name: "ring", displayName: "Ring" }
2897
+ ]
2898
+ },
2899
+ {
2900
+ key: "destructive",
2901
+ label: "Destructive Colors",
2902
+ colors: [{ name: "destructive", displayName: "Destructive" }]
2903
+ },
2904
+ {
2905
+ key: "shadows",
2906
+ label: "Shadow Styles",
2907
+ colors: [
2908
+ { name: "shadow2xs", displayName: "Shadow 2XS" },
2909
+ { name: "shadowXs", displayName: "Shadow XS" },
2910
+ { name: "shadowSm", displayName: "Shadow SM" },
2911
+ { name: "shadowMd", displayName: "Shadow MD" },
2912
+ { name: "shadowLg", displayName: "Shadow LG" },
2913
+ { name: "shadowXl", displayName: "Shadow XL" },
2914
+ { name: "shadow2xl", displayName: "Shadow 2XL" }
2915
+ ]
2916
+ }
2917
+ ];
2918
+ var paletteColorOrder = [
2919
+ "primary",
2920
+ "accent",
2921
+ "secondary",
2922
+ "border"
2923
+ ];
2924
+ function capitalize(value) {
2925
+ return value.charAt(0).toUpperCase() + value.slice(1);
2926
+ }
2927
+ function copyName(baseName, taken) {
2928
+ const base = `${baseName} - Copy`;
2929
+ if (!taken.includes(base)) return base;
2930
+ let counter = 2;
2931
+ while (taken.includes(`${base} ${counter}`)) counter++;
2932
+ return `${base} ${counter}`;
2933
+ }
2934
+ function ThemeEditor({
2935
+ theme,
2936
+ appliedThemeId,
2937
+ systemThemes,
2938
+ customThemes,
2939
+ colorMode,
2940
+ onColorModeChange,
2941
+ onPreview,
2942
+ onApply,
2943
+ onCustomThemesChange
2944
+ }) {
2945
+ const [navigationStack, setNavigationStack] = useState(
2946
+ []
2947
+ );
2948
+ const [collapsedCategories, setCollapsedCategories] = useState(() => {
2949
+ const initial = {};
2950
+ typographyCategories.forEach((category) => {
2951
+ initial[category.name] = true;
2952
+ });
2953
+ return initial;
2954
+ });
2955
+ const navigateTo = useCallback((item) => {
2956
+ setNavigationStack((prev) => [...prev, item]);
2957
+ }, []);
2958
+ const navigateBack = useCallback(() => {
2959
+ setNavigationStack((prev) => prev.slice(0, -1));
2960
+ }, []);
2961
+ const toggleCategory = useCallback((categoryName) => {
2962
+ setCollapsedCategories((prev) => ({
2963
+ ...prev,
2964
+ [categoryName]: !prev[categoryName]
2965
+ }));
2966
+ }, []);
2967
+ const applyStyle = useCallback(
2968
+ (style) => {
2969
+ const updated = { ...theme, theme: style };
2970
+ onApply(updated);
2971
+ onCustomThemesChange(
2972
+ customThemes.map((t) => t.id === updated.id ? updated : t)
2973
+ );
2974
+ },
2975
+ [theme, customThemes, onApply, onCustomThemesChange]
2976
+ );
2977
+ const startEditing = useCallback(
2978
+ (themeItem, isSystem) => {
2979
+ let target = themeItem;
2980
+ if (isSystem) {
2981
+ const takenNames = [...systemThemes, ...customThemes].map(
2982
+ (t) => t.name
2983
+ );
2984
+ target = {
2985
+ id: generateUniqueId(),
2986
+ name: copyName(themeItem.name, takenNames),
2987
+ theme: JSON.parse(JSON.stringify(themeItem.theme))
2988
+ };
2989
+ onCustomThemesChange([...customThemes, target]);
2990
+ }
2991
+ onApply(target);
2992
+ setNavigationStack([{ id: "categories", label: target.name }]);
2993
+ },
2994
+ [systemThemes, customThemes, onApply, onCustomThemesChange]
2995
+ );
2996
+ const renderDrillButton = (label, onClick) => /* @__PURE__ */ jsxs(
2997
+ "button",
2998
+ {
2999
+ onClick,
3000
+ className: "group flex w-full items-center justify-between rounded-md px-3 py-2 text-xs transition-colors hover:bg-muted",
3001
+ children: [
3002
+ /* @__PURE__ */ jsx("span", { className: "transition-colors group-hover:text-foreground", children: label }),
3003
+ /* @__PURE__ */ jsx(ChevronRight, { className: "h-4 w-4 text-muted-foreground transition-colors group-hover:text-foreground" })
3004
+ ]
3005
+ },
3006
+ label
3007
+ );
3008
+ const renderThemeCard = (themeItem, isSystem) => /* @__PURE__ */ jsx(
3009
+ ThemeCard,
3010
+ {
3011
+ name: themeItem.name,
3012
+ palette: paletteColorOrder.map((key) => themeItem.theme.colors.light[key]).filter(Boolean),
3013
+ selected: themeItem.id === theme.id,
3014
+ active: themeItem.id === appliedThemeId,
3015
+ onPreview: () => onPreview(themeItem),
3016
+ onUse: () => onApply(themeItem),
3017
+ onEdit: () => startEditing(themeItem, isSystem)
3018
+ },
3019
+ themeItem.id
3020
+ );
3021
+ const renderRootView = () => /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
3022
+ /* @__PURE__ */ jsx("div", { className: "text-sm font-medium text-muted-foreground", children: "System Themes" }),
3023
+ /* @__PURE__ */ jsx("div", { className: "space-y-2", children: systemThemes.map(
3024
+ (themeItem) => renderThemeCard(themeItem, true)
3025
+ ) }),
3026
+ /* @__PURE__ */ jsx("hr", { className: "my-4 border-muted" }),
3027
+ /* @__PURE__ */ jsx("div", { className: "text-sm font-medium text-muted-foreground", children: "Custom Themes" }),
3028
+ /* @__PURE__ */ jsx("div", { className: "space-y-2", children: customThemes.length === 0 ? /* @__PURE__ */ jsx("div", { className: "text-sm text-muted-foreground", children: "No custom themes yet" }) : customThemes.map(
3029
+ (themeItem) => renderThemeCard(themeItem, false)
3030
+ ) })
3031
+ ] });
3032
+ const renderCategoriesView = () => /* @__PURE__ */ jsx("div", { className: "space-y-1", children: sections.map((section) => /* @__PURE__ */ jsxs(
3033
+ "button",
3034
+ {
3035
+ onClick: () => navigateTo({
3036
+ id: section.id,
3037
+ label: section.label
3038
+ }),
3039
+ className: "group flex w-full items-center justify-between rounded-md px-3 py-2 text-sm transition-colors hover:bg-muted",
3040
+ children: [
3041
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
3042
+ /* @__PURE__ */ jsx("div", { className: "text-muted-foreground transition-colors group-hover:text-foreground", children: section.icon }),
3043
+ /* @__PURE__ */ jsx("span", { className: "transition-colors group-hover:text-foreground", children: section.label })
3044
+ ] }),
3045
+ /* @__PURE__ */ jsx(ChevronRight, { className: "h-4 w-4 text-muted-foreground transition-colors group-hover:text-foreground" })
3046
+ ]
3047
+ },
3048
+ section.id
3049
+ )) });
3050
+ const renderColorsView = () => {
3051
+ const colors = theme.theme.colors[colorMode];
3052
+ const updateColor = (name, value) => {
3053
+ applyStyle({
3054
+ ...theme.theme,
3055
+ colors: {
3056
+ ...theme.theme.colors,
3057
+ [colorMode]: {
3058
+ ...theme.theme.colors[colorMode],
3059
+ [name]: value
3060
+ }
3061
+ }
3062
+ });
3063
+ };
3064
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
3065
+ /* @__PURE__ */ jsx(
3066
+ "div",
3067
+ {
3068
+ role: "tablist",
3069
+ className: "grid w-full grid-cols-2 rounded-md border border-input bg-muted p-0.5",
3070
+ children: ["light", "dark"].map((mode) => /* @__PURE__ */ jsx(
3071
+ "button",
3072
+ {
3073
+ type: "button",
3074
+ role: "tab",
3075
+ "aria-selected": colorMode === mode,
3076
+ className: cn(
3077
+ "rounded-sm px-2 py-1 text-xs font-medium transition-colors",
3078
+ colorMode === mode ? "bg-background text-foreground shadow-sm" : "text-muted-foreground hover:text-foreground"
3079
+ ),
3080
+ onClick: () => onColorModeChange(mode),
3081
+ children: capitalize(mode)
3082
+ },
3083
+ mode
3084
+ ))
3085
+ }
3086
+ ),
3087
+ /* @__PURE__ */ jsx("div", { className: "space-y-4", children: colorCategories.map((category) => /* @__PURE__ */ jsx(
3088
+ AccordionSection,
3089
+ {
3090
+ title: category.label,
3091
+ children: /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-3", children: category.colors.map(
3092
+ (color) => category.key === "shadows" ? /* @__PURE__ */ jsxs(
3093
+ "div",
3094
+ {
3095
+ className: "space-y-1.5",
3096
+ children: [
3097
+ /* @__PURE__ */ jsx(PanelLabel, { children: color.displayName }),
3098
+ /* @__PURE__ */ jsx(
3099
+ PanelInput2,
3100
+ {
3101
+ value: colors[color.name],
3102
+ onChange: (e) => updateColor(
3103
+ color.name,
3104
+ e.target.value
3105
+ )
3106
+ }
3107
+ )
3108
+ ]
3109
+ },
3110
+ color.name
3111
+ ) : /* @__PURE__ */ jsx(
3112
+ ColorSelector,
3113
+ {
3114
+ title: color.displayName,
3115
+ value: colors[color.name],
3116
+ onChange: (value) => updateColor(
3117
+ color.name,
3118
+ value ?? ""
3119
+ ),
3120
+ allowReset: false
3121
+ },
3122
+ color.name
3123
+ )
3124
+ ) })
3125
+ },
3126
+ category.key
3127
+ )) })
3128
+ ] });
3129
+ };
3130
+ const renderTypographyList = () => /* @__PURE__ */ jsx("div", { className: "space-y-1", children: typographyCategories.map((category) => /* @__PURE__ */ jsxs("div", { className: "mb-2", children: [
3131
+ /* @__PURE__ */ jsxs(
3132
+ "button",
3133
+ {
3134
+ type: "button",
3135
+ className: "flex w-full items-center rounded px-2 py-1.5 text-sm transition-colors hover:bg-muted",
3136
+ onClick: () => toggleCategory(category.name),
3137
+ children: [
3138
+ /* @__PURE__ */ jsx(
3139
+ ChevronDown,
3140
+ {
3141
+ className: cn(
3142
+ "mr-2 h-4 w-4 transition-transform",
3143
+ collapsedCategories[category.name] ? "rotate-[-90deg]" : "rotate-0"
3144
+ )
3145
+ }
3146
+ ),
3147
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-muted-foreground", children: category.name })
3148
+ ]
3149
+ }
3150
+ ),
3151
+ !collapsedCategories[category.name] && /* @__PURE__ */ jsx("div", { children: category.items.map(
3152
+ (typography) => renderDrillButton(
3153
+ typographyDisplayNames[typography] || capitalize(typography),
3154
+ () => navigateTo({
3155
+ id: typography,
3156
+ label: typographyDisplayNames[typography] || capitalize(typography)
3157
+ })
3158
+ )
3159
+ ) })
3160
+ ] }, category.name)) });
3161
+ const renderInteractivesList = () => /* @__PURE__ */ jsx("div", { className: "space-y-1", children: Object.keys(interactiveDisplayNames).map(
3162
+ (interactive) => renderDrillButton(
3163
+ interactiveDisplayNames[interactive] || capitalize(interactive),
3164
+ () => navigateTo({
3165
+ id: interactive,
3166
+ label: interactiveDisplayNames[interactive] || capitalize(interactive)
3167
+ })
3168
+ )
3169
+ ) });
3170
+ const renderStructureList = () => /* @__PURE__ */ jsx("div", { className: "space-y-1", children: Object.keys(structureDisplayNames).map(
3171
+ (structure) => renderDrillButton(
3172
+ structureDisplayNames[structure] || capitalize(structure),
3173
+ () => navigateTo({
3174
+ id: structure,
3175
+ label: structureDisplayNames[structure] || capitalize(structure)
3176
+ })
3177
+ )
3178
+ ) });
3179
+ const getCurrentView = () => {
3180
+ const currentItem = navigationStack[navigationStack.length - 1];
3181
+ const parentItem = navigationStack[navigationStack.length - 2];
3182
+ if (!currentItem) {
3183
+ return renderRootView();
3184
+ }
3185
+ if (parentItem?.id === "typography" && currentItem.id in theme.theme.typography) {
3186
+ const key = currentItem.id;
3187
+ return /* @__PURE__ */ jsx(
3188
+ TypographySelector,
3189
+ {
3190
+ value: theme.theme.typography[key],
3191
+ onChange: (value) => applyStyle({
3192
+ ...theme.theme,
3193
+ typography: {
3194
+ ...theme.theme.typography,
3195
+ [key]: value
3196
+ }
3197
+ })
3198
+ }
3199
+ );
3200
+ }
3201
+ if (parentItem?.id === "interactives" && currentItem.id in theme.theme.interactives) {
3202
+ return /* @__PURE__ */ jsx(
3203
+ InteractiveSelector,
3204
+ {
3205
+ type: currentItem.id,
3206
+ theme: theme.theme,
3207
+ onChange: applyStyle
3208
+ }
3209
+ );
3210
+ }
3211
+ if (parentItem?.id === "structure" && currentItem.id in theme.theme.structure) {
3212
+ return /* @__PURE__ */ jsx(
3213
+ StructureSelector,
3214
+ {
3215
+ type: currentItem.id,
3216
+ theme: theme.theme,
3217
+ onChange: applyStyle
3218
+ }
3219
+ );
3220
+ }
3221
+ switch (currentItem.id) {
3222
+ case "categories":
3223
+ return renderCategoriesView();
3224
+ case "colors":
3225
+ return renderColorsView();
3226
+ case "typography":
3227
+ return renderTypographyList();
3228
+ case "interactives":
3229
+ return renderInteractivesList();
3230
+ case "structure":
3231
+ return renderStructureList();
3232
+ default:
3233
+ return /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: "Coming soon" });
3234
+ }
3235
+ };
3236
+ return /* @__PURE__ */ jsxs("div", { children: [
3237
+ navigationStack.length === 0 && /* @__PURE__ */ jsx("div", { className: "space-y-4", children: getCurrentView() }),
3238
+ navigationStack.length > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
3239
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 pb-2", children: [
3240
+ /* @__PURE__ */ jsx(
3241
+ "button",
3242
+ {
3243
+ onClick: navigateBack,
3244
+ className: "flex items-center rounded-md p-1 transition-colors hover:bg-muted focus:outline-none",
3245
+ "aria-label": "Back",
3246
+ children: /* @__PURE__ */ jsx(ChevronLeft, { className: "h-4 w-4" })
3247
+ }
3248
+ ),
3249
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-muted-foreground", children: navigationStack[navigationStack.length - 1].label })
3250
+ ] }),
3251
+ /* @__PURE__ */ jsx("div", { className: "py-2", children: getCurrentView() })
3252
+ ] })
3253
+ ] });
3254
+ }
3255
+ function SeoEditor({
3256
+ seo,
3257
+ onChange
3258
+ }) {
3259
+ const update = (next) => onChange({ ...seo, ...next });
3260
+ const imageUrl = seo.socialImage?.file;
3261
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-col", children: [
3262
+ /* @__PURE__ */ jsxs(FieldGroup, { title: "Search results", children: [
3263
+ /* @__PURE__ */ jsx(
3264
+ TextField,
3265
+ {
3266
+ label: "Title",
3267
+ value: seo.title,
3268
+ placeholder: "Page title",
3269
+ onChange: (title) => update({ title })
3270
+ }
3271
+ ),
3272
+ /* @__PURE__ */ jsx(
3273
+ TextField,
3274
+ {
3275
+ label: "Description",
3276
+ multiline: true,
3277
+ value: seo.description,
3278
+ placeholder: "A short summary of this page",
3279
+ onChange: (description) => update({ description })
3280
+ }
3281
+ ),
3282
+ /* @__PURE__ */ jsx(
3283
+ ToggleField,
3284
+ {
3285
+ label: "Search engine visibility",
3286
+ value: seo.robotsAllowed ?? true,
3287
+ onChange: (robotsAllowed) => update({ robotsAllowed })
3288
+ }
3289
+ )
3290
+ ] }),
3291
+ /* @__PURE__ */ jsxs(FieldGroup, { title: "Social card image", children: [
3292
+ /* @__PURE__ */ jsx(
3293
+ TextField,
3294
+ {
3295
+ label: "Image URL",
3296
+ value: imageUrl,
3297
+ placeholder: "https://...",
3298
+ onChange: (file) => update({
3299
+ socialImage: file ? { mediaId: file, file } : void 0
3300
+ })
3301
+ }
3302
+ ),
3303
+ imageUrl && /* @__PURE__ */ jsx(
3304
+ "img",
3305
+ {
3306
+ src: imageUrl,
3307
+ alt: "Social card preview",
3308
+ className: "aspect-[1200/630] w-full rounded-md border border-border object-cover"
3309
+ }
3310
+ ),
3311
+ /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: "Shown when the page is shared on social networks. Recommended size 1200\xD7630." })
3312
+ ] })
3313
+ ] });
3314
+ }
3315
+ function PageBuilder({
3316
+ initialLayout,
3317
+ initialTheme,
3318
+ initialSeo,
3319
+ themes: providedThemes,
3320
+ pageData,
3321
+ onChange,
3322
+ allowThemeEditing = true,
3323
+ linkComponent,
3324
+ className
3325
+ }) {
3326
+ const [layout, setLayout] = useState(
3327
+ () => initialLayout ? ensureRequiredSharedWidgets(initialLayout) : createDefaultLayout()
3328
+ );
3329
+ const [theme, setTheme] = useState(
3330
+ () => initialTheme ?? getDefaultTheme()
3331
+ );
3332
+ const [previewTheme, setPreviewTheme] = useState(null);
3333
+ const paintedTheme = previewTheme ?? theme;
3334
+ const [seo, setSeo] = useState(
3335
+ () => initialSeo ?? { robotsAllowed: true }
3336
+ );
3337
+ const systemThemes = providedThemes?.system ?? themes;
3338
+ const [customThemes, setCustomThemes] = useState(
3339
+ () => providedThemes?.custom ?? []
3340
+ );
3341
+ const [mode, setMode] = useState("system");
3342
+ const [systemPrefersDark, setSystemPrefersDark] = useState(false);
3343
+ const [device, setDevice] = useState("desktop");
3344
+ const [panel, setPanel] = useState(null);
3345
+ const [insertAfter, setInsertAfter] = useState(null);
3346
+ const [movedWidgetId, setMovedWidgetId] = useState(null);
3347
+ const movedTimeoutRef = useRef(null);
3348
+ useEffect(() => {
3349
+ return () => {
3350
+ if (movedTimeoutRef.current) clearTimeout(movedTimeoutRef.current);
3351
+ };
3352
+ }, []);
3353
+ useEffect(() => {
3354
+ const media = window.matchMedia("(prefers-color-scheme: dark)");
3355
+ const apply = () => setSystemPrefersDark(media.matches);
3356
+ apply();
3357
+ media.addEventListener("change", apply);
3358
+ return () => media.removeEventListener("change", apply);
3359
+ }, []);
3360
+ const resolvedMode = resolveColorMode(mode, systemPrefersDark);
3361
+ const onChangeRef = useRef(onChange);
3362
+ onChangeRef.current = onChange;
3363
+ const emitChange = useMemo(
3364
+ () => debounce(
3365
+ (state) => onChangeRef.current?.(state),
3366
+ 500
3367
+ ),
3368
+ []
3369
+ );
3370
+ useEffect(() => {
3371
+ emitChange({ layout, theme, seo, customThemes });
3372
+ }, [layout, theme, seo, customThemes]);
3373
+ const addableBlocks = useMemo(() => getAddableBlocks(), []);
3374
+ function updateWidgetSettings(widgetId, settings) {
3375
+ setLayout(
3376
+ (prev) => prev.map(
3377
+ (widget) => widget.widgetId === widgetId ? { ...widget, settings } : widget
3378
+ )
3379
+ );
3380
+ }
3381
+ function moveWidget(widgetId, direction) {
3382
+ const index = layout.findIndex((w) => w.widgetId === widgetId);
3383
+ if (index === -1 || ["header", "footer"].includes(layout[index].name) || !layout[index].moveable) {
3384
+ return;
3385
+ }
3386
+ const targetIndex = index + direction;
3387
+ if (targetIndex < 0 || targetIndex >= layout.length || ["header", "footer"].includes(layout[targetIndex].name)) {
3388
+ return;
3389
+ }
3390
+ setLayout((prev) => {
3391
+ const next = prev.slice();
3392
+ [next[index], next[targetIndex]] = [next[targetIndex], next[index]];
3393
+ return next;
3394
+ });
3395
+ if (movedTimeoutRef.current) clearTimeout(movedTimeoutRef.current);
3396
+ setMovedWidgetId(widgetId);
3397
+ movedTimeoutRef.current = setTimeout(
3398
+ () => setMovedWidgetId(null),
3399
+ 1500
3400
+ );
3401
+ }
3402
+ function deleteWidget(widgetId) {
3403
+ setLayout((prev) => {
3404
+ const widget = prev.find((item) => item.widgetId === widgetId);
3405
+ if (!widget || !widget.deletable || ["header", "footer"].includes(widget.name)) {
3406
+ return prev;
3407
+ }
3408
+ return prev.filter((item) => item.widgetId !== widgetId);
3409
+ });
3410
+ setPanel(
3411
+ (current) => current?.mode === "block" && current.widgetId === widgetId ? null : current
3412
+ );
3413
+ }
3414
+ function addWidget(name) {
3415
+ const instance = createWidgetInstance(name);
3416
+ setLayout((prev) => {
3417
+ if (insertAfter === "end" || insertAfter === null) {
3418
+ const footerIndex = prev.findIndex((w) => w.name === "footer");
3419
+ if (footerIndex === -1) return [...prev, instance];
3420
+ return [
3421
+ ...prev.slice(0, footerIndex),
3422
+ instance,
3423
+ ...prev.slice(footerIndex)
3424
+ ];
3425
+ }
3426
+ const afterIndex = prev.findIndex(
3427
+ (w) => w.widgetId === insertAfter
3428
+ );
3429
+ if (afterIndex === -1) return [...prev, instance];
3430
+ return [
3431
+ ...prev.slice(0, afterIndex + 1),
3432
+ instance,
3433
+ ...prev.slice(afterIndex + 1)
3434
+ ];
3435
+ });
3436
+ setInsertAfter(null);
3437
+ setPanel({ mode: "block", widgetId: instance.widgetId });
3438
+ }
3439
+ const selectedWidgetId = panel?.mode === "block" ? panel.widgetId : null;
3440
+ const selectedInstance = layout.find(
3441
+ (w) => w.widgetId === selectedWidgetId
3442
+ );
3443
+ const selectedDefinition = selectedInstance ? getWidget(selectedInstance.name) : void 0;
3444
+ const showBlockPanel = Boolean(
3445
+ panel?.mode === "block" && selectedInstance && selectedDefinition?.editor
3446
+ );
3447
+ const showThemePanel = allowThemeEditing && panel?.mode === "theme";
3448
+ const showSeoPanel = panel?.mode === "seo";
3449
+ const panelOpen = showBlockPanel || showThemePanel || showSeoPanel;
3450
+ function closePanel() {
3451
+ setPreviewTheme(null);
3452
+ setPanel(null);
3453
+ }
3454
+ return /* @__PURE__ */ jsxs(
3455
+ "div",
3456
+ {
3457
+ className: cn(
3458
+ // Same token scope as PageRenderer. Builder chrome (top bar,
3459
+ // add-block overlay, device-frame pattern) lives outside the
3460
+ // canvas but still uses bg-background / --muted-foreground.
3461
+ "pb-theme flex h-full w-full flex-col overflow-hidden bg-background text-foreground",
3462
+ resolvedMode === "dark" && "dark",
3463
+ className
3464
+ ),
3465
+ children: [
3466
+ /* @__PURE__ */ jsx(
3467
+ BuilderTopBar,
3468
+ {
3469
+ resolvedMode,
3470
+ onModeChange: setMode,
3471
+ device,
3472
+ onDeviceChange: setDevice,
3473
+ onOpenTheme: () => setPanel({ mode: "theme" }),
3474
+ onOpenSeo: () => setPanel({ mode: "seo" }),
3475
+ onAddBlock: () => setInsertAfter("end"),
3476
+ allowThemeEditing
3477
+ }
3478
+ ),
3479
+ /* @__PURE__ */ jsxs("div", { className: "flex min-h-0 w-full min-w-0 flex-1 gap-4 overflow-hidden bg-muted/10 p-4", children: [
3480
+ /* @__PURE__ */ jsx(
3481
+ "div",
3482
+ {
3483
+ className: cn(
3484
+ "flex min-h-0 min-w-0 justify-center overflow-hidden rounded-xl border bg-background/98 shadow-sm backdrop-blur supports-[backdrop-filter]:bg-background/80",
3485
+ panelOpen ? "flex-1" : "w-full"
3486
+ ),
3487
+ style: {
3488
+ backgroundImage: "radial-gradient(color-mix(in srgb, var(--muted-foreground) 25%, transparent) 1px, transparent 1px)",
3489
+ backgroundSize: "14px 14px"
3490
+ },
3491
+ children: /* @__PURE__ */ jsx(
3492
+ PreviewFrame,
3493
+ {
3494
+ className: cn(
3495
+ "h-full w-full max-w-full transition-[max-width] duration-300",
3496
+ device === "mobile" && "max-w-[390px] border-x",
3497
+ device === "tablet" && "max-w-[768px] border-x"
3498
+ ),
3499
+ children: /* @__PURE__ */ jsx(
3500
+ PageRenderer,
3501
+ {
3502
+ layout,
3503
+ theme: paintedTheme,
3504
+ pageData,
3505
+ nextTheme: resolvedMode,
3506
+ themeMode: mode,
3507
+ onThemeModeChange: setMode,
3508
+ editing: true,
3509
+ selectedWidgetId: selectedWidgetId ?? void 0,
3510
+ highlightedWidgetId: movedWidgetId ?? void 0,
3511
+ onSelectWidget: (id) => setPanel({ mode: "block", widgetId: id }),
3512
+ onMoveWidgetUp: (id) => moveWidget(id, -1),
3513
+ onMoveWidgetDown: (id) => moveWidget(id, 1),
3514
+ onDeleteWidget: deleteWidget,
3515
+ onAddWidgetBelow: (id) => setInsertAfter(id),
3516
+ linkComponent
3517
+ }
3518
+ )
3519
+ }
3520
+ )
3521
+ }
3522
+ ),
3523
+ showBlockPanel && selectedInstance && selectedDefinition?.editor && /* @__PURE__ */ jsx(
3524
+ SettingsPanel,
3525
+ {
3526
+ title: selectedDefinition.metadata.displayName,
3527
+ eyebrow: "Block settings",
3528
+ description: selectedDefinition.metadata.description,
3529
+ icon: LayoutTemplate,
3530
+ onClose: closePanel,
3531
+ children: /* @__PURE__ */ jsx(
3532
+ BlockSettings,
3533
+ {
3534
+ instance: selectedInstance,
3535
+ definition: selectedDefinition,
3536
+ theme,
3537
+ onChange: (settings) => updateWidgetSettings(
3538
+ selectedInstance.widgetId,
3539
+ settings
3540
+ )
3541
+ },
3542
+ selectedInstance.widgetId
3543
+ )
3544
+ }
3545
+ ),
3546
+ showThemePanel && /* @__PURE__ */ jsx(SettingsPanel, { title: "Theme", onClose: closePanel, children: /* @__PURE__ */ jsx(
3547
+ ThemeEditor,
3548
+ {
3549
+ theme: paintedTheme,
3550
+ appliedThemeId: theme.id,
3551
+ systemThemes,
3552
+ customThemes,
3553
+ colorMode: resolvedMode,
3554
+ onColorModeChange: setMode,
3555
+ onPreview: setPreviewTheme,
3556
+ onApply: (next) => {
3557
+ setPreviewTheme(null);
3558
+ setTheme(next);
3559
+ },
3560
+ onCustomThemesChange: setCustomThemes
3561
+ }
3562
+ ) }),
3563
+ showSeoPanel && /* @__PURE__ */ jsx(SettingsPanel, { title: "SEO", onClose: closePanel, children: /* @__PURE__ */ jsx(SeoEditor, { seo, onChange: setSeo }) })
3564
+ ] }),
3565
+ insertAfter !== null && /* @__PURE__ */ jsx(
3566
+ BlockPicker,
3567
+ {
3568
+ blocks: addableBlocks,
3569
+ onPick: addWidget,
3570
+ onClose: () => setInsertAfter(null)
3571
+ }
3572
+ )
3573
+ ]
3574
+ }
3575
+ );
3576
+ }
3577
+
3578
+ export { BlockPicker, BuilderTopBar, ColorSelector, ContentPaddingSelector, MaxWidthSelector, PATTERN_PRESETS, PageBuilder, PageBuilderPropertyHeader, PageBuilderSlider, SectionBackgroundPanel, SeoEditor, SettingsPanel, ThemeEditor, VerticalPaddingSelector, createDefaultLayout, createWidgetInstance, ensureRequiredSharedWidgets, getDefaultTheme, getMaxWidthTWClass, getVerticalPaddingTWClass };
3579
+ //# sourceMappingURL=chunk-MO76KZY2.js.map
3580
+ //# sourceMappingURL=chunk-MO76KZY2.js.map