@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,4229 @@
1
+ "use client";
2
+ import { NavLink, VideoWithPreview, Image, TextRenderer, SandboxedEmbed, ThemeModeSwitcher } from './chunk-7W6Q4WA7.js';
3
+ import { Button2, Drawer, Section, cn, Preheader, Header1, Subheader1, PageCard, PageCardContent, Header2, Header4, Text2, Badge, PageCardHeader, Text1, Input, Label, Link, Caption, Header3 } from './chunk-RBJBODQP.js';
4
+ import React7, { useState, useId, useRef, useEffect } from 'react';
5
+ import clsx3 from 'clsx';
6
+ import * as Icons from 'lucide-react';
7
+ import { MenuIcon, Code2, LockKeyhole, ChevronDown, Plus } from 'lucide-react';
8
+ import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
9
+ import { Editor, emptyDoc } from '@frontlit/text-editor';
10
+
11
+ // src/blocks/header/metadata.ts
12
+ var headerMetadata = {
13
+ name: "header",
14
+ displayName: "Header",
15
+ description: "The site navigation bar, pinned to the top of every page.",
16
+ compatibleWith: ["site", "landing", "blog", "post", "custom"]
17
+ };
18
+
19
+ // src/utils/is-video.ts
20
+ function isVideo(youtubeLink, media) {
21
+ return Boolean(
22
+ youtubeLink || media && RegExp("video/").test(media?.mimeType || "")
23
+ );
24
+ }
25
+
26
+ // src/utils/generate-unique-id.ts
27
+ function generateUniqueId() {
28
+ return Math.random().toString(36).slice(2) + Date.now().toString(36);
29
+ }
30
+ function HeaderLink({
31
+ link,
32
+ theme,
33
+ onClick
34
+ }) {
35
+ if (link.isButton) {
36
+ return /* @__PURE__ */ jsx(NavLink, { href: link.href, onClick, children: /* @__PURE__ */ jsx(Button2, { size: "sm", theme, children: link.label }) });
37
+ }
38
+ return /* @__PURE__ */ jsx(NavLink, { href: link.href, onClick, className: "no-underline", children: /* @__PURE__ */ jsx(Link, { theme, className: "no-underline", children: link.label }) });
39
+ }
40
+ function HeaderWidget({
41
+ settings: {
42
+ logoText,
43
+ logoImage,
44
+ links = [],
45
+ ctaLabel,
46
+ ctaHref,
47
+ linkAlignment = "left",
48
+ layout = "fixed",
49
+ backdropBlur = false,
50
+ showThemeSwitcher = true,
51
+ cssId,
52
+ maxWidth,
53
+ verticalPadding,
54
+ background
55
+ },
56
+ state: { theme },
57
+ pageData,
58
+ nextTheme,
59
+ toggleTheme
60
+ }) {
61
+ const overriddenTheme = JSON.parse(JSON.stringify(theme.theme));
62
+ overriddenTheme.structure.page.width = maxWidth || theme.theme.structure.page.width;
63
+ overriddenTheme.structure.section.padding.y = verticalPadding || theme.theme.structure.section.padding.y;
64
+ const [mobileNavOpen, setMobileNavOpen] = useState(false);
65
+ const isFloating = layout === "floating";
66
+ const isCentered = layout === "centered";
67
+ const isFixed = !isFloating;
68
+ const primaryLinks = links.filter((link) => !link.isButton);
69
+ const buttonLinks = links.filter((link) => link.isButton);
70
+ const resolvedLogoImage = pageData.siteLogoUrl || logoImage;
71
+ const resolvedLogoAlt = pageData.siteLogoAlt || logoText || "";
72
+ const logo = /* @__PURE__ */ jsx(NavLink, { href: "/", className: "no-underline", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
73
+ resolvedLogoImage && /* @__PURE__ */ jsx(
74
+ Image,
75
+ {
76
+ src: resolvedLogoImage,
77
+ alt: resolvedLogoAlt,
78
+ width: "w-8",
79
+ height: "h-8",
80
+ borderRadius: 2
81
+ }
82
+ ),
83
+ logoText && /* @__PURE__ */ jsx(Header4, { theme: overriddenTheme, children: logoText })
84
+ ] }) });
85
+ const colorModeControl = showThemeSwitcher && toggleTheme ? /* @__PURE__ */ jsx(
86
+ ThemeModeSwitcher,
87
+ {
88
+ resolved: nextTheme ?? "light",
89
+ onToggle: toggleTheme,
90
+ theme: overriddenTheme,
91
+ size: "sm",
92
+ variant: "outline"
93
+ }
94
+ ) : null;
95
+ const ctaAndMenu = /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
96
+ colorModeControl,
97
+ buttonLinks.map((link) => /* @__PURE__ */ jsx("div", { className: "hidden lg:!block", children: /* @__PURE__ */ jsx(HeaderLink, { link, theme: overriddenTheme }) }, link.id)),
98
+ ctaLabel && ctaHref && /* @__PURE__ */ jsx("div", { className: "hidden lg:!block", children: /* @__PURE__ */ jsx(NavLink, { href: ctaHref, children: /* @__PURE__ */ jsx(Button2, { theme: overriddenTheme, size: "sm", children: ctaLabel }) }) }),
99
+ /* @__PURE__ */ jsx("div", { className: "lg:hidden", children: /* @__PURE__ */ jsx(
100
+ Drawer,
101
+ {
102
+ open: mobileNavOpen,
103
+ setOpen: setMobileNavOpen,
104
+ side: "right",
105
+ theme: overriddenTheme,
106
+ trigger: /* @__PURE__ */ jsxs(
107
+ Button2,
108
+ {
109
+ variant: "outline",
110
+ size: "sm",
111
+ theme: overriddenTheme,
112
+ children: [
113
+ /* @__PURE__ */ jsx(MenuIcon, { className: "h-4 w-4" }),
114
+ /* @__PURE__ */ jsx("span", { className: "sr-only", children: "Toggle menu" })
115
+ ]
116
+ }
117
+ ),
118
+ children: /* @__PURE__ */ jsxs("div", { className: "mt-6 flex flex-col gap-4", children: [
119
+ links.map((link) => /* @__PURE__ */ jsx(
120
+ HeaderLink,
121
+ {
122
+ link,
123
+ theme: overriddenTheme,
124
+ onClick: () => setMobileNavOpen(false)
125
+ },
126
+ link.id
127
+ )),
128
+ ctaLabel && ctaHref && /* @__PURE__ */ jsx(
129
+ NavLink,
130
+ {
131
+ href: ctaHref,
132
+ onClick: () => setMobileNavOpen(false),
133
+ children: /* @__PURE__ */ jsx(
134
+ Button2,
135
+ {
136
+ theme: overriddenTheme,
137
+ className: "w-full",
138
+ children: ctaLabel
139
+ }
140
+ )
141
+ }
142
+ )
143
+ ] })
144
+ }
145
+ ) })
146
+ ] });
147
+ let mainContent;
148
+ if (isCentered) {
149
+ const half = Math.ceil(primaryLinks.length / 2);
150
+ const leftLinks = primaryLinks.slice(0, half);
151
+ const rightLinks = primaryLinks.slice(half);
152
+ mainContent = /* @__PURE__ */ jsxs("div", { className: "grid w-full grid-cols-[1fr_auto_1fr] items-center gap-4", children: [
153
+ /* @__PURE__ */ jsx("div", { className: "hidden items-center justify-end gap-6 lg:!flex", children: leftLinks.map((link) => /* @__PURE__ */ jsx(
154
+ HeaderLink,
155
+ {
156
+ link,
157
+ theme: overriddenTheme
158
+ },
159
+ link.id
160
+ )) }),
161
+ /* @__PURE__ */ jsx("div", { className: "flex justify-center", children: logo }),
162
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-end gap-3", children: [
163
+ /* @__PURE__ */ jsx("div", { className: "hidden items-center gap-6 lg:!flex", children: rightLinks.map((link) => /* @__PURE__ */ jsx(
164
+ HeaderLink,
165
+ {
166
+ link,
167
+ theme: overriddenTheme
168
+ },
169
+ link.id
170
+ )) }),
171
+ ctaAndMenu
172
+ ] })
173
+ ] });
174
+ } else {
175
+ mainContent = /* @__PURE__ */ jsxs("div", { className: "flex w-full items-center justify-between gap-4", children: [
176
+ logo,
177
+ /* @__PURE__ */ jsx(
178
+ "div",
179
+ {
180
+ className: clsx3(
181
+ "hidden items-center gap-6 lg:!flex lg:!grow",
182
+ linkAlignment === "right" ? "justify-end" : linkAlignment === "center" ? "justify-center" : "justify-start"
183
+ ),
184
+ children: primaryLinks.map((link) => /* @__PURE__ */ jsx(
185
+ HeaderLink,
186
+ {
187
+ link,
188
+ theme: overriddenTheme
189
+ },
190
+ link.id
191
+ ))
192
+ }
193
+ ),
194
+ ctaAndMenu
195
+ ] });
196
+ }
197
+ return /* @__PURE__ */ jsx(
198
+ Section,
199
+ {
200
+ theme: overriddenTheme,
201
+ component: "header",
202
+ id: cssId,
203
+ background,
204
+ nextTheme,
205
+ className: clsx3(
206
+ "sticky top-0 z-20",
207
+ isFixed && "border-b",
208
+ isFixed && backdropBlur && "bg-transparent backdrop-blur-2xl",
209
+ isFixed && !backdropBlur && "bg-background",
210
+ !isFixed && "bg-transparent"
211
+ ),
212
+ children: isFloating ? /* @__PURE__ */ jsx(
213
+ "div",
214
+ {
215
+ className: clsx3(
216
+ "p-2",
217
+ overriddenTheme.interactives?.card?.border?.width ?? "border",
218
+ overriddenTheme.interactives?.card?.border?.radius ?? "rounded-xl",
219
+ overriddenTheme.interactives?.card?.border?.style,
220
+ overriddenTheme.interactives?.card?.shadow ?? "shadow-md",
221
+ backdropBlur ? "bg-transparent backdrop-blur-2xl" : "bg-card text-card-foreground"
222
+ ),
223
+ children: mainContent
224
+ }
225
+ ) : mainContent
226
+ }
227
+ );
228
+ }
229
+ function FieldGroup({
230
+ title,
231
+ children
232
+ }) {
233
+ return /* @__PURE__ */ jsxs("div", { className: "mb-6 flex min-w-0 max-w-full flex-col gap-4 border-b border-border/80 pb-6 last:mb-0 last:border-0 last:pb-0", children: [
234
+ title && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
235
+ /* @__PURE__ */ jsx("span", { className: "h-1.5 w-1.5 rounded-full bg-primary" }),
236
+ /* @__PURE__ */ jsx("p", { className: "text-[10px] font-bold uppercase tracking-[0.16em] text-muted-foreground", children: title })
237
+ ] }),
238
+ children
239
+ ] });
240
+ }
241
+ function TextField({
242
+ label,
243
+ value,
244
+ onChange,
245
+ placeholder,
246
+ multiline
247
+ }) {
248
+ const generatedId = useId();
249
+ const inputId = `field-${generatedId.replace(/:/g, "")}`;
250
+ return /* @__PURE__ */ jsxs("label", { htmlFor: inputId, className: "flex flex-col gap-1.5 text-sm", children: [
251
+ /* @__PURE__ */ jsx("span", { className: "text-xs font-semibold text-foreground", children: label }),
252
+ multiline ? /* @__PURE__ */ jsx(
253
+ "textarea",
254
+ {
255
+ id: inputId,
256
+ className: "min-h-24 resize-y rounded-lg border border-input bg-muted/20 px-3 py-2.5 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",
257
+ value: value ?? "",
258
+ placeholder,
259
+ onChange: (e) => onChange(e.target.value)
260
+ }
261
+ ) : /* @__PURE__ */ jsx(
262
+ "input",
263
+ {
264
+ id: inputId,
265
+ className: "h-9 rounded-lg border border-input bg-muted/20 px-3 py-2 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",
266
+ value: value ?? "",
267
+ placeholder,
268
+ onChange: (e) => onChange(e.target.value)
269
+ }
270
+ )
271
+ ] });
272
+ }
273
+ function NumberField({
274
+ label,
275
+ value,
276
+ onChange,
277
+ min,
278
+ max
279
+ }) {
280
+ const generatedId = useId();
281
+ const inputId = `field-${generatedId.replace(/:/g, "")}`;
282
+ return /* @__PURE__ */ jsxs("label", { htmlFor: inputId, className: "flex flex-col gap-1.5 text-sm", children: [
283
+ /* @__PURE__ */ jsx("span", { className: "text-xs font-semibold text-foreground", children: label }),
284
+ /* @__PURE__ */ jsx(
285
+ "input",
286
+ {
287
+ id: inputId,
288
+ type: "number",
289
+ className: "h-9 rounded-lg border border-input bg-muted/20 px-3 py-2 text-sm text-foreground outline-none transition-colors focus:border-ring focus:bg-background focus:ring-2 focus:ring-ring/20",
290
+ value: value ?? "",
291
+ min,
292
+ max,
293
+ onChange: (e) => onChange(Number(e.target.value))
294
+ }
295
+ )
296
+ ] });
297
+ }
298
+ function SelectField({
299
+ label,
300
+ value,
301
+ onChange,
302
+ options
303
+ }) {
304
+ const generatedId = useId();
305
+ const inputId = `field-${generatedId.replace(/:/g, "")}`;
306
+ return /* @__PURE__ */ jsxs("label", { htmlFor: inputId, className: "flex flex-col gap-1.5 text-sm", children: [
307
+ /* @__PURE__ */ jsx("span", { className: "text-xs font-semibold text-foreground", children: label }),
308
+ /* @__PURE__ */ jsx(
309
+ "select",
310
+ {
311
+ id: inputId,
312
+ className: "h-9 w-full rounded-lg border border-input bg-muted/20 px-3 py-2 text-sm text-foreground outline-none transition-colors focus:border-ring focus:bg-background focus:ring-2 focus:ring-ring/20",
313
+ value: value ?? "",
314
+ onChange: (e) => onChange(e.target.value),
315
+ children: options.map((option) => /* @__PURE__ */ jsx("option", { value: option.value, children: option.label }, option.value))
316
+ }
317
+ )
318
+ ] });
319
+ }
320
+ function ToggleField({
321
+ label,
322
+ value,
323
+ onChange
324
+ }) {
325
+ return /* @__PURE__ */ jsxs("label", { className: "flex cursor-pointer items-center justify-between gap-3 rounded-lg border border-border/80 bg-muted/20 px-3 py-2.5 text-sm transition-colors hover:bg-muted/50", children: [
326
+ /* @__PURE__ */ jsx("span", { className: "text-xs font-semibold text-foreground", children: label }),
327
+ /* @__PURE__ */ jsx(
328
+ "input",
329
+ {
330
+ type: "checkbox",
331
+ className: "peer sr-only",
332
+ checked: Boolean(value),
333
+ onChange: (e) => onChange(e.target.checked)
334
+ }
335
+ ),
336
+ /* @__PURE__ */ jsx(
337
+ "span",
338
+ {
339
+ "aria-hidden": "true",
340
+ className: cn(
341
+ "relative h-5 w-9 shrink-0 rounded-full bg-muted-foreground/25 transition-colors after:absolute after:left-0.5 after:top-0.5 after:h-4 after:w-4 after:rounded-full after:bg-background after:shadow-sm after:transition-transform peer-focus-visible:ring-2 peer-focus-visible:ring-ring peer-focus-visible:ring-offset-2",
342
+ value && "bg-primary after:translate-x-4"
343
+ )
344
+ }
345
+ )
346
+ ] });
347
+ }
348
+ function ListField({
349
+ label,
350
+ items,
351
+ onChange,
352
+ renderItem,
353
+ createItem: createItem2,
354
+ addLabel = "Add item"
355
+ }) {
356
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2.5 text-sm", children: [
357
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-3", children: [
358
+ /* @__PURE__ */ jsx("span", { className: "text-xs font-semibold text-foreground", children: label }),
359
+ /* @__PURE__ */ jsxs("span", { className: "text-[10px] font-medium uppercase tracking-wider text-muted-foreground", children: [
360
+ items.length,
361
+ " ",
362
+ items.length === 1 ? "item" : "items"
363
+ ] })
364
+ ] }),
365
+ /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-2.5", children: items.map((item, index) => /* @__PURE__ */ jsxs(
366
+ "div",
367
+ {
368
+ className: "flex flex-col gap-3 rounded-xl border border-border/80 bg-muted/15 p-3.5 [&>button]:self-end [&>button]:rounded-md [&>button]:px-2 [&>button]:py-1 [&>button]:text-[11px] [&>button]:font-semibold [&>button]:transition-colors [&>button]:hover:bg-destructive/10",
369
+ children: [
370
+ /* @__PURE__ */ jsx("div", { className: "flex items-center justify-between border-b border-border/60 pb-2", children: /* @__PURE__ */ jsxs("span", { className: "text-[10px] font-bold uppercase tracking-[0.14em] text-muted-foreground", children: [
371
+ "Item ",
372
+ String(index + 1).padStart(2, "0")
373
+ ] }) }),
374
+ renderItem(
375
+ item,
376
+ (next) => {
377
+ const copy = items.slice();
378
+ copy[index] = { ...item, ...next };
379
+ onChange(copy);
380
+ },
381
+ () => {
382
+ onChange(items.filter((_, i) => i !== index));
383
+ },
384
+ index
385
+ )
386
+ ]
387
+ },
388
+ index
389
+ )) }),
390
+ /* @__PURE__ */ jsxs(
391
+ "button",
392
+ {
393
+ type: "button",
394
+ className: "inline-flex h-9 w-full items-center justify-center gap-2 rounded-lg border border-dashed border-input text-xs font-semibold text-muted-foreground transition-colors hover:border-primary/50 hover:bg-primary/5 hover:text-foreground",
395
+ onClick: () => onChange([...items, createItem2()]),
396
+ children: [
397
+ /* @__PURE__ */ jsx(Plus, { className: "h-3.5 w-3.5", "aria-hidden": "true" }),
398
+ addLabel
399
+ ]
400
+ }
401
+ )
402
+ ] });
403
+ }
404
+ function HeaderEditor({
405
+ settings,
406
+ onChange
407
+ }) {
408
+ const update = (partial) => onChange({ ...settings, ...partial });
409
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-col", children: [
410
+ /* @__PURE__ */ jsxs(FieldGroup, { title: "Logo", children: [
411
+ /* @__PURE__ */ jsx(
412
+ TextField,
413
+ {
414
+ label: "Logo text",
415
+ value: settings.logoText,
416
+ onChange: (logoText) => update({ logoText })
417
+ }
418
+ ),
419
+ /* @__PURE__ */ jsx(
420
+ TextField,
421
+ {
422
+ label: "Logo image URL",
423
+ value: settings.logoImage,
424
+ placeholder: "https://...",
425
+ onChange: (logoImage) => update({ logoImage })
426
+ }
427
+ )
428
+ ] }),
429
+ /* @__PURE__ */ jsx(FieldGroup, { title: "Navigation links", children: /* @__PURE__ */ jsx(
430
+ ListField,
431
+ {
432
+ label: "Links",
433
+ items: settings.links ?? [],
434
+ onChange: (links) => update({ links }),
435
+ createItem: () => ({
436
+ id: generateUniqueId(),
437
+ label: "New link",
438
+ href: "#"
439
+ }),
440
+ addLabel: "Add link",
441
+ renderItem: (link, updateLink, remove) => /* @__PURE__ */ jsxs(Fragment, { children: [
442
+ /* @__PURE__ */ jsx(
443
+ TextField,
444
+ {
445
+ label: "Label",
446
+ value: link.label,
447
+ onChange: (label) => updateLink({ label })
448
+ }
449
+ ),
450
+ /* @__PURE__ */ jsx(
451
+ TextField,
452
+ {
453
+ label: "Link",
454
+ value: link.href,
455
+ onChange: (href) => updateLink({ href })
456
+ }
457
+ ),
458
+ /* @__PURE__ */ jsx(
459
+ ToggleField,
460
+ {
461
+ label: "Show as button",
462
+ value: link.isButton,
463
+ onChange: (isButton) => updateLink({ isButton })
464
+ }
465
+ ),
466
+ /* @__PURE__ */ jsx(
467
+ "button",
468
+ {
469
+ type: "button",
470
+ className: "self-start text-xs text-destructive",
471
+ onClick: remove,
472
+ children: "Remove"
473
+ }
474
+ )
475
+ ] })
476
+ }
477
+ ) }),
478
+ /* @__PURE__ */ jsxs(FieldGroup, { title: "Call to action", children: [
479
+ /* @__PURE__ */ jsx(
480
+ TextField,
481
+ {
482
+ label: "Button text",
483
+ value: settings.ctaLabel,
484
+ onChange: (ctaLabel) => update({ ctaLabel })
485
+ }
486
+ ),
487
+ /* @__PURE__ */ jsx(
488
+ TextField,
489
+ {
490
+ label: "Button link",
491
+ value: settings.ctaHref,
492
+ onChange: (ctaHref) => update({ ctaHref })
493
+ }
494
+ )
495
+ ] }),
496
+ /* @__PURE__ */ jsxs(FieldGroup, { title: "Layout", children: [
497
+ /* @__PURE__ */ jsx(
498
+ SelectField,
499
+ {
500
+ label: "Link alignment",
501
+ value: settings.linkAlignment ?? "left",
502
+ onChange: (linkAlignment) => update({ linkAlignment }),
503
+ options: [
504
+ { label: "Left", value: "left" },
505
+ { label: "Center", value: "center" },
506
+ { label: "Right", value: "right" }
507
+ ]
508
+ }
509
+ ),
510
+ /* @__PURE__ */ jsx(
511
+ ToggleField,
512
+ {
513
+ label: "Backdrop blur",
514
+ value: settings.backdropBlur,
515
+ onChange: (backdropBlur) => update({ backdropBlur })
516
+ }
517
+ ),
518
+ /* @__PURE__ */ jsx(
519
+ ToggleField,
520
+ {
521
+ label: "Color mode switcher",
522
+ value: settings.showThemeSwitcher !== false,
523
+ onChange: (showThemeSwitcher) => update({ showThemeSwitcher })
524
+ }
525
+ )
526
+ ] })
527
+ ] });
528
+ }
529
+
530
+ // src/blocks/header/defaults.ts
531
+ var headerDefaults = {
532
+ logoText: "FrontLit",
533
+ links: [
534
+ { id: generateUniqueId(), label: "Features", href: "/#features" },
535
+ { id: generateUniqueId(), label: "Pricing", href: "/#pricing" },
536
+ { id: generateUniqueId(), label: "Docs", href: "/docs" },
537
+ { id: generateUniqueId(), label: "Blog", href: "/blog" }
538
+ ],
539
+ ctaLabel: "Get started",
540
+ ctaHref: "/help",
541
+ linkAlignment: "left",
542
+ layout: "fixed",
543
+ backdropBlur: false,
544
+ showThemeSwitcher: true,
545
+ // Slider value 1. Keeps a new header compact instead of inheriting the
546
+ // theme's content-section padding (`py-12` / slider 5 on most themes).
547
+ verticalPadding: "py-4"
548
+ };
549
+
550
+ // src/blocks/header/index.ts
551
+ var Header = {
552
+ metadata: headerMetadata,
553
+ widget: HeaderWidget,
554
+ editor: HeaderEditor,
555
+ getDefaultSettings: () => headerDefaults,
556
+ layouts: [
557
+ { id: "fixed", name: "Fixed" },
558
+ { id: "floating", name: "Floating" },
559
+ { id: "centered", name: "Centered logo" }
560
+ ]
561
+ };
562
+
563
+ // src/blocks/hero/metadata.ts
564
+ var heroMetadata = {
565
+ name: "hero",
566
+ displayName: "Hero",
567
+ description: "A large introductory section with a title, copy and CTAs.",
568
+ compatibleWith: ["site", "landing", "blog", "post", "custom"]
569
+ };
570
+ var roundedMap = {
571
+ 0: "rounded-none",
572
+ 1: "rounded-sm",
573
+ 2: "rounded",
574
+ 3: "rounded-md",
575
+ 4: "rounded-lg",
576
+ 5: "rounded-xl",
577
+ 6: "rounded-2xl",
578
+ 7: "rounded-3xl",
579
+ 8: "rounded-full"
580
+ };
581
+ function HeroWidget({
582
+ settings: {
583
+ preTitle,
584
+ title,
585
+ description,
586
+ buttonCaption,
587
+ buttonAction,
588
+ media,
589
+ youtubeLink,
590
+ alignment = "right",
591
+ mediaRadius = 3,
592
+ contentAlignment = "left",
593
+ secondaryButtonAction,
594
+ secondaryButtonCaption,
595
+ cssId,
596
+ maxWidth,
597
+ verticalPadding,
598
+ background,
599
+ layout = "normal"
600
+ },
601
+ state: { theme },
602
+ nextTheme
603
+ }) {
604
+ const overriddenTheme = JSON.parse(JSON.stringify(theme.theme));
605
+ overriddenTheme.structure.page.width = maxWidth || theme.theme.structure.page.width;
606
+ overriddenTheme.structure.section.padding.y = verticalPadding || theme.theme.structure.section.padding.y;
607
+ const hasHeroGraphic = youtubeLink || media && media.mediaId;
608
+ const direction = alignment === "right" ? "md:!flex-row-reverse" : "md:!flex-row";
609
+ const isCentered = layout === "centered" || layout === "media-below";
610
+ const graphic = hasHeroGraphic && /* @__PURE__ */ jsx(
611
+ "div",
612
+ {
613
+ className: `w-full overflow-hidden ${roundedMap[mediaRadius] ?? "rounded-md"}`,
614
+ children: isVideo(youtubeLink, media) ? /* @__PURE__ */ jsx(
615
+ VideoWithPreview,
616
+ {
617
+ videoUrl: youtubeLink || media?.file || "",
618
+ title: media?.caption || "",
619
+ thumbnailUrl: media?.thumbnail || ""
620
+ }
621
+ ) : /* @__PURE__ */ jsx(
622
+ Image,
623
+ {
624
+ src: media?.file || "",
625
+ alt: media?.caption || "",
626
+ borderRadius: mediaRadius
627
+ }
628
+ )
629
+ }
630
+ );
631
+ const textContent = /* @__PURE__ */ jsxs(
632
+ "div",
633
+ {
634
+ className: clsx3(
635
+ "flex flex-col gap-4",
636
+ isCentered || contentAlignment === "center" ? "items-center text-center" : "items-start justify-center h-full"
637
+ ),
638
+ children: [
639
+ preTitle && /* @__PURE__ */ jsx(
640
+ Preheader,
641
+ {
642
+ theme: overriddenTheme,
643
+ className: "text-muted-foreground",
644
+ children: preTitle
645
+ }
646
+ ),
647
+ /* @__PURE__ */ jsx(Header1, { theme: overriddenTheme, children: title }),
648
+ description && /* @__PURE__ */ jsx(Subheader1, { theme: overriddenTheme, component: "span", children: /* @__PURE__ */ jsx(TextRenderer, { json: description, theme: overriddenTheme }) }),
649
+ /* @__PURE__ */ jsxs(
650
+ "div",
651
+ {
652
+ className: clsx3(
653
+ "flex flex-col md:!flex-row gap-2 w-full",
654
+ (isCentered || contentAlignment === "center") && "md:!justify-center"
655
+ ),
656
+ children: [
657
+ buttonAction && buttonCaption && /* @__PURE__ */ jsx(NavLink, { href: buttonAction, children: /* @__PURE__ */ jsx(
658
+ Button2,
659
+ {
660
+ theme: overriddenTheme,
661
+ className: "w-full md:min-w-[150px]",
662
+ children: buttonCaption
663
+ }
664
+ ) }),
665
+ secondaryButtonAction && secondaryButtonCaption && /* @__PURE__ */ jsx(NavLink, { href: secondaryButtonAction, children: /* @__PURE__ */ jsx(
666
+ Button2,
667
+ {
668
+ theme: overriddenTheme,
669
+ variant: "secondary",
670
+ className: "w-full md:min-w-[150px]",
671
+ children: secondaryButtonCaption
672
+ }
673
+ ) })
674
+ ]
675
+ }
676
+ )
677
+ ]
678
+ }
679
+ );
680
+ let mainContent;
681
+ if (layout === "centered") {
682
+ mainContent = /* @__PURE__ */ jsx("div", { className: "mx-auto flex max-w-2xl flex-col gap-4", children: textContent });
683
+ } else if (layout === "media-below") {
684
+ mainContent = /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-10", children: [
685
+ /* @__PURE__ */ jsx("div", { className: "mx-auto flex max-w-2xl flex-col gap-4", children: textContent }),
686
+ graphic && /* @__PURE__ */ jsx("div", { className: "w-full", children: graphic })
687
+ ] });
688
+ } else {
689
+ mainContent = /* @__PURE__ */ jsxs("div", { className: clsx3("flex flex-col gap-8", direction), children: [
690
+ graphic && /* @__PURE__ */ jsx("div", { className: "w-full md:w-1/2 flex items-center", children: graphic }),
691
+ /* @__PURE__ */ jsx("div", { className: `w-full ${hasHeroGraphic ? "md:w-1/2" : ""}`, children: textContent })
692
+ ] });
693
+ }
694
+ return /* @__PURE__ */ jsx(
695
+ Section,
696
+ {
697
+ theme: overriddenTheme,
698
+ id: cssId,
699
+ background,
700
+ nextTheme,
701
+ children: layout === "card" ? /* @__PURE__ */ jsx(PageCard, { theme: overriddenTheme, children: /* @__PURE__ */ jsx(PageCardContent, { theme: overriddenTheme, children: mainContent }) }) : mainContent
702
+ }
703
+ );
704
+ }
705
+ function HeroEditor({
706
+ settings,
707
+ onChange
708
+ }) {
709
+ const update = (partial) => onChange({ ...settings, ...partial });
710
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-col", children: [
711
+ /* @__PURE__ */ jsxs(FieldGroup, { title: "Content", children: [
712
+ /* @__PURE__ */ jsx(
713
+ TextField,
714
+ {
715
+ label: "Eyebrow",
716
+ value: settings.preTitle,
717
+ placeholder: "e.g. Careers",
718
+ onChange: (preTitle) => update({ preTitle })
719
+ }
720
+ ),
721
+ /* @__PURE__ */ jsx(
722
+ TextField,
723
+ {
724
+ label: "Title",
725
+ value: settings.title,
726
+ onChange: (title) => update({ title })
727
+ }
728
+ ),
729
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1 text-sm", children: [
730
+ /* @__PURE__ */ jsx("span", { className: "font-medium text-foreground", children: "Description" }),
731
+ /* @__PURE__ */ jsx(
732
+ Editor,
733
+ {
734
+ initialContent: settings.description ?? emptyDoc,
735
+ onChange: (description) => update({ description }),
736
+ showToolbar: false,
737
+ editorClassName: "min-h-[100px]"
738
+ }
739
+ )
740
+ ] })
741
+ ] }),
742
+ /* @__PURE__ */ jsxs(FieldGroup, { title: "Media", children: [
743
+ /* @__PURE__ */ jsx(
744
+ TextField,
745
+ {
746
+ label: "Image URL",
747
+ value: settings.media?.file,
748
+ placeholder: "https://...",
749
+ onChange: (file) => update({
750
+ media: file ? { mediaId: file, file } : void 0
751
+ })
752
+ }
753
+ ),
754
+ /* @__PURE__ */ jsx(
755
+ TextField,
756
+ {
757
+ label: "YouTube / Vimeo link",
758
+ value: settings.youtubeLink,
759
+ placeholder: "https://youtube.com/watch?v=...",
760
+ onChange: (youtubeLink) => update({ youtubeLink })
761
+ }
762
+ )
763
+ ] }),
764
+ /* @__PURE__ */ jsxs(FieldGroup, { title: "Calls to action", children: [
765
+ /* @__PURE__ */ jsx(
766
+ TextField,
767
+ {
768
+ label: "Primary button text",
769
+ value: settings.buttonCaption,
770
+ onChange: (buttonCaption) => update({ buttonCaption })
771
+ }
772
+ ),
773
+ /* @__PURE__ */ jsx(
774
+ TextField,
775
+ {
776
+ label: "Primary button link",
777
+ value: settings.buttonAction,
778
+ onChange: (buttonAction) => update({ buttonAction })
779
+ }
780
+ ),
781
+ /* @__PURE__ */ jsx(
782
+ TextField,
783
+ {
784
+ label: "Secondary button text",
785
+ value: settings.secondaryButtonCaption,
786
+ onChange: (secondaryButtonCaption) => update({ secondaryButtonCaption })
787
+ }
788
+ ),
789
+ /* @__PURE__ */ jsx(
790
+ TextField,
791
+ {
792
+ label: "Secondary button link",
793
+ value: settings.secondaryButtonAction,
794
+ onChange: (secondaryButtonAction) => update({ secondaryButtonAction })
795
+ }
796
+ )
797
+ ] }),
798
+ /* @__PURE__ */ jsxs(FieldGroup, { title: "Layout", children: [
799
+ /* @__PURE__ */ jsx(
800
+ SelectField,
801
+ {
802
+ label: "Media position",
803
+ value: settings.alignment ?? "right",
804
+ onChange: (alignment) => update({ alignment }),
805
+ options: [
806
+ { label: "Left", value: "left" },
807
+ { label: "Right", value: "right" }
808
+ ]
809
+ }
810
+ ),
811
+ /* @__PURE__ */ jsx(
812
+ SelectField,
813
+ {
814
+ label: "Content alignment",
815
+ value: settings.contentAlignment ?? "left",
816
+ onChange: (contentAlignment) => update({ contentAlignment }),
817
+ options: [
818
+ { label: "Left", value: "left" },
819
+ { label: "Center", value: "center" }
820
+ ]
821
+ }
822
+ )
823
+ ] })
824
+ ] });
825
+ }
826
+
827
+ // src/blocks/hero/defaults.ts
828
+ var heroDefaults = {
829
+ title: "Run your whole business from one front office",
830
+ buttonCaption: "See features",
831
+ buttonAction: "/#features",
832
+ secondaryButtonCaption: "Read the docs",
833
+ secondaryButtonAction: "/docs",
834
+ alignment: "right",
835
+ contentAlignment: "left",
836
+ mediaRadius: 3,
837
+ layout: "normal"
838
+ };
839
+
840
+ // src/blocks/hero/index.ts
841
+ var Hero = {
842
+ metadata: heroMetadata,
843
+ widget: HeroWidget,
844
+ editor: HeroEditor,
845
+ getDefaultSettings: () => heroDefaults,
846
+ layouts: [
847
+ { id: "normal", name: "Standard" },
848
+ { id: "card", name: "Card" },
849
+ { id: "centered", name: "Centered" },
850
+ { id: "media-below", name: "Media below" }
851
+ ]
852
+ };
853
+
854
+ // src/blocks/content/metadata.ts
855
+ var contentMetadata = {
856
+ name: "content",
857
+ displayName: "Content",
858
+ description: "A rich-text section with an optional image, for things like an about-us or feature explainer.",
859
+ compatibleWith: ["site", "landing", "blog", "post", "custom"]
860
+ };
861
+ var roundedMap2 = {
862
+ 0: "rounded-none",
863
+ 1: "rounded-sm",
864
+ 2: "rounded",
865
+ 3: "rounded-md",
866
+ 4: "rounded-lg",
867
+ 5: "rounded-xl",
868
+ 6: "rounded-2xl",
869
+ 7: "rounded-3xl",
870
+ 8: "rounded-full"
871
+ };
872
+ function ContentWidget({
873
+ settings: {
874
+ title,
875
+ body,
876
+ media,
877
+ alignment = "left",
878
+ headerAlignment = "left",
879
+ mediaRadius = 3,
880
+ layout = "side",
881
+ cssId,
882
+ maxWidth,
883
+ verticalPadding,
884
+ background
885
+ },
886
+ state: { theme },
887
+ nextTheme
888
+ }) {
889
+ const overriddenTheme = JSON.parse(JSON.stringify(theme.theme));
890
+ overriddenTheme.structure.page.width = maxWidth || theme.theme.structure.page.width;
891
+ overriddenTheme.structure.section.padding.y = verticalPadding || theme.theme.structure.section.padding.y;
892
+ const hasMedia = Boolean(media && media.file);
893
+ const sideBySide = layout !== "stacked" && layout !== "overlay";
894
+ const direction = alignment === "right" ? "md:!flex-row-reverse" : "md:!flex-row";
895
+ if (layout === "overlay") {
896
+ const textPosition = alignment === "right" ? "items-end text-right" : "items-start text-left";
897
+ return /* @__PURE__ */ jsx(
898
+ Section,
899
+ {
900
+ theme: overriddenTheme,
901
+ id: cssId,
902
+ background,
903
+ nextTheme,
904
+ children: /* @__PURE__ */ jsxs("div", { className: "relative min-h-[420px] w-full overflow-hidden rounded-xl", children: [
905
+ hasMedia && /* @__PURE__ */ jsx(
906
+ Image,
907
+ {
908
+ src: media?.file || "",
909
+ alt: media?.caption || "",
910
+ width: "w-full",
911
+ height: "h-full",
912
+ className: "absolute inset-0",
913
+ borderRadius: 0
914
+ }
915
+ ),
916
+ /* @__PURE__ */ jsx("div", { className: "absolute inset-0 bg-gradient-to-t from-black/70 via-black/20 to-transparent" }),
917
+ /* @__PURE__ */ jsx(
918
+ "div",
919
+ {
920
+ className: clsx3(
921
+ "relative flex h-full flex-col justify-end gap-4 p-6 md:p-10",
922
+ textPosition
923
+ ),
924
+ children: /* @__PURE__ */ jsxs("div", { className: "max-w-lg text-white", children: [
925
+ title && /* @__PURE__ */ jsx(Header2, { theme: overriddenTheme, children: title }),
926
+ body && /* @__PURE__ */ jsx(
927
+ TextRenderer,
928
+ {
929
+ json: body,
930
+ theme: overriddenTheme
931
+ }
932
+ )
933
+ ] })
934
+ }
935
+ )
936
+ ] })
937
+ }
938
+ );
939
+ }
940
+ return /* @__PURE__ */ jsx(
941
+ Section,
942
+ {
943
+ theme: overriddenTheme,
944
+ id: cssId,
945
+ background,
946
+ nextTheme,
947
+ children: /* @__PURE__ */ jsxs(
948
+ "div",
949
+ {
950
+ className: clsx3("flex flex-col gap-8", sideBySide && direction),
951
+ children: [
952
+ hasMedia && /* @__PURE__ */ jsx(
953
+ "div",
954
+ {
955
+ className: clsx3(
956
+ "flex w-full items-center",
957
+ sideBySide && "md:w-1/2"
958
+ ),
959
+ children: /* @__PURE__ */ jsx(
960
+ "div",
961
+ {
962
+ className: `w-full overflow-hidden ${roundedMap2[mediaRadius] ?? "rounded-md"}`,
963
+ children: /* @__PURE__ */ jsx(
964
+ Image,
965
+ {
966
+ src: media?.file || "",
967
+ alt: media?.caption || "",
968
+ borderRadius: mediaRadius
969
+ }
970
+ )
971
+ }
972
+ )
973
+ }
974
+ ),
975
+ /* @__PURE__ */ jsx(
976
+ "div",
977
+ {
978
+ className: clsx3(
979
+ "w-full",
980
+ hasMedia && sideBySide && "md:w-1/2"
981
+ ),
982
+ children: /* @__PURE__ */ jsxs(
983
+ "div",
984
+ {
985
+ className: clsx3(
986
+ "flex h-full flex-col justify-center gap-4",
987
+ headerAlignment === "center" ? "items-center text-center" : "items-start"
988
+ ),
989
+ children: [
990
+ title && /* @__PURE__ */ jsx(Header2, { theme: overriddenTheme, children: title }),
991
+ body && /* @__PURE__ */ jsx(TextRenderer, { json: body, theme: overriddenTheme })
992
+ ]
993
+ }
994
+ )
995
+ }
996
+ )
997
+ ]
998
+ }
999
+ )
1000
+ }
1001
+ );
1002
+ }
1003
+ function ContentEditor({
1004
+ settings,
1005
+ onChange
1006
+ }) {
1007
+ const update = (partial) => onChange({ ...settings, ...partial });
1008
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-col", children: [
1009
+ /* @__PURE__ */ jsxs(FieldGroup, { title: "Content", children: [
1010
+ /* @__PURE__ */ jsx(
1011
+ TextField,
1012
+ {
1013
+ label: "Title",
1014
+ value: settings.title,
1015
+ onChange: (title) => update({ title })
1016
+ }
1017
+ ),
1018
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1 text-sm", children: [
1019
+ /* @__PURE__ */ jsx("span", { className: "font-medium text-foreground", children: "Body" }),
1020
+ /* @__PURE__ */ jsx(
1021
+ Editor,
1022
+ {
1023
+ initialContent: settings.body ?? emptyDoc,
1024
+ onChange: (body) => update({ body }),
1025
+ editorClassName: "min-h-[160px]"
1026
+ }
1027
+ )
1028
+ ] })
1029
+ ] }),
1030
+ /* @__PURE__ */ jsxs(FieldGroup, { title: "Media", children: [
1031
+ /* @__PURE__ */ jsx(
1032
+ TextField,
1033
+ {
1034
+ label: "Image URL",
1035
+ value: settings.media?.file,
1036
+ placeholder: "https://...",
1037
+ onChange: (file) => update({
1038
+ media: file ? { mediaId: file, file } : void 0
1039
+ })
1040
+ }
1041
+ ),
1042
+ /* @__PURE__ */ jsx(
1043
+ NumberField,
1044
+ {
1045
+ label: "Image corner radius",
1046
+ value: settings.mediaRadius,
1047
+ min: 0,
1048
+ max: 8,
1049
+ onChange: (mediaRadius) => update({ mediaRadius })
1050
+ }
1051
+ )
1052
+ ] }),
1053
+ /* @__PURE__ */ jsxs(FieldGroup, { title: "Layout", children: [
1054
+ /* @__PURE__ */ jsx(
1055
+ SelectField,
1056
+ {
1057
+ label: "Media position",
1058
+ value: settings.alignment ?? "left",
1059
+ onChange: (alignment) => update({ alignment }),
1060
+ options: [
1061
+ { label: "Left", value: "left" },
1062
+ { label: "Right", value: "right" }
1063
+ ]
1064
+ }
1065
+ ),
1066
+ /* @__PURE__ */ jsx(
1067
+ SelectField,
1068
+ {
1069
+ label: "Header alignment",
1070
+ value: settings.headerAlignment ?? "left",
1071
+ onChange: (headerAlignment) => update({ headerAlignment }),
1072
+ options: [
1073
+ { label: "Left", value: "left" },
1074
+ { label: "Center", value: "center" }
1075
+ ]
1076
+ }
1077
+ )
1078
+ ] })
1079
+ ] });
1080
+ }
1081
+
1082
+ // src/blocks/content/defaults.ts
1083
+ var contentDefaults = {
1084
+ title: "One dashboard, every channel",
1085
+ body: {
1086
+ type: "doc",
1087
+ content: [
1088
+ {
1089
+ type: "paragraph",
1090
+ content: [
1091
+ {
1092
+ type: "text",
1093
+ text: "FrontLit brings your website, content, customer support, and audience engagement into one connected front office."
1094
+ }
1095
+ ]
1096
+ }
1097
+ ]
1098
+ },
1099
+ alignment: "left",
1100
+ headerAlignment: "left",
1101
+ mediaRadius: 3
1102
+ };
1103
+
1104
+ // src/blocks/content/index.ts
1105
+ var Content = {
1106
+ metadata: contentMetadata,
1107
+ widget: ContentWidget,
1108
+ editor: ContentEditor,
1109
+ getDefaultSettings: () => contentDefaults,
1110
+ layouts: [
1111
+ { id: "side", name: "Media beside" },
1112
+ { id: "stacked", name: "Media on top" },
1113
+ { id: "overlay", name: "Overlay" }
1114
+ ]
1115
+ };
1116
+
1117
+ // src/blocks/featured/metadata.ts
1118
+ var featuredMetadata = {
1119
+ name: "featured",
1120
+ displayName: "Feature grid",
1121
+ description: "A grid of feature highlights, each with an icon, title and description.",
1122
+ compatibleWith: ["site", "landing", "custom"]
1123
+ };
1124
+ function resolveIcon(name) {
1125
+ if (!name) return Icons.Sparkles;
1126
+ const icon = Icons[name];
1127
+ return icon ?? Icons.Sparkles;
1128
+ }
1129
+ function FeaturedWidget({
1130
+ settings: {
1131
+ title,
1132
+ description,
1133
+ headerAlignment = "center",
1134
+ items,
1135
+ layout = "grid",
1136
+ cssId,
1137
+ maxWidth,
1138
+ verticalPadding,
1139
+ background
1140
+ },
1141
+ state: { theme },
1142
+ nextTheme
1143
+ }) {
1144
+ const overriddenTheme = JSON.parse(JSON.stringify(theme.theme));
1145
+ overriddenTheme.structure.page.width = maxWidth || theme.theme.structure.page.width;
1146
+ overriddenTheme.structure.section.padding.y = verticalPadding || theme.theme.structure.section.padding.y;
1147
+ return /* @__PURE__ */ jsx(
1148
+ Section,
1149
+ {
1150
+ theme: overriddenTheme,
1151
+ id: cssId,
1152
+ background,
1153
+ nextTheme,
1154
+ children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-8", children: [
1155
+ /* @__PURE__ */ jsxs(
1156
+ "div",
1157
+ {
1158
+ className: clsx3(
1159
+ "flex flex-col gap-4",
1160
+ headerAlignment === "center" ? "items-center text-center" : "items-start"
1161
+ ),
1162
+ children: [
1163
+ /* @__PURE__ */ jsx(Header1, { theme: overriddenTheme, children: title }),
1164
+ description && /* @__PURE__ */ jsx(Subheader1, { theme: overriddenTheme, component: "span", children: /* @__PURE__ */ jsx(
1165
+ TextRenderer,
1166
+ {
1167
+ json: description,
1168
+ theme: overriddenTheme
1169
+ }
1170
+ ) })
1171
+ ]
1172
+ }
1173
+ ),
1174
+ items && items.length > 0 && layout === "numbered" && /* @__PURE__ */ jsx("div", { className: "grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8", children: items.map((item, index) => /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", children: [
1175
+ /* @__PURE__ */ jsx("span", { className: "text-3xl font-bold text-primary/30", children: String(index + 1).padStart(2, "0") }),
1176
+ /* @__PURE__ */ jsx(Header4, { theme: overriddenTheme, children: item.title }),
1177
+ item.description && /* @__PURE__ */ jsx(Text2, { theme: overriddenTheme, children: item.description })
1178
+ ] }, index)) }),
1179
+ items && items.length > 0 && layout !== "numbered" && /* @__PURE__ */ jsx(
1180
+ "div",
1181
+ {
1182
+ className: clsx3(
1183
+ layout === "list" && "flex flex-col gap-4",
1184
+ layout === "compact" && "grid grid-cols-2 md:grid-cols-4 gap-4 items-stretch",
1185
+ layout !== "list" && layout !== "compact" && "grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6 items-stretch"
1186
+ ),
1187
+ children: items.map((item, index) => {
1188
+ const Icon = resolveIcon(item.icon);
1189
+ return /* @__PURE__ */ jsx(PageCard, { theme: overriddenTheme, children: /* @__PURE__ */ jsxs(
1190
+ PageCardContent,
1191
+ {
1192
+ theme: overriddenTheme,
1193
+ className: clsx3(
1194
+ "h-full flex gap-3",
1195
+ layout === "list" ? "flex-row items-start" : "flex-col",
1196
+ layout === "compact" && "items-center text-center gap-2"
1197
+ ),
1198
+ children: [
1199
+ /* @__PURE__ */ jsx(
1200
+ "div",
1201
+ {
1202
+ className: clsx3(
1203
+ "flex shrink-0 items-center justify-center rounded-md bg-primary/10 text-primary",
1204
+ layout === "compact" ? "h-8 w-8" : "h-10 w-10"
1205
+ ),
1206
+ children: /* @__PURE__ */ jsx(Icon, { className: "h-5 w-5" })
1207
+ }
1208
+ ),
1209
+ /* @__PURE__ */ jsxs(
1210
+ "div",
1211
+ {
1212
+ className: clsx3(
1213
+ "flex flex-col gap-2",
1214
+ layout === "compact" && "items-center gap-1"
1215
+ ),
1216
+ children: [
1217
+ /* @__PURE__ */ jsx(Header4, { theme: overriddenTheme, children: item.title }),
1218
+ item.description && layout !== "compact" && /* @__PURE__ */ jsx(
1219
+ Text2,
1220
+ {
1221
+ theme: overriddenTheme,
1222
+ children: item.description
1223
+ }
1224
+ )
1225
+ ]
1226
+ }
1227
+ )
1228
+ ]
1229
+ }
1230
+ ) }, index);
1231
+ })
1232
+ }
1233
+ )
1234
+ ] })
1235
+ }
1236
+ );
1237
+ }
1238
+ var createItem = () => ({
1239
+ title: "New feature",
1240
+ description: "",
1241
+ icon: "Sparkles"
1242
+ });
1243
+ function FeaturedEditor({
1244
+ settings,
1245
+ onChange
1246
+ }) {
1247
+ const update = (partial) => onChange({ ...settings, ...partial });
1248
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-col", children: [
1249
+ /* @__PURE__ */ jsxs(FieldGroup, { title: "Content", children: [
1250
+ /* @__PURE__ */ jsx(
1251
+ TextField,
1252
+ {
1253
+ label: "Title",
1254
+ value: settings.title,
1255
+ onChange: (title) => update({ title })
1256
+ }
1257
+ ),
1258
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1 text-sm", children: [
1259
+ /* @__PURE__ */ jsx("span", { className: "font-medium text-foreground", children: "Description" }),
1260
+ /* @__PURE__ */ jsx(
1261
+ Editor,
1262
+ {
1263
+ initialContent: settings.description ?? emptyDoc,
1264
+ onChange: (description) => update({ description }),
1265
+ showToolbar: false,
1266
+ editorClassName: "min-h-[80px]"
1267
+ }
1268
+ )
1269
+ ] }),
1270
+ /* @__PURE__ */ jsx(
1271
+ SelectField,
1272
+ {
1273
+ label: "Header alignment",
1274
+ value: settings.headerAlignment ?? "center",
1275
+ onChange: (headerAlignment) => update({ headerAlignment }),
1276
+ options: [
1277
+ { label: "Left", value: "left" },
1278
+ { label: "Center", value: "center" }
1279
+ ]
1280
+ }
1281
+ )
1282
+ ] }),
1283
+ /* @__PURE__ */ jsx(FieldGroup, { title: "Features", children: /* @__PURE__ */ jsx(
1284
+ ListField,
1285
+ {
1286
+ label: "Feature items",
1287
+ items: settings.items ?? [],
1288
+ onChange: (items) => update({ items }),
1289
+ createItem,
1290
+ addLabel: "Add feature",
1291
+ renderItem: (item, updateItem, remove) => /* @__PURE__ */ jsxs(Fragment, { children: [
1292
+ /* @__PURE__ */ jsx(
1293
+ TextField,
1294
+ {
1295
+ label: "Title",
1296
+ value: item.title,
1297
+ onChange: (title) => updateItem({ title })
1298
+ }
1299
+ ),
1300
+ /* @__PURE__ */ jsx(
1301
+ TextField,
1302
+ {
1303
+ label: "Description",
1304
+ value: item.description,
1305
+ multiline: true,
1306
+ onChange: (description) => updateItem({ description })
1307
+ }
1308
+ ),
1309
+ /* @__PURE__ */ jsx(
1310
+ TextField,
1311
+ {
1312
+ label: "Icon (lucide-react name)",
1313
+ value: item.icon,
1314
+ placeholder: "Globe",
1315
+ onChange: (icon) => updateItem({ icon })
1316
+ }
1317
+ ),
1318
+ /* @__PURE__ */ jsx(
1319
+ "button",
1320
+ {
1321
+ type: "button",
1322
+ className: "self-start text-xs font-medium text-destructive hover:underline",
1323
+ onClick: remove,
1324
+ children: "Remove feature"
1325
+ }
1326
+ )
1327
+ ] })
1328
+ }
1329
+ ) })
1330
+ ] });
1331
+ }
1332
+
1333
+ // src/blocks/featured/defaults.ts
1334
+ var featuredDefaults = {
1335
+ title: "Everything you need to run your business online",
1336
+ headerAlignment: "center",
1337
+ items: [
1338
+ {
1339
+ title: "Website",
1340
+ description: "Design and publish beautiful pages with a drag-and-drop editor.",
1341
+ icon: "Globe"
1342
+ },
1343
+ {
1344
+ title: "Content",
1345
+ description: "Publish articles, documentation, and product updates from one content system.",
1346
+ icon: "Newspaper"
1347
+ },
1348
+ {
1349
+ title: "Support",
1350
+ description: "Support customers with a shared inbox, ticketing, help center, and AI answers.",
1351
+ icon: "LifeBuoy"
1352
+ },
1353
+ {
1354
+ title: "Engage",
1355
+ description: "Grow your audience and send email without leaving FrontLit.",
1356
+ icon: "Mail"
1357
+ }
1358
+ ]
1359
+ };
1360
+
1361
+ // src/blocks/featured/index.ts
1362
+ var Featured = {
1363
+ metadata: featuredMetadata,
1364
+ widget: FeaturedWidget,
1365
+ editor: FeaturedEditor,
1366
+ getDefaultSettings: () => featuredDefaults,
1367
+ layouts: [
1368
+ { id: "grid", name: "Grid" },
1369
+ { id: "list", name: "List" },
1370
+ { id: "compact", name: "Compact" },
1371
+ { id: "numbered", name: "Numbered" }
1372
+ ]
1373
+ };
1374
+
1375
+ // src/blocks/faq/metadata.ts
1376
+ var faqMetadata = {
1377
+ name: "faq",
1378
+ displayName: "FAQs",
1379
+ description: "A list of frequently asked questions, rendered as an accordion.",
1380
+ compatibleWith: ["site", "landing", "blog", "post", "custom"]
1381
+ };
1382
+ function FaqWidget({
1383
+ settings: {
1384
+ title,
1385
+ headerAlignment = "left",
1386
+ items = [],
1387
+ layout = "list",
1388
+ cssId,
1389
+ maxWidth,
1390
+ verticalPadding,
1391
+ background
1392
+ },
1393
+ state: { theme },
1394
+ nextTheme
1395
+ }) {
1396
+ const overriddenTheme = JSON.parse(JSON.stringify(theme.theme));
1397
+ overriddenTheme.structure.page.width = maxWidth || theme.theme.structure.page.width;
1398
+ overriddenTheme.structure.section.padding.y = verticalPadding || theme.theme.structure.section.padding.y;
1399
+ const [expandedIndex, setExpandedIndex] = useState(
1400
+ items.length > 0 ? 0 : null
1401
+ );
1402
+ return /* @__PURE__ */ jsx(
1403
+ Section,
1404
+ {
1405
+ theme: overriddenTheme,
1406
+ id: cssId,
1407
+ background,
1408
+ nextTheme,
1409
+ children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-6", children: [
1410
+ title && /* @__PURE__ */ jsx(
1411
+ "div",
1412
+ {
1413
+ className: clsx3(
1414
+ "flex flex-col",
1415
+ headerAlignment === "center" ? "items-center text-center" : "items-start"
1416
+ ),
1417
+ children: /* @__PURE__ */ jsx(Header2, { theme: overriddenTheme, children: title })
1418
+ }
1419
+ ),
1420
+ items.length > 0 && (() => {
1421
+ const renderItem = (item, index) => {
1422
+ const isOpen = expandedIndex === index;
1423
+ return /* @__PURE__ */ jsxs(
1424
+ "div",
1425
+ {
1426
+ className: layout === "cards" ? "rounded-lg border border-border bg-card px-4 py-4" : layout === "columns" ? "border-b border-border py-4" : "py-4",
1427
+ children: [
1428
+ /* @__PURE__ */ jsxs(
1429
+ "button",
1430
+ {
1431
+ type: "button",
1432
+ className: "flex w-full items-center justify-between gap-4 text-left",
1433
+ onClick: () => setExpandedIndex(
1434
+ isOpen ? null : index
1435
+ ),
1436
+ "aria-expanded": isOpen,
1437
+ children: [
1438
+ /* @__PURE__ */ jsx(
1439
+ Text1,
1440
+ {
1441
+ theme: overriddenTheme,
1442
+ className: "font-medium",
1443
+ children: item.question
1444
+ }
1445
+ ),
1446
+ /* @__PURE__ */ jsx(
1447
+ ChevronDown,
1448
+ {
1449
+ className: clsx3(
1450
+ "h-4 w-4 shrink-0 transition-transform",
1451
+ isOpen && "rotate-180"
1452
+ )
1453
+ }
1454
+ )
1455
+ ]
1456
+ }
1457
+ ),
1458
+ isOpen && /* @__PURE__ */ jsx("div", { className: "pt-3", children: /* @__PURE__ */ jsx(
1459
+ Text1,
1460
+ {
1461
+ theme: overriddenTheme,
1462
+ className: "text-muted-foreground",
1463
+ children: item.answer
1464
+ }
1465
+ ) })
1466
+ ]
1467
+ },
1468
+ item.id
1469
+ );
1470
+ };
1471
+ if (layout === "columns") {
1472
+ const half = Math.ceil(items.length / 2);
1473
+ const left = items.slice(0, half);
1474
+ const right = items.slice(half);
1475
+ return /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 gap-x-10 md:grid-cols-2", children: [
1476
+ /* @__PURE__ */ jsx("div", { className: "flex flex-col", children: left.map(
1477
+ (item, i) => renderItem(item, i)
1478
+ ) }),
1479
+ /* @__PURE__ */ jsx("div", { className: "flex flex-col", children: right.map(
1480
+ (item, i) => renderItem(item, half + i)
1481
+ ) })
1482
+ ] });
1483
+ }
1484
+ return /* @__PURE__ */ jsx(
1485
+ "div",
1486
+ {
1487
+ className: layout === "cards" ? "flex flex-col gap-3" : "flex flex-col divide-y divide-border border-b border-t border-border",
1488
+ children: items.map(
1489
+ (item, index) => renderItem(item, index)
1490
+ )
1491
+ }
1492
+ );
1493
+ })()
1494
+ ] })
1495
+ }
1496
+ );
1497
+ }
1498
+ function FaqEditor({
1499
+ settings,
1500
+ onChange
1501
+ }) {
1502
+ const update = (partial) => onChange({ ...settings, ...partial });
1503
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-col", children: [
1504
+ /* @__PURE__ */ jsxs(FieldGroup, { title: "Header", children: [
1505
+ /* @__PURE__ */ jsx(
1506
+ TextField,
1507
+ {
1508
+ label: "Title",
1509
+ value: settings.title,
1510
+ onChange: (title) => update({ title })
1511
+ }
1512
+ ),
1513
+ /* @__PURE__ */ jsx(
1514
+ SelectField,
1515
+ {
1516
+ label: "Header alignment",
1517
+ value: settings.headerAlignment ?? "left",
1518
+ onChange: (headerAlignment) => update({ headerAlignment }),
1519
+ options: [
1520
+ { label: "Left", value: "left" },
1521
+ { label: "Center", value: "center" }
1522
+ ]
1523
+ }
1524
+ )
1525
+ ] }),
1526
+ /* @__PURE__ */ jsx(FieldGroup, { title: "Questions", children: /* @__PURE__ */ jsx(
1527
+ ListField,
1528
+ {
1529
+ label: "Items",
1530
+ items: settings.items ?? [],
1531
+ onChange: (items) => update({ items }),
1532
+ createItem: () => ({
1533
+ id: generateUniqueId(),
1534
+ question: "New question",
1535
+ answer: ""
1536
+ }),
1537
+ addLabel: "Add question",
1538
+ renderItem: (item, updateItem, remove) => /* @__PURE__ */ jsxs(Fragment, { children: [
1539
+ /* @__PURE__ */ jsx(
1540
+ TextField,
1541
+ {
1542
+ label: "Question",
1543
+ value: item.question,
1544
+ onChange: (question) => updateItem({ question })
1545
+ }
1546
+ ),
1547
+ /* @__PURE__ */ jsx(
1548
+ TextField,
1549
+ {
1550
+ label: "Answer",
1551
+ value: item.answer,
1552
+ multiline: true,
1553
+ onChange: (answer) => updateItem({ answer })
1554
+ }
1555
+ ),
1556
+ /* @__PURE__ */ jsx(
1557
+ "button",
1558
+ {
1559
+ type: "button",
1560
+ className: "self-start text-xs text-destructive",
1561
+ onClick: remove,
1562
+ children: "Remove"
1563
+ }
1564
+ )
1565
+ ] })
1566
+ }
1567
+ ) })
1568
+ ] });
1569
+ }
1570
+
1571
+ // src/blocks/faq/defaults.ts
1572
+ var faqDefaults = {
1573
+ title: "Frequently asked questions",
1574
+ headerAlignment: "left",
1575
+ items: [
1576
+ {
1577
+ id: generateUniqueId(),
1578
+ question: "What can I build with FrontLit?",
1579
+ answer: "A marketing website, articles, documentation, product updates, customer support, and email \u2014 all from one connected dashboard."
1580
+ },
1581
+ {
1582
+ id: generateUniqueId(),
1583
+ question: "Do I need to know how to code?",
1584
+ answer: "No. Every page is assembled from ready-made blocks like this one, and you can customize copy, media and layout without touching code."
1585
+ },
1586
+ {
1587
+ id: generateUniqueId(),
1588
+ question: "Can I use my own domain?",
1589
+ answer: "Yes, every FrontLit site can be connected to a custom domain in a few clicks."
1590
+ },
1591
+ {
1592
+ id: generateUniqueId(),
1593
+ question: "Is there a free plan?",
1594
+ answer: "Yes, you can get started for free and upgrade as your audience grows."
1595
+ }
1596
+ ]
1597
+ };
1598
+
1599
+ // src/blocks/faq/index.ts
1600
+ var Faq = {
1601
+ metadata: faqMetadata,
1602
+ widget: FaqWidget,
1603
+ editor: FaqEditor,
1604
+ getDefaultSettings: () => faqDefaults,
1605
+ layouts: [
1606
+ { id: "list", name: "List" },
1607
+ { id: "cards", name: "Cards" },
1608
+ { id: "columns", name: "Columns" }
1609
+ ]
1610
+ };
1611
+
1612
+ // src/blocks/pricing/metadata.ts
1613
+ var pricingMetadata = {
1614
+ name: "pricing",
1615
+ displayName: "Pricing",
1616
+ description: "A grid of pricing tiers with features and calls to action.",
1617
+ compatibleWith: ["site", "landing", "custom"]
1618
+ };
1619
+ function PricingWidget({
1620
+ settings: {
1621
+ title,
1622
+ description,
1623
+ headerAlignment = "center",
1624
+ tiers,
1625
+ layout = "grid",
1626
+ cssId,
1627
+ maxWidth,
1628
+ verticalPadding,
1629
+ background
1630
+ },
1631
+ state: { theme },
1632
+ nextTheme
1633
+ }) {
1634
+ const overriddenTheme = JSON.parse(JSON.stringify(theme.theme));
1635
+ overriddenTheme.structure.page.width = maxWidth || theme.theme.structure.page.width;
1636
+ overriddenTheme.structure.section.padding.y = verticalPadding || theme.theme.structure.section.padding.y;
1637
+ return /* @__PURE__ */ jsx(
1638
+ Section,
1639
+ {
1640
+ theme: overriddenTheme,
1641
+ id: cssId,
1642
+ background,
1643
+ nextTheme,
1644
+ children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-8", children: [
1645
+ /* @__PURE__ */ jsxs(
1646
+ "div",
1647
+ {
1648
+ className: clsx3(
1649
+ "flex flex-col gap-4",
1650
+ headerAlignment === "center" ? "items-center text-center" : "items-start"
1651
+ ),
1652
+ children: [
1653
+ /* @__PURE__ */ jsx(Header1, { theme: overriddenTheme, children: title }),
1654
+ description && /* @__PURE__ */ jsx(Subheader1, { theme: overriddenTheme, component: "span", children: /* @__PURE__ */ jsx(
1655
+ TextRenderer,
1656
+ {
1657
+ json: description,
1658
+ theme: overriddenTheme
1659
+ }
1660
+ ) })
1661
+ ]
1662
+ }
1663
+ ),
1664
+ tiers && tiers.length > 0 && /* @__PURE__ */ jsx(
1665
+ "div",
1666
+ {
1667
+ className: layout === "stacked" ? "mx-auto flex w-full max-w-xl flex-col gap-6" : "grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6 items-stretch",
1668
+ children: tiers.map((tier, index) => /* @__PURE__ */ jsx(
1669
+ PageCard,
1670
+ {
1671
+ theme: overriddenTheme,
1672
+ className: clsx3(
1673
+ tier.highlighted && "ring-2 ring-primary",
1674
+ layout === "highlight" && tier.highlighted && "relative z-10 shadow-xl md:-translate-y-3"
1675
+ ),
1676
+ children: /* @__PURE__ */ jsxs(
1677
+ PageCardContent,
1678
+ {
1679
+ theme: overriddenTheme,
1680
+ className: "h-full flex flex-col gap-4",
1681
+ children: [
1682
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2", children: [
1683
+ /* @__PURE__ */ jsx(Preheader, { theme: overriddenTheme, children: tier.name }),
1684
+ tier.highlighted && /* @__PURE__ */ jsx(Badge, { theme: overriddenTheme, children: "Most popular" })
1685
+ ] }),
1686
+ /* @__PURE__ */ jsx(PageCardHeader, { theme: overriddenTheme, children: tier.price }),
1687
+ tier.description && /* @__PURE__ */ jsx(Text1, { theme: overriddenTheme, children: tier.description }),
1688
+ tier.features && tier.features.length > 0 && /* @__PURE__ */ jsx("div", { className: "grow flex flex-col gap-2", children: tier.features.map(
1689
+ (feature, i) => /* @__PURE__ */ jsx(
1690
+ Text2,
1691
+ {
1692
+ theme: overriddenTheme,
1693
+ children: feature
1694
+ },
1695
+ i
1696
+ )
1697
+ ) }),
1698
+ tier.buttonAction && tier.buttonCaption && /* @__PURE__ */ jsx(
1699
+ NavLink,
1700
+ {
1701
+ href: tier.buttonAction,
1702
+ className: "w-full mt-auto",
1703
+ children: /* @__PURE__ */ jsx(
1704
+ Button2,
1705
+ {
1706
+ theme: overriddenTheme,
1707
+ variant: tier.highlighted ? "default" : "secondary",
1708
+ className: "w-full",
1709
+ children: tier.buttonCaption
1710
+ }
1711
+ )
1712
+ }
1713
+ )
1714
+ ]
1715
+ }
1716
+ )
1717
+ },
1718
+ index
1719
+ ))
1720
+ }
1721
+ )
1722
+ ] })
1723
+ }
1724
+ );
1725
+ }
1726
+ var createTier = () => ({
1727
+ name: "New plan",
1728
+ price: "$0/mo",
1729
+ description: "",
1730
+ features: [],
1731
+ buttonCaption: "Get started",
1732
+ buttonAction: "#"
1733
+ });
1734
+ function PricingEditor({
1735
+ settings,
1736
+ onChange
1737
+ }) {
1738
+ const update = (partial) => onChange({ ...settings, ...partial });
1739
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-col", children: [
1740
+ /* @__PURE__ */ jsxs(FieldGroup, { title: "Content", children: [
1741
+ /* @__PURE__ */ jsx(
1742
+ TextField,
1743
+ {
1744
+ label: "Title",
1745
+ value: settings.title,
1746
+ onChange: (title) => update({ title })
1747
+ }
1748
+ ),
1749
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1 text-sm", children: [
1750
+ /* @__PURE__ */ jsx("span", { className: "font-medium text-foreground", children: "Description" }),
1751
+ /* @__PURE__ */ jsx(
1752
+ Editor,
1753
+ {
1754
+ initialContent: settings.description ?? emptyDoc,
1755
+ onChange: (description) => update({ description }),
1756
+ showToolbar: false,
1757
+ editorClassName: "min-h-[80px]"
1758
+ }
1759
+ )
1760
+ ] }),
1761
+ /* @__PURE__ */ jsx(
1762
+ SelectField,
1763
+ {
1764
+ label: "Header alignment",
1765
+ value: settings.headerAlignment ?? "center",
1766
+ onChange: (headerAlignment) => update({ headerAlignment }),
1767
+ options: [
1768
+ { label: "Left", value: "left" },
1769
+ { label: "Center", value: "center" }
1770
+ ]
1771
+ }
1772
+ )
1773
+ ] }),
1774
+ /* @__PURE__ */ jsx(FieldGroup, { title: "Pricing tiers", children: /* @__PURE__ */ jsx(
1775
+ ListField,
1776
+ {
1777
+ label: "Tiers",
1778
+ items: settings.tiers ?? [],
1779
+ onChange: (tiers) => update({ tiers }),
1780
+ createItem: createTier,
1781
+ addLabel: "Add tier",
1782
+ renderItem: (tier, updateTier, remove) => /* @__PURE__ */ jsxs(Fragment, { children: [
1783
+ /* @__PURE__ */ jsx(
1784
+ TextField,
1785
+ {
1786
+ label: "Name",
1787
+ value: tier.name,
1788
+ onChange: (name) => updateTier({ name })
1789
+ }
1790
+ ),
1791
+ /* @__PURE__ */ jsx(
1792
+ TextField,
1793
+ {
1794
+ label: "Price",
1795
+ value: tier.price,
1796
+ placeholder: "$29/mo",
1797
+ onChange: (price) => updateTier({ price })
1798
+ }
1799
+ ),
1800
+ /* @__PURE__ */ jsx(
1801
+ TextField,
1802
+ {
1803
+ label: "Description",
1804
+ value: tier.description,
1805
+ multiline: true,
1806
+ onChange: (description) => updateTier({ description })
1807
+ }
1808
+ ),
1809
+ /* @__PURE__ */ jsx(
1810
+ TextField,
1811
+ {
1812
+ label: "Features (one per line)",
1813
+ value: (tier.features ?? []).join("\n"),
1814
+ multiline: true,
1815
+ onChange: (value) => updateTier({
1816
+ features: value.split("\n").map((f) => f.trim()).filter(Boolean)
1817
+ })
1818
+ }
1819
+ ),
1820
+ /* @__PURE__ */ jsx(
1821
+ TextField,
1822
+ {
1823
+ label: "Button text",
1824
+ value: tier.buttonCaption,
1825
+ onChange: (buttonCaption) => updateTier({ buttonCaption })
1826
+ }
1827
+ ),
1828
+ /* @__PURE__ */ jsx(
1829
+ TextField,
1830
+ {
1831
+ label: "Button link",
1832
+ value: tier.buttonAction,
1833
+ onChange: (buttonAction) => updateTier({ buttonAction })
1834
+ }
1835
+ ),
1836
+ /* @__PURE__ */ jsx(
1837
+ ToggleField,
1838
+ {
1839
+ label: "Highlight this tier",
1840
+ value: tier.highlighted,
1841
+ onChange: (highlighted) => updateTier({ highlighted })
1842
+ }
1843
+ ),
1844
+ /* @__PURE__ */ jsx(
1845
+ "button",
1846
+ {
1847
+ type: "button",
1848
+ className: "self-start text-xs font-medium text-destructive hover:underline",
1849
+ onClick: remove,
1850
+ children: "Remove tier"
1851
+ }
1852
+ )
1853
+ ] })
1854
+ }
1855
+ ) })
1856
+ ] });
1857
+ }
1858
+
1859
+ // src/blocks/pricing/defaults.ts
1860
+ var pricingDefaults = {
1861
+ title: "Simple, transparent pricing",
1862
+ headerAlignment: "center",
1863
+ tiers: [
1864
+ {
1865
+ name: "Starter",
1866
+ price: "$0/mo",
1867
+ description: "For hobby sites and side projects.",
1868
+ features: ["1 website", "FrontLit branding", "Community support"],
1869
+ buttonCaption: "Get started",
1870
+ buttonAction: "/help"
1871
+ },
1872
+ {
1873
+ name: "Pro",
1874
+ price: "$29/mo",
1875
+ description: "For growing businesses that need more.",
1876
+ features: [
1877
+ "5 websites",
1878
+ "Custom domain",
1879
+ "Blog + Email",
1880
+ "Priority support"
1881
+ ],
1882
+ buttonCaption: "Talk to us",
1883
+ buttonAction: "/help",
1884
+ highlighted: true
1885
+ },
1886
+ {
1887
+ name: "Business",
1888
+ price: "$79/mo",
1889
+ description: "For teams that need scale and control.",
1890
+ features: [
1891
+ "Unlimited websites",
1892
+ "Help desk & chatbot",
1893
+ "Team seats",
1894
+ "Dedicated support"
1895
+ ],
1896
+ buttonCaption: "Contact sales",
1897
+ buttonAction: "/help"
1898
+ }
1899
+ ]
1900
+ };
1901
+
1902
+ // src/blocks/pricing/index.ts
1903
+ var Pricing = {
1904
+ metadata: pricingMetadata,
1905
+ widget: PricingWidget,
1906
+ editor: PricingEditor,
1907
+ getDefaultSettings: () => pricingDefaults,
1908
+ layouts: [
1909
+ { id: "grid", name: "Grid" },
1910
+ { id: "stacked", name: "Stacked" },
1911
+ { id: "highlight", name: "Highlight" }
1912
+ ]
1913
+ };
1914
+
1915
+ // src/blocks/banner/metadata.ts
1916
+ var bannerMetadata = {
1917
+ name: "banner",
1918
+ displayName: "Banner",
1919
+ description: "A strong, punchy call-to-action strip.",
1920
+ compatibleWith: ["site", "landing", "blog", "post", "custom"]
1921
+ };
1922
+ function BannerWidget({
1923
+ settings: {
1924
+ title,
1925
+ description,
1926
+ buttonCaption,
1927
+ buttonAction,
1928
+ secondaryButtonCaption,
1929
+ secondaryButtonAction,
1930
+ contentAlignment = "center",
1931
+ layout = "stacked",
1932
+ cssId,
1933
+ maxWidth,
1934
+ verticalPadding,
1935
+ background
1936
+ },
1937
+ state: { theme },
1938
+ nextTheme
1939
+ }) {
1940
+ const overriddenTheme = JSON.parse(JSON.stringify(theme.theme));
1941
+ overriddenTheme.structure.page.width = maxWidth || theme.theme.structure.page.width;
1942
+ overriddenTheme.structure.section.padding.y = verticalPadding || theme.theme.structure.section.padding.y;
1943
+ const isCentered = contentAlignment === "center";
1944
+ const isSplit = layout === "split";
1945
+ const isBoxed = layout === "boxed";
1946
+ const heading = /* @__PURE__ */ jsxs(Fragment, { children: [
1947
+ /* @__PURE__ */ jsx(Header1, { theme: overriddenTheme, children: title }),
1948
+ description && /* @__PURE__ */ jsx(Subheader1, { theme: overriddenTheme, component: "span", children: /* @__PURE__ */ jsx(TextRenderer, { json: description, theme: overriddenTheme }) })
1949
+ ] });
1950
+ const buttons = (buttonAction || secondaryButtonAction) && /* @__PURE__ */ jsxs(
1951
+ "div",
1952
+ {
1953
+ className: clsx3(
1954
+ "flex flex-col md:!flex-row gap-2 w-full md:w-auto",
1955
+ isSplit ? "shrink-0" : "mt-2",
1956
+ !isSplit && isCentered ? "md:!justify-center" : ""
1957
+ ),
1958
+ children: [
1959
+ buttonAction && buttonCaption && /* @__PURE__ */ jsx(NavLink, { href: buttonAction, children: /* @__PURE__ */ jsx(
1960
+ Button2,
1961
+ {
1962
+ theme: overriddenTheme,
1963
+ className: "w-full md:min-w-[150px]",
1964
+ children: buttonCaption
1965
+ }
1966
+ ) }),
1967
+ secondaryButtonAction && secondaryButtonCaption && /* @__PURE__ */ jsx(NavLink, { href: secondaryButtonAction, children: /* @__PURE__ */ jsx(
1968
+ Button2,
1969
+ {
1970
+ theme: overriddenTheme,
1971
+ variant: "secondary",
1972
+ className: "w-full md:min-w-[150px]",
1973
+ children: secondaryButtonCaption
1974
+ }
1975
+ ) })
1976
+ ]
1977
+ }
1978
+ );
1979
+ const content = isSplit ? /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-6 md:!flex-row md:items-center md:justify-between", children: [
1980
+ /* @__PURE__ */ jsx("div", { className: "flex flex-col items-start gap-4", children: heading }),
1981
+ buttons
1982
+ ] }) : /* @__PURE__ */ jsxs(
1983
+ "div",
1984
+ {
1985
+ className: clsx3(
1986
+ "flex flex-col gap-4",
1987
+ isCentered ? "items-center text-center" : "items-start"
1988
+ ),
1989
+ children: [
1990
+ heading,
1991
+ buttons
1992
+ ]
1993
+ }
1994
+ );
1995
+ return /* @__PURE__ */ jsx(
1996
+ Section,
1997
+ {
1998
+ theme: overriddenTheme,
1999
+ id: cssId,
2000
+ background,
2001
+ nextTheme,
2002
+ children: isBoxed ? /* @__PURE__ */ jsx(PageCard, { theme: overriddenTheme, className: "mx-auto max-w-3xl", children: /* @__PURE__ */ jsx(
2003
+ PageCardContent,
2004
+ {
2005
+ theme: overriddenTheme,
2006
+ className: "items-center p-8 text-center md:p-12",
2007
+ children: content
2008
+ }
2009
+ ) }) : content
2010
+ }
2011
+ );
2012
+ }
2013
+ function BannerEditor({
2014
+ settings,
2015
+ onChange
2016
+ }) {
2017
+ const update = (partial) => onChange({ ...settings, ...partial });
2018
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-col", children: [
2019
+ /* @__PURE__ */ jsxs(FieldGroup, { title: "Content", children: [
2020
+ /* @__PURE__ */ jsx(
2021
+ TextField,
2022
+ {
2023
+ label: "Title",
2024
+ value: settings.title,
2025
+ onChange: (title) => update({ title })
2026
+ }
2027
+ ),
2028
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1 text-sm", children: [
2029
+ /* @__PURE__ */ jsx("span", { className: "font-medium text-foreground", children: "Description" }),
2030
+ /* @__PURE__ */ jsx(
2031
+ Editor,
2032
+ {
2033
+ initialContent: settings.description ?? emptyDoc,
2034
+ onChange: (description) => update({ description }),
2035
+ showToolbar: false,
2036
+ editorClassName: "min-h-[80px]"
2037
+ }
2038
+ )
2039
+ ] })
2040
+ ] }),
2041
+ /* @__PURE__ */ jsxs(FieldGroup, { title: "Calls to action", children: [
2042
+ /* @__PURE__ */ jsx(
2043
+ TextField,
2044
+ {
2045
+ label: "Primary button text",
2046
+ value: settings.buttonCaption,
2047
+ onChange: (buttonCaption) => update({ buttonCaption })
2048
+ }
2049
+ ),
2050
+ /* @__PURE__ */ jsx(
2051
+ TextField,
2052
+ {
2053
+ label: "Primary button link",
2054
+ value: settings.buttonAction,
2055
+ onChange: (buttonAction) => update({ buttonAction })
2056
+ }
2057
+ ),
2058
+ /* @__PURE__ */ jsx(
2059
+ TextField,
2060
+ {
2061
+ label: "Secondary button text",
2062
+ value: settings.secondaryButtonCaption,
2063
+ onChange: (secondaryButtonCaption) => update({ secondaryButtonCaption })
2064
+ }
2065
+ ),
2066
+ /* @__PURE__ */ jsx(
2067
+ TextField,
2068
+ {
2069
+ label: "Secondary button link",
2070
+ value: settings.secondaryButtonAction,
2071
+ onChange: (secondaryButtonAction) => update({ secondaryButtonAction })
2072
+ }
2073
+ )
2074
+ ] }),
2075
+ /* @__PURE__ */ jsx(FieldGroup, { title: "Layout", children: /* @__PURE__ */ jsx(
2076
+ SelectField,
2077
+ {
2078
+ label: "Content alignment",
2079
+ value: settings.contentAlignment ?? "center",
2080
+ onChange: (contentAlignment) => update({ contentAlignment }),
2081
+ options: [
2082
+ { label: "Left", value: "left" },
2083
+ { label: "Center", value: "center" }
2084
+ ]
2085
+ }
2086
+ ) })
2087
+ ] });
2088
+ }
2089
+
2090
+ // src/blocks/banner/defaults.ts
2091
+ var bannerDefaults = {
2092
+ title: "Ready to launch your site?",
2093
+ buttonCaption: "Contact us",
2094
+ buttonAction: "/help",
2095
+ secondaryButtonCaption: "Read the docs",
2096
+ secondaryButtonAction: "/docs",
2097
+ contentAlignment: "center"
2098
+ };
2099
+
2100
+ // src/blocks/banner/index.ts
2101
+ var Banner = {
2102
+ metadata: bannerMetadata,
2103
+ widget: BannerWidget,
2104
+ editor: BannerEditor,
2105
+ getDefaultSettings: () => bannerDefaults,
2106
+ layouts: [
2107
+ { id: "stacked", name: "Stacked" },
2108
+ { id: "split", name: "Split" },
2109
+ { id: "boxed", name: "Boxed" }
2110
+ ]
2111
+ };
2112
+
2113
+ // src/blocks/email-form/metadata.ts
2114
+ var emailFormMetadata = {
2115
+ name: "email-form",
2116
+ displayName: "Email signup",
2117
+ description: "An email signup form with a title, description and email input.",
2118
+ compatibleWith: ["site", "landing", "blog", "post", "custom"]
2119
+ };
2120
+ async function subscribeEmail(email, pageData) {
2121
+ if (!pageData.teamId || !pageData.publicApiBaseUrl) {
2122
+ await new Promise((resolve) => setTimeout(resolve, 600));
2123
+ return { success: Boolean(email) };
2124
+ }
2125
+ try {
2126
+ const res = await fetch(
2127
+ `${pageData.publicApiBaseUrl}/public/${pageData.teamId}/leads`,
2128
+ {
2129
+ method: "POST",
2130
+ headers: { "content-type": "application/json" },
2131
+ body: JSON.stringify({
2132
+ email,
2133
+ sourcePath: pageData.pageSlug ? `/${pageData.pageSlug}` : void 0
2134
+ })
2135
+ }
2136
+ );
2137
+ if (!res.ok) return { success: false };
2138
+ const body = await res.json();
2139
+ return { success: body.accepted };
2140
+ } catch {
2141
+ return { success: false };
2142
+ }
2143
+ }
2144
+ function EmailFormWidget({
2145
+ settings: {
2146
+ title,
2147
+ description,
2148
+ buttonCaption = "Subscribe",
2149
+ successMessage = "Thanks for subscribing!",
2150
+ alignment = "left",
2151
+ layout = "stacked",
2152
+ cssId,
2153
+ maxWidth,
2154
+ verticalPadding,
2155
+ background
2156
+ },
2157
+ state: { theme },
2158
+ pageData,
2159
+ editing,
2160
+ nextTheme
2161
+ }) {
2162
+ const overriddenTheme = JSON.parse(JSON.stringify(theme.theme));
2163
+ overriddenTheme.structure.page.width = maxWidth || theme.theme.structure.page.width;
2164
+ overriddenTheme.structure.section.padding.y = verticalPadding || theme.theme.structure.section.padding.y;
2165
+ const [email, setEmail] = useState("");
2166
+ const [isSubmitting, setIsSubmitting] = useState(false);
2167
+ const [success, setSuccess] = useState(false);
2168
+ const isCentered = alignment === "center";
2169
+ const onSubmit = async (e) => {
2170
+ e.preventDefault();
2171
+ if (editing) return;
2172
+ setIsSubmitting(true);
2173
+ try {
2174
+ const result = await subscribeEmail(email, pageData);
2175
+ if (result.success) {
2176
+ setSuccess(true);
2177
+ setEmail("");
2178
+ }
2179
+ } finally {
2180
+ setIsSubmitting(false);
2181
+ }
2182
+ };
2183
+ if (layout === "inline") {
2184
+ return /* @__PURE__ */ jsx(
2185
+ Section,
2186
+ {
2187
+ theme: overriddenTheme,
2188
+ id: cssId,
2189
+ background,
2190
+ nextTheme,
2191
+ children: /* @__PURE__ */ jsxs(
2192
+ "form",
2193
+ {
2194
+ onSubmit,
2195
+ className: "flex flex-col items-start gap-4 md:!flex-row md:items-center md:justify-between",
2196
+ children: [
2197
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1", children: [
2198
+ /* @__PURE__ */ jsx(Header1, { theme: overriddenTheme, className: "text-xl", children: title }),
2199
+ description && /* @__PURE__ */ jsx(
2200
+ Subheader1,
2201
+ {
2202
+ theme: overriddenTheme,
2203
+ component: "span",
2204
+ children: /* @__PURE__ */ jsx(
2205
+ TextRenderer,
2206
+ {
2207
+ json: description,
2208
+ theme: overriddenTheme
2209
+ }
2210
+ )
2211
+ }
2212
+ )
2213
+ ] }),
2214
+ success ? /* @__PURE__ */ jsx(Text2, { theme: overriddenTheme, children: successMessage }) : /* @__PURE__ */ jsxs("div", { className: "flex w-full flex-col gap-2 sm:!flex-row md:w-auto md:shrink-0", children: [
2215
+ /* @__PURE__ */ jsx(
2216
+ Input,
2217
+ {
2218
+ theme: overriddenTheme,
2219
+ id: "email",
2220
+ value: email,
2221
+ onChange: (e) => setEmail(e.target.value),
2222
+ placeholder: "Enter your email",
2223
+ type: "email",
2224
+ required: true,
2225
+ className: "md:w-64"
2226
+ }
2227
+ ),
2228
+ /* @__PURE__ */ jsx(
2229
+ Button2,
2230
+ {
2231
+ theme: overriddenTheme,
2232
+ type: "submit",
2233
+ disabled: !editing && (isSubmitting || !email),
2234
+ children: buttonCaption
2235
+ }
2236
+ )
2237
+ ] })
2238
+ ]
2239
+ }
2240
+ )
2241
+ }
2242
+ );
2243
+ }
2244
+ return /* @__PURE__ */ jsx(
2245
+ Section,
2246
+ {
2247
+ theme: overriddenTheme,
2248
+ id: cssId,
2249
+ background,
2250
+ nextTheme,
2251
+ children: /* @__PURE__ */ jsxs(
2252
+ "form",
2253
+ {
2254
+ onSubmit,
2255
+ className: clsx3(
2256
+ "flex gap-4",
2257
+ layout === "split" ? "flex-col md:!flex-row md:items-center md:justify-between md:gap-10" : clsx3(
2258
+ "flex-col",
2259
+ isCentered ? "items-center text-center" : "items-start"
2260
+ )
2261
+ ),
2262
+ children: [
2263
+ /* @__PURE__ */ jsxs(
2264
+ "div",
2265
+ {
2266
+ className: clsx3(
2267
+ "flex flex-col gap-4",
2268
+ layout === "split" ? "items-start" : "contents"
2269
+ ),
2270
+ children: [
2271
+ /* @__PURE__ */ jsx(Header1, { theme: overriddenTheme, children: title }),
2272
+ description && /* @__PURE__ */ jsx(Subheader1, { theme: overriddenTheme, component: "span", children: /* @__PURE__ */ jsx(
2273
+ TextRenderer,
2274
+ {
2275
+ json: description,
2276
+ theme: overriddenTheme
2277
+ }
2278
+ ) })
2279
+ ]
2280
+ }
2281
+ ),
2282
+ success ? /* @__PURE__ */ jsx(Text2, { theme: overriddenTheme, children: successMessage }) : /* @__PURE__ */ jsxs(
2283
+ "div",
2284
+ {
2285
+ className: clsx3(
2286
+ "flex flex-col md:!flex-row md:!items-end gap-2",
2287
+ layout === "split" ? "md:shrink-0" : "w-full",
2288
+ layout !== "split" && isCentered ? "md:!justify-center" : ""
2289
+ ),
2290
+ children: [
2291
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1 items-start", children: [
2292
+ /* @__PURE__ */ jsx(Label, { theme: overriddenTheme, htmlFor: "email", children: "Email" }),
2293
+ /* @__PURE__ */ jsx(
2294
+ Input,
2295
+ {
2296
+ theme: overriddenTheme,
2297
+ id: "email",
2298
+ value: email,
2299
+ onChange: (e) => setEmail(e.target.value),
2300
+ placeholder: "Enter your email",
2301
+ type: "email",
2302
+ required: true
2303
+ }
2304
+ )
2305
+ ] }),
2306
+ /* @__PURE__ */ jsx(
2307
+ Button2,
2308
+ {
2309
+ theme: overriddenTheme,
2310
+ type: "submit",
2311
+ disabled: !editing && (isSubmitting || !email),
2312
+ children: buttonCaption
2313
+ }
2314
+ )
2315
+ ]
2316
+ }
2317
+ )
2318
+ ]
2319
+ }
2320
+ )
2321
+ }
2322
+ );
2323
+ }
2324
+ function EmailFormEditor({
2325
+ settings,
2326
+ onChange
2327
+ }) {
2328
+ const update = (partial) => onChange({ ...settings, ...partial });
2329
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-col", children: [
2330
+ /* @__PURE__ */ jsxs(FieldGroup, { title: "Content", children: [
2331
+ /* @__PURE__ */ jsx(
2332
+ TextField,
2333
+ {
2334
+ label: "Title",
2335
+ value: settings.title,
2336
+ onChange: (title) => update({ title })
2337
+ }
2338
+ ),
2339
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1 text-sm", children: [
2340
+ /* @__PURE__ */ jsx("span", { className: "font-medium text-foreground", children: "Description" }),
2341
+ /* @__PURE__ */ jsx(
2342
+ Editor,
2343
+ {
2344
+ initialContent: settings.description ?? emptyDoc,
2345
+ onChange: (description) => update({ description }),
2346
+ showToolbar: false,
2347
+ editorClassName: "min-h-[80px]"
2348
+ }
2349
+ )
2350
+ ] })
2351
+ ] }),
2352
+ /* @__PURE__ */ jsxs(FieldGroup, { title: "Form", children: [
2353
+ /* @__PURE__ */ jsx(
2354
+ TextField,
2355
+ {
2356
+ label: "Button text",
2357
+ value: settings.buttonCaption,
2358
+ onChange: (buttonCaption) => update({ buttonCaption })
2359
+ }
2360
+ ),
2361
+ /* @__PURE__ */ jsx(
2362
+ TextField,
2363
+ {
2364
+ label: "Success message",
2365
+ value: settings.successMessage,
2366
+ onChange: (successMessage) => update({ successMessage })
2367
+ }
2368
+ )
2369
+ ] }),
2370
+ /* @__PURE__ */ jsx(FieldGroup, { title: "Layout", children: /* @__PURE__ */ jsx(
2371
+ SelectField,
2372
+ {
2373
+ label: "Alignment",
2374
+ value: settings.alignment ?? "left",
2375
+ onChange: (alignment) => update({ alignment }),
2376
+ options: [
2377
+ { label: "Left", value: "left" },
2378
+ { label: "Center", value: "center" }
2379
+ ]
2380
+ }
2381
+ ) })
2382
+ ] });
2383
+ }
2384
+
2385
+ // src/blocks/email-form/defaults.ts
2386
+ var emailFormDefaults = {
2387
+ title: "Get product updates in your inbox",
2388
+ buttonCaption: "Subscribe",
2389
+ successMessage: "Thanks for subscribing! Check your inbox soon.",
2390
+ alignment: "left"
2391
+ };
2392
+
2393
+ // src/blocks/email-form/index.ts
2394
+ var EmailForm = {
2395
+ metadata: emailFormMetadata,
2396
+ widget: EmailFormWidget,
2397
+ editor: EmailFormEditor,
2398
+ getDefaultSettings: () => emailFormDefaults,
2399
+ layouts: [
2400
+ { id: "stacked", name: "Stacked" },
2401
+ { id: "split", name: "Split" },
2402
+ { id: "inline", name: "Inline" }
2403
+ ]
2404
+ };
2405
+
2406
+ // src/blocks/support-form/metadata.ts
2407
+ var supportFormMetadata = {
2408
+ name: "support-form",
2409
+ displayName: "Support form",
2410
+ description: "Raises a support ticket, suggesting matching help articles before the visitor submits.",
2411
+ compatibleWith: ["site", "landing", "custom"]
2412
+ };
2413
+ function suggestionHref(item) {
2414
+ if (item.kind === "blog") return `/blog/${item.slug}`;
2415
+ if (item.kind === "changelog") return `/changelog`;
2416
+ return `/docs/${item.slug}`;
2417
+ }
2418
+ async function searchDocumentation(query, pageData) {
2419
+ if (!pageData.teamId || !pageData.publicApiBaseUrl || query.trim().length < 3)
2420
+ return [];
2421
+ try {
2422
+ const res = await fetch(
2423
+ `${pageData.publicApiBaseUrl}/public/${pageData.teamId}/content/documentation/search?q=${encodeURIComponent(query)}&k=3`
2424
+ );
2425
+ if (!res.ok) return [];
2426
+ const body = await res.json();
2427
+ const seen = /* @__PURE__ */ new Set();
2428
+ return body.items.filter(
2429
+ (item) => seen.has(item.documentId) ? false : (seen.add(item.documentId), true)
2430
+ );
2431
+ } catch {
2432
+ return [];
2433
+ }
2434
+ }
2435
+ async function submitTicket(input, pageData) {
2436
+ if (!pageData.teamId || !pageData.publicApiBaseUrl) {
2437
+ await new Promise((resolve) => setTimeout(resolve, 600));
2438
+ return { success: true };
2439
+ }
2440
+ try {
2441
+ const res = await fetch(
2442
+ `${pageData.publicApiBaseUrl}/public/${pageData.teamId}/tickets`,
2443
+ {
2444
+ method: "POST",
2445
+ headers: { "content-type": "application/json" },
2446
+ body: JSON.stringify({ ...input, source: "form" })
2447
+ }
2448
+ );
2449
+ return { success: res.ok };
2450
+ } catch {
2451
+ return { success: false };
2452
+ }
2453
+ }
2454
+ function SupportFormWidget({
2455
+ settings: {
2456
+ title,
2457
+ description,
2458
+ buttonCaption = "Submit ticket",
2459
+ successMessage = "Thanks \u2014 we've got your ticket and will reply by email soon.",
2460
+ alignment = "left",
2461
+ cssId,
2462
+ maxWidth,
2463
+ verticalPadding,
2464
+ background
2465
+ },
2466
+ state: { theme },
2467
+ pageData,
2468
+ editing,
2469
+ nextTheme
2470
+ }) {
2471
+ const overriddenTheme = JSON.parse(JSON.stringify(theme.theme));
2472
+ overriddenTheme.structure.page.width = maxWidth || theme.theme.structure.page.width;
2473
+ overriddenTheme.structure.section.padding.y = verticalPadding || theme.theme.structure.section.padding.y;
2474
+ const [subject, setSubject] = useState("");
2475
+ const [message, setMessage] = useState("");
2476
+ const [email, setEmail] = useState("");
2477
+ const [name, setName] = useState("");
2478
+ const [suggestions, setSuggestions] = useState(
2479
+ []
2480
+ );
2481
+ const [isSubmitting, setIsSubmitting] = useState(false);
2482
+ const [success, setSuccess] = useState(false);
2483
+ const debounceRef = useRef(null);
2484
+ useEffect(() => {
2485
+ if (editing) return;
2486
+ if (debounceRef.current) clearTimeout(debounceRef.current);
2487
+ debounceRef.current = setTimeout(() => {
2488
+ searchDocumentation(subject, pageData).then(setSuggestions);
2489
+ }, 400);
2490
+ return () => {
2491
+ if (debounceRef.current) clearTimeout(debounceRef.current);
2492
+ };
2493
+ }, [subject]);
2494
+ const onSubmit = async (e) => {
2495
+ e.preventDefault();
2496
+ if (editing) return;
2497
+ setIsSubmitting(true);
2498
+ try {
2499
+ const result = await submitTicket(
2500
+ { subject, message, email, name: name || void 0 },
2501
+ pageData
2502
+ );
2503
+ if (result.success) {
2504
+ setSuccess(true);
2505
+ setSubject("");
2506
+ setMessage("");
2507
+ setEmail("");
2508
+ setName("");
2509
+ setSuggestions([]);
2510
+ }
2511
+ } finally {
2512
+ setIsSubmitting(false);
2513
+ }
2514
+ };
2515
+ const isCentered = alignment === "center";
2516
+ const inputStyles = overriddenTheme.interactives.input;
2517
+ const inputTypography = overriddenTheme.typography.input;
2518
+ const cardStyles = overriddenTheme.interactives.card;
2519
+ return /* @__PURE__ */ jsxs(
2520
+ Section,
2521
+ {
2522
+ theme: overriddenTheme,
2523
+ id: cssId,
2524
+ background,
2525
+ nextTheme,
2526
+ children: [
2527
+ /* @__PURE__ */ jsxs(
2528
+ "div",
2529
+ {
2530
+ className: clsx3(
2531
+ "flex flex-col gap-4",
2532
+ isCentered ? "items-center text-center" : "items-start"
2533
+ ),
2534
+ children: [
2535
+ /* @__PURE__ */ jsx(Header1, { theme: overriddenTheme, children: title }),
2536
+ description && /* @__PURE__ */ jsx(Subheader1, { theme: overriddenTheme, component: "span", children: /* @__PURE__ */ jsx(
2537
+ TextRenderer,
2538
+ {
2539
+ json: description,
2540
+ theme: overriddenTheme
2541
+ }
2542
+ ) })
2543
+ ]
2544
+ }
2545
+ ),
2546
+ success ? /* @__PURE__ */ jsx(Text2, { theme: overriddenTheme, className: "mt-4", children: successMessage }) : /* @__PURE__ */ jsxs(
2547
+ "form",
2548
+ {
2549
+ onSubmit,
2550
+ className: "mt-4 flex w-full max-w-lg flex-col gap-3",
2551
+ children: [
2552
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1 items-start", children: [
2553
+ /* @__PURE__ */ jsx(Label, { theme: overriddenTheme, htmlFor: "subject", children: "Subject" }),
2554
+ /* @__PURE__ */ jsx(
2555
+ Input,
2556
+ {
2557
+ theme: overriddenTheme,
2558
+ id: "subject",
2559
+ value: subject,
2560
+ onChange: (e) => setSubject(e.target.value),
2561
+ placeholder: "What do you need help with?",
2562
+ required: true,
2563
+ className: "w-full"
2564
+ }
2565
+ )
2566
+ ] }),
2567
+ suggestions.length > 0 && /* @__PURE__ */ jsxs(
2568
+ "div",
2569
+ {
2570
+ className: cn(
2571
+ "rounded-lg border border-border bg-card p-3 text-sm text-card-foreground shadow-sm",
2572
+ cardStyles.border?.width,
2573
+ cardStyles.border?.radius,
2574
+ cardStyles.border?.style,
2575
+ cardStyles.shadow
2576
+ ),
2577
+ children: [
2578
+ /* @__PURE__ */ jsx("p", { className: "mb-1 font-medium", children: "This might help:" }),
2579
+ /* @__PURE__ */ jsx("ul", { className: "list-inside list-disc", children: suggestions.map((doc) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx(
2580
+ "a",
2581
+ {
2582
+ href: suggestionHref(doc),
2583
+ target: "_blank",
2584
+ rel: "noreferrer",
2585
+ className: "underline",
2586
+ children: doc.title ?? doc.slug
2587
+ }
2588
+ ) }, doc.documentId)) })
2589
+ ]
2590
+ }
2591
+ ),
2592
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1 items-start", children: [
2593
+ /* @__PURE__ */ jsx(Label, { theme: overriddenTheme, htmlFor: "message", children: "Message" }),
2594
+ /* @__PURE__ */ jsx(
2595
+ "textarea",
2596
+ {
2597
+ id: "message",
2598
+ value: message,
2599
+ onChange: (e) => setMessage(e.target.value),
2600
+ placeholder: "Describe the issue\u2026",
2601
+ required: true,
2602
+ rows: 4,
2603
+ className: cn(
2604
+ "w-full border border-input bg-background px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
2605
+ inputTypography.fontFamily,
2606
+ inputTypography.fontSize,
2607
+ inputTypography.fontWeight,
2608
+ inputTypography.lineHeight,
2609
+ inputTypography.letterSpacing,
2610
+ inputStyles.border?.width,
2611
+ inputStyles.border?.style,
2612
+ inputStyles.border?.radius,
2613
+ inputStyles.shadow,
2614
+ inputStyles.custom
2615
+ )
2616
+ }
2617
+ )
2618
+ ] }),
2619
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-3 sm:!flex-row", children: [
2620
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-1 flex-col gap-1 items-start", children: [
2621
+ /* @__PURE__ */ jsx(Label, { theme: overriddenTheme, htmlFor: "email", children: "Email" }),
2622
+ /* @__PURE__ */ jsx(
2623
+ Input,
2624
+ {
2625
+ theme: overriddenTheme,
2626
+ id: "email",
2627
+ type: "email",
2628
+ value: email,
2629
+ onChange: (e) => setEmail(e.target.value),
2630
+ placeholder: "you@example.com",
2631
+ required: true,
2632
+ className: "w-full"
2633
+ }
2634
+ )
2635
+ ] }),
2636
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-1 flex-col gap-1 items-start", children: [
2637
+ /* @__PURE__ */ jsx(Label, { theme: overriddenTheme, htmlFor: "name", children: "Name (optional)" }),
2638
+ /* @__PURE__ */ jsx(
2639
+ Input,
2640
+ {
2641
+ theme: overriddenTheme,
2642
+ id: "name",
2643
+ value: name,
2644
+ onChange: (e) => setName(e.target.value),
2645
+ placeholder: "Your name",
2646
+ className: "w-full"
2647
+ }
2648
+ )
2649
+ ] })
2650
+ ] }),
2651
+ /* @__PURE__ */ jsx(
2652
+ Button2,
2653
+ {
2654
+ theme: overriddenTheme,
2655
+ type: "submit",
2656
+ disabled: !editing && (isSubmitting || !subject || !message || !email),
2657
+ className: "self-start",
2658
+ children: buttonCaption
2659
+ }
2660
+ )
2661
+ ]
2662
+ }
2663
+ )
2664
+ ]
2665
+ }
2666
+ );
2667
+ }
2668
+ function SupportFormEditor({
2669
+ settings,
2670
+ onChange
2671
+ }) {
2672
+ const update = (partial) => onChange({ ...settings, ...partial });
2673
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-col", children: [
2674
+ /* @__PURE__ */ jsxs(FieldGroup, { title: "Content", children: [
2675
+ /* @__PURE__ */ jsx(
2676
+ TextField,
2677
+ {
2678
+ label: "Title",
2679
+ value: settings.title,
2680
+ onChange: (title) => update({ title })
2681
+ }
2682
+ ),
2683
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1 text-sm", children: [
2684
+ /* @__PURE__ */ jsx("span", { className: "font-medium text-foreground", children: "Description" }),
2685
+ /* @__PURE__ */ jsx(
2686
+ Editor,
2687
+ {
2688
+ initialContent: settings.description ?? emptyDoc,
2689
+ onChange: (description) => update({ description }),
2690
+ showToolbar: false,
2691
+ editorClassName: "min-h-[80px]"
2692
+ }
2693
+ )
2694
+ ] })
2695
+ ] }),
2696
+ /* @__PURE__ */ jsxs(FieldGroup, { title: "Form", children: [
2697
+ /* @__PURE__ */ jsx(
2698
+ TextField,
2699
+ {
2700
+ label: "Button text",
2701
+ value: settings.buttonCaption,
2702
+ onChange: (buttonCaption) => update({ buttonCaption })
2703
+ }
2704
+ ),
2705
+ /* @__PURE__ */ jsx(
2706
+ TextField,
2707
+ {
2708
+ label: "Success message",
2709
+ value: settings.successMessage,
2710
+ onChange: (successMessage) => update({ successMessage })
2711
+ }
2712
+ )
2713
+ ] }),
2714
+ /* @__PURE__ */ jsx(FieldGroup, { title: "Layout", children: /* @__PURE__ */ jsx(
2715
+ SelectField,
2716
+ {
2717
+ label: "Alignment",
2718
+ value: settings.alignment ?? "left",
2719
+ onChange: (alignment) => update({ alignment }),
2720
+ options: [
2721
+ { label: "Left", value: "left" },
2722
+ { label: "Center", value: "center" }
2723
+ ]
2724
+ }
2725
+ ) })
2726
+ ] });
2727
+ }
2728
+
2729
+ // src/blocks/support-form/defaults.ts
2730
+ var supportFormDefaults = {
2731
+ title: "Need help?",
2732
+ buttonCaption: "Submit ticket",
2733
+ successMessage: "Thanks \u2014 we've got your ticket and will reply by email soon.",
2734
+ alignment: "left"
2735
+ };
2736
+
2737
+ // src/blocks/support-form/index.ts
2738
+ var SupportForm = {
2739
+ metadata: supportFormMetadata,
2740
+ widget: SupportFormWidget,
2741
+ editor: SupportFormEditor,
2742
+ getDefaultSettings: () => supportFormDefaults,
2743
+ layouts: [{ id: "stacked", name: "Stacked" }]
2744
+ };
2745
+
2746
+ // src/blocks/footer/metadata.ts
2747
+ var footerMetadata = {
2748
+ name: "footer",
2749
+ displayName: "Footer",
2750
+ description: "The site footer, pinned to the bottom of every page.",
2751
+ compatibleWith: ["site", "landing", "blog", "post", "custom"]
2752
+ };
2753
+ function FooterWidget({
2754
+ settings: {
2755
+ logoText,
2756
+ tagline,
2757
+ columns = [],
2758
+ copyrightText,
2759
+ layout = "columns",
2760
+ cssId,
2761
+ maxWidth,
2762
+ verticalPadding,
2763
+ background
2764
+ },
2765
+ state: { theme },
2766
+ nextTheme
2767
+ }) {
2768
+ const overriddenTheme = JSON.parse(JSON.stringify(theme.theme));
2769
+ overriddenTheme.structure.page.width = maxWidth || theme.theme.structure.page.width;
2770
+ overriddenTheme.structure.section.padding.y = verticalPadding || theme.theme.structure.section.padding.y;
2771
+ return /* @__PURE__ */ jsxs(
2772
+ Section,
2773
+ {
2774
+ theme: overriddenTheme,
2775
+ component: "footer",
2776
+ id: cssId,
2777
+ background,
2778
+ nextTheme,
2779
+ className: "border-t",
2780
+ children: [
2781
+ layout === "minimal" ? /* @__PURE__ */ jsxs("div", { className: "flex w-full flex-wrap items-center justify-between gap-4", children: [
2782
+ logoText && /* @__PURE__ */ jsx(Header4, { theme: overriddenTheme, children: logoText }),
2783
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-x-6 gap-y-2", children: [
2784
+ columns.flatMap(
2785
+ (column) => column.links.map((link) => /* @__PURE__ */ jsx(
2786
+ NavLink,
2787
+ {
2788
+ href: link.href,
2789
+ className: "no-underline",
2790
+ children: /* @__PURE__ */ jsx(
2791
+ Link,
2792
+ {
2793
+ theme: overriddenTheme,
2794
+ className: "text-xs",
2795
+ children: link.label
2796
+ }
2797
+ )
2798
+ },
2799
+ `${column.id}-${link.id}`
2800
+ ))
2801
+ ),
2802
+ copyrightText && /* @__PURE__ */ jsx(Caption, { theme: overriddenTheme, children: copyrightText })
2803
+ ] })
2804
+ ] }) : layout === "centered" ? /* @__PURE__ */ jsxs("div", { className: "flex w-full flex-col items-center gap-6 text-center", children: [
2805
+ /* @__PURE__ */ jsxs("div", { className: "flex max-w-sm flex-col items-center gap-3", children: [
2806
+ logoText && /* @__PURE__ */ jsx(Header3, { theme: overriddenTheme, children: logoText }),
2807
+ tagline && /* @__PURE__ */ jsx(Text2, { theme: overriddenTheme, children: tagline })
2808
+ ] }),
2809
+ /* @__PURE__ */ jsx("div", { className: "flex flex-wrap justify-center gap-x-6 gap-y-2", children: columns.flatMap(
2810
+ (column) => column.links.map((link) => /* @__PURE__ */ jsx(
2811
+ NavLink,
2812
+ {
2813
+ href: link.href,
2814
+ className: "no-underline",
2815
+ children: /* @__PURE__ */ jsx(
2816
+ Link,
2817
+ {
2818
+ theme: overriddenTheme,
2819
+ className: "text-xs",
2820
+ children: link.label
2821
+ }
2822
+ )
2823
+ },
2824
+ `${column.id}-${link.id}`
2825
+ ))
2826
+ ) })
2827
+ ] }) : /* @__PURE__ */ jsxs("div", { className: "flex w-full flex-col gap-8 lg:!flex-row lg:justify-between", children: [
2828
+ /* @__PURE__ */ jsxs("div", { className: "flex max-w-sm flex-col gap-3", children: [
2829
+ logoText && /* @__PURE__ */ jsx(Header3, { theme: overriddenTheme, children: logoText }),
2830
+ tagline && /* @__PURE__ */ jsx(Text2, { theme: overriddenTheme, children: tagline })
2831
+ ] }),
2832
+ /* @__PURE__ */ jsx("div", { className: "flex flex-col flex-wrap gap-8 lg:!flex-row", children: columns.map((column) => /* @__PURE__ */ jsxs(
2833
+ "div",
2834
+ {
2835
+ className: "flex flex-col gap-3 lg:min-w-[140px]",
2836
+ children: [
2837
+ /* @__PURE__ */ jsx(
2838
+ Header4,
2839
+ {
2840
+ theme: overriddenTheme,
2841
+ className: "text-sm",
2842
+ children: column.title
2843
+ }
2844
+ ),
2845
+ /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-2", children: column.links.map((link) => /* @__PURE__ */ jsx(
2846
+ NavLink,
2847
+ {
2848
+ href: link.href,
2849
+ className: "no-underline",
2850
+ children: /* @__PURE__ */ jsx(
2851
+ Link,
2852
+ {
2853
+ theme: overriddenTheme,
2854
+ className: "text-xs",
2855
+ children: link.label
2856
+ }
2857
+ )
2858
+ },
2859
+ link.id
2860
+ )) })
2861
+ ]
2862
+ },
2863
+ column.id
2864
+ )) })
2865
+ ] }),
2866
+ copyrightText && layout !== "minimal" && /* @__PURE__ */ jsx(
2867
+ "div",
2868
+ {
2869
+ className: layout === "centered" ? "mt-8 border-t pt-6 text-center" : "mt-8 border-t pt-6",
2870
+ children: /* @__PURE__ */ jsx(Caption, { theme: overriddenTheme, children: copyrightText })
2871
+ }
2872
+ )
2873
+ ]
2874
+ }
2875
+ );
2876
+ }
2877
+ function FooterEditor({
2878
+ settings,
2879
+ onChange
2880
+ }) {
2881
+ const update = (partial) => onChange({ ...settings, ...partial });
2882
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-col", children: [
2883
+ /* @__PURE__ */ jsxs(FieldGroup, { title: "Brand", children: [
2884
+ /* @__PURE__ */ jsx(
2885
+ TextField,
2886
+ {
2887
+ label: "Logo text",
2888
+ value: settings.logoText,
2889
+ onChange: (logoText) => update({ logoText })
2890
+ }
2891
+ ),
2892
+ /* @__PURE__ */ jsx(
2893
+ TextField,
2894
+ {
2895
+ label: "Tagline",
2896
+ value: settings.tagline,
2897
+ multiline: true,
2898
+ onChange: (tagline) => update({ tagline })
2899
+ }
2900
+ )
2901
+ ] }),
2902
+ /* @__PURE__ */ jsx(FieldGroup, { title: "Link columns", children: /* @__PURE__ */ jsx(
2903
+ ListField,
2904
+ {
2905
+ label: "Columns",
2906
+ items: settings.columns ?? [],
2907
+ onChange: (columns) => update({ columns }),
2908
+ createItem: () => ({
2909
+ id: generateUniqueId(),
2910
+ title: "New column",
2911
+ links: []
2912
+ }),
2913
+ addLabel: "Add column",
2914
+ renderItem: (column, updateColumn, removeColumn) => /* @__PURE__ */ jsxs(Fragment, { children: [
2915
+ /* @__PURE__ */ jsx(
2916
+ TextField,
2917
+ {
2918
+ label: "Column title",
2919
+ value: column.title,
2920
+ onChange: (title) => updateColumn({ title })
2921
+ }
2922
+ ),
2923
+ /* @__PURE__ */ jsx(
2924
+ ListField,
2925
+ {
2926
+ label: "Links",
2927
+ items: column.links,
2928
+ onChange: (links) => updateColumn({ links }),
2929
+ createItem: () => ({
2930
+ id: generateUniqueId(),
2931
+ label: "New link",
2932
+ href: "#"
2933
+ }),
2934
+ addLabel: "Add link",
2935
+ renderItem: (link, updateLink, removeLink) => /* @__PURE__ */ jsxs(Fragment, { children: [
2936
+ /* @__PURE__ */ jsx(
2937
+ TextField,
2938
+ {
2939
+ label: "Label",
2940
+ value: link.label,
2941
+ onChange: (label) => updateLink({ label })
2942
+ }
2943
+ ),
2944
+ /* @__PURE__ */ jsx(
2945
+ TextField,
2946
+ {
2947
+ label: "Link",
2948
+ value: link.href,
2949
+ onChange: (href) => updateLink({ href })
2950
+ }
2951
+ ),
2952
+ /* @__PURE__ */ jsx(
2953
+ "button",
2954
+ {
2955
+ type: "button",
2956
+ className: "self-start text-xs text-destructive",
2957
+ onClick: removeLink,
2958
+ children: "Remove link"
2959
+ }
2960
+ )
2961
+ ] })
2962
+ }
2963
+ ),
2964
+ /* @__PURE__ */ jsx(
2965
+ "button",
2966
+ {
2967
+ type: "button",
2968
+ className: "self-start text-xs text-destructive",
2969
+ onClick: removeColumn,
2970
+ children: "Remove column"
2971
+ }
2972
+ )
2973
+ ] })
2974
+ }
2975
+ ) }),
2976
+ /* @__PURE__ */ jsx(FieldGroup, { title: "Copyright", children: /* @__PURE__ */ jsx(
2977
+ TextField,
2978
+ {
2979
+ label: "Copyright text",
2980
+ value: settings.copyrightText,
2981
+ onChange: (copyrightText) => update({ copyrightText })
2982
+ }
2983
+ ) })
2984
+ ] });
2985
+ }
2986
+
2987
+ // src/blocks/footer/defaults.ts
2988
+ var footerDefaults = {
2989
+ logoText: "FrontLit",
2990
+ tagline: "Build your website, publish content, support users, and grow your audience from one dashboard.",
2991
+ columns: [
2992
+ {
2993
+ id: generateUniqueId(),
2994
+ title: "Product",
2995
+ links: [
2996
+ {
2997
+ id: generateUniqueId(),
2998
+ label: "Features",
2999
+ href: "/#features"
3000
+ },
3001
+ { id: generateUniqueId(), label: "Pricing", href: "/#pricing" },
3002
+ {
3003
+ id: generateUniqueId(),
3004
+ label: "Changelog",
3005
+ href: "/changelog"
3006
+ }
3007
+ ]
3008
+ },
3009
+ {
3010
+ id: generateUniqueId(),
3011
+ title: "Resources",
3012
+ links: [
3013
+ { id: generateUniqueId(), label: "Blog", href: "/blog" },
3014
+ { id: generateUniqueId(), label: "Help center", href: "/help" },
3015
+ { id: generateUniqueId(), label: "Docs", href: "/docs" }
3016
+ ]
3017
+ },
3018
+ {
3019
+ id: generateUniqueId(),
3020
+ title: "Legal",
3021
+ links: [
3022
+ {
3023
+ id: generateUniqueId(),
3024
+ label: "Terms of use",
3025
+ href: "/terms"
3026
+ },
3027
+ {
3028
+ id: generateUniqueId(),
3029
+ label: "Privacy policy",
3030
+ href: "/privacy"
3031
+ }
3032
+ ]
3033
+ }
3034
+ ],
3035
+ copyrightText: "\xA9 FrontLit. All rights reserved."
3036
+ };
3037
+
3038
+ // src/blocks/footer/index.ts
3039
+ var Footer = {
3040
+ metadata: footerMetadata,
3041
+ widget: FooterWidget,
3042
+ editor: FooterEditor,
3043
+ getDefaultSettings: () => footerDefaults,
3044
+ layouts: [
3045
+ { id: "columns", name: "Columns" },
3046
+ { id: "centered", name: "Centered" },
3047
+ { id: "minimal", name: "Minimal" }
3048
+ ]
3049
+ };
3050
+
3051
+ // src/blocks/rich-text/metadata.ts
3052
+ var richTextMetadata = {
3053
+ name: "rich-text",
3054
+ displayName: "Rich text",
3055
+ description: "A block of formatted text \u2014 paragraphs, headings, lists and links \u2014 with no title or media.",
3056
+ compatibleWith: ["site", "landing", "blog", "post", "custom"]
3057
+ };
3058
+ function RichTextWidget({
3059
+ settings: {
3060
+ text,
3061
+ alignment = "left",
3062
+ cssId,
3063
+ maxWidth,
3064
+ verticalPadding,
3065
+ background
3066
+ },
3067
+ state: { theme },
3068
+ nextTheme
3069
+ }) {
3070
+ const overriddenTheme = JSON.parse(JSON.stringify(theme.theme));
3071
+ overriddenTheme.structure.page.width = maxWidth || theme.theme.structure.page.width;
3072
+ overriddenTheme.structure.section.padding.y = verticalPadding || theme.theme.structure.section.padding.y;
3073
+ if (!text) return null;
3074
+ return /* @__PURE__ */ jsx(
3075
+ Section,
3076
+ {
3077
+ theme: overriddenTheme,
3078
+ id: cssId,
3079
+ background,
3080
+ nextTheme,
3081
+ children: /* @__PURE__ */ jsx("div", { style: { textAlign: alignment }, children: /* @__PURE__ */ jsx(Text1, { theme: overriddenTheme, component: "span", children: /* @__PURE__ */ jsx(TextRenderer, { json: text, theme: overriddenTheme }) }) })
3082
+ }
3083
+ );
3084
+ }
3085
+ function RichTextEditor({
3086
+ settings,
3087
+ onChange
3088
+ }) {
3089
+ const update = (partial) => onChange({ ...settings, ...partial });
3090
+ return /* @__PURE__ */ jsx("div", { className: "flex flex-col", children: /* @__PURE__ */ jsxs(FieldGroup, { title: "Content", children: [
3091
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1 text-sm", children: [
3092
+ /* @__PURE__ */ jsx("span", { className: "font-medium text-foreground", children: "Text" }),
3093
+ /* @__PURE__ */ jsx(
3094
+ Editor,
3095
+ {
3096
+ initialContent: settings.text ?? emptyDoc,
3097
+ onChange: (text) => update({ text }),
3098
+ editorClassName: "min-h-[160px]"
3099
+ }
3100
+ )
3101
+ ] }),
3102
+ /* @__PURE__ */ jsx(
3103
+ SelectField,
3104
+ {
3105
+ label: "Alignment",
3106
+ value: settings.alignment ?? "left",
3107
+ onChange: (alignment) => update({ alignment }),
3108
+ options: [
3109
+ { label: "Left", value: "left" },
3110
+ { label: "Center", value: "center" },
3111
+ { label: "Right", value: "right" }
3112
+ ]
3113
+ }
3114
+ )
3115
+ ] }) });
3116
+ }
3117
+
3118
+ // src/blocks/rich-text/defaults.ts
3119
+ var richTextDefaults = {
3120
+ text: {
3121
+ type: "doc",
3122
+ content: [
3123
+ {
3124
+ type: "paragraph",
3125
+ content: [
3126
+ {
3127
+ type: "text",
3128
+ text: "Write anything here \u2014 an announcement, a longer explanation, or a passage of prose that doesn't need a title or media."
3129
+ }
3130
+ ]
3131
+ }
3132
+ ]
3133
+ },
3134
+ alignment: "left"
3135
+ };
3136
+
3137
+ // src/blocks/rich-text/index.ts
3138
+ var RichText = {
3139
+ metadata: richTextMetadata,
3140
+ widget: RichTextWidget,
3141
+ editor: RichTextEditor,
3142
+ getDefaultSettings: () => richTextDefaults
3143
+ };
3144
+
3145
+ // src/blocks/grid/metadata.ts
3146
+ var gridMetadata = {
3147
+ name: "grid",
3148
+ displayName: "Grid",
3149
+ description: "A header with a page-level call to action, plus a responsive grid of cards \u2014 each with its own icon or image, title, description and link.",
3150
+ compatibleWith: ["site", "landing", "blog", "post", "custom"]
3151
+ };
3152
+ function resolveIcon2(name) {
3153
+ if (!name) return null;
3154
+ return Icons[name] ?? null;
3155
+ }
3156
+ function GridItemCard({ item, theme }) {
3157
+ const Icon = resolveIcon2(item.icon);
3158
+ const hasMedia = Boolean(item.media?.file);
3159
+ const isOverlay = item.mediaAlignment === "overlay";
3160
+ const mediaFirst = item.mediaAlignment !== "bottom";
3161
+ if (isOverlay) {
3162
+ const card = /* @__PURE__ */ jsx(PageCard, { theme, isLink: Boolean(item.buttonAction), children: /* @__PURE__ */ jsxs("div", { className: "relative aspect-[4/5] w-full overflow-hidden", children: [
3163
+ hasMedia && /* @__PURE__ */ jsx(
3164
+ Image,
3165
+ {
3166
+ src: item.media?.file || "",
3167
+ alt: item.media?.caption || "",
3168
+ className: "absolute inset-0",
3169
+ width: "w-full",
3170
+ height: "h-full",
3171
+ objectFit: "cover",
3172
+ borderRadius: 0
3173
+ }
3174
+ ),
3175
+ /* @__PURE__ */ jsx("div", { className: "absolute inset-0 bg-gradient-to-t from-black/70 via-black/10 to-transparent" }),
3176
+ /* @__PURE__ */ jsx("div", { className: "absolute inset-x-0 bottom-0 p-4", children: /* @__PURE__ */ jsx(Header4, { theme, className: "text-white", children: item.title }) })
3177
+ ] }) });
3178
+ return item.buttonAction ? /* @__PURE__ */ jsx(NavLink, { href: item.buttonAction, className: "block", children: card }) : card;
3179
+ }
3180
+ const media = hasMedia ? /* @__PURE__ */ jsx("div", { className: "overflow-hidden rounded-md", children: /* @__PURE__ */ jsx(
3181
+ Image,
3182
+ {
3183
+ src: item.media?.file || "",
3184
+ alt: item.media?.caption || ""
3185
+ }
3186
+ ) }) : Icon ? /* @__PURE__ */ jsx("div", { className: "flex h-10 w-10 items-center justify-center rounded-md bg-primary/10 text-primary", children: /* @__PURE__ */ jsx(Icon, { className: "h-5 w-5" }) }) : null;
3187
+ const text = /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", children: [
3188
+ /* @__PURE__ */ jsx(Header4, { theme, children: item.title }),
3189
+ item.description && /* @__PURE__ */ jsx(Text2, { theme, children: item.description }),
3190
+ item.buttonAction && item.buttonCaption && /* @__PURE__ */ jsx(NavLink, { href: item.buttonAction, className: "mt-2", children: /* @__PURE__ */ jsx(Button2, { theme, variant: "secondary", size: "sm", children: item.buttonCaption }) })
3191
+ ] });
3192
+ return /* @__PURE__ */ jsx(PageCard, { theme, children: /* @__PURE__ */ jsx(
3193
+ PageCardContent,
3194
+ {
3195
+ theme,
3196
+ className: "flex h-full flex-col gap-3",
3197
+ children: mediaFirst ? /* @__PURE__ */ jsxs(Fragment, { children: [
3198
+ media,
3199
+ text
3200
+ ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
3201
+ text,
3202
+ media
3203
+ ] })
3204
+ }
3205
+ ) });
3206
+ }
3207
+ function GridWidget({
3208
+ settings: {
3209
+ title,
3210
+ description,
3211
+ headerAlignment = "center",
3212
+ buttonCaption,
3213
+ buttonAction,
3214
+ items,
3215
+ columns = 3,
3216
+ cssId,
3217
+ maxWidth,
3218
+ verticalPadding,
3219
+ background
3220
+ },
3221
+ state: { theme },
3222
+ nextTheme
3223
+ }) {
3224
+ const overriddenTheme = JSON.parse(JSON.stringify(theme.theme));
3225
+ overriddenTheme.structure.page.width = maxWidth || theme.theme.structure.page.width;
3226
+ overriddenTheme.structure.section.padding.y = verticalPadding || theme.theme.structure.section.padding.y;
3227
+ return /* @__PURE__ */ jsx(
3228
+ Section,
3229
+ {
3230
+ theme: overriddenTheme,
3231
+ id: cssId,
3232
+ background,
3233
+ nextTheme,
3234
+ children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-8", children: [
3235
+ /* @__PURE__ */ jsxs(
3236
+ "div",
3237
+ {
3238
+ className: clsx3(
3239
+ "flex flex-col gap-4",
3240
+ headerAlignment === "center" ? "items-center text-center" : "items-start"
3241
+ ),
3242
+ children: [
3243
+ /* @__PURE__ */ jsx(Header1, { theme: overriddenTheme, children: title }),
3244
+ description && /* @__PURE__ */ jsx(Subheader1, { theme: overriddenTheme, component: "span", children: /* @__PURE__ */ jsx(
3245
+ TextRenderer,
3246
+ {
3247
+ json: description,
3248
+ theme: overriddenTheme
3249
+ }
3250
+ ) }),
3251
+ buttonAction && buttonCaption && /* @__PURE__ */ jsx(NavLink, { href: buttonAction, className: "mt-2", children: /* @__PURE__ */ jsx(Button2, { theme: overriddenTheme, children: buttonCaption }) })
3252
+ ]
3253
+ }
3254
+ ),
3255
+ items && items.length > 0 && /* @__PURE__ */ jsx(
3256
+ "div",
3257
+ {
3258
+ className: clsx3(
3259
+ "grid grid-cols-1 md:grid-cols-2 gap-6 items-stretch",
3260
+ columns === 3 ? "lg:grid-cols-3" : "lg:grid-cols-2"
3261
+ ),
3262
+ children: items.map((item, index) => /* @__PURE__ */ jsx(
3263
+ GridItemCard,
3264
+ {
3265
+ item,
3266
+ theme: overriddenTheme
3267
+ },
3268
+ index
3269
+ ))
3270
+ }
3271
+ )
3272
+ ] })
3273
+ }
3274
+ );
3275
+ }
3276
+ function GridEditor({
3277
+ settings,
3278
+ onChange
3279
+ }) {
3280
+ const update = (partial) => onChange({ ...settings, ...partial });
3281
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-col", children: [
3282
+ /* @__PURE__ */ jsxs(FieldGroup, { title: "Header", children: [
3283
+ /* @__PURE__ */ jsx(
3284
+ TextField,
3285
+ {
3286
+ label: "Title",
3287
+ value: settings.title,
3288
+ onChange: (title) => update({ title })
3289
+ }
3290
+ ),
3291
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1 text-sm", children: [
3292
+ /* @__PURE__ */ jsx("span", { className: "font-medium text-foreground", children: "Description" }),
3293
+ /* @__PURE__ */ jsx(
3294
+ Editor,
3295
+ {
3296
+ initialContent: settings.description ?? emptyDoc,
3297
+ onChange: (description) => update({ description }),
3298
+ editorClassName: "min-h-[100px]"
3299
+ }
3300
+ )
3301
+ ] }),
3302
+ /* @__PURE__ */ jsx(
3303
+ SelectField,
3304
+ {
3305
+ label: "Header alignment",
3306
+ value: settings.headerAlignment ?? "center",
3307
+ onChange: (headerAlignment) => update({ headerAlignment }),
3308
+ options: [
3309
+ { label: "Left", value: "left" },
3310
+ { label: "Center", value: "center" }
3311
+ ]
3312
+ }
3313
+ )
3314
+ ] }),
3315
+ /* @__PURE__ */ jsxs(FieldGroup, { title: "Call to action", children: [
3316
+ /* @__PURE__ */ jsx(
3317
+ TextField,
3318
+ {
3319
+ label: "Button text",
3320
+ value: settings.buttonCaption,
3321
+ onChange: (buttonCaption) => update({ buttonCaption })
3322
+ }
3323
+ ),
3324
+ /* @__PURE__ */ jsx(
3325
+ TextField,
3326
+ {
3327
+ label: "Button link",
3328
+ value: settings.buttonAction,
3329
+ placeholder: "https://...",
3330
+ onChange: (buttonAction) => update({ buttonAction })
3331
+ }
3332
+ )
3333
+ ] }),
3334
+ /* @__PURE__ */ jsxs(FieldGroup, { title: "Items", children: [
3335
+ /* @__PURE__ */ jsx(
3336
+ NumberField,
3337
+ {
3338
+ label: "Columns",
3339
+ value: settings.columns ?? 3,
3340
+ min: 2,
3341
+ max: 3,
3342
+ onChange: (columns) => update({ columns })
3343
+ }
3344
+ ),
3345
+ /* @__PURE__ */ jsx(
3346
+ ListField,
3347
+ {
3348
+ label: "Cards",
3349
+ items: settings.items ?? [],
3350
+ onChange: (items) => update({ items }),
3351
+ createItem: () => ({
3352
+ title: "New item",
3353
+ mediaAlignment: "top"
3354
+ }),
3355
+ addLabel: "Add card",
3356
+ renderItem: (item, updateItem, remove) => /* @__PURE__ */ jsxs(Fragment, { children: [
3357
+ /* @__PURE__ */ jsx(
3358
+ TextField,
3359
+ {
3360
+ label: "Title",
3361
+ value: item.title,
3362
+ onChange: (title) => updateItem({ title })
3363
+ }
3364
+ ),
3365
+ /* @__PURE__ */ jsx(
3366
+ TextField,
3367
+ {
3368
+ label: "Description",
3369
+ value: item.description,
3370
+ multiline: true,
3371
+ onChange: (description) => updateItem({ description })
3372
+ }
3373
+ ),
3374
+ /* @__PURE__ */ jsx(
3375
+ TextField,
3376
+ {
3377
+ label: "Icon (lucide-react name)",
3378
+ value: item.icon,
3379
+ placeholder: "e.g. Globe",
3380
+ onChange: (icon) => updateItem({ icon })
3381
+ }
3382
+ ),
3383
+ /* @__PURE__ */ jsx(
3384
+ TextField,
3385
+ {
3386
+ label: "Image URL (overrides icon)",
3387
+ value: item.media?.file,
3388
+ placeholder: "https://...",
3389
+ onChange: (file) => updateItem({
3390
+ media: file ? { mediaId: file, file } : void 0
3391
+ })
3392
+ }
3393
+ ),
3394
+ /* @__PURE__ */ jsx(
3395
+ SelectField,
3396
+ {
3397
+ label: "Image position",
3398
+ value: item.mediaAlignment ?? "top",
3399
+ onChange: (mediaAlignment) => updateItem({ mediaAlignment }),
3400
+ options: [
3401
+ { label: "Above title", value: "top" },
3402
+ { label: "Below content", value: "bottom" },
3403
+ {
3404
+ label: "Overlay (title over image)",
3405
+ value: "overlay"
3406
+ }
3407
+ ]
3408
+ }
3409
+ ),
3410
+ /* @__PURE__ */ jsx(
3411
+ TextField,
3412
+ {
3413
+ label: "Button text",
3414
+ value: item.buttonCaption,
3415
+ onChange: (buttonCaption) => updateItem({ buttonCaption })
3416
+ }
3417
+ ),
3418
+ /* @__PURE__ */ jsx(
3419
+ TextField,
3420
+ {
3421
+ label: "Button link",
3422
+ value: item.buttonAction,
3423
+ placeholder: "https://...",
3424
+ onChange: (buttonAction) => updateItem({ buttonAction })
3425
+ }
3426
+ ),
3427
+ /* @__PURE__ */ jsx(
3428
+ "button",
3429
+ {
3430
+ type: "button",
3431
+ className: "self-start text-xs text-destructive",
3432
+ onClick: remove,
3433
+ children: "Remove"
3434
+ }
3435
+ )
3436
+ ] })
3437
+ }
3438
+ )
3439
+ ] })
3440
+ ] });
3441
+ }
3442
+
3443
+ // src/blocks/grid/defaults.ts
3444
+ var gridDefaults = {
3445
+ title: "Everything you need, in one place",
3446
+ headerAlignment: "center",
3447
+ columns: 3,
3448
+ items: [
3449
+ {
3450
+ title: "Website builder",
3451
+ description: "Design and publish pages with a drag-and-drop editor.",
3452
+ icon: "LayoutTemplate",
3453
+ mediaAlignment: "top",
3454
+ buttonCaption: "See features",
3455
+ buttonAction: "/#features"
3456
+ },
3457
+ {
3458
+ title: "Blog",
3459
+ description: "Publish articles and grow your audience.",
3460
+ icon: "Newspaper",
3461
+ mediaAlignment: "top",
3462
+ buttonCaption: "Read articles",
3463
+ buttonAction: "/blog"
3464
+ },
3465
+ {
3466
+ title: "Help desk",
3467
+ description: "Support your customers with a shared inbox.",
3468
+ icon: "Headset",
3469
+ mediaAlignment: "top",
3470
+ buttonCaption: "Contact us",
3471
+ buttonAction: "/help"
3472
+ }
3473
+ ]
3474
+ };
3475
+
3476
+ // src/blocks/grid/index.ts
3477
+ var Grid = {
3478
+ metadata: gridMetadata,
3479
+ widget: GridWidget,
3480
+ editor: GridEditor,
3481
+ getDefaultSettings: () => gridDefaults
3482
+ };
3483
+
3484
+ // src/blocks/media/metadata.ts
3485
+ var mediaMetadata = {
3486
+ name: "media",
3487
+ displayName: "Media",
3488
+ description: "A single image or video \u2014 YouTube, Vimeo or self-hosted \u2014 with an optional click-to-play preview.",
3489
+ compatibleWith: ["site", "landing", "blog", "post", "custom"]
3490
+ };
3491
+ var roundedMap3 = {
3492
+ 0: "rounded-none",
3493
+ 1: "rounded-sm",
3494
+ 2: "rounded",
3495
+ 3: "rounded-md",
3496
+ 4: "rounded-lg",
3497
+ 5: "rounded-xl",
3498
+ 6: "rounded-2xl",
3499
+ 7: "rounded-3xl",
3500
+ 8: "rounded-full"
3501
+ };
3502
+ function MediaWidget({
3503
+ settings: {
3504
+ media,
3505
+ youtubeLink,
3506
+ mediaRadius = 2,
3507
+ aspectRatio = "16/9",
3508
+ objectFit = "cover",
3509
+ hasBorder = true,
3510
+ playVideoInModal = false,
3511
+ cssId,
3512
+ maxWidth,
3513
+ verticalPadding,
3514
+ background
3515
+ },
3516
+ state: { theme },
3517
+ nextTheme
3518
+ }) {
3519
+ const overriddenTheme = JSON.parse(JSON.stringify(theme.theme));
3520
+ overriddenTheme.structure.page.width = maxWidth || theme.theme.structure.page.width;
3521
+ overriddenTheme.structure.section.padding.y = verticalPadding || theme.theme.structure.section.padding.y;
3522
+ const isVideoContent = isVideo(youtubeLink, media);
3523
+ return /* @__PURE__ */ jsx(
3524
+ Section,
3525
+ {
3526
+ theme: overriddenTheme,
3527
+ id: cssId,
3528
+ background,
3529
+ nextTheme,
3530
+ children: /* @__PURE__ */ jsx(
3531
+ "div",
3532
+ {
3533
+ className: cn(
3534
+ "overflow-hidden",
3535
+ roundedMap3[mediaRadius] ?? "rounded",
3536
+ hasBorder && (overriddenTheme.interactives?.card?.border?.width || "border")
3537
+ ),
3538
+ children: isVideoContent ? /* @__PURE__ */ jsx(
3539
+ VideoWithPreview,
3540
+ {
3541
+ videoUrl: youtubeLink || media?.file || "",
3542
+ title: media?.caption || "",
3543
+ thumbnailUrl: media?.thumbnail || "",
3544
+ aspectRatio,
3545
+ modal: playVideoInModal
3546
+ }
3547
+ ) : /* @__PURE__ */ jsx(
3548
+ Image,
3549
+ {
3550
+ src: media?.file || "",
3551
+ alt: media?.caption || "",
3552
+ objectFit,
3553
+ borderRadius: mediaRadius
3554
+ }
3555
+ )
3556
+ }
3557
+ )
3558
+ }
3559
+ );
3560
+ }
3561
+ function MediaEditor({
3562
+ settings,
3563
+ onChange
3564
+ }) {
3565
+ const update = (partial) => onChange({ ...settings, ...partial });
3566
+ const isVideoContent = isVideo(settings.youtubeLink, settings.media);
3567
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-col", children: [
3568
+ /* @__PURE__ */ jsxs(FieldGroup, { title: "Media", children: [
3569
+ /* @__PURE__ */ jsx(
3570
+ TextField,
3571
+ {
3572
+ label: "Image URL",
3573
+ value: settings.media?.file,
3574
+ placeholder: "https://...",
3575
+ onChange: (file) => update({
3576
+ media: file ? { mediaId: file, file } : void 0
3577
+ })
3578
+ }
3579
+ ),
3580
+ /* @__PURE__ */ jsx(
3581
+ TextField,
3582
+ {
3583
+ label: "YouTube / Vimeo link",
3584
+ value: settings.youtubeLink,
3585
+ placeholder: "https://youtube.com/watch?v=...",
3586
+ onChange: (youtubeLink) => update({ youtubeLink })
3587
+ }
3588
+ ),
3589
+ isVideoContent ? /* @__PURE__ */ jsxs(Fragment, { children: [
3590
+ /* @__PURE__ */ jsx(
3591
+ ToggleField,
3592
+ {
3593
+ label: "Play video in a pop-up",
3594
+ value: settings.playVideoInModal,
3595
+ onChange: (playVideoInModal) => update({ playVideoInModal })
3596
+ }
3597
+ ),
3598
+ /* @__PURE__ */ jsx(
3599
+ SelectField,
3600
+ {
3601
+ label: "Aspect ratio",
3602
+ value: settings.aspectRatio ?? "16/9",
3603
+ onChange: (aspectRatio) => update({ aspectRatio }),
3604
+ options: [
3605
+ { label: "Widescreen (16:9)", value: "16/9" },
3606
+ { label: "Standard (4:3)", value: "4/3" },
3607
+ { label: "Square (1:1)", value: "1/1" },
3608
+ { label: "Vertical (9:16)", value: "9/16" }
3609
+ ]
3610
+ }
3611
+ )
3612
+ ] }) : /* @__PURE__ */ jsx(
3613
+ SelectField,
3614
+ {
3615
+ label: "Object fit",
3616
+ value: settings.objectFit ?? "cover",
3617
+ onChange: (objectFit) => update({ objectFit }),
3618
+ options: [
3619
+ { label: "Cover", value: "cover" },
3620
+ { label: "Contain", value: "contain" },
3621
+ { label: "Fill", value: "fill" },
3622
+ { label: "None", value: "none" },
3623
+ { label: "Scale down", value: "scale-down" }
3624
+ ]
3625
+ }
3626
+ )
3627
+ ] }),
3628
+ /* @__PURE__ */ jsxs(FieldGroup, { title: "Appearance", children: [
3629
+ /* @__PURE__ */ jsx(
3630
+ NumberField,
3631
+ {
3632
+ label: "Corner radius",
3633
+ value: settings.mediaRadius,
3634
+ min: 0,
3635
+ max: 8,
3636
+ onChange: (mediaRadius) => update({ mediaRadius })
3637
+ }
3638
+ ),
3639
+ /* @__PURE__ */ jsx(
3640
+ ToggleField,
3641
+ {
3642
+ label: "Border",
3643
+ value: settings.hasBorder,
3644
+ onChange: (hasBorder) => update({ hasBorder })
3645
+ }
3646
+ )
3647
+ ] })
3648
+ ] });
3649
+ }
3650
+
3651
+ // src/blocks/media/defaults.ts
3652
+ var mediaDefaults = {
3653
+ mediaRadius: 2,
3654
+ aspectRatio: "16/9",
3655
+ objectFit: "cover",
3656
+ hasBorder: true,
3657
+ playVideoInModal: false
3658
+ };
3659
+
3660
+ // src/blocks/media/index.ts
3661
+ var MediaBlock = {
3662
+ metadata: mediaMetadata,
3663
+ widget: MediaWidget,
3664
+ editor: MediaEditor,
3665
+ getDefaultSettings: () => mediaDefaults
3666
+ };
3667
+
3668
+ // src/blocks/marquee/metadata.ts
3669
+ var marqueeMetadata = {
3670
+ name: "marquee",
3671
+ displayName: "Marquee",
3672
+ description: "An infinite horizontally-scrolling row of text or icons \u2014 useful for logo clouds or highlight tickers.",
3673
+ compatibleWith: ["site", "landing", "blog", "post", "custom"]
3674
+ };
3675
+ function MarqueeTrack({
3676
+ children,
3677
+ speed,
3678
+ direction,
3679
+ pauseOnHover
3680
+ }) {
3681
+ const containerRef = useRef(null);
3682
+ const contentRef = useRef(null);
3683
+ const [containerWidth, setContainerWidth] = useState(0);
3684
+ const [contentWidth, setContentWidth] = useState(0);
3685
+ const animationName = `pb-marquee-${useId().replace(/[^a-zA-Z0-9]/g, "")}`;
3686
+ useEffect(() => {
3687
+ const measure = () => {
3688
+ setContainerWidth(containerRef.current?.offsetWidth ?? 0);
3689
+ setContentWidth(contentRef.current?.offsetWidth ?? 0);
3690
+ };
3691
+ measure();
3692
+ window.addEventListener("resize", measure);
3693
+ return () => window.removeEventListener("resize", measure);
3694
+ }, [children, speed]);
3695
+ const duration = contentWidth > 0 ? contentWidth / speed : 0;
3696
+ const duplicatesNeeded = contentWidth > 0 ? Math.max(2, Math.ceil(containerWidth * 2 / contentWidth)) : 2;
3697
+ return /* @__PURE__ */ jsxs("div", { ref: containerRef, className: "w-full overflow-hidden", children: [
3698
+ contentWidth > 0 && /* @__PURE__ */ jsx("style", { children: `
3699
+ @keyframes ${animationName} {
3700
+ from { transform: translateX(0); }
3701
+ to { transform: translateX(-${contentWidth}px); }
3702
+ }
3703
+ ` }),
3704
+ /* @__PURE__ */ jsxs(
3705
+ "div",
3706
+ {
3707
+ className: cn(
3708
+ "inline-flex w-max",
3709
+ pauseOnHover && "hover:[animation-play-state:paused]"
3710
+ ),
3711
+ style: contentWidth > 0 ? {
3712
+ animation: `${animationName} ${duration}s linear infinite`,
3713
+ animationDirection: direction === "left" ? "reverse" : "normal"
3714
+ } : void 0,
3715
+ children: [
3716
+ /* @__PURE__ */ jsx("div", { ref: contentRef, className: "inline-flex shrink-0", children }),
3717
+ Array.from({ length: duplicatesNeeded }).map((_, i) => /* @__PURE__ */ jsx("div", { className: "inline-flex shrink-0", "aria-hidden": true, children }, i))
3718
+ ]
3719
+ }
3720
+ )
3721
+ ] });
3722
+ }
3723
+ function resolveIcon3(name) {
3724
+ if (!name) return null;
3725
+ return Icons[name] ?? null;
3726
+ }
3727
+ function MarqueeWidget({
3728
+ settings: {
3729
+ items = [],
3730
+ direction = "right",
3731
+ speed = 60,
3732
+ pauseOnHover = true,
3733
+ fadeWidth = 50,
3734
+ itemBackgroundColor,
3735
+ itemForegroundColor,
3736
+ itemBorderColor,
3737
+ itemBorderRadius = 12,
3738
+ itemBorderWidth = 1,
3739
+ itemBorderStyle = "solid",
3740
+ cssId,
3741
+ maxWidth,
3742
+ verticalPadding,
3743
+ background
3744
+ },
3745
+ state: { theme },
3746
+ nextTheme
3747
+ }) {
3748
+ const overriddenTheme = JSON.parse(JSON.stringify(theme.theme));
3749
+ overriddenTheme.structure.page.width = maxWidth || theme.theme.structure.page.width;
3750
+ overriddenTheme.structure.section.padding.y = verticalPadding || theme.theme.structure.section.padding.y;
3751
+ if (items.length === 0) return null;
3752
+ const itemStyle = {
3753
+ backgroundColor: itemBackgroundColor || void 0,
3754
+ color: itemForegroundColor || void 0,
3755
+ borderColor: itemBorderColor || void 0,
3756
+ borderRadius: itemBorderRadius,
3757
+ borderWidth: itemBorderStyle === "none" ? 0 : itemBorderWidth,
3758
+ borderStyle: itemBorderStyle === "none" ? void 0 : itemBorderStyle
3759
+ };
3760
+ const renderItem = (item) => {
3761
+ const Icon = resolveIcon3(item.icon);
3762
+ const content = /* @__PURE__ */ jsx(
3763
+ "div",
3764
+ {
3765
+ className: "mx-2 flex min-h-[80px] min-w-[160px] items-center justify-center px-6",
3766
+ style: itemStyle,
3767
+ children: Icon ? /* @__PURE__ */ jsx(Icon, { className: "h-8 w-8" }) : /* @__PURE__ */ jsx("span", { className: "whitespace-nowrap text-sm font-medium", children: item.text })
3768
+ }
3769
+ );
3770
+ return item.href ? /* @__PURE__ */ jsx(NavLink, { href: item.href, openInSameTab: false, children: content }, item.id) : /* @__PURE__ */ jsx(React7.Fragment, { children: content }, item.id);
3771
+ };
3772
+ return /* @__PURE__ */ jsx(
3773
+ Section,
3774
+ {
3775
+ theme: overriddenTheme,
3776
+ id: cssId,
3777
+ background,
3778
+ nextTheme,
3779
+ children: /* @__PURE__ */ jsxs("div", { className: "relative", "aria-hidden": "true", children: [
3780
+ /* @__PURE__ */ jsx(
3781
+ MarqueeTrack,
3782
+ {
3783
+ speed,
3784
+ direction,
3785
+ pauseOnHover,
3786
+ children: items.map(renderItem)
3787
+ }
3788
+ ),
3789
+ fadeWidth > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
3790
+ /* @__PURE__ */ jsx(
3791
+ "div",
3792
+ {
3793
+ className: "pointer-events-none absolute inset-y-0 left-0 z-10",
3794
+ style: {
3795
+ width: fadeWidth,
3796
+ background: "linear-gradient(to right, var(--background), transparent)"
3797
+ }
3798
+ }
3799
+ ),
3800
+ /* @__PURE__ */ jsx(
3801
+ "div",
3802
+ {
3803
+ className: "pointer-events-none absolute inset-y-0 right-0 z-10",
3804
+ style: {
3805
+ width: fadeWidth,
3806
+ background: "linear-gradient(to left, var(--background), transparent)"
3807
+ }
3808
+ }
3809
+ )
3810
+ ] })
3811
+ ] })
3812
+ }
3813
+ );
3814
+ }
3815
+ function MarqueeEditor({
3816
+ settings,
3817
+ onChange
3818
+ }) {
3819
+ const update = (partial) => onChange({ ...settings, ...partial });
3820
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-col", children: [
3821
+ /* @__PURE__ */ jsx(FieldGroup, { title: "Items", children: /* @__PURE__ */ jsx(
3822
+ ListField,
3823
+ {
3824
+ label: "Items",
3825
+ items: settings.items ?? [],
3826
+ onChange: (items) => update({ items }),
3827
+ createItem: () => ({
3828
+ id: generateUniqueId(),
3829
+ text: "New item"
3830
+ }),
3831
+ addLabel: "Add item",
3832
+ renderItem: (item, updateItem, remove) => /* @__PURE__ */ jsxs(Fragment, { children: [
3833
+ /* @__PURE__ */ jsx(
3834
+ TextField,
3835
+ {
3836
+ label: "Text",
3837
+ value: item.text,
3838
+ onChange: (text) => updateItem({ text })
3839
+ }
3840
+ ),
3841
+ /* @__PURE__ */ jsx(
3842
+ TextField,
3843
+ {
3844
+ label: "Icon (lucide-react name)",
3845
+ value: item.icon,
3846
+ placeholder: "e.g. Globe \u2014 overrides text",
3847
+ onChange: (icon) => updateItem({ icon })
3848
+ }
3849
+ ),
3850
+ /* @__PURE__ */ jsx(
3851
+ TextField,
3852
+ {
3853
+ label: "Link (optional)",
3854
+ value: item.href,
3855
+ placeholder: "https://...",
3856
+ onChange: (href) => updateItem({ href })
3857
+ }
3858
+ ),
3859
+ /* @__PURE__ */ jsx(
3860
+ "button",
3861
+ {
3862
+ type: "button",
3863
+ className: "self-start text-xs text-destructive",
3864
+ onClick: remove,
3865
+ children: "Remove"
3866
+ }
3867
+ )
3868
+ ] })
3869
+ }
3870
+ ) }),
3871
+ /* @__PURE__ */ jsxs(FieldGroup, { title: "Scroll", children: [
3872
+ /* @__PURE__ */ jsx(
3873
+ SelectField,
3874
+ {
3875
+ label: "Direction",
3876
+ value: settings.direction ?? "right",
3877
+ onChange: (direction) => update({ direction }),
3878
+ options: [
3879
+ { label: "Right to left", value: "right" },
3880
+ { label: "Left to right", value: "left" }
3881
+ ]
3882
+ }
3883
+ ),
3884
+ /* @__PURE__ */ jsx(
3885
+ NumberField,
3886
+ {
3887
+ label: "Speed (px/sec)",
3888
+ value: settings.speed,
3889
+ min: 10,
3890
+ max: 200,
3891
+ onChange: (speed) => update({ speed })
3892
+ }
3893
+ ),
3894
+ /* @__PURE__ */ jsx(
3895
+ ToggleField,
3896
+ {
3897
+ label: "Pause on hover",
3898
+ value: settings.pauseOnHover,
3899
+ onChange: (pauseOnHover) => update({ pauseOnHover })
3900
+ }
3901
+ ),
3902
+ /* @__PURE__ */ jsx(
3903
+ NumberField,
3904
+ {
3905
+ label: "Edge fade width (px)",
3906
+ value: settings.fadeWidth,
3907
+ min: 0,
3908
+ max: 200,
3909
+ onChange: (fadeWidth) => update({ fadeWidth })
3910
+ }
3911
+ )
3912
+ ] }),
3913
+ /* @__PURE__ */ jsxs(FieldGroup, { title: "Item style", children: [
3914
+ /* @__PURE__ */ jsx(
3915
+ TextField,
3916
+ {
3917
+ label: "Background color",
3918
+ value: settings.itemBackgroundColor,
3919
+ placeholder: "#ffffff",
3920
+ onChange: (itemBackgroundColor) => update({ itemBackgroundColor })
3921
+ }
3922
+ ),
3923
+ /* @__PURE__ */ jsx(
3924
+ TextField,
3925
+ {
3926
+ label: "Foreground color",
3927
+ value: settings.itemForegroundColor,
3928
+ placeholder: "#000000",
3929
+ onChange: (itemForegroundColor) => update({ itemForegroundColor })
3930
+ }
3931
+ ),
3932
+ /* @__PURE__ */ jsx(
3933
+ TextField,
3934
+ {
3935
+ label: "Border color",
3936
+ value: settings.itemBorderColor,
3937
+ placeholder: "#e2e8f0",
3938
+ onChange: (itemBorderColor) => update({ itemBorderColor })
3939
+ }
3940
+ ),
3941
+ /* @__PURE__ */ jsx(
3942
+ SelectField,
3943
+ {
3944
+ label: "Border style",
3945
+ value: settings.itemBorderStyle ?? "solid",
3946
+ onChange: (itemBorderStyle) => update({ itemBorderStyle }),
3947
+ options: [
3948
+ { label: "Solid", value: "solid" },
3949
+ { label: "Dashed", value: "dashed" },
3950
+ { label: "Dotted", value: "dotted" },
3951
+ { label: "Double", value: "double" },
3952
+ { label: "None", value: "none" }
3953
+ ]
3954
+ }
3955
+ ),
3956
+ /* @__PURE__ */ jsx(
3957
+ NumberField,
3958
+ {
3959
+ label: "Border width",
3960
+ value: settings.itemBorderWidth,
3961
+ min: 0,
3962
+ max: 10,
3963
+ onChange: (itemBorderWidth) => update({ itemBorderWidth })
3964
+ }
3965
+ ),
3966
+ /* @__PURE__ */ jsx(
3967
+ NumberField,
3968
+ {
3969
+ label: "Border radius",
3970
+ value: settings.itemBorderRadius,
3971
+ min: 0,
3972
+ max: 50,
3973
+ onChange: (itemBorderRadius) => update({ itemBorderRadius })
3974
+ }
3975
+ )
3976
+ ] })
3977
+ ] });
3978
+ }
3979
+
3980
+ // src/blocks/marquee/defaults.ts
3981
+ var marqueeDefaults = {
3982
+ items: [
3983
+ { id: generateUniqueId(), text: "Website" },
3984
+ { id: generateUniqueId(), text: "Content" },
3985
+ { id: generateUniqueId(), text: "Support" },
3986
+ { id: generateUniqueId(), text: "Engage" }
3987
+ ],
3988
+ direction: "right",
3989
+ speed: 60,
3990
+ pauseOnHover: true,
3991
+ fadeWidth: 50,
3992
+ itemForegroundColor: "",
3993
+ itemBackgroundColor: "",
3994
+ itemBorderColor: "",
3995
+ itemBorderRadius: 12,
3996
+ itemBorderWidth: 1,
3997
+ itemBorderStyle: "solid"
3998
+ };
3999
+
4000
+ // src/blocks/marquee/index.ts
4001
+ var Marquee = {
4002
+ metadata: marqueeMetadata,
4003
+ widget: MarqueeWidget,
4004
+ editor: MarqueeEditor,
4005
+ getDefaultSettings: () => marqueeDefaults
4006
+ };
4007
+
4008
+ // src/blocks/embed/metadata.ts
4009
+ var embedMetadata = {
4010
+ name: "embed",
4011
+ displayName: "Embed",
4012
+ description: "Embed a URL in an iframe, or paste a script/iframe snippet from a third-party tool.",
4013
+ compatibleWith: ["site", "landing", "blog", "post", "custom"]
4014
+ };
4015
+ var aspectRatioMap = {
4016
+ "16:9": 16 / 9,
4017
+ "4:3": 4 / 3,
4018
+ "1:1": 1
4019
+ };
4020
+ function EmbedWidget({
4021
+ settings: {
4022
+ contentType = "url",
4023
+ content,
4024
+ aspectRatio = "default",
4025
+ height = 500,
4026
+ cssId,
4027
+ maxWidth,
4028
+ verticalPadding,
4029
+ background
4030
+ },
4031
+ state: { theme },
4032
+ editing,
4033
+ nextTheme
4034
+ }) {
4035
+ const overriddenTheme = JSON.parse(JSON.stringify(theme.theme));
4036
+ overriddenTheme.structure.page.width = maxWidth || theme.theme.structure.page.width;
4037
+ overriddenTheme.structure.section.padding.y = verticalPadding || theme.theme.structure.section.padding.y;
4038
+ if (!content) {
4039
+ if (!editing) return null;
4040
+ return /* @__PURE__ */ jsx(
4041
+ Section,
4042
+ {
4043
+ theme: overriddenTheme,
4044
+ id: cssId,
4045
+ background,
4046
+ nextTheme,
4047
+ children: /* @__PURE__ */ jsxs("div", { className: "flex min-h-48 flex-col items-center justify-center gap-3 rounded-lg border border-dashed border-border bg-muted/30 px-6 py-12 text-center", children: [
4048
+ /* @__PURE__ */ jsx("span", { className: "flex h-10 w-10 items-center justify-center rounded-full border border-border bg-background text-muted-foreground", children: /* @__PURE__ */ jsx(Code2, { className: "h-4 w-4" }) }),
4049
+ /* @__PURE__ */ jsxs("div", { children: [
4050
+ /* @__PURE__ */ jsx("p", { className: "font-semibold text-foreground", children: "Embed external content" }),
4051
+ /* @__PURE__ */ jsx("p", { className: "mt-1 max-w-md text-sm text-muted-foreground", children: "Paste an embed URL or script snippet in the block settings." })
4052
+ ] })
4053
+ ] })
4054
+ }
4055
+ );
4056
+ }
4057
+ const isScript = contentType === "script";
4058
+ const ratio = aspectRatio !== "default" ? aspectRatioMap[aspectRatio] : void 0;
4059
+ const containerStyle = ratio ? { position: "relative", paddingTop: `${100 / ratio}%` } : !isScript ? { height } : {};
4060
+ const iframeStyle = ratio ? { position: "absolute", inset: 0, width: "100%", height: "100%" } : isScript ? { width: "100%", height } : { width: "100%", height: "100%" };
4061
+ return /* @__PURE__ */ jsx(
4062
+ Section,
4063
+ {
4064
+ theme: overriddenTheme,
4065
+ id: cssId,
4066
+ background,
4067
+ nextTheme,
4068
+ children: /* @__PURE__ */ jsx("div", { style: containerStyle, children: isScript ? /* @__PURE__ */ jsx(SandboxedEmbed, { content, style: iframeStyle }) : /* @__PURE__ */ jsx(
4069
+ "iframe",
4070
+ {
4071
+ src: content,
4072
+ style: iframeStyle,
4073
+ allow: "accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture",
4074
+ allowFullScreen: true,
4075
+ title: "Embedded content"
4076
+ }
4077
+ ) })
4078
+ }
4079
+ );
4080
+ }
4081
+ function EmbedEditor({
4082
+ settings,
4083
+ onChange
4084
+ }) {
4085
+ const update = (partial) => onChange({ ...settings, ...partial });
4086
+ const contentType = settings.contentType ?? "url";
4087
+ const aspectRatio = settings.aspectRatio ?? "default";
4088
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-col", children: [
4089
+ /* @__PURE__ */ jsx("div", { className: "mx-1 mb-3 rounded-md border border-destructive/40 bg-destructive/5 p-3 text-xs text-destructive", children: "Embedding external content may expose visitors to scripts and trackers you don't control. Only embed sources you trust." }),
4090
+ /* @__PURE__ */ jsxs(FieldGroup, { title: "Content", children: [
4091
+ /* @__PURE__ */ jsx(
4092
+ SelectField,
4093
+ {
4094
+ label: "Type",
4095
+ value: contentType,
4096
+ onChange: (value) => update({ contentType: value }),
4097
+ options: [
4098
+ { label: "URL", value: "url" },
4099
+ { label: "Script / iframe snippet", value: "script" }
4100
+ ]
4101
+ }
4102
+ ),
4103
+ contentType === "url" ? /* @__PURE__ */ jsx(
4104
+ TextField,
4105
+ {
4106
+ label: "URL",
4107
+ value: settings.content,
4108
+ placeholder: "https://...",
4109
+ onChange: (content) => update({ content })
4110
+ }
4111
+ ) : /* @__PURE__ */ jsx(
4112
+ TextField,
4113
+ {
4114
+ label: "Snippet",
4115
+ multiline: true,
4116
+ value: settings.content,
4117
+ placeholder: "<script>...</script> or <iframe>...</iframe>",
4118
+ onChange: (content) => update({ content })
4119
+ }
4120
+ )
4121
+ ] }),
4122
+ /* @__PURE__ */ jsxs(FieldGroup, { title: "Sizing", children: [
4123
+ /* @__PURE__ */ jsx(
4124
+ SelectField,
4125
+ {
4126
+ label: "Aspect ratio",
4127
+ value: aspectRatio,
4128
+ onChange: (value) => update({ aspectRatio: value }),
4129
+ options: [
4130
+ { label: "Default (fixed height)", value: "default" },
4131
+ { label: "Widescreen (16:9)", value: "16:9" },
4132
+ { label: "Standard (4:3)", value: "4:3" },
4133
+ { label: "Square (1:1)", value: "1:1" }
4134
+ ]
4135
+ }
4136
+ ),
4137
+ aspectRatio === "default" && /* @__PURE__ */ jsx(
4138
+ NumberField,
4139
+ {
4140
+ label: "Height (px)",
4141
+ value: settings.height,
4142
+ min: 100,
4143
+ max: 2e3,
4144
+ onChange: (height) => update({ height })
4145
+ }
4146
+ )
4147
+ ] })
4148
+ ] });
4149
+ }
4150
+
4151
+ // src/blocks/embed/defaults.ts
4152
+ var embedDefaults = {
4153
+ contentType: "url",
4154
+ content: "",
4155
+ aspectRatio: "default",
4156
+ height: 500
4157
+ };
4158
+
4159
+ // src/blocks/embed/index.ts
4160
+ var Embed = {
4161
+ metadata: embedMetadata,
4162
+ widget: EmbedWidget,
4163
+ editor: EmbedEditor,
4164
+ getDefaultSettings: () => embedDefaults
4165
+ };
4166
+
4167
+ // src/blocks/data-slot/metadata.ts
4168
+ var dataSlotMetadata = {
4169
+ name: "data-slot",
4170
+ displayName: "Data slot",
4171
+ description: "A host-resolved data region. Its data source and presentation belong to the embedding application.",
4172
+ compatibleWith: ["site", "blog", "landing", "post", "custom"]
4173
+ };
4174
+ function DataSlotWidget({
4175
+ data,
4176
+ editing,
4177
+ settings: { slot, cssId, maxWidth, verticalPadding, background },
4178
+ state: { theme },
4179
+ nextTheme
4180
+ }) {
4181
+ const overriddenTheme = JSON.parse(JSON.stringify(theme.theme));
4182
+ overriddenTheme.structure.page.width = maxWidth || theme.theme.structure.page.width;
4183
+ overriddenTheme.structure.section.padding.y = verticalPadding || theme.theme.structure.section.padding.y;
4184
+ if (data !== void 0) {
4185
+ return /* @__PURE__ */ jsx(
4186
+ Section,
4187
+ {
4188
+ theme: overriddenTheme,
4189
+ id: cssId,
4190
+ background,
4191
+ nextTheme,
4192
+ children: data
4193
+ }
4194
+ );
4195
+ }
4196
+ if (!editing) return null;
4197
+ return /* @__PURE__ */ jsx(
4198
+ Section,
4199
+ {
4200
+ theme: overriddenTheme,
4201
+ id: cssId,
4202
+ background,
4203
+ nextTheme,
4204
+ className: "border-y border-dashed border-border bg-muted/30",
4205
+ children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center gap-3 text-center", children: [
4206
+ /* @__PURE__ */ jsx("span", { className: "flex h-10 w-10 items-center justify-center rounded-full border border-border bg-background text-muted-foreground", children: /* @__PURE__ */ jsx(LockKeyhole, { className: "h-4 w-4" }) }),
4207
+ /* @__PURE__ */ jsxs("div", { children: [
4208
+ /* @__PURE__ */ jsx("p", { className: "font-semibold text-foreground", children: "Data slot" }),
4209
+ /* @__PURE__ */ jsxs("p", { className: "mt-1 text-sm text-muted-foreground", children: [
4210
+ "The embedding application resolves data for",
4211
+ " ",
4212
+ String(slot || "this slot"),
4213
+ " here."
4214
+ ] })
4215
+ ] })
4216
+ ] })
4217
+ }
4218
+ );
4219
+ }
4220
+
4221
+ // src/blocks/data-slot/index.ts
4222
+ var DataSlot = {
4223
+ metadata: dataSlotMetadata,
4224
+ widget: DataSlotWidget
4225
+ };
4226
+
4227
+ export { Banner, Content, DataSlot, EmailForm, Embed, Faq, Featured, FieldGroup, Footer, Grid, Header, Hero, Marquee, MediaBlock, Pricing, RichText, SupportForm, TextField, ToggleField, generateUniqueId };
4228
+ //# sourceMappingURL=chunk-7KIX4WAO.js.map
4229
+ //# sourceMappingURL=chunk-7KIX4WAO.js.map