@drawnagency/primitives 0.1.59 → 0.1.61

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 (95) hide show
  1. package/dist/auth/capabilities.d.ts +16 -0
  2. package/dist/auth/capabilities.d.ts.map +1 -0
  3. package/dist/auth/index.d.ts +2 -0
  4. package/dist/auth/index.d.ts.map +1 -1
  5. package/dist/auth/index.js +3 -1
  6. package/dist/auth/types.d.ts +26 -22
  7. package/dist/auth/types.d.ts.map +1 -1
  8. package/dist/chunk-4OX3Z7SB.js +1421 -0
  9. package/dist/{chunk-RFZNNCAS.js → chunk-53DKVAB6.js} +1 -61
  10. package/dist/chunk-677NTHAZ.js +27 -0
  11. package/dist/chunk-7EOSDFS3.js +10 -0
  12. package/dist/{chunk-NTGSA3TI.js → chunk-AM5LZD6Q.js} +7 -30
  13. package/dist/{chunk-ICLXLWQ5.js → chunk-BT7STGDW.js} +31 -23
  14. package/dist/{chunk-L2JJFOXD.js → chunk-CBWK3KPV.js} +5 -1
  15. package/dist/{chunk-V7JN2DDU.js → chunk-ODCJQOVO.js} +1 -1
  16. package/dist/chunk-TZQPOR5A.js +24 -0
  17. package/dist/{chunk-XTK4BR27.js → chunk-UNWNT52N.js} +13 -0
  18. package/dist/chunk-UQ4OYRW7.js +670 -0
  19. package/dist/chunk-WDSVHKAH.js +54 -0
  20. package/dist/chunk-WLMJCNU7.js +70 -0
  21. package/dist/chunk-ZGFAYZWB.js +8 -0
  22. package/dist/components/editor/DropEdgeIndicator.d.ts +8 -0
  23. package/dist/components/editor/DropEdgeIndicator.d.ts.map +1 -0
  24. package/dist/components/editor/PagesModal.d.ts.map +1 -1
  25. package/dist/components/editor/SectionOrderingModal.d.ts.map +1 -1
  26. package/dist/components/editor/index.js +640 -0
  27. package/dist/components/primitives/defineHeadingSection.d.ts +28 -0
  28. package/dist/components/primitives/defineHeadingSection.d.ts.map +1 -0
  29. package/dist/components/primitives/index.d.ts +10 -0
  30. package/dist/components/primitives/index.d.ts.map +1 -0
  31. package/dist/components/primitives/index.js +166 -0
  32. package/dist/components/sections/Colors/index.d.ts.map +1 -1
  33. package/dist/components/sections/Container/index.d.ts.map +1 -1
  34. package/dist/components/sections/IconList/IconList.d.ts +1 -6
  35. package/dist/components/sections/IconList/IconList.d.ts.map +1 -1
  36. package/dist/components/sections/IconList/index.d.ts +11 -0
  37. package/dist/components/sections/IconList/index.d.ts.map +1 -1
  38. package/dist/components/sections/LinkHeading/index.d.ts.map +1 -1
  39. package/dist/components/sections/SubHeading/index.d.ts +0 -1
  40. package/dist/components/sections/SubHeading/index.d.ts.map +1 -1
  41. package/dist/components/sections/SubSubHeading/index.d.ts +0 -1
  42. package/dist/components/sections/SubSubHeading/index.d.ts.map +1 -1
  43. package/dist/components/sections/all-sections.d.ts +0 -2
  44. package/dist/components/sections/all-sections.d.ts.map +1 -1
  45. package/dist/components/sections/register-schemas.js +376 -2453
  46. package/dist/components/shell/BugReportFAB.d.ts.map +1 -1
  47. package/dist/hooks/useEditorPublish.d.ts.map +1 -1
  48. package/dist/hooks/useMediaPipeline.d.ts.map +1 -1
  49. package/dist/hooks/useSortableRow.d.ts +25 -0
  50. package/dist/hooks/useSortableRow.d.ts.map +1 -0
  51. package/dist/index.js +46 -37
  52. package/dist/lib/image-refs.d.ts +21 -0
  53. package/dist/lib/image-refs.d.ts.map +1 -0
  54. package/dist/lib/index.js +21 -14
  55. package/dist/lib/platform-broker.d.ts +14 -0
  56. package/dist/lib/platform-broker.d.ts.map +1 -0
  57. package/dist/lib/platform-broker.js +60 -0
  58. package/dist/lib/reorder.d.ts +12 -0
  59. package/dist/lib/reorder.d.ts.map +1 -0
  60. package/dist/lib/text.d.ts +2 -0
  61. package/dist/lib/text.d.ts.map +1 -1
  62. package/dist/media/index.js +3 -2
  63. package/dist/media/utils.d.ts.map +1 -1
  64. package/dist/schemas/index.js +17 -17
  65. package/dist/schemas/link.js +13 -0
  66. package/dist/types/database.d.ts +46 -0
  67. package/dist/types/database.d.ts.map +1 -1
  68. package/package.json +14 -2
  69. package/src/auth/capabilities.ts +26 -0
  70. package/src/auth/index.ts +3 -0
  71. package/src/auth/types.ts +36 -26
  72. package/src/components/editor/DropEdgeIndicator.tsx +13 -0
  73. package/src/components/editor/PagesModal.tsx +12 -80
  74. package/src/components/editor/SectionOrderingModal.tsx +8 -78
  75. package/src/components/primitives/defineHeadingSection.tsx +56 -0
  76. package/src/components/primitives/index.ts +11 -0
  77. package/src/components/sections/Colors/index.tsx +2 -4
  78. package/src/components/sections/Container/index.tsx +2 -4
  79. package/src/components/sections/IconList/IconList.tsx +1 -7
  80. package/src/components/sections/IconList/index.tsx +11 -5
  81. package/src/components/sections/LinkHeading/index.tsx +5 -20
  82. package/src/components/sections/SubHeading/index.tsx +6 -23
  83. package/src/components/sections/SubSubHeading/index.tsx +6 -23
  84. package/src/components/shell/BugReportFAB.tsx +13 -9
  85. package/src/hooks/useEditorPublish.ts +71 -49
  86. package/src/hooks/useMediaPipeline.ts +11 -34
  87. package/src/hooks/useSortableRow.ts +99 -0
  88. package/src/lib/image-refs.ts +53 -0
  89. package/src/lib/platform-broker.ts +72 -0
  90. package/src/lib/reorder.ts +20 -0
  91. package/src/lib/text.ts +5 -0
  92. package/src/media/utils.ts +6 -1
  93. package/src/types/database.ts +41 -0
  94. package/dist/chunk-DKOUFIP6.js +0 -35
  95. /package/dist/{chunk-ZU2MKPTG.js → chunk-3DCG4DNU.js} +0 -0
@@ -1,979 +1,118 @@
1
1
  import {
2
- getMediaProvider
3
- } from "../../chunk-L2JJFOXD.js";
2
+ Checkbox,
3
+ DeleteButton,
4
+ DragHandle as DragHandle2,
5
+ FormLabel,
6
+ Input,
7
+ Popover,
8
+ PopoverItem,
9
+ SettingsButton,
10
+ SettingsForm,
11
+ buildBlockDragData,
12
+ buildBlockDropData,
13
+ canDropBlock,
14
+ isBlockDragData
15
+ } from "../../chunk-UQ4OYRW7.js";
16
+ import {
17
+ EditableGrid,
18
+ EditablePlainText,
19
+ EditableRichText,
20
+ MAX_CONTAINER_COLUMNS,
21
+ MediaBlock,
22
+ colSpanClass,
23
+ containerGridClass,
24
+ defineHeadingSection,
25
+ spacerHiddenClass,
26
+ useEditableCollection
27
+ } from "../../chunk-4OX3Z7SB.js";
28
+ import {
29
+ AddIcon,
30
+ DeleteIcon,
31
+ DragHandle,
32
+ IconButton
33
+ } from "../../chunk-WLMJCNU7.js";
34
+ import "../../chunk-CBWK3KPV.js";
4
35
  import {
5
36
  DEFAULT_LINK,
6
37
  LinkValueSchema
7
- } from "../../chunk-ZU2MKPTG.js";
38
+ } from "../../chunk-3DCG4DNU.js";
8
39
  import {
9
- cn,
10
40
  curatedIcons,
11
- getIcon,
12
- sanitizeHtml,
13
- toSectionId
14
- } from "../../chunk-RFZNNCAS.js";
41
+ getIcon
42
+ } from "../../chunk-WDSVHKAH.js";
43
+ import {
44
+ sanitizeHtml
45
+ } from "../../chunk-53DKVAB6.js";
46
+ import {
47
+ cn
48
+ } from "../../chunk-7EOSDFS3.js";
49
+ import "../../chunk-ZGFAYZWB.js";
15
50
  import {
16
- ColorItemSchema,
17
- SingleMediaReferenceSchema,
18
51
  getSectionSchema
19
- } from "../../chunk-ICLXLWQ5.js";
20
- import "../../chunk-DKOUFIP6.js";
21
- import "../../chunk-NSCT3AMV.js";
52
+ } from "../../chunk-TZQPOR5A.js";
22
53
  import {
23
54
  defineSection,
24
55
  getSection,
25
56
  registerRichText,
26
57
  registerSchema
27
58
  } from "../../chunk-VKAGMEKE.js";
59
+ import {
60
+ ColorItemSchema,
61
+ SingleMediaReferenceSchema
62
+ } from "../../chunk-BT7STGDW.js";
63
+ import "../../chunk-NSCT3AMV.js";
28
64
  import "../../chunk-JSBRDJBE.js";
29
65
 
30
66
  // src/components/sections/LinkHeading/index.tsx
31
- import { z } from "zod";
32
67
  import { Heading1 } from "lucide-react";
33
-
34
- // src/components/primitives/EditablePlainText.tsx
35
- import {
36
- useRef as useRef2,
37
- useEffect as useEffect2,
38
- useCallback as useCallback2,
39
- createElement
40
- } from "react";
41
-
42
- // src/components/primitives/useEditablePlainText.ts
43
- import { useState, useEffect, useRef, useCallback } from "react";
44
- function useEditablePlainText({
45
- value: propValue,
46
- onChange,
47
- multiline = false
48
- }) {
49
- const [value, setValue] = useState(propValue);
50
- const lastPropValue = useRef(propValue);
51
- useEffect(() => {
52
- if (propValue !== lastPropValue.current) {
53
- setValue(propValue);
54
- lastPropValue.current = propValue;
55
- }
56
- }, [propValue]);
57
- const handleInput = useCallback((text) => {
58
- setValue(text);
59
- }, []);
60
- const handleBlur = useCallback(() => {
61
- if (value !== propValue) {
62
- onChange(value);
63
- lastPropValue.current = value;
64
- }
65
- }, [value, propValue, onChange]);
66
- return {
67
- value,
68
- handleInput,
69
- handleBlur,
70
- shouldPreventEnter: !multiline
71
- };
72
- }
73
-
74
- // src/components/primitives/EditablePlainText.tsx
75
- function EditablePlainText({
76
- tag,
77
- value,
78
- onChange,
79
- isEditMode,
80
- multiline = false,
81
- placeholder,
82
- className,
83
- ...htmlAttrs
84
- }) {
85
- const ref = useRef2(null);
86
- const {
87
- value: localValue,
88
- handleInput,
89
- handleBlur,
90
- shouldPreventEnter
91
- } = useEditablePlainText({ value, onChange, multiline });
92
- useEffect2(() => {
93
- if (ref.current && ref.current.textContent !== localValue) {
94
- ref.current.textContent = localValue;
95
- }
96
- }, [localValue]);
97
- const onInput = useCallback2(() => {
98
- if (ref.current) {
99
- handleInput(ref.current.textContent || "");
100
- }
101
- }, [handleInput]);
102
- const onKeyDown = useCallback2(
103
- (e) => {
104
- if (e.key === "Enter" && shouldPreventEnter) {
105
- e.preventDefault();
106
- }
107
- },
108
- [shouldPreventEnter]
109
- );
110
- const onPaste = useCallback2(
111
- (e) => {
112
- e.preventDefault();
113
- const text = e.clipboardData.getData("text/plain");
114
- const selection = window.getSelection();
115
- if (selection && selection.rangeCount > 0) {
116
- const range = selection.getRangeAt(0);
117
- range.deleteContents();
118
- range.insertNode(document.createTextNode(text));
119
- range.collapse(false);
120
- selection.removeAllRanges();
121
- selection.addRange(range);
122
- }
123
- if (ref.current) {
124
- handleInput(ref.current.textContent || "");
125
- }
126
- },
127
- [handleInput]
128
- );
129
- return createElement(
130
- tag,
131
- {
132
- ref,
133
- className: isEditMode && placeholder ? `editable-placeholder ${className ?? ""}` : className,
134
- contentEditable: isEditMode ? "true" : void 0,
135
- suppressContentEditableWarning: isEditMode || void 0,
136
- onInput: isEditMode ? onInput : void 0,
137
- onBlur: isEditMode ? handleBlur : void 0,
138
- onKeyDown: isEditMode ? onKeyDown : void 0,
139
- onPaste: isEditMode ? onPaste : void 0,
140
- "data-placeholder": isEditMode && placeholder ? placeholder : void 0,
141
- ...htmlAttrs
142
- },
143
- value
144
- );
145
- }
146
-
147
- // src/components/primitives/HeadingSection.tsx
148
68
  import { jsx } from "react/jsx-runtime";
149
- function HeadingSection({ heading, tag, placeholder, className, onChange }) {
150
- return /* @__PURE__ */ jsx(
151
- EditablePlainText,
152
- {
153
- tag,
154
- value: heading,
155
- onChange: onChange ?? (() => {
156
- }),
157
- isEditMode: !!onChange,
158
- placeholder,
159
- id: toSectionId(heading),
160
- className: cn("scroll-mt-20", className)
161
- }
162
- );
163
- }
164
-
165
- // src/components/sections/LinkHeading/index.tsx
166
- import { jsx as jsx2 } from "react/jsx-runtime";
167
- var schema = z.object({
168
- type: z.literal("link_heading"),
169
- content: z.object({ heading: z.string() })
170
- });
171
- var LinkHeading_default = defineSection({
69
+ var LinkHeading_default = defineHeadingSection({
172
70
  type: "link_heading",
173
71
  label: "Link Heading",
174
- icon: /* @__PURE__ */ jsx2(Heading1, { size: 18 }),
175
- schema,
72
+ icon: /* @__PURE__ */ jsx(Heading1, { size: 18 }),
176
73
  navRole: "h1",
177
- component: ({ content, onChange }) => /* @__PURE__ */ jsx2(
178
- HeadingSection,
179
- {
180
- heading: content.content.heading,
181
- tag: "h2",
182
- placeholder: "Section heading",
183
- onChange: onChange ? (heading) => onChange({ type: "link_heading", content: { heading } }) : void 0
184
- }
185
- ),
186
- defaults: () => ({ type: "link_heading", content: { heading: "New Section" } }),
187
- getLabel: (content) => content.content.heading
74
+ tag: "h2",
75
+ placeholder: "Section heading",
76
+ defaultHeading: "New Section"
188
77
  });
189
78
 
190
79
  // src/components/sections/SubHeading/index.tsx
191
- import { z as z2 } from "zod";
192
80
  import { Heading2 } from "lucide-react";
193
- import { jsx as jsx3 } from "react/jsx-runtime";
194
- var schema2 = z2.object({
195
- type: z2.literal("sub_heading"),
196
- content: z2.object({ heading: z2.string(), excludeFromNav: z2.boolean().optional() })
197
- });
198
- var SubHeading_default = defineSection({
81
+ import { jsx as jsx2 } from "react/jsx-runtime";
82
+ var SubHeading_default = defineHeadingSection({
199
83
  type: "sub_heading",
200
84
  label: "Sub Heading",
201
- icon: /* @__PURE__ */ jsx3(Heading2, { size: 18 }),
202
- schema: schema2,
85
+ icon: /* @__PURE__ */ jsx2(Heading2, { size: 18 }),
203
86
  navRole: "h2",
204
- component: ({ content, onChange }) => /* @__PURE__ */ jsx3(
205
- HeadingSection,
206
- {
207
- heading: content.content.heading,
208
- tag: "h3",
209
- placeholder: "Sub heading",
210
- onChange: onChange ? (heading) => onChange({ ...content, content: { ...content.content, heading } }) : void 0
211
- }
212
- ),
213
- defaults: () => ({ type: "sub_heading", content: { heading: "New Sub Heading" } }),
214
- getLabel: (content) => content.content.heading,
215
- settings: {
216
- excludeFromNav: { type: "checkbox", label: "Exclude from navigation", default: false, target: "content" }
217
- }
87
+ tag: "h3",
88
+ placeholder: "Sub heading",
89
+ defaultHeading: "New Sub Heading",
90
+ navExclusion: true
218
91
  });
219
92
 
220
93
  // src/components/sections/SubSubHeading/index.tsx
221
- import { z as z3 } from "zod";
222
94
  import { Heading3 } from "lucide-react";
223
- import { jsx as jsx4 } from "react/jsx-runtime";
224
- var schema3 = z3.object({
225
- type: z3.literal("sub_sub_heading"),
226
- content: z3.object({ heading: z3.string(), excludeFromNav: z3.boolean().optional() })
227
- });
228
- var SubSubHeading_default = defineSection({
95
+ import { jsx as jsx3 } from "react/jsx-runtime";
96
+ var SubSubHeading_default = defineHeadingSection({
229
97
  type: "sub_sub_heading",
230
98
  label: "Sub Sub Heading",
231
- icon: /* @__PURE__ */ jsx4(Heading3, { size: 18 }),
232
- schema: schema3,
99
+ icon: /* @__PURE__ */ jsx3(Heading3, { size: 18 }),
233
100
  navRole: "h3",
234
- component: ({ content, onChange }) => /* @__PURE__ */ jsx4(
235
- HeadingSection,
236
- {
237
- heading: content.content.heading,
238
- tag: "h4",
239
- placeholder: "Sub sub heading",
240
- onChange: onChange ? (heading) => onChange({ ...content, content: { ...content.content, heading } }) : void 0
241
- }
242
- ),
243
- defaults: () => ({ type: "sub_sub_heading", content: { heading: "New Sub Sub Heading" } }),
244
- getLabel: (content) => content.content.heading,
245
- settings: {
246
- excludeFromNav: { type: "checkbox", label: "Exclude from navigation", default: false, target: "content" }
247
- }
248
- });
249
-
250
- // src/components/sections/Prose/index.tsx
251
- import { z as z4 } from "zod";
252
- import { AlignLeft } from "lucide-react";
253
-
254
- // src/components/primitives/EditableRichText.tsx
255
- import { useRef as useRef4, useCallback as useCallback4, useEffect as useEffect4 } from "react";
256
- import { EditorContent } from "@tiptap/react";
257
- import { BubbleMenu } from "@tiptap/react/menus";
258
-
259
- // src/components/primitives/useEditableRichText.ts
260
- import { useState as useState2, useRef as useRef3, useCallback as useCallback3, useEffect as useEffect3 } from "react";
261
- import { Editor } from "@tiptap/core";
262
- import Placeholder from "@tiptap/extension-placeholder";
263
-
264
- // src/components/primitives/tiptap-presets.ts
265
- import StarterKit from "@tiptap/starter-kit";
266
- import Underline from "@tiptap/extension-underline";
267
- import Link from "@tiptap/extension-link";
268
-
269
- // ../../node_modules/.pnpm/@tiptap+extension-paragraph@3.22.4_@tiptap+core@3.22.4_@tiptap+pm@3.22.4_/node_modules/@tiptap/extension-paragraph/dist/index.js
270
- import { mergeAttributes, Node } from "@tiptap/core";
271
- var EMPTY_PARAGRAPH_MARKDOWN = " ";
272
- var NBSP_CHAR = "\xA0";
273
- var Paragraph = Node.create({
274
- name: "paragraph",
275
- priority: 1e3,
276
- addOptions() {
277
- return {
278
- HTMLAttributes: {}
279
- };
280
- },
281
- group: "block",
282
- content: "inline*",
283
- parseHTML() {
284
- return [{ tag: "p" }];
285
- },
286
- renderHTML({ HTMLAttributes }) {
287
- return ["p", mergeAttributes(this.options.HTMLAttributes, HTMLAttributes), 0];
288
- },
289
- parseMarkdown: (token, helpers) => {
290
- const tokens = token.tokens || [];
291
- if (tokens.length === 1 && tokens[0].type === "image") {
292
- return helpers.parseChildren([tokens[0]]);
293
- }
294
- const content = helpers.parseInline(tokens);
295
- const hasExplicitEmptyParagraphMarker = tokens.length === 1 && tokens[0].type === "text" && (tokens[0].raw === EMPTY_PARAGRAPH_MARKDOWN || tokens[0].text === EMPTY_PARAGRAPH_MARKDOWN || tokens[0].raw === NBSP_CHAR || tokens[0].text === NBSP_CHAR);
296
- if (hasExplicitEmptyParagraphMarker && content.length === 1 && content[0].type === "text" && (content[0].text === EMPTY_PARAGRAPH_MARKDOWN || content[0].text === NBSP_CHAR)) {
297
- return helpers.createNode("paragraph", void 0, []);
298
- }
299
- return helpers.createNode("paragraph", void 0, content);
300
- },
301
- renderMarkdown: (node, h, ctx) => {
302
- var _a, _b;
303
- if (!node) {
304
- return "";
305
- }
306
- const content = Array.isArray(node.content) ? node.content : [];
307
- if (content.length === 0) {
308
- const previousContent = Array.isArray((_a = ctx == null ? void 0 : ctx.previousNode) == null ? void 0 : _a.content) ? ctx.previousNode.content : [];
309
- const previousNodeIsEmptyParagraph = ((_b = ctx == null ? void 0 : ctx.previousNode) == null ? void 0 : _b.type) === "paragraph" && previousContent.length === 0;
310
- return previousNodeIsEmptyParagraph ? EMPTY_PARAGRAPH_MARKDOWN : "";
311
- }
312
- return h.renderChildren(content);
313
- },
314
- addCommands() {
315
- return {
316
- setParagraph: () => ({ commands }) => {
317
- return commands.setNode(this.name);
318
- }
319
- };
320
- },
321
- addKeyboardShortcuts() {
322
- return {
323
- "Mod-Alt-0": () => this.editor.commands.setParagraph()
324
- };
325
- }
326
- });
327
- var index_default = Paragraph;
328
-
329
- // src/components/primitives/CustomParagraph.ts
330
- var ALLOWED_CLASSES = ["large", "lead-in"];
331
- var CustomParagraph = index_default.extend({
332
- addAttributes() {
333
- return {
334
- ...this.parent?.(),
335
- class: {
336
- default: null,
337
- parseHTML: (element) => {
338
- const cls = element.getAttribute("class");
339
- if (cls && ALLOWED_CLASSES.includes(cls)) {
340
- return cls;
341
- }
342
- return null;
343
- },
344
- renderHTML: (attributes) => {
345
- if (!attributes.class) return {};
346
- return { class: attributes.class };
347
- }
348
- }
349
- };
350
- }
351
- });
352
-
353
- // src/components/primitives/tiptap-presets.ts
354
- var basic = [
355
- StarterKit.configure({
356
- paragraph: false,
357
- heading: false,
358
- blockquote: false,
359
- bulletList: false,
360
- orderedList: false,
361
- listItem: false,
362
- codeBlock: false,
363
- horizontalRule: false,
364
- code: false,
365
- strike: false
366
- }),
367
- CustomParagraph,
368
- Underline
369
- ];
370
- var rich = [
371
- StarterKit.configure({
372
- paragraph: false,
373
- heading: { levels: [3, 4] },
374
- blockquote: false,
375
- codeBlock: false,
376
- horizontalRule: false,
377
- code: false,
378
- strike: false
379
- }),
380
- CustomParagraph,
381
- Underline,
382
- // `page` protocol: stable internal-page hrefs (page://{pageId}#{sectionId})
383
- // resolved to real routes at SSR — see lib/links.ts.
384
- Link.configure({ openOnClick: false, protocols: ["page"] })
385
- ];
386
- var presets = { basic, rich };
387
-
388
- // src/components/primitives/useEditableRichText.ts
389
- function useEditableRichText({
390
- value,
391
- onChange,
392
- preset,
393
- placeholder
394
- }) {
395
- const editorRef = useRef3(null);
396
- const [editor, setEditor] = useState2(null);
397
- const valueRef = useRef3(value);
398
- const onChangeRef = useRef3(onChange);
399
- useEffect3(() => {
400
- valueRef.current = value;
401
- }, [value]);
402
- useEffect3(() => {
403
- onChangeRef.current = onChange;
404
- }, [onChange]);
405
- const activate = useCallback3(() => {
406
- if (editorRef.current) return;
407
- const extensions = placeholder ? [...presets[preset], Placeholder.configure({ placeholder })] : presets[preset];
408
- const ed = new Editor({
409
- extensions,
410
- content: valueRef.current,
411
- // Commit edits to onChange on every transaction, not only on deactivate().
412
- // Without this, content lives only inside TipTap until blur; clicking a
413
- // Save button while the editor is still focused flushes persistence before
414
- // the (150ms-delayed) blur->deactivate commit fires, silently dropping the
415
- // in-progress edit. Live-committing keeps persistence in sync continuously.
416
- onUpdate: ({ editor: updated }) => {
417
- const html = updated.getHTML();
418
- if (html !== valueRef.current) {
419
- valueRef.current = html;
420
- onChangeRef.current(html);
421
- }
422
- }
423
- });
424
- editorRef.current = ed;
425
- setEditor(ed);
426
- }, [preset, placeholder]);
427
- const deactivate = useCallback3(() => {
428
- const ed = editorRef.current;
429
- if (!ed) return;
430
- const html = ed.getHTML();
431
- ed.destroy();
432
- editorRef.current = null;
433
- setEditor(null);
434
- if (html !== valueRef.current) {
435
- onChangeRef.current(html);
436
- }
437
- }, []);
438
- useEffect3(() => {
439
- return () => {
440
- editorRef.current?.destroy();
441
- editorRef.current = null;
442
- };
443
- }, []);
444
- return {
445
- isEditorActive: editor !== null,
446
- editor,
447
- activate,
448
- deactivate
449
- };
450
- }
451
-
452
- // src/components/primitives/RichTextToolbar.tsx
453
- import { useState as useState4 } from "react";
454
- import { useEditorState } from "@tiptap/react";
455
-
456
- // src/components/primitives/LinkPopover.tsx
457
- import { useState as useState3 } from "react";
458
-
459
- // src/components/shared/Button.tsx
460
- import { forwardRef } from "react";
461
- import { jsx as jsx5 } from "react/jsx-runtime";
462
- var boxSize = {
463
- sm: "rounded px-3 py-1.5 text-xs font-medium",
464
- md: "rounded px-4 py-2 text-sm font-medium"
465
- };
466
- var ghostSize = {
467
- sm: "text-xs font-medium",
468
- md: "text-sm font-medium"
469
- };
470
- var variantClasses = {
471
- primary: "bg-base-contrast text-base-accent hover:bg-base-contrast/90",
472
- brand: "bg-primary text-primary-contrast hover:opacity-90",
473
- secondary: "border border-base-200 text-base-contrast hover:bg-base-accent",
474
- destructive: "border border-red-600 text-red-600 hover:bg-red-600 hover:text-white"
475
- };
476
- var ghostToneClasses = {
477
- neutral: "text-base-contrast-light hover:text-base-contrast",
478
- destructive: "text-red-600 hover:text-red-800"
479
- };
480
- var Button = forwardRef(function Button2({
481
- variant = "primary",
482
- size = "sm",
483
- tone = "neutral",
484
- fullWidth = false,
485
- isLoading = false,
486
- loadingLabel,
487
- disabled,
488
- className,
489
- children,
490
- ...rest
491
- }, ref) {
492
- const isGhost = variant === "ghost";
493
- const resolvedDisabled = disabled || isLoading;
494
- const label = isLoading && loadingLabel ? loadingLabel : children;
495
- return /* @__PURE__ */ jsx5(
496
- "button",
497
- {
498
- ref,
499
- disabled: resolvedDisabled,
500
- className: cn(
501
- "inline-flex items-center justify-center transition-colors",
502
- isGhost ? ghostSize[size] : boxSize[size],
503
- isGhost ? ghostToneClasses[tone] : variantClasses[variant],
504
- fullWidth && "w-full",
505
- resolvedDisabled ? "cursor-not-allowed opacity-50" : "cursor-pointer",
506
- className
507
- ),
508
- ...rest,
509
- children: label
510
- }
511
- );
512
- });
513
-
514
- // src/components/shared/PagesContext.tsx
515
- import { createContext, useContext } from "react";
516
- var PagesContext = createContext({ pages: [], getPageHeadings: () => [] });
517
- function usePages() {
518
- return useContext(PagesContext);
519
- }
520
-
521
- // src/lib/links.ts
522
- function internalHref(pageId, anchorSectionId) {
523
- return `page://${pageId}${anchorSectionId ? `#${anchorSectionId}` : ""}`;
524
- }
525
- function parseInternalHref(href) {
526
- const m = /^page:\/\/([^#]+)(?:#(.+))?$/.exec(href);
527
- return m ? { pageId: m[1], anchorSectionId: m[2] ?? null } : null;
528
- }
529
-
530
- // src/components/primitives/LinkPopover.tsx
531
- import { Fragment, jsx as jsx6, jsxs } from "react/jsx-runtime";
532
- var selectClasses = "w-full rounded border border-base-contrast/20 bg-base px-2 py-1 text-sm text-base-contrast focus:outline-none focus:ring-1 focus:ring-primary";
533
- function LinkPopover({ editor, onClose }) {
534
- const { pages, getPageHeadings } = usePages();
535
- const existingAttrs = editor.getAttributes("link");
536
- const existingInternal = parseInternalHref(existingAttrs.href ?? "");
537
- const [mode, setMode] = useState3(existingInternal ? "internal" : "external");
538
- const [url, setUrl] = useState3(existingInternal ? "" : existingAttrs.href ?? "");
539
- const [pageId, setPageId] = useState3(existingInternal?.pageId ?? "");
540
- const [anchorId, setAnchorId] = useState3(existingInternal?.anchorSectionId ?? "");
541
- const [openInNewTab, setOpenInNewTab] = useState3(
542
- existingAttrs.target === "_blank"
543
- );
544
- const hasLink = Boolean(existingAttrs.href);
545
- const headings = pageId ? getPageHeadings(pageId) : [];
546
- const handleApply = () => {
547
- if (mode === "internal") {
548
- if (pageId) {
549
- editor.chain().focus().extendMarkRange("link").setLink({ href: internalHref(pageId, anchorId || null), target: null }).run();
550
- }
551
- } else if (url.trim()) {
552
- editor.chain().focus().extendMarkRange("link").setLink({ href: url.trim(), target: openInNewTab ? "_blank" : null }).run();
553
- }
554
- onClose();
555
- };
556
- const handleRemove = () => {
557
- editor.chain().focus().extendMarkRange("link").unsetLink().run();
558
- onClose();
559
- };
560
- const handleKeyDown = (e) => {
561
- if (e.key === "Enter") {
562
- e.preventDefault();
563
- handleApply();
564
- } else if (e.key === "Escape") {
565
- e.preventDefault();
566
- onClose();
567
- }
568
- };
569
- return /* @__PURE__ */ jsxs(
570
- "div",
571
- {
572
- className: "flex w-64 flex-col gap-2 rounded bg-base-accent p-3 shadow-lg",
573
- onMouseDown: (e) => e.stopPropagation(),
574
- onKeyDown: handleKeyDown,
575
- children: [
576
- /* @__PURE__ */ jsx6("div", { className: "flex gap-2", role: "tablist", children: ["external", "internal"].map((kind) => /* @__PURE__ */ jsx6(
577
- "button",
578
- {
579
- type: "button",
580
- role: "tab",
581
- "aria-selected": mode === kind,
582
- onClick: () => setMode(kind),
583
- className: cn(
584
- "cursor-pointer rounded border px-2 py-0.5 text-xs",
585
- mode === kind ? "border-primary bg-primary text-primary-contrast" : "border-base-200 text-base-contrast"
586
- ),
587
- children: kind === "external" ? "External URL" : "Internal page"
588
- },
589
- kind
590
- )) }),
591
- mode === "external" ? /* @__PURE__ */ jsxs(Fragment, { children: [
592
- /* @__PURE__ */ jsx6(
593
- "input",
594
- {
595
- autoFocus: true,
596
- type: "url",
597
- value: url,
598
- onChange: (e) => setUrl(e.target.value),
599
- placeholder: "https://example.com",
600
- className: "rounded border border-base-contrast/20 bg-base px-2 py-1 text-sm text-base-contrast placeholder:text-base-contrast/40 focus:outline-none focus:ring-1 focus:ring-primary"
601
- }
602
- ),
603
- /* @__PURE__ */ jsxs("label", { className: "flex cursor-pointer items-center gap-2 text-sm text-base-contrast", children: [
604
- /* @__PURE__ */ jsx6(
605
- "input",
606
- {
607
- type: "checkbox",
608
- checked: openInNewTab,
609
- onChange: (e) => setOpenInNewTab(e.target.checked),
610
- className: "accent-primary"
611
- }
612
- ),
613
- "Open in new tab"
614
- ] })
615
- ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
616
- /* @__PURE__ */ jsxs(
617
- "select",
618
- {
619
- autoFocus: true,
620
- "aria-label": "Page",
621
- value: pageId,
622
- onChange: (e) => {
623
- setPageId(e.target.value);
624
- setAnchorId("");
625
- },
626
- className: selectClasses,
627
- children: [
628
- /* @__PURE__ */ jsx6("option", { value: "", children: "Select a page\u2026" }),
629
- pages.map((p) => /* @__PURE__ */ jsx6("option", { value: p.id, children: p.title }, p.id))
630
- ]
631
- }
632
- ),
633
- /* @__PURE__ */ jsxs(
634
- "select",
635
- {
636
- "aria-label": "Section",
637
- value: anchorId,
638
- disabled: !pageId,
639
- onChange: (e) => setAnchorId(e.target.value),
640
- className: cn(selectClasses, !pageId && "cursor-not-allowed opacity-50"),
641
- children: [
642
- /* @__PURE__ */ jsx6("option", { value: "", children: "None (top of page)" }),
643
- headings.map((h) => /* @__PURE__ */ jsx6("option", { value: h.id, children: h.label }, h.id))
644
- ]
645
- }
646
- )
647
- ] }),
648
- /* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
649
- /* @__PURE__ */ jsx6(
650
- Button,
651
- {
652
- variant: "brand",
653
- size: "sm",
654
- onMouseDown: (e) => {
655
- e.preventDefault();
656
- handleApply();
657
- },
658
- children: "Apply"
659
- }
660
- ),
661
- hasLink && /* @__PURE__ */ jsx6(
662
- Button,
663
- {
664
- variant: "secondary",
665
- size: "sm",
666
- onMouseDown: (e) => {
667
- e.preventDefault();
668
- handleRemove();
669
- },
670
- children: "Remove"
671
- }
672
- ),
673
- /* @__PURE__ */ jsx6(
674
- Button,
675
- {
676
- variant: "secondary",
677
- size: "sm",
678
- onMouseDown: (e) => {
679
- e.preventDefault();
680
- onClose();
681
- },
682
- children: "Cancel"
683
- }
684
- )
685
- ] })
686
- ]
687
- }
688
- );
689
- }
690
-
691
- // src/components/primitives/RichTextToolbar.tsx
692
- import { Fragment as Fragment2, jsx as jsx7, jsxs as jsxs2 } from "react/jsx-runtime";
693
- function ToolbarButton({ isActive, onClick, title, children }) {
694
- return /* @__PURE__ */ jsx7(
695
- "button",
696
- {
697
- type: "button",
698
- title,
699
- "aria-label": title,
700
- "aria-pressed": isActive,
701
- onMouseDown: (e) => {
702
- e.preventDefault();
703
- onClick();
704
- },
705
- className: cn(
706
- "cursor-pointer rounded px-2 py-1 text-sm font-medium transition-colors",
707
- isActive ? "bg-primary text-primary-contrast" : "bg-base-accent text-base-contrast hover:opacity-80"
708
- ),
709
- children
710
- }
711
- );
712
- }
713
- function Separator() {
714
- return /* @__PURE__ */ jsx7("div", { className: "mx-1 h-5 w-px bg-base-contrast/20" });
715
- }
716
- function RichTextToolbar({ editor, preset }) {
717
- const [showLinkPopover, setShowLinkPopover] = useState4(false);
718
- const state = useEditorState({
719
- editor,
720
- selector: ({ editor: ed }) => ({
721
- isBold: ed.isActive("bold"),
722
- isItalic: ed.isActive("italic"),
723
- isUnderline: ed.isActive("underline"),
724
- isLink: ed.isActive("link"),
725
- isBulletList: ed.isActive("bulletList"),
726
- isOrderedList: ed.isActive("orderedList"),
727
- isH3: ed.isActive("heading", { level: 3 }),
728
- isH4: ed.isActive("heading", { level: 4 }),
729
- isLarge: ed.isActive("paragraph", { class: "large" }),
730
- isLeadIn: ed.isActive("paragraph", { class: "lead-in" })
731
- })
732
- });
733
- const toggleParagraphVariant = (cls, isCurrentlyActive) => {
734
- if (isCurrentlyActive) {
735
- editor.chain().focus().setNode("paragraph").run();
736
- } else {
737
- editor.chain().focus().setNode("paragraph", { class: cls }).run();
738
- }
739
- };
740
- return /* @__PURE__ */ jsxs2(
741
- "div",
742
- {
743
- "data-testid": "rich-text-toolbar",
744
- className: "flex items-center gap-1 rounded border border-base-contrast/10 bg-base-accent p-1 shadow-md",
745
- children: [
746
- /* @__PURE__ */ jsx7(
747
- ToolbarButton,
748
- {
749
- isActive: state.isBold,
750
- onClick: () => editor.chain().focus().toggleBold().run(),
751
- title: "Bold",
752
- children: /* @__PURE__ */ jsx7("strong", { children: "B" })
753
- }
754
- ),
755
- /* @__PURE__ */ jsx7(
756
- ToolbarButton,
757
- {
758
- isActive: state.isItalic,
759
- onClick: () => editor.chain().focus().toggleItalic().run(),
760
- title: "Italic",
761
- children: /* @__PURE__ */ jsx7("em", { children: "I" })
762
- }
763
- ),
764
- /* @__PURE__ */ jsx7(
765
- ToolbarButton,
766
- {
767
- isActive: state.isUnderline,
768
- onClick: () => editor.chain().focus().toggleUnderline().run(),
769
- title: "Underline",
770
- children: /* @__PURE__ */ jsx7("span", { className: "underline", children: "U" })
771
- }
772
- ),
773
- preset === "rich" && /* @__PURE__ */ jsxs2(Fragment2, { children: [
774
- /* @__PURE__ */ jsx7(Separator, {}),
775
- /* @__PURE__ */ jsxs2("div", { className: "relative", children: [
776
- /* @__PURE__ */ jsx7(
777
- ToolbarButton,
778
- {
779
- isActive: state.isLink || showLinkPopover,
780
- onClick: () => setShowLinkPopover((prev) => !prev),
781
- title: "Link",
782
- children: "\u{1F517}"
783
- }
784
- ),
785
- showLinkPopover && /* @__PURE__ */ jsx7("div", { className: "absolute left-0 top-full z-50 mt-1", children: /* @__PURE__ */ jsx7(
786
- LinkPopover,
787
- {
788
- editor,
789
- onClose: () => setShowLinkPopover(false)
790
- }
791
- ) })
792
- ] }),
793
- /* @__PURE__ */ jsx7(Separator, {}),
794
- /* @__PURE__ */ jsx7(
795
- ToolbarButton,
796
- {
797
- isActive: state.isBulletList,
798
- onClick: () => editor.chain().focus().toggleBulletList().run(),
799
- title: "Bullet List",
800
- children: "\u2022\u2261"
801
- }
802
- ),
803
- /* @__PURE__ */ jsx7(
804
- ToolbarButton,
805
- {
806
- isActive: state.isOrderedList,
807
- onClick: () => editor.chain().focus().toggleOrderedList().run(),
808
- title: "Ordered List",
809
- children: "1\u2261"
810
- }
811
- ),
812
- /* @__PURE__ */ jsx7(Separator, {}),
813
- /* @__PURE__ */ jsx7(
814
- ToolbarButton,
815
- {
816
- isActive: state.isH3,
817
- onClick: () => editor.chain().focus().toggleHeading({ level: 3 }).run(),
818
- title: "Heading 3",
819
- children: "H3"
820
- }
821
- ),
822
- /* @__PURE__ */ jsx7(
823
- ToolbarButton,
824
- {
825
- isActive: state.isH4,
826
- onClick: () => editor.chain().focus().toggleHeading({ level: 4 }).run(),
827
- title: "Heading 4",
828
- children: "H4"
829
- }
830
- ),
831
- /* @__PURE__ */ jsx7(
832
- ToolbarButton,
833
- {
834
- isActive: state.isLarge,
835
- onClick: () => toggleParagraphVariant("large", state.isLarge),
836
- title: "Large Paragraph",
837
- children: "Lg"
838
- }
839
- ),
840
- /* @__PURE__ */ jsx7(
841
- ToolbarButton,
842
- {
843
- isActive: state.isLeadIn,
844
- onClick: () => toggleParagraphVariant("lead-in", state.isLeadIn),
845
- title: "Lead-In",
846
- children: "Li"
847
- }
848
- )
849
- ] })
850
- ]
851
- }
852
- );
853
- }
854
-
855
- // src/components/primitives/EditableRichText.tsx
856
- import { jsx as jsx8, jsxs as jsxs3 } from "react/jsx-runtime";
857
- function isContentEmpty(html) {
858
- return html.replace(/<[^>]*>/g, "").trim().length === 0;
859
- }
860
- var DEFAULT_PLACEHOLDER = "Click to edit\u2026";
861
- function EditableRichText({
862
- value,
863
- onChange,
864
- isEditMode,
865
- preset = "rich",
866
- className,
867
- placeholder = DEFAULT_PLACEHOLDER
868
- }) {
869
- const { isEditorActive, editor, activate, deactivate } = useEditableRichText({ value, onChange, preset, placeholder });
870
- const wrapperRef = useRef4(null);
871
- const blurTimeoutRef = useRef4(null);
872
- const clickCoordsRef = useRef4(null);
873
- useEffect4(() => {
874
- return () => {
875
- if (blurTimeoutRef.current) {
876
- clearTimeout(blurTimeoutRef.current);
877
- }
878
- };
879
- }, []);
880
- useEffect4(() => {
881
- if (!isEditorActive || !editor) return;
882
- requestAnimationFrame(() => {
883
- if (editor.isDestroyed) return;
884
- const coords = clickCoordsRef.current;
885
- if (coords) {
886
- try {
887
- const pos = editor.view.posAtCoords(coords);
888
- if (pos) {
889
- editor.commands.focus();
890
- editor.commands.setTextSelection(pos.pos);
891
- } else {
892
- editor.commands.focus("end");
893
- }
894
- } catch {
895
- editor.commands.focus("end");
896
- } finally {
897
- clickCoordsRef.current = null;
898
- }
899
- } else {
900
- editor.commands.focus("end");
901
- }
902
- });
903
- }, [isEditorActive, editor]);
904
- const handleClick = useCallback4((e) => {
905
- if (isEditMode && !isEditorActive) {
906
- clickCoordsRef.current = { left: e.clientX, top: e.clientY };
907
- activate();
908
- }
909
- }, [isEditMode, isEditorActive, activate]);
910
- const handleFocus = useCallback4(() => {
911
- if (blurTimeoutRef.current) {
912
- clearTimeout(blurTimeoutRef.current);
913
- blurTimeoutRef.current = null;
914
- }
915
- }, []);
916
- const handleBlur = useCallback4(() => {
917
- blurTimeoutRef.current = setTimeout(() => {
918
- if (wrapperRef.current && !wrapperRef.current.contains(document.activeElement)) {
919
- deactivate();
920
- }
921
- }, 150);
922
- }, [deactivate]);
923
- if (!isEditMode) {
924
- return /* @__PURE__ */ jsx8(
925
- "div",
926
- {
927
- className,
928
- dangerouslySetInnerHTML: { __html: sanitizeHtml(value) }
929
- }
930
- );
931
- }
932
- if (!isEditorActive || !editor) {
933
- const empty = isContentEmpty(value);
934
- if (empty) {
935
- return /* @__PURE__ */ jsx8("div", { className, onClick: handleClick, children: /* @__PURE__ */ jsx8("p", { className: "tiptap-placeholder", children: placeholder }) });
936
- }
937
- return /* @__PURE__ */ jsx8(
938
- "div",
939
- {
940
- className,
941
- onClick: handleClick,
942
- dangerouslySetInnerHTML: { __html: sanitizeHtml(value) }
943
- }
944
- );
945
- }
946
- return /* @__PURE__ */ jsxs3(
947
- "div",
948
- {
949
- ref: wrapperRef,
950
- className,
951
- onFocus: handleFocus,
952
- onBlur: handleBlur,
953
- children: [
954
- /* @__PURE__ */ jsx8(
955
- BubbleMenu,
956
- {
957
- editor,
958
- className: "z-50",
959
- shouldShow: ({ editor: ed, state }) => {
960
- const { from, to } = state.selection;
961
- return ed.isFocused && from !== to;
962
- },
963
- children: /* @__PURE__ */ jsx8(RichTextToolbar, { editor, preset })
964
- }
965
- ),
966
- /* @__PURE__ */ jsx8(EditorContent, { editor })
967
- ]
968
- }
969
- );
970
- }
101
+ tag: "h4",
102
+ placeholder: "Sub sub heading",
103
+ defaultHeading: "New Sub Sub Heading",
104
+ navExclusion: true
105
+ });
106
+
107
+ // src/components/sections/Prose/index.tsx
108
+ import { z } from "zod";
109
+ import { AlignLeft } from "lucide-react";
971
110
 
972
111
  // src/components/sections/Prose/Prose.tsx
973
- import { jsx as jsx9 } from "react/jsx-runtime";
112
+ import { jsx as jsx4 } from "react/jsx-runtime";
974
113
  function Prose({ body, onChange }) {
975
114
  if (onChange) {
976
- return /* @__PURE__ */ jsx9("div", { className: "prose-content", children: /* @__PURE__ */ jsx9(
115
+ return /* @__PURE__ */ jsx4("div", { className: "prose-content", children: /* @__PURE__ */ jsx4(
977
116
  EditableRichText,
978
117
  {
979
118
  value: body,
@@ -984,7 +123,7 @@ function Prose({ body, onChange }) {
984
123
  }
985
124
  ) });
986
125
  }
987
- return /* @__PURE__ */ jsx9(
126
+ return /* @__PURE__ */ jsx4(
988
127
  "div",
989
128
  {
990
129
  className: "prose-content",
@@ -1001,237 +140,51 @@ function truncate(text, maxLength) {
1001
140
  if (text.length <= maxLength) return text;
1002
141
  return text.slice(0, maxLength) + "...";
1003
142
  }
143
+ function pluralize(n, word) {
144
+ return `${n} ${word}${n === 1 ? "" : "s"}`;
145
+ }
1004
146
 
1005
147
  // src/components/sections/Prose/index.tsx
1006
- import { jsx as jsx10 } from "react/jsx-runtime";
1007
- var schema4 = z4.object({
1008
- type: z4.literal("prose"),
1009
- content: z4.object({ body: z4.string() })
148
+ import { jsx as jsx5 } from "react/jsx-runtime";
149
+ var schema = z.object({
150
+ type: z.literal("prose"),
151
+ content: z.object({ body: z.string() })
1010
152
  });
1011
153
  var Prose_default = defineSection({
1012
154
  type: "prose",
1013
155
  label: "Prose",
1014
- icon: /* @__PURE__ */ jsx10(AlignLeft, { size: 18 }),
1015
- schema: schema4,
156
+ icon: /* @__PURE__ */ jsx5(AlignLeft, { size: 18 }),
157
+ schema,
1016
158
  richTextFields: ["body"],
1017
- component: ({ content, onChange }) => /* @__PURE__ */ jsx10(Prose, { body: content.content.body, onChange: onChange ? (c) => onChange(c) : void 0 }),
159
+ component: ({ content, onChange }) => /* @__PURE__ */ jsx5(Prose, { body: content.content.body, onChange: onChange ? (c) => onChange(c) : void 0 }),
1018
160
  defaults: () => ({ type: "prose", content: { body: "<p></p>" } }),
1019
161
  getLabel: (content) => truncate(stripHtmlToPlainText(content.content.body), 60)
1020
162
  });
1021
163
 
1022
164
  // src/components/sections/Media/index.tsx
1023
- import { z as z5 } from "zod";
1024
- import { Image as ImageIcon2 } from "lucide-react";
1025
-
1026
- // src/components/sections/Media/MediaBlock.tsx
1027
- import { Check, X } from "lucide-react";
1028
-
1029
- // src/components/primitives/ResolvedMedia.tsx
1030
- import { ImageIcon } from "lucide-react";
1031
-
1032
- // src/hooks/useResolvedMedia.ts
1033
- import { useMemo } from "react";
1034
-
1035
- // src/components/shell/MediaLibraryContext.tsx
1036
- import { createContext as createContext2, useContext as useContext2 } from "react";
1037
- var MediaLibraryContext = createContext2(null);
1038
- function useMediaLibrary() {
1039
- return useContext2(MediaLibraryContext);
1040
- }
1041
-
1042
- // src/hooks/useResolvedMedia.ts
1043
- function useResolvedMedia(imageId) {
1044
- const ctx = useMediaLibrary();
1045
- return useMemo(() => {
1046
- if (!imageId || !ctx) {
1047
- return { src: void 0, srcset: void 0, poster: void 0, alt: "", kind: "image" };
1048
- }
1049
- const { manifest, pendingItems, pendingLocalUrls, siteConfig } = ctx;
1050
- const sizes = siteConfig?.media?.sizes ?? [640, 1080, 1920];
1051
- const manifestItem = manifest.images[imageId];
1052
- const pendingItem = pendingItems.find((i) => i.id === imageId);
1053
- const item = manifestItem ?? pendingItem;
1054
- const alt = item?.alt ?? "";
1055
- const kind = item?.kind ?? "image";
1056
- const localUrl = pendingLocalUrls[imageId];
1057
- if (localUrl) {
1058
- return { src: localUrl, srcset: void 0, poster: void 0, alt, kind };
1059
- }
1060
- if (manifestItem) {
1061
- const provider = getMediaProvider();
1062
- const resolved = provider.resolve(manifestItem, sizes);
1063
- if (resolved && resolved.tag === "img") {
1064
- return {
1065
- src: resolved.src,
1066
- srcset: "srcSet" in resolved ? resolved.srcSet : void 0,
1067
- poster: void 0,
1068
- alt,
1069
- kind
1070
- };
1071
- }
1072
- if (resolved && resolved.tag === "video") {
1073
- return { src: resolved.src, srcset: void 0, poster: resolved.poster, alt, kind };
1074
- }
1075
- }
1076
- return { src: void 0, srcset: void 0, poster: void 0, alt, kind };
1077
- }, [imageId, ctx]);
1078
- }
1079
-
1080
- // src/components/primitives/ResolvedMedia.tsx
1081
- import { jsx as jsx11, jsxs as jsxs4 } from "react/jsx-runtime";
1082
- function ResolvedMedia({
1083
- imageId,
1084
- src: propSrc,
1085
- srcset: propSrcset,
1086
- alt: propAlt,
1087
- className,
1088
- imgClassName,
1089
- invertFrom
1090
- }) {
1091
- const resolved = useResolvedMedia(imageId);
1092
- const src = propSrc || resolved.src;
1093
- const srcset = propSrcset || resolved.srcset;
1094
- const poster = resolved.poster;
1095
- const alt = propAlt ?? resolved.alt;
1096
- const kind = resolved.kind;
1097
- const invertClass = invertFrom === "light" ? "invert dark:invert-0" : invertFrom === "dark" ? "dark:invert" : void 0;
1098
- const mediaClass = cn("h-full w-full", imgClassName, invertClass);
1099
- return /* @__PURE__ */ jsx11("div", { className, children: src ? kind === "video" ? /* @__PURE__ */ jsx11("video", { src, poster, muted: true, playsInline: true, loop: true, autoPlay: true, className: mediaClass }) : /* @__PURE__ */ jsx11("img", { src, srcSet: srcset, alt, className: mediaClass }) : /* @__PURE__ */ jsxs4("div", { className: "flex aspect-video h-full w-full flex-col items-center justify-center gap-1 rounded bg-base-accent text-base-contrast-light/50", children: [
1100
- /* @__PURE__ */ jsx11(ImageIcon, { size: 24 }),
1101
- /* @__PURE__ */ jsx11("span", { className: "text-sm", children: "No Image" })
1102
- ] }) });
1103
- }
1104
-
1105
- // src/components/primitives/ImageDropZone.tsx
1106
- import { useState as useState5 } from "react";
1107
- import { Fragment as Fragment3, jsx as jsx12, jsxs as jsxs5 } from "react/jsx-runtime";
1108
- var ACCEPTED = /^(image|video)\//;
1109
- function ImageDropZone({ onImageSelected, className, children }) {
1110
- const mediaLibrary = useMediaLibrary();
1111
- const [dragging, setDragging] = useState5(false);
1112
- if (!mediaLibrary) return /* @__PURE__ */ jsx12(Fragment3, { children });
1113
- const maxFileSize = mediaLibrary.siteConfig?.media.maxFileSize;
1114
- const hasFiles = (e) => e.dataTransfer.types.includes("Files");
1115
- const handleDragOver = (e) => {
1116
- if (!hasFiles(e)) return;
1117
- e.preventDefault();
1118
- e.dataTransfer.dropEffect = "copy";
1119
- setDragging(true);
1120
- };
1121
- const handleDragLeave = (e) => {
1122
- if (!hasFiles(e)) return;
1123
- const related = e.relatedTarget;
1124
- if (related && e.currentTarget.contains(related)) return;
1125
- setDragging(false);
1126
- };
1127
- const handleDrop = (e) => {
1128
- if (!hasFiles(e)) return;
1129
- e.preventDefault();
1130
- e.stopPropagation();
1131
- setDragging(false);
1132
- const file = e.dataTransfer.files[0];
1133
- if (!file) return;
1134
- if (!ACCEPTED.test(file.type)) {
1135
- mediaLibrary.reportRejectedUploads([{ name: file.name, reason: "type" }]);
1136
- return;
1137
- }
1138
- if (maxFileSize && file.size > maxFileSize) {
1139
- mediaLibrary.reportRejectedUploads([{ name: file.name, reason: "size" }]);
1140
- return;
1141
- }
1142
- mediaLibrary.uploadFileWithCallback(file, onImageSelected);
1143
- };
1144
- return /* @__PURE__ */ jsxs5(
1145
- "div",
1146
- {
1147
- className: cn("relative", className),
1148
- onDragOver: handleDragOver,
1149
- onDragLeave: handleDragLeave,
1150
- onDrop: handleDrop,
1151
- children: [
1152
- children,
1153
- dragging && /* @__PURE__ */ jsx12("div", { className: "pointer-events-none absolute inset-0 z-10 rounded-md border-2 border-primary bg-primary/5" })
1154
- ]
1155
- }
1156
- );
1157
- }
1158
-
1159
- // src/components/sections/Media/MediaBlock.tsx
1160
- import { jsx as jsx13, jsxs as jsxs6 } from "react/jsx-runtime";
1161
- function MediaBlock({ refValue, options, link, dodont, isEditMode, onRefChange }) {
1162
- const mediaLibrary = useMediaLibrary();
1163
- const fit = refValue.objectFit ?? options.objectFit;
1164
- const fitClass = fit === "cover" ? "object-cover" : "object-contain";
1165
- const showBorder = refValue.border ?? options.border;
1166
- const captionStr = refValue.caption ? Array.isArray(refValue.caption) ? refValue.caption.join("\n") : refValue.caption : "";
1167
- const replace = onRefChange ? (imageId) => onRefChange({ ...refValue, imageId }) : void 0;
1168
- const resolved = /* @__PURE__ */ jsx13(
1169
- ResolvedMedia,
1170
- {
1171
- imageId: refValue.imageId || void 0,
1172
- className: "h-full w-full",
1173
- imgClassName: fitClass,
1174
- invertFrom: refValue.invertFrom
1175
- }
1176
- );
1177
- let media = isEditMode && replace ? /* @__PURE__ */ jsx13(ImageDropZone, { onImageSelected: replace, className: "h-full w-full", children: resolved }) : resolved;
1178
- if (!isEditMode && link && link.kind === "external" && link.href) {
1179
- media = /* @__PURE__ */ jsx13("a", { href: link.href, target: link.target, className: "group block", children: media });
1180
- }
1181
- const onImageClick = isEditMode && replace && mediaLibrary ? () => mediaLibrary.openSelectModal((imageId) => replace(imageId)) : void 0;
1182
- return /* @__PURE__ */ jsxs6("figure", { children: [
1183
- dodont && /* @__PURE__ */ jsx13("div", { className: "flex h-10 items-center justify-center pb-1", children: dodont === "do" ? /* @__PURE__ */ jsx13(Check, { size: 28, className: "text-green-600" }) : /* @__PURE__ */ jsx13(X, { size: 28, className: "text-red-600" }) }),
1184
- /* @__PURE__ */ jsx13(
1185
- "div",
1186
- {
1187
- "data-testid": "media-open-surface",
1188
- className: cn(
1189
- "overflow-hidden rounded-md",
1190
- showBorder && "border border-base-200",
1191
- options.square && "aspect-square",
1192
- onImageClick && "cursor-pointer"
1193
- ),
1194
- onClick: onImageClick,
1195
- onPointerDown: onImageClick ? (e) => e.stopPropagation() : void 0,
1196
- children: media
1197
- }
1198
- ),
1199
- options.showCaption && /* @__PURE__ */ jsx13("figcaption", { className: "mt-2 min-h-[1em] text-sm text-base-contrast-light", children: isEditMode && onRefChange ? /* @__PURE__ */ jsx13(
1200
- EditablePlainText,
1201
- {
1202
- tag: "span",
1203
- value: captionStr,
1204
- onChange: (caption) => onRefChange({ ...refValue, caption: caption || void 0 }),
1205
- isEditMode: true,
1206
- placeholder: "Caption",
1207
- className: "block min-h-[1em]"
1208
- }
1209
- ) : captionStr || " " })
1210
- ] });
1211
- }
1212
-
1213
- // src/components/sections/Media/index.tsx
1214
- import { jsx as jsx14 } from "react/jsx-runtime";
1215
- var schema5 = z5.object({
1216
- type: z5.literal("media"),
1217
- content: z5.object({
165
+ import { z as z2 } from "zod";
166
+ import { Image as ImageIcon } from "lucide-react";
167
+ import { jsx as jsx6 } from "react/jsx-runtime";
168
+ var schema2 = z2.object({
169
+ type: z2.literal("media"),
170
+ content: z2.object({
1218
171
  ref: SingleMediaReferenceSchema,
1219
172
  link: LinkValueSchema.optional(),
1220
- dodont: z5.enum(["do", "dont"]).optional()
173
+ dodont: z2.enum(["do", "dont"]).optional()
1221
174
  }),
1222
- options: z5.object({
1223
- square: z5.boolean().optional(),
1224
- showCaption: z5.boolean().optional(),
1225
- border: z5.boolean().optional(),
1226
- objectFit: z5.enum(["cover", "contain"]).optional()
175
+ options: z2.object({
176
+ square: z2.boolean().optional(),
177
+ showCaption: z2.boolean().optional(),
178
+ border: z2.boolean().optional(),
179
+ objectFit: z2.enum(["cover", "contain"]).optional()
1227
180
  }).default({})
1228
181
  });
1229
182
  var Media_default = defineSection({
1230
183
  type: "media",
1231
184
  label: "Media",
1232
- icon: /* @__PURE__ */ jsx14(ImageIcon2, { size: 18 }),
1233
- schema: schema5,
1234
- component: ({ content, options, onChange }) => /* @__PURE__ */ jsx14(
185
+ icon: /* @__PURE__ */ jsx6(ImageIcon, { size: 18 }),
186
+ schema: schema2,
187
+ component: ({ content, options, onChange }) => /* @__PURE__ */ jsx6(
1235
188
  MediaBlock,
1236
189
  {
1237
190
  refValue: content.content.ref,
@@ -1285,19 +238,19 @@ var Media_default = defineSection({
1285
238
  });
1286
239
 
1287
240
  // src/components/sections/Button/index.tsx
1288
- import { z as z6 } from "zod";
241
+ import { z as z3 } from "zod";
1289
242
  import { RectangleHorizontal } from "lucide-react";
1290
243
 
1291
244
  // src/components/sections/Button/CTAButton.tsx
1292
245
  import { Download } from "lucide-react";
1293
- import { jsx as jsx15, jsxs as jsxs7 } from "react/jsx-runtime";
1294
- function Button3({ text, link, download, onChange }) {
246
+ import { jsx as jsx7, jsxs } from "react/jsx-runtime";
247
+ function Button({ text, link, download, onChange }) {
1295
248
  const base = "inline-block rounded-md px-6 py-3 font-bold transition-colors";
1296
249
  const variant = "border-2 border-primary text-primary hover:bg-primary hover:text-primary-contrast";
1297
250
  if (onChange) {
1298
- return /* @__PURE__ */ jsxs7("span", { className: cn(base, variant, "inline-flex items-center gap-2"), children: [
1299
- download && /* @__PURE__ */ jsx15(Download, { size: 16, className: "shrink-0" }),
1300
- /* @__PURE__ */ jsx15(
251
+ return /* @__PURE__ */ jsxs("span", { className: cn(base, variant, "inline-flex items-center gap-2"), children: [
252
+ download && /* @__PURE__ */ jsx7(Download, { size: 16, className: "shrink-0" }),
253
+ /* @__PURE__ */ jsx7(
1301
254
  EditablePlainText,
1302
255
  {
1303
256
  tag: "span",
@@ -1312,7 +265,7 @@ function Button3({ text, link, download, onChange }) {
1312
265
  const href = link && link.kind === "external" ? link.href : "";
1313
266
  const target = link?.target;
1314
267
  if (href) {
1315
- return /* @__PURE__ */ jsxs7(
268
+ return /* @__PURE__ */ jsxs(
1316
269
  "a",
1317
270
  {
1318
271
  href,
@@ -1320,488 +273,63 @@ function Button3({ text, link, download, onChange }) {
1320
273
  download: download ? "" : void 0,
1321
274
  className: cn(base, variant, download && "inline-flex items-center gap-2"),
1322
275
  children: [
1323
- download && /* @__PURE__ */ jsx15(Download, { size: 16, className: "shrink-0" }),
276
+ download && /* @__PURE__ */ jsx7(Download, { size: 16, className: "shrink-0" }),
1324
277
  text
1325
278
  ]
1326
279
  }
1327
280
  );
1328
281
  }
1329
- return /* @__PURE__ */ jsx15("button", { className: cn("cursor-pointer", base, variant), children: text });
282
+ return /* @__PURE__ */ jsx7("button", { className: cn("cursor-pointer", base, variant), children: text });
1330
283
  }
1331
284
 
1332
285
  // src/components/sections/Button/index.tsx
1333
- import { jsx as jsx16 } from "react/jsx-runtime";
1334
- var schema6 = z6.object({
1335
- type: z6.literal("button"),
1336
- content: z6.object({
1337
- text: z6.string(),
286
+ import { jsx as jsx8 } from "react/jsx-runtime";
287
+ var schema3 = z3.object({
288
+ type: z3.literal("button"),
289
+ content: z3.object({
290
+ text: z3.string(),
1338
291
  link: LinkValueSchema.optional(),
1339
- download: z6.boolean().optional()
292
+ download: z3.boolean().optional()
1340
293
  })
1341
294
  });
1342
295
  var Button_default = defineSection({
1343
296
  type: "button",
1344
297
  label: "Button",
1345
- icon: /* @__PURE__ */ jsx16(RectangleHorizontal, { size: 18 }),
1346
- schema: schema6,
1347
- component: ({ content, onChange }) => /* @__PURE__ */ jsx16(
1348
- Button3,
298
+ icon: /* @__PURE__ */ jsx8(RectangleHorizontal, { size: 18 }),
299
+ schema: schema3,
300
+ component: ({ content, onChange }) => /* @__PURE__ */ jsx8(
301
+ Button,
1349
302
  {
1350
303
  text: content.content.text,
1351
304
  link: content.content.link,
1352
- download: content.content.download,
1353
- onChange: onChange ? (c) => onChange(c) : void 0
1354
- }
1355
- ),
1356
- defaults: () => ({ type: "button", content: { text: "Button" } }),
1357
- getLabel: (content) => content.content.text,
1358
- settings: {
1359
- link: { type: "link", label: "Link", default: DEFAULT_LINK, target: "content" },
1360
- download: { type: "checkbox", label: "Download link", default: false, target: "content" }
1361
- }
1362
- });
1363
-
1364
- // src/components/sections/Colors/index.tsx
1365
- import { z as z7 } from "zod";
1366
- import { Palette } from "lucide-react";
1367
-
1368
- // src/components/brandguide/Colors.tsx
1369
- import { useState as useState9, useEffect as useEffect7 } from "react";
1370
- import { Eye, EyeOff } from "lucide-react";
1371
-
1372
- // src/lib/container-grid.ts
1373
- var MAX_CONTAINER_COLUMNS = 6;
1374
- var containerGridClass = {
1375
- 1: "grid-cols-1",
1376
- 2: "grid-cols-1 @sm:grid-cols-2",
1377
- 3: "grid-cols-1 @sm:grid-cols-2 @lg:grid-cols-3",
1378
- 4: "grid-cols-1 @xs:grid-cols-2 @lg:grid-cols-4",
1379
- 5: "grid-cols-1 @xs:grid-cols-2 @md:grid-cols-3 @xl:grid-cols-5",
1380
- 6: "grid-cols-1 @xs:grid-cols-2 @md:grid-cols-3 @2xl:grid-cols-6"
1381
- };
1382
- var colSpanClass = {
1383
- 1: { 1: "" },
1384
- 2: { 1: "", 2: "col-span-1 @sm:col-span-2" },
1385
- 3: { 1: "", 2: "col-span-1 @sm:col-span-2", 3: "col-span-1 @sm:col-span-2 @lg:col-span-3" },
1386
- 4: { 1: "", 2: "col-span-1 @xs:col-span-2", 3: "col-span-1 @xs:col-span-2 @lg:col-span-3", 4: "col-span-1 @xs:col-span-2 @lg:col-span-4" },
1387
- 5: { 1: "", 2: "col-span-1 @xs:col-span-2", 3: "col-span-1 @xs:col-span-2 @md:col-span-3", 4: "col-span-1 @xs:col-span-2 @md:col-span-3 @xl:col-span-4", 5: "col-span-1 @xs:col-span-2 @md:col-span-3 @xl:col-span-5" },
1388
- 6: { 1: "", 2: "col-span-1 @xs:col-span-2", 3: "col-span-1 @xs:col-span-2 @md:col-span-3", 4: "col-span-1 @xs:col-span-2 @md:col-span-3 @2xl:col-span-4", 5: "col-span-1 @xs:col-span-2 @md:col-span-3 @2xl:col-span-5", 6: "col-span-1 @xs:col-span-2 @md:col-span-3 @2xl:col-span-6" }
1389
- };
1390
- var spacerHiddenClass = {
1391
- 1: "hidden",
1392
- 2: "hidden @sm:block",
1393
- 3: "hidden @sm:block",
1394
- 4: "hidden @xs:block",
1395
- 5: "hidden @xs:block",
1396
- 6: "hidden @xs:block"
1397
- };
1398
-
1399
- // src/components/primitives/EditableGrid.tsx
1400
- import { useRef as useRef6, useEffect as useEffect6, useState as useState7 } from "react";
1401
- import { ImageIcon as ImageIcon3 } from "lucide-react";
1402
-
1403
- // src/components/shared/icons.tsx
1404
- import { GripVertical, Plus, Trash2, X as X2, Settings } from "lucide-react";
1405
- import { GripVertical as GripVertical2, Plus as Plus2, Trash2 as Trash22, X as X3, Settings as Settings2 } from "lucide-react";
1406
- import { jsx as jsx17 } from "react/jsx-runtime";
1407
- function DragHandle({ size = 16 }) {
1408
- return /* @__PURE__ */ jsx17(GripVertical, { size });
1409
- }
1410
- function AddIcon({ size = 14 }) {
1411
- return /* @__PURE__ */ jsx17(Plus, { size });
1412
- }
1413
- function DeleteIcon({ size = 14 }) {
1414
- return /* @__PURE__ */ jsx17(Trash2, { size });
1415
- }
1416
- function SettingsIcon({ size = 14 }) {
1417
- return /* @__PURE__ */ jsx17(Settings, { size });
1418
- }
1419
-
1420
- // src/components/shared/IconButton.tsx
1421
- import { forwardRef as forwardRef2 } from "react";
1422
- import { jsx as jsx18 } from "react/jsx-runtime";
1423
- var sizeClasses = {
1424
- sm: "h-chrome-sm w-chrome-sm",
1425
- md: "h-chrome-md w-chrome-md",
1426
- lg: "h-chrome-lg w-chrome-lg"
1427
- };
1428
- var intentClasses = {
1429
- default: "text-base-contrast-light hover:text-base-contrast",
1430
- destructive: "text-destructive hover:text-destructive-hover",
1431
- primary: "text-primary/60 hover:text-primary"
1432
- };
1433
- var IconButton = forwardRef2(
1434
- function IconButton2({ icon, label, size = "md", intent = "default", className, ...rest }, ref) {
1435
- return /* @__PURE__ */ jsx18(
1436
- "button",
1437
- {
1438
- ref,
1439
- type: "button",
1440
- className: cn(
1441
- "cursor-pointer flex items-center justify-center rounded transition-colors",
1442
- sizeClasses[size],
1443
- intentClasses[intent],
1444
- className
1445
- ),
1446
- "aria-label": label,
1447
- ...rest,
1448
- children: icon
1449
- }
1450
- );
1451
- }
1452
- );
1453
-
1454
- // src/components/primitives/useEditableCollection.ts
1455
- import { useState as useState6, useCallback as useCallback5, useRef as useRef5, useEffect as useEffect5 } from "react";
1456
- var nextId = 0;
1457
- function uid() {
1458
- return typeof crypto !== "undefined" && typeof crypto.randomUUID === "function" ? crypto.randomUUID() : `_id_${nextId++}_${Date.now()}`;
1459
- }
1460
- function wrapItems(items) {
1461
- return items.map((data) => ({ id: uid(), data }));
1462
- }
1463
- function useEditableCollection({
1464
- items,
1465
- onChange,
1466
- createItem
1467
- }) {
1468
- const [wrappedItems, setWrappedItems] = useState6(
1469
- () => wrapItems(items)
1470
- );
1471
- const prevItemsRef = useRef5(items);
1472
- useEffect5(() => {
1473
- if (items === prevItemsRef.current) return;
1474
- prevItemsRef.current = items;
1475
- setWrappedItems((prev) => {
1476
- return items.map((data, i) => {
1477
- if (i < prev.length) {
1478
- return { id: prev[i].id, data };
1479
- }
1480
- return { id: uid(), data };
1481
- });
1482
- });
1483
- }, [items]);
1484
- const onReorder = useCallback5(
1485
- (fromIndex, toIndex) => {
1486
- setWrappedItems((prev) => {
1487
- const next = [...prev];
1488
- const [moved] = next.splice(fromIndex, 1);
1489
- next.splice(toIndex, 0, moved);
1490
- onChange(next.map((w) => w.data));
1491
- return next;
1492
- });
1493
- },
1494
- [onChange]
1495
- );
1496
- const onAdd = useCallback5(() => {
1497
- const newItem = createItem();
1498
- setWrappedItems((prev) => {
1499
- const next = [...prev, { id: uid(), data: newItem }];
1500
- onChange(next.map((w) => w.data));
1501
- return next;
1502
- });
1503
- }, [createItem, onChange]);
1504
- const onInsert = useCallback5(
1505
- (index) => {
1506
- const newItem = createItem();
1507
- setWrappedItems((prev) => {
1508
- const next = [...prev];
1509
- next.splice(index, 0, { id: uid(), data: newItem });
1510
- onChange(next.map((w) => w.data));
1511
- return next;
1512
- });
1513
- },
1514
- [createItem, onChange]
1515
- );
1516
- const onRemove = useCallback5(
1517
- (id) => {
1518
- setWrappedItems((prev) => {
1519
- const next = prev.filter((w) => w.id !== id);
1520
- onChange(next.map((w) => w.data));
1521
- return next;
1522
- });
1523
- },
1524
- [onChange]
1525
- );
1526
- return { wrappedItems, onReorder, onAdd, onInsert, onRemove };
1527
- }
1528
-
1529
- // src/components/primitives/EditableGrid.tsx
1530
- import { jsx as jsx19, jsxs as jsxs8 } from "react/jsx-runtime";
1531
- function EditableGrid({
1532
- items,
1533
- columns,
1534
- onChange,
1535
- createItem,
1536
- isEditMode,
1537
- renderItem,
1538
- onItemSettings,
1539
- onItemImageClick,
1540
- chromeTopClass,
1541
- className
1542
- }) {
1543
- const { wrappedItems, onReorder, onAdd, onInsert, onRemove } = useEditableCollection({
1544
- items,
1545
- onChange,
1546
- createItem
1547
- });
1548
- const [dragState, setDragState] = useState7({ sourceId: null, closestEdge: null });
1549
- const [isDragging, setIsDragging] = useState7(false);
1550
- useEffect6(() => {
1551
- let cleanup;
1552
- let cancelled = false;
1553
- import("../../adapter-HH47ZPGM.js").then(({ monitorForElements }) => {
1554
- if (cancelled) return;
1555
- cleanup = monitorForElements({
1556
- onDragStart: ({ source }) => {
1557
- if (source.data.dragType === "grid-cell") setIsDragging(true);
1558
- },
1559
- onDrop: ({ source }) => {
1560
- if (source.data.dragType === "grid-cell") setIsDragging(false);
1561
- }
1562
- });
1563
- });
1564
- return () => {
1565
- cancelled = true;
1566
- cleanup?.();
1567
- };
1568
- }, []);
1569
- if (!isEditMode) {
1570
- return /* @__PURE__ */ jsx19("div", { className: "@container", children: /* @__PURE__ */ jsx19("div", { className: cn("grid gap-4", containerGridClass[columns] || containerGridClass[1], className), children: wrappedItems.map((wrapped, index) => /* @__PURE__ */ jsx19("div", { children: renderItem(wrapped.data, { isEditMode: false, index }) }, wrapped.id)) }) });
1571
- }
1572
- return /* @__PURE__ */ jsx19("div", { className: "@container", children: /* @__PURE__ */ jsxs8("div", { className: cn("group/grid relative grid gap-4", containerGridClass[columns] || containerGridClass[1], className), children: [
1573
- wrappedItems.map((wrapped, index) => /* @__PURE__ */ jsx19(
1574
- GridCell,
1575
- {
1576
- id: wrapped.id,
1577
- index,
1578
- isLast: index === wrappedItems.length - 1,
1579
- isDragging,
1580
- onReorder,
1581
- onRemove,
1582
- onInsert,
1583
- onSettings: onItemSettings ? () => onItemSettings(index) : void 0,
1584
- onImageClick: onItemImageClick ? () => onItemImageClick(index) : void 0,
1585
- chromeTopClass,
1586
- dragState,
1587
- setDragState,
1588
- children: renderItem(wrapped.data, { isEditMode: true, index })
1589
- },
1590
- wrapped.id
1591
- )),
1592
- /* @__PURE__ */ jsx19(
1593
- IconButton,
1594
- {
1595
- icon: /* @__PURE__ */ jsx19(AddIcon, { size: 16 }),
1596
- label: "Add item",
1597
- size: "lg",
1598
- intent: "primary",
1599
- onClick: onAdd,
1600
- className: "absolute -bottom-6 left-1/2 z-20 -translate-x-1/2 rounded-full border border-base-200 bg-base opacity-0 transition-opacity group-hover/grid:opacity-100"
1601
- }
1602
- )
1603
- ] }) });
1604
- }
1605
- function GridCell({
1606
- id,
1607
- index,
1608
- isLast,
1609
- isDragging,
1610
- onReorder,
1611
- onRemove,
1612
- onInsert,
1613
- onSettings,
1614
- onImageClick,
1615
- chromeTopClass,
1616
- dragState,
1617
- setDragState,
1618
- children
1619
- }) {
1620
- const cellRef = useRef6(null);
1621
- const handleRef = useRef6(null);
1622
- useEffect6(() => {
1623
- const cell = cellRef.current;
1624
- const handle = handleRef.current;
1625
- if (!cell || !handle) return;
1626
- let cleanup;
1627
- let cancelled = false;
1628
- Promise.all([
1629
- import("../../adapter-HH47ZPGM.js"),
1630
- import("../../closest-edge-EBOXL3YW.js")
1631
- ]).then(([{ draggable, dropTargetForElements }, { attachClosestEdge, extractClosestEdge }]) => {
1632
- if (cancelled) return;
1633
- const cleanupDraggable = draggable({
1634
- element: handle,
1635
- getInitialData: () => ({ dragType: "grid-cell", id, index }),
1636
- onDragStart: () => setDragState({ sourceId: id, closestEdge: null }),
1637
- onDrop: () => setDragState({ sourceId: null, closestEdge: null })
1638
- });
1639
- const cleanupDropTarget = dropTargetForElements({
1640
- element: cell,
1641
- canDrop: ({ source }) => source.data.dragType === "grid-cell",
1642
- getData: ({ input, element }) => attachClosestEdge(
1643
- { id, index },
1644
- { input, element, allowedEdges: ["left", "right"] }
1645
- ),
1646
- onDragEnter: ({ self }) => {
1647
- const edge = extractClosestEdge(self.data);
1648
- if (edge) setDragState((prev) => ({ ...prev, closestEdge: { id, edge } }));
1649
- },
1650
- onDrag: ({ self }) => {
1651
- const edge = extractClosestEdge(self.data);
1652
- if (edge) setDragState((prev) => ({ ...prev, closestEdge: { id, edge } }));
1653
- },
1654
- onDragLeave: () => {
1655
- setDragState((prev) => prev.closestEdge?.id === id ? { ...prev, closestEdge: null } : prev);
1656
- },
1657
- onDrop: ({ source, self }) => {
1658
- const fromIndex = source.data.index;
1659
- const edge = extractClosestEdge(self.data);
1660
- let toIndex = index;
1661
- if (edge === "right") toIndex = index + 1;
1662
- if (fromIndex < toIndex) toIndex--;
1663
- if (fromIndex !== toIndex) {
1664
- onReorder(fromIndex, toIndex);
1665
- }
1666
- }
1667
- });
1668
- cleanup = () => {
1669
- cleanupDraggable();
1670
- cleanupDropTarget();
1671
- };
1672
- });
1673
- return () => {
1674
- cancelled = true;
1675
- cleanup?.();
1676
- };
1677
- }, [id, index, onReorder, setDragState]);
1678
- const isCellDragging = dragState.sourceId === id;
1679
- const showLeftEdge = dragState.closestEdge?.id === id && dragState.closestEdge.edge === "left" && dragState.sourceId !== id;
1680
- const showRightEdge = dragState.closestEdge?.id === id && dragState.closestEdge.edge === "right" && dragState.sourceId !== id;
1681
- return /* @__PURE__ */ jsxs8(
1682
- "div",
1683
- {
1684
- ref: cellRef,
1685
- className: cn(
1686
- "group/cell relative",
1687
- isCellDragging && "opacity-50"
1688
- ),
1689
- children: [
1690
- showLeftEdge && /* @__PURE__ */ jsx19("div", { className: "absolute top-0 bottom-0 left-0 z-10 w-0.5 -translate-x-2 bg-primary" }),
1691
- showRightEdge && /* @__PURE__ */ jsx19("div", { className: "absolute top-0 right-0 bottom-0 z-10 w-0.5 translate-x-2 bg-primary" }),
1692
- !isDragging && !isLast && /* @__PURE__ */ jsx19(
1693
- IconButton,
1694
- {
1695
- icon: /* @__PURE__ */ jsx19(AddIcon, { size: 12 }),
1696
- label: "Insert item",
1697
- size: "sm",
1698
- intent: "primary",
1699
- onClick: () => onInsert(index + 1),
1700
- className: "absolute top-1/2 -right-2 z-20 -translate-y-1/2 translate-x-1/2 rounded-full border border-base-200 bg-base opacity-0 group-hover/cell:opacity-100"
1701
- }
1702
- ),
1703
- /* @__PURE__ */ jsx19(
1704
- IconButton,
1705
- {
1706
- ref: handleRef,
1707
- icon: /* @__PURE__ */ jsx19(DragHandle, { size: 16 }),
1708
- label: "Drag to reorder",
1709
- className: cn("absolute left-1.5 z-10 cursor-grab bg-base/80 opacity-0 shadow-sm group-hover/cell:opacity-100 no-hover:opacity-100", chromeTopClass || "top-1.5"),
1710
- tabIndex: -1
1711
- }
1712
- ),
1713
- /* @__PURE__ */ jsxs8("div", { className: cn("absolute right-1.5 z-10 flex items-center gap-1 opacity-0 group-hover/cell:opacity-100 no-hover:opacity-100", chromeTopClass || "top-1.5"), children: [
1714
- onImageClick && /* @__PURE__ */ jsx19(
1715
- IconButton,
1716
- {
1717
- icon: /* @__PURE__ */ jsx19(ImageIcon3, { size: 16 }),
1718
- label: "Change image",
1719
- onClick: onImageClick,
1720
- className: "bg-base/80 shadow-sm"
1721
- }
1722
- ),
1723
- onSettings && /* @__PURE__ */ jsx19(
1724
- IconButton,
1725
- {
1726
- icon: /* @__PURE__ */ jsx19(SettingsIcon, { size: 16 }),
1727
- label: "Item settings",
1728
- onClick: onSettings,
1729
- className: "bg-base/80 shadow-sm"
1730
- }
1731
- ),
1732
- /* @__PURE__ */ jsx19(
1733
- IconButton,
1734
- {
1735
- icon: /* @__PURE__ */ jsx19(DeleteIcon, { size: 16 }),
1736
- label: "Delete item",
1737
- intent: "destructive",
1738
- onClick: () => onRemove(id),
1739
- className: "bg-base/80 shadow-sm"
1740
- }
1741
- )
1742
- ] }),
1743
- children
1744
- ]
1745
- }
1746
- );
1747
- }
1748
-
1749
- // src/components/brandguide/ColorSwatchSettings.tsx
1750
- import { useState as useState8 } from "react";
1751
-
1752
- // src/components/shared/Input.tsx
1753
- import { forwardRef as forwardRef3, useId } from "react";
1754
-
1755
- // src/components/shared/FormLabel.tsx
1756
- import { jsx as jsx20 } from "react/jsx-runtime";
1757
- function FormLabel({ htmlFor, children, className }) {
1758
- return /* @__PURE__ */ jsx20(
1759
- "label",
1760
- {
1761
- htmlFor,
1762
- className: cn("mb-1.5 block text-sm font-medium text-base-contrast", className),
1763
- children
305
+ download: content.content.download,
306
+ onChange: onChange ? (c) => onChange(c) : void 0
1764
307
  }
1765
- );
1766
- }
1767
-
1768
- // src/components/shared/Input.tsx
1769
- import { jsx as jsx21, jsxs as jsxs9 } from "react/jsx-runtime";
1770
- var Input = forwardRef3(function Input2({ label, value, onChange, type = "text", className, disabled, error, ...rest }, ref) {
1771
- const id = useId();
1772
- const errorId = `${id}-error`;
1773
- return /* @__PURE__ */ jsxs9("div", { className, children: [
1774
- /* @__PURE__ */ jsx21(FormLabel, { htmlFor: id, children: label }),
1775
- /* @__PURE__ */ jsx21(
1776
- "input",
1777
- {
1778
- ref,
1779
- id,
1780
- type,
1781
- value,
1782
- onChange: (e) => onChange(e.target.value),
1783
- disabled,
1784
- "aria-invalid": error ? true : void 0,
1785
- "aria-describedby": error ? errorId : void 0,
1786
- className: cn(
1787
- "w-full rounded border bg-base px-3 py-2 text-sm text-base-contrast focus:outline-none focus:ring-1",
1788
- error ? "border-red-600 focus:border-red-600 focus:ring-red-600" : "border-base-200 focus:border-base-contrast focus:ring-base-contrast",
1789
- disabled && "cursor-not-allowed opacity-50"
1790
- ),
1791
- ...rest
1792
- }
1793
- ),
1794
- error && /* @__PURE__ */ jsx21("p", { id: errorId, className: "mt-1 text-xs text-red-600", children: error })
1795
- ] });
308
+ ),
309
+ defaults: () => ({ type: "button", content: { text: "Button" } }),
310
+ getLabel: (content) => content.content.text,
311
+ settings: {
312
+ link: { type: "link", label: "Link", default: DEFAULT_LINK, target: "content" },
313
+ download: { type: "checkbox", label: "Download link", default: false, target: "content" }
314
+ }
1796
315
  });
1797
316
 
317
+ // src/components/sections/Colors/index.tsx
318
+ import { z as z4 } from "zod";
319
+ import { Palette } from "lucide-react";
320
+
321
+ // src/components/brandguide/Colors.tsx
322
+ import { useState as useState2, useEffect } from "react";
323
+ import { Eye, EyeOff } from "lucide-react";
324
+
1798
325
  // src/components/brandguide/ColorSwatchSettings.tsx
1799
- import { jsx as jsx22, jsxs as jsxs10 } from "react/jsx-runtime";
326
+ import { useState } from "react";
327
+ import { jsx as jsx9, jsxs as jsxs2 } from "react/jsx-runtime";
1800
328
  function mergeSpaces(spaces) {
1801
329
  return Object.assign({}, ...spaces);
1802
330
  }
1803
331
  function ColorSwatchSettings({ color, onChange }) {
1804
- const [localColor, setLocalColor] = useState8(() => ({
332
+ const [localColor, setLocalColor] = useState(() => ({
1805
333
  ...color,
1806
334
  spaces: [mergeSpaces(color.spaces)]
1807
335
  }));
@@ -1814,8 +342,8 @@ function ColorSwatchSettings({ color, onChange }) {
1814
342
  const newSpace = { ...space, [key]: value || void 0 };
1815
343
  update({ ...localColor, spaces: [newSpace] });
1816
344
  };
1817
- return /* @__PURE__ */ jsxs10("div", { className: "space-y-3", children: [
1818
- /* @__PURE__ */ jsx22(
345
+ return /* @__PURE__ */ jsxs2("div", { className: "space-y-3", children: [
346
+ /* @__PURE__ */ jsx9(
1819
347
  Input,
1820
348
  {
1821
349
  label: "Name",
@@ -1823,7 +351,7 @@ function ColorSwatchSettings({ color, onChange }) {
1823
351
  onChange: (name) => update({ ...localColor, name })
1824
352
  }
1825
353
  ),
1826
- ["hex", "rgb", "cmyk", "pantone"].map((key) => /* @__PURE__ */ jsx22(
354
+ ["hex", "rgb", "cmyk", "pantone"].map((key) => /* @__PURE__ */ jsx9(
1827
355
  Input,
1828
356
  {
1829
357
  label: key.toUpperCase(),
@@ -1836,7 +364,7 @@ function ColorSwatchSettings({ color, onChange }) {
1836
364
  }
1837
365
 
1838
366
  // src/components/brandguide/Colors.tsx
1839
- import { jsx as jsx23, jsxs as jsxs11 } from "react/jsx-runtime";
367
+ import { jsx as jsx10, jsxs as jsxs3 } from "react/jsx-runtime";
1840
368
  function getContrastClass(hex) {
1841
369
  if (!hex) return "text-white";
1842
370
  const h = hex.replace("#", "");
@@ -1861,7 +389,7 @@ function buildContent(colors, options) {
1861
389
  }
1862
390
  function Colors({ colors, columns = 3, label, collapsing, showLabel = true, onChange, openModal }) {
1863
391
  if (onChange && openModal) {
1864
- return /* @__PURE__ */ jsx23(
392
+ return /* @__PURE__ */ jsx10(
1865
393
  ColorsEditable,
1866
394
  {
1867
395
  colors,
@@ -1874,7 +402,7 @@ function Colors({ colors, columns = 3, label, collapsing, showLabel = true, onCh
1874
402
  }
1875
403
  );
1876
404
  }
1877
- return /* @__PURE__ */ jsx23(ColorsView, { colors, columns, label, collapsing, showLabel });
405
+ return /* @__PURE__ */ jsx10(ColorsView, { colors, columns, label, collapsing, showLabel });
1878
406
  }
1879
407
  function ColorsView({
1880
408
  colors,
@@ -1883,8 +411,8 @@ function ColorsView({
1883
411
  collapsing,
1884
412
  showLabel
1885
413
  }) {
1886
- const [expanded, setExpanded] = useState9(!collapsing);
1887
- const [copiedIndex, setCopiedIndex] = useState9(null);
414
+ const [expanded, setExpanded] = useState2(!collapsing);
415
+ const [copiedIndex, setCopiedIndex] = useState2(null);
1888
416
  const handleCopy = (value, colorIndex) => {
1889
417
  if (navigator.clipboard?.writeText) {
1890
418
  navigator.clipboard.writeText(value);
@@ -1901,37 +429,37 @@ function ColorsView({
1901
429
  setCopiedIndex(colorIndex);
1902
430
  setTimeout(() => setCopiedIndex(null), 1500);
1903
431
  };
1904
- return /* @__PURE__ */ jsxs11("div", { children: [
1905
- (showLabel && !!label || collapsing) && /* @__PURE__ */ jsxs11("div", { className: "mb-4 flex items-center gap-2", children: [
1906
- showLabel && !!label && /* @__PURE__ */ jsx23("span", { className: "text-lg font-bold", children: label }),
1907
- collapsing && /* @__PURE__ */ jsxs11(
432
+ return /* @__PURE__ */ jsxs3("div", { children: [
433
+ (showLabel && !!label || collapsing) && /* @__PURE__ */ jsxs3("div", { className: "mb-4 flex items-center gap-2", children: [
434
+ showLabel && !!label && /* @__PURE__ */ jsx10("span", { className: "text-lg font-bold", children: label }),
435
+ collapsing && /* @__PURE__ */ jsxs3(
1908
436
  "button",
1909
437
  {
1910
438
  onClick: () => setExpanded(!expanded),
1911
439
  className: "cursor-pointer ml-auto flex items-center gap-1.5 text-sm text-base-contrast-light hover:text-base-contrast",
1912
440
  children: [
1913
- /* @__PURE__ */ jsx23("span", { children: expanded ? "Hide Details" : "Show Details" }),
1914
- expanded ? /* @__PURE__ */ jsx23(EyeOff, { size: 16 }) : /* @__PURE__ */ jsx23(Eye, { size: 16 })
441
+ /* @__PURE__ */ jsx10("span", { children: expanded ? "Hide Details" : "Show Details" }),
442
+ expanded ? /* @__PURE__ */ jsx10(EyeOff, { size: 16 }) : /* @__PURE__ */ jsx10(Eye, { size: 16 })
1915
443
  ]
1916
444
  }
1917
445
  )
1918
446
  ] }),
1919
- /* @__PURE__ */ jsx23("div", { className: "@container", children: /* @__PURE__ */ jsx23("div", { className: cn("grid gap-4", containerGridClass[columns] || containerGridClass[3]), children: colors.map((color, i) => {
447
+ /* @__PURE__ */ jsx10("div", { className: "@container", children: /* @__PURE__ */ jsx10("div", { className: cn("grid gap-4", containerGridClass[columns] || containerGridClass[3]), children: colors.map((color, i) => {
1920
448
  const hex = color.spaces[0]?.hex;
1921
449
  const contrast = getContrastClass(hex);
1922
- return /* @__PURE__ */ jsxs11("div", { className: "overflow-hidden rounded-md border border-base-200", children: [
1923
- /* @__PURE__ */ jsxs11("div", { className: cn("relative flex min-h-[80px] items-end p-3", contrast), style: { backgroundColor: hex || "#ccc" }, children: [
1924
- color.name && /* @__PURE__ */ jsx23("span", { className: "text-sm font-bold", children: color.name }),
1925
- copiedIndex === i && /* @__PURE__ */ jsx23("span", { className: cn(
450
+ return /* @__PURE__ */ jsxs3("div", { className: "overflow-hidden rounded-md border border-base-200", children: [
451
+ /* @__PURE__ */ jsxs3("div", { className: cn("relative flex min-h-[80px] items-end p-3", contrast), style: { backgroundColor: hex || "#ccc" }, children: [
452
+ color.name && /* @__PURE__ */ jsx10("span", { className: "text-sm font-bold", children: color.name }),
453
+ copiedIndex === i && /* @__PURE__ */ jsx10("span", { className: cn(
1926
454
  "absolute top-2 right-2 rounded-full px-2.5 py-0.5 text-xs font-medium",
1927
455
  getContrastClass(hex) === "text-black" ? "bg-black/15 text-black" : "bg-white/25 text-white"
1928
456
  ), children: "Copied!" })
1929
457
  ] }),
1930
- expanded && /* @__PURE__ */ jsx23("div", { className: "space-y-1 bg-base-accent p-3 text-sm", children: color.spaces.map(
458
+ expanded && /* @__PURE__ */ jsx10("div", { className: "space-y-1 bg-base-accent p-3 text-sm", children: color.spaces.map(
1931
459
  (space, j) => Object.entries(space).map(
1932
- ([key, value]) => value ? /* @__PURE__ */ jsxs11("button", { onClick: () => handleCopy(value, i), className: "cursor-pointer flex w-full justify-between hover:text-primary", children: [
1933
- /* @__PURE__ */ jsx23("span", { className: "font-medium uppercase", children: key }),
1934
- /* @__PURE__ */ jsx23("span", { children: value })
460
+ ([key, value]) => value ? /* @__PURE__ */ jsxs3("button", { onClick: () => handleCopy(value, i), className: "cursor-pointer flex w-full justify-between hover:text-primary", children: [
461
+ /* @__PURE__ */ jsx10("span", { className: "font-medium uppercase", children: key }),
462
+ /* @__PURE__ */ jsx10("span", { children: value })
1935
463
  ] }, `${j}-${key}`) : null
1936
464
  )
1937
465
  ) })
@@ -1948,24 +476,24 @@ function ColorsEditable({
1948
476
  onChange,
1949
477
  openModal
1950
478
  }) {
1951
- const [expanded, setExpanded] = useState9(!collapsing);
479
+ const [expanded, setExpanded] = useState2(!collapsing);
1952
480
  const options = { label, columns, collapsing, showLabel };
1953
- useEffect7(() => {
481
+ useEffect(() => {
1954
482
  setExpanded(!collapsing);
1955
483
  }, [collapsing]);
1956
484
  const handleItemSettings = (index) => {
1957
485
  const color = colors[index];
1958
486
  openModal(
1959
487
  "Color Settings",
1960
- /* @__PURE__ */ jsx23(ColorSwatchSettings, { color, onChange: (updated) => {
488
+ /* @__PURE__ */ jsx10(ColorSwatchSettings, { color, onChange: (updated) => {
1961
489
  const newColors = colors.map((c, i) => i === index ? updated : c);
1962
490
  onChange(buildContent(newColors, options));
1963
491
  } })
1964
492
  );
1965
493
  };
1966
- return /* @__PURE__ */ jsxs11("div", { children: [
1967
- (showLabel && !!label || collapsing) && /* @__PURE__ */ jsxs11("div", { className: "mb-4 flex items-center gap-2", children: [
1968
- showLabel && !!label && /* @__PURE__ */ jsx23(
494
+ return /* @__PURE__ */ jsxs3("div", { children: [
495
+ (showLabel && !!label || collapsing) && /* @__PURE__ */ jsxs3("div", { className: "mb-4 flex items-center gap-2", children: [
496
+ showLabel && !!label && /* @__PURE__ */ jsx10(
1969
497
  EditablePlainText,
1970
498
  {
1971
499
  tag: "span",
@@ -1975,19 +503,19 @@ function ColorsEditable({
1975
503
  className: "text-lg font-bold"
1976
504
  }
1977
505
  ),
1978
- collapsing && /* @__PURE__ */ jsxs11(
506
+ collapsing && /* @__PURE__ */ jsxs3(
1979
507
  "button",
1980
508
  {
1981
509
  onClick: () => setExpanded(!expanded),
1982
510
  className: "cursor-pointer ml-auto flex items-center gap-1.5 text-sm text-base-contrast-light hover:text-base-contrast",
1983
511
  children: [
1984
- /* @__PURE__ */ jsx23("span", { children: expanded ? "Hide Details" : "Show Details" }),
1985
- expanded ? /* @__PURE__ */ jsx23(EyeOff, { size: 16 }) : /* @__PURE__ */ jsx23(Eye, { size: 16 })
512
+ /* @__PURE__ */ jsx10("span", { children: expanded ? "Hide Details" : "Show Details" }),
513
+ expanded ? /* @__PURE__ */ jsx10(EyeOff, { size: 16 }) : /* @__PURE__ */ jsx10(Eye, { size: 16 })
1986
514
  ]
1987
515
  }
1988
516
  )
1989
517
  ] }),
1990
- /* @__PURE__ */ jsx23(
518
+ /* @__PURE__ */ jsx10(
1991
519
  EditableGrid,
1992
520
  {
1993
521
  items: colors,
@@ -1996,7 +524,7 @@ function ColorsEditable({
1996
524
  createItem: createColorItem,
1997
525
  isEditMode: true,
1998
526
  onItemSettings: handleItemSettings,
1999
- renderItem: (color, { index }) => /* @__PURE__ */ jsx23(
527
+ renderItem: (color, { index }) => /* @__PURE__ */ jsx10(
2000
528
  ColorSwatchEditable,
2001
529
  {
2002
530
  color,
@@ -2025,8 +553,8 @@ function ColorSwatchEditable({
2025
553
  const newColors = colors.map((c, i) => i === index ? updated : c);
2026
554
  onChange(buildContent(newColors, options));
2027
555
  };
2028
- return /* @__PURE__ */ jsxs11("div", { className: "overflow-hidden rounded-md border border-base-200", children: [
2029
- /* @__PURE__ */ jsx23("div", { className: cn("flex min-h-[80px] items-end p-3", contrast), style: { backgroundColor: hex || "#ccc" }, children: /* @__PURE__ */ jsx23(
556
+ return /* @__PURE__ */ jsxs3("div", { className: "overflow-hidden rounded-md border border-base-200", children: [
557
+ /* @__PURE__ */ jsx10("div", { className: cn("flex min-h-[80px] items-end p-3", contrast), style: { backgroundColor: hex || "#ccc" }, children: /* @__PURE__ */ jsx10(
2030
558
  EditablePlainText,
2031
559
  {
2032
560
  tag: "span",
@@ -2037,11 +565,11 @@ function ColorSwatchEditable({
2037
565
  className: "text-sm font-bold"
2038
566
  }
2039
567
  ) }),
2040
- expanded && /* @__PURE__ */ jsx23("div", { className: "space-y-1 bg-base-accent p-3 text-sm", children: color.spaces.map(
568
+ expanded && /* @__PURE__ */ jsx10("div", { className: "space-y-1 bg-base-accent p-3 text-sm", children: color.spaces.map(
2041
569
  (space, j) => Object.entries(space).map(
2042
- ([key, value]) => value ? /* @__PURE__ */ jsxs11("div", { className: "flex w-full justify-between", children: [
2043
- /* @__PURE__ */ jsx23("span", { className: "font-medium uppercase", children: key }),
2044
- /* @__PURE__ */ jsx23("span", { children: value })
570
+ ([key, value]) => value ? /* @__PURE__ */ jsxs3("div", { className: "flex w-full justify-between", children: [
571
+ /* @__PURE__ */ jsx10("span", { className: "font-medium uppercase", children: key }),
572
+ /* @__PURE__ */ jsx10("span", { children: value })
2045
573
  ] }, `${j}-${key}`) : null
2046
574
  )
2047
575
  ) })
@@ -2049,23 +577,23 @@ function ColorSwatchEditable({
2049
577
  }
2050
578
 
2051
579
  // src/components/sections/Colors/index.tsx
2052
- import { jsx as jsx24 } from "react/jsx-runtime";
2053
- var schema7 = z7.object({
2054
- type: z7.literal("colors"),
2055
- content: z7.object({ colors: z7.array(ColorItemSchema) }),
2056
- options: z7.object({
2057
- label: z7.string().optional(),
2058
- columns: z7.number().int().min(2).max(4).optional(),
2059
- collapsing: z7.boolean().optional(),
2060
- showLabel: z7.boolean().optional()
580
+ import { jsx as jsx11 } from "react/jsx-runtime";
581
+ var schema4 = z4.object({
582
+ type: z4.literal("colors"),
583
+ content: z4.object({ colors: z4.array(ColorItemSchema) }),
584
+ options: z4.object({
585
+ label: z4.string().optional(),
586
+ columns: z4.number().int().min(2).max(4).optional(),
587
+ collapsing: z4.boolean().optional(),
588
+ showLabel: z4.boolean().optional()
2061
589
  }).optional()
2062
590
  });
2063
591
  var Colors_default = defineSection({
2064
592
  type: "colors",
2065
593
  label: "Colors",
2066
- icon: /* @__PURE__ */ jsx24(Palette, { size: 18 }),
2067
- schema: schema7,
2068
- component: ({ content, options, onChange, openModal }) => /* @__PURE__ */ jsx24(
594
+ icon: /* @__PURE__ */ jsx11(Palette, { size: 18 }),
595
+ schema: schema4,
596
+ component: ({ content, options, onChange, openModal }) => /* @__PURE__ */ jsx11(
2069
597
  Colors,
2070
598
  {
2071
599
  colors: content.content.colors,
@@ -2081,10 +609,7 @@ var Colors_default = defineSection({
2081
609
  type: "colors",
2082
610
  content: { colors: [{ spaces: [{ hex: "#000000" }] }] }
2083
611
  }),
2084
- getLabel: (content) => {
2085
- const n = content.content.colors.length;
2086
- return `${n} color${n === 1 ? "" : "s"}`;
2087
- },
612
+ getLabel: (content) => pluralize(content.content.colors.length, "color"),
2088
613
  getThumbnails: (content) => content.content.colors.filter((c) => c.spaces[0]?.hex).map((c) => ({ type: "color", value: c.spaces[0].hex })),
2089
614
  settings: {
2090
615
  columns: {
@@ -2101,17 +626,17 @@ var Colors_default = defineSection({
2101
626
  });
2102
627
 
2103
628
  // src/components/sections/IconList/index.tsx
2104
- import { z as z8 } from "zod";
629
+ import { z as z5 } from "zod";
2105
630
  import { List } from "lucide-react";
2106
631
 
2107
632
  // src/components/sections/IconList/IconList.tsx
2108
- import { useRef as useRef8, useEffect as useEffect9, useState as useState11, useCallback as useCallback6 } from "react";
633
+ import { useRef as useRef2, useEffect as useEffect3, useState as useState4, useCallback } from "react";
2109
634
  import { CopyPlus, Pencil } from "lucide-react";
2110
635
 
2111
636
  // src/components/primitives/IconPicker.tsx
2112
- import { useEffect as useEffect8, useRef as useRef7, useState as useState10 } from "react";
2113
- import { Check as Check2, X as X4 } from "lucide-react";
2114
- import { Fragment as Fragment4, jsx as jsx25, jsxs as jsxs12 } from "react/jsx-runtime";
637
+ import { useEffect as useEffect2, useRef, useState as useState3 } from "react";
638
+ import { Check, X } from "lucide-react";
639
+ import { Fragment, jsx as jsx12, jsxs as jsxs4 } from "react/jsx-runtime";
2115
640
  function IconPicker({
2116
641
  selected,
2117
642
  onSelect,
@@ -2121,9 +646,9 @@ function IconPicker({
2121
646
  dodont,
2122
647
  onSelectDoDont
2123
648
  }) {
2124
- const panelRef = useRef7(null);
2125
- const [mode, setMode] = useState10(dodont ? "dodont" : "none");
2126
- useEffect8(() => {
649
+ const panelRef = useRef(null);
650
+ const [mode, setMode] = useState3(dodont ? "dodont" : "none");
651
+ useEffect2(() => {
2127
652
  function handleMouseDown(e) {
2128
653
  if (panelRef.current && !panelRef.current.contains(e.target)) {
2129
654
  onClose();
@@ -2133,14 +658,14 @@ function IconPicker({
2133
658
  return () => document.removeEventListener("mousedown", handleMouseDown);
2134
659
  }, [onClose]);
2135
660
  const showDoDont = allowDoDont && mode === "dodont";
2136
- return /* @__PURE__ */ jsxs12(
661
+ return /* @__PURE__ */ jsxs4(
2137
662
  "div",
2138
663
  {
2139
664
  ref: panelRef,
2140
665
  className: "absolute z-50 w-56 rounded-lg border border-base-200 bg-base p-2 shadow-lg",
2141
666
  children: [
2142
- allowDoDont && /* @__PURE__ */ jsxs12("div", { role: "tablist", "aria-label": "Icon mode", className: "mb-2 flex gap-1 rounded-md bg-base-200 p-0.5", children: [
2143
- /* @__PURE__ */ jsx25(
667
+ allowDoDont && /* @__PURE__ */ jsxs4("div", { role: "tablist", "aria-label": "Icon mode", className: "mb-2 flex gap-1 rounded-md bg-base-200 p-0.5", children: [
668
+ /* @__PURE__ */ jsx12(
2144
669
  "button",
2145
670
  {
2146
671
  type: "button",
@@ -2158,7 +683,7 @@ function IconPicker({
2158
683
  children: "None"
2159
684
  }
2160
685
  ),
2161
- /* @__PURE__ */ jsx25(
686
+ /* @__PURE__ */ jsx12(
2162
687
  "button",
2163
688
  {
2164
689
  type: "button",
@@ -2174,8 +699,8 @@ function IconPicker({
2174
699
  }
2175
700
  )
2176
701
  ] }),
2177
- showDoDont ? /* @__PURE__ */ jsxs12("div", { className: "grid grid-cols-2 gap-2", children: [
2178
- /* @__PURE__ */ jsxs12(
702
+ showDoDont ? /* @__PURE__ */ jsxs4("div", { className: "grid grid-cols-2 gap-2", children: [
703
+ /* @__PURE__ */ jsxs4(
2179
704
  "button",
2180
705
  {
2181
706
  type: "button",
@@ -2187,12 +712,12 @@ function IconPicker({
2187
712
  ),
2188
713
  onClick: () => onSelectDoDont?.("do"),
2189
714
  children: [
2190
- /* @__PURE__ */ jsx25(Check2, { size: 20 }),
2191
- /* @__PURE__ */ jsx25("span", { className: "text-xs font-medium", children: "Do" })
715
+ /* @__PURE__ */ jsx12(Check, { size: 20 }),
716
+ /* @__PURE__ */ jsx12("span", { className: "text-xs font-medium", children: "Do" })
2192
717
  ]
2193
718
  }
2194
719
  ),
2195
- /* @__PURE__ */ jsxs12(
720
+ /* @__PURE__ */ jsxs4(
2196
721
  "button",
2197
722
  {
2198
723
  type: "button",
@@ -2204,16 +729,16 @@ function IconPicker({
2204
729
  ),
2205
730
  onClick: () => onSelectDoDont?.("dont"),
2206
731
  children: [
2207
- /* @__PURE__ */ jsx25(X4, { size: 20 }),
2208
- /* @__PURE__ */ jsx25("span", { className: "text-xs font-medium", children: "Don't" })
732
+ /* @__PURE__ */ jsx12(X, { size: 20 }),
733
+ /* @__PURE__ */ jsx12("span", { className: "text-xs font-medium", children: "Don't" })
2209
734
  ]
2210
735
  }
2211
736
  )
2212
- ] }) : /* @__PURE__ */ jsxs12(Fragment4, { children: [
2213
- /* @__PURE__ */ jsx25("div", { className: "grid grid-cols-5 gap-1", children: curatedIcons.map((entry) => {
737
+ ] }) : /* @__PURE__ */ jsxs4(Fragment, { children: [
738
+ /* @__PURE__ */ jsx12("div", { className: "grid grid-cols-5 gap-1", children: curatedIcons.map((entry) => {
2214
739
  const Icon = entry.icon;
2215
740
  const isSelected = selected === entry.id;
2216
- return /* @__PURE__ */ jsx25(
741
+ return /* @__PURE__ */ jsx12(
2217
742
  "button",
2218
743
  {
2219
744
  "aria-label": entry.label,
@@ -2223,12 +748,12 @@ function IconPicker({
2223
748
  isSelected && "ring-2 ring-primary bg-base-accent text-primary"
2224
749
  ),
2225
750
  onClick: () => onSelect(entry.id),
2226
- children: /* @__PURE__ */ jsx25(Icon, { size: 18 })
751
+ children: /* @__PURE__ */ jsx12(Icon, { size: 18 })
2227
752
  },
2228
753
  entry.id
2229
754
  );
2230
755
  }) }),
2231
- showRemove && /* @__PURE__ */ jsx25(
756
+ showRemove && /* @__PURE__ */ jsx12(
2232
757
  "button",
2233
758
  {
2234
759
  className: "cursor-pointer mt-2 w-full rounded px-2 py-1.5 text-center text-xs text-base-contrast-light hover:bg-base-accent hover:text-base-contrast",
@@ -2243,7 +768,7 @@ function IconPicker({
2243
768
  }
2244
769
 
2245
770
  // src/components/sections/IconList/IconList.tsx
2246
- import { jsx as jsx26, jsxs as jsxs13 } from "react/jsx-runtime";
771
+ import { jsx as jsx13, jsxs as jsxs5 } from "react/jsx-runtime";
2247
772
  function IconList({
2248
773
  items,
2249
774
  icon = null,
@@ -2256,7 +781,7 @@ function IconList({
2256
781
  onItemsChange
2257
782
  }) {
2258
783
  const isEditMode = !!onChange || !!onItemsChange;
2259
- const emitItems = useCallback6(
784
+ const emitItems = useCallback(
2260
785
  (newItems) => {
2261
786
  onChange?.({
2262
787
  type: "icon_list",
@@ -2268,7 +793,7 @@ function IconList({
2268
793
  [onChange, onItemsChange, icon, showLabel, stackText]
2269
794
  );
2270
795
  if (!isEditMode) {
2271
- return /* @__PURE__ */ jsx26(
796
+ return /* @__PURE__ */ jsx13(
2272
797
  ViewIconList,
2273
798
  {
2274
799
  items,
@@ -2281,7 +806,7 @@ function IconList({
2281
806
  }
2282
807
  );
2283
808
  }
2284
- return /* @__PURE__ */ jsx26(
809
+ return /* @__PURE__ */ jsx13(
2285
810
  EditIconList,
2286
811
  {
2287
812
  items,
@@ -2316,10 +841,10 @@ function ViewIconList({
2316
841
  iconClassName
2317
842
  }) {
2318
843
  const hasAnyIcon = items.some((item) => resolveIcon(item, defaultIcon));
2319
- return /* @__PURE__ */ jsx26("div", { className: "grid gap-4 pb-4", children: items.map((item, i) => {
844
+ return /* @__PURE__ */ jsx13("div", { className: "grid gap-4 pb-4", children: items.map((item, i) => {
2320
845
  const iconEntry = resolveIcon(item, defaultIcon);
2321
846
  const iconColor = resolveIconColor(item, iconClassName);
2322
- return /* @__PURE__ */ jsxs13(
847
+ return /* @__PURE__ */ jsxs5(
2323
848
  "div",
2324
849
  {
2325
850
  className: cn(
@@ -2327,25 +852,25 @@ function ViewIconList({
2327
852
  hasAnyIcon ? "grid-cols-[24px_1fr]" : "grid-cols-1"
2328
853
  ),
2329
854
  children: [
2330
- hasAnyIcon && /* @__PURE__ */ jsx26(
855
+ hasAnyIcon && /* @__PURE__ */ jsx13(
2331
856
  "div",
2332
857
  {
2333
858
  "data-testid": "icon-list-icon",
2334
859
  className: cn("flex items-start pt-0.5", iconColor),
2335
- children: iconEntry && /* @__PURE__ */ jsx26(iconEntry.icon, { size: 18 })
860
+ children: iconEntry && /* @__PURE__ */ jsx13(iconEntry.icon, { size: 18 })
2336
861
  }
2337
862
  ),
2338
- /* @__PURE__ */ jsxs13(
863
+ /* @__PURE__ */ jsxs5(
2339
864
  "div",
2340
865
  {
2341
866
  "data-testid": "icon-list-content",
2342
867
  className: stackText ? "flex flex-col" : void 0,
2343
868
  children: [
2344
- showLabel && /* @__PURE__ */ jsxs13("span", { className: labelClassName, children: [
869
+ showLabel && /* @__PURE__ */ jsxs5("span", { className: labelClassName, children: [
2345
870
  item.label,
2346
871
  !stackText && " "
2347
872
  ] }),
2348
- /* @__PURE__ */ jsx26("span", { className: textClassName, children: item.text })
873
+ /* @__PURE__ */ jsx13("span", { className: textClassName, children: item.text })
2349
874
  ]
2350
875
  }
2351
876
  )
@@ -2370,8 +895,8 @@ function EditIconList({
2370
895
  onChange: onItemsChange,
2371
896
  createItem: () => ({ label: "", text: "" })
2372
897
  });
2373
- const [dragState, setDragState] = useState11({ sourceId: null, targetId: null });
2374
- const updateItem = useCallback6(
898
+ const [dragState, setDragState] = useState4({ sourceId: null, targetId: null });
899
+ const updateItem = useCallback(
2375
900
  (index, patch) => {
2376
901
  const updated = wrappedItems.map(
2377
902
  (w, i) => i === index ? { ...w.data, ...patch } : w.data
@@ -2380,7 +905,7 @@ function EditIconList({
2380
905
  },
2381
906
  [wrappedItems, onItemsChange]
2382
907
  );
2383
- const duplicateItem = useCallback6(
908
+ const duplicateItem = useCallback(
2384
909
  (index) => {
2385
910
  const copy = { ...wrappedItems[index].data };
2386
911
  const next = [
@@ -2393,8 +918,8 @@ function EditIconList({
2393
918
  [wrappedItems, onItemsChange]
2394
919
  );
2395
920
  const hasAnyIcon = true;
2396
- return /* @__PURE__ */ jsxs13("div", { className: "group/list relative grid gap-4 pb-4", children: [
2397
- wrappedItems.map((wrapped, index) => /* @__PURE__ */ jsx26(
921
+ return /* @__PURE__ */ jsxs5("div", { className: "group/list relative grid gap-4 pb-4", children: [
922
+ wrappedItems.map((wrapped, index) => /* @__PURE__ */ jsx13(
2398
923
  EditableRow,
2399
924
  {
2400
925
  id: wrapped.id,
@@ -2416,7 +941,7 @@ function EditIconList({
2416
941
  },
2417
942
  wrapped.id
2418
943
  )),
2419
- /* @__PURE__ */ jsx26(
944
+ /* @__PURE__ */ jsx13(
2420
945
  TrailingDropZone,
2421
946
  {
2422
947
  index: wrappedItems.length,
@@ -2425,10 +950,10 @@ function EditIconList({
2425
950
  onReorder
2426
951
  }
2427
952
  ),
2428
- /* @__PURE__ */ jsx26(
953
+ /* @__PURE__ */ jsx13(
2429
954
  IconButton,
2430
955
  {
2431
- icon: /* @__PURE__ */ jsx26(AddIcon, { size: 16 }),
956
+ icon: /* @__PURE__ */ jsx13(AddIcon, { size: 16 }),
2432
957
  label: "Add item",
2433
958
  size: "lg",
2434
959
  intent: "primary",
@@ -2456,15 +981,15 @@ function EditableRow({
2456
981
  onUpdateItem,
2457
982
  onDuplicate
2458
983
  }) {
2459
- const rowRef = useRef8(null);
2460
- const handleRef = useRef8(null);
2461
- const [iconHover, setIconHover] = useState11(false);
2462
- const [pickerOpen, setPickerOpen] = useState11(false);
2463
- const [editingSuppressed, setEditingSuppressed] = useState11(false);
984
+ const rowRef = useRef2(null);
985
+ const handleRef = useRef2(null);
986
+ const [iconHover, setIconHover] = useState4(false);
987
+ const [pickerOpen, setPickerOpen] = useState4(false);
988
+ const [editingSuppressed, setEditingSuppressed] = useState4(false);
2464
989
  const iconEntry = resolveIcon(item, defaultIcon);
2465
990
  const effectiveIconId = item.icon ?? defaultIcon ?? null;
2466
991
  const iconColor = resolveIconColor(item, iconClassName);
2467
- useEffect9(() => {
992
+ useEffect3(() => {
2468
993
  const row = rowRef.current;
2469
994
  const handle = handleRef.current;
2470
995
  if (!row || !handle) return;
@@ -2508,7 +1033,7 @@ function EditableRow({
2508
1033
  setEditingSuppressed(true);
2509
1034
  }
2510
1035
  };
2511
- return /* @__PURE__ */ jsxs13(
1036
+ return /* @__PURE__ */ jsxs5(
2512
1037
  "div",
2513
1038
  {
2514
1039
  ref: rowRef,
@@ -2521,7 +1046,7 @@ function EditableRow({
2521
1046
  onInput: onEditableActivity,
2522
1047
  onMouseMove: () => setEditingSuppressed(false),
2523
1048
  children: [
2524
- hasAnyIcon && /* @__PURE__ */ jsxs13(
1049
+ hasAnyIcon && /* @__PURE__ */ jsxs5(
2525
1050
  "div",
2526
1051
  {
2527
1052
  "data-testid": "icon-list-icon",
@@ -2529,16 +1054,16 @@ function EditableRow({
2529
1054
  onMouseEnter: () => setIconHover(true),
2530
1055
  onMouseLeave: () => setIconHover(false),
2531
1056
  children: [
2532
- /* @__PURE__ */ jsx26(
1057
+ /* @__PURE__ */ jsx13(
2533
1058
  "button",
2534
1059
  {
2535
1060
  className: "cursor-pointer transition-opacity hover:opacity-70",
2536
1061
  "aria-label": "Change icon",
2537
1062
  onClick: () => setPickerOpen((prev) => !prev),
2538
- children: iconHover && !pickerOpen ? /* @__PURE__ */ jsx26(Pencil, { size: 18, className: "text-base-contrast-light" }) : iconEntry ? /* @__PURE__ */ jsx26(iconEntry.icon, { size: 18 }) : /* @__PURE__ */ jsx26("span", { className: "flex h-[18px] w-[18px] items-center justify-center rounded-sm border border-dashed border-base-contrast-light/40" })
1063
+ children: iconHover && !pickerOpen ? /* @__PURE__ */ jsx13(Pencil, { size: 18, className: "text-base-contrast-light" }) : iconEntry ? /* @__PURE__ */ jsx13(iconEntry.icon, { size: 18 }) : /* @__PURE__ */ jsx13("span", { className: "flex h-[18px] w-[18px] items-center justify-center rounded-sm border border-dashed border-base-contrast-light/40" })
2539
1064
  }
2540
1065
  ),
2541
- pickerOpen && /* @__PURE__ */ jsx26("div", { className: "absolute top-full left-0 z-50 mt-1", children: /* @__PURE__ */ jsx26(
1066
+ pickerOpen && /* @__PURE__ */ jsx13("div", { className: "absolute top-full left-0 z-50 mt-1", children: /* @__PURE__ */ jsx13(
2542
1067
  IconPicker,
2543
1068
  {
2544
1069
  selected: effectiveIconId,
@@ -2566,8 +1091,8 @@ function EditableRow({
2566
1091
  ]
2567
1092
  }
2568
1093
  ),
2569
- /* @__PURE__ */ jsxs13("div", { className: stackText ? "flex flex-col" : void 0, children: [
2570
- showLabel && /* @__PURE__ */ jsx26(
1094
+ /* @__PURE__ */ jsxs5("div", { className: stackText ? "flex flex-col" : void 0, children: [
1095
+ showLabel && /* @__PURE__ */ jsx13(
2571
1096
  EditablePlainText,
2572
1097
  {
2573
1098
  tag: "span",
@@ -2578,7 +1103,7 @@ function EditableRow({
2578
1103
  className: cn(labelClassName, !stackText && "mr-1")
2579
1104
  }
2580
1105
  ),
2581
- /* @__PURE__ */ jsx26(
1106
+ /* @__PURE__ */ jsx13(
2582
1107
  EditablePlainText,
2583
1108
  {
2584
1109
  tag: "span",
@@ -2590,7 +1115,7 @@ function EditableRow({
2590
1115
  }
2591
1116
  )
2592
1117
  ] }),
2593
- /* @__PURE__ */ jsxs13(
1118
+ /* @__PURE__ */ jsxs5(
2594
1119
  "div",
2595
1120
  {
2596
1121
  className: cn(
@@ -2598,30 +1123,30 @@ function EditableRow({
2598
1123
  editingSuppressed ? "opacity-0 pointer-events-none" : "opacity-0 group-hover/row:opacity-100 no-hover:opacity-100"
2599
1124
  ),
2600
1125
  children: [
2601
- /* @__PURE__ */ jsx26(
1126
+ /* @__PURE__ */ jsx13(
2602
1127
  IconButton,
2603
1128
  {
2604
1129
  ref: handleRef,
2605
- icon: /* @__PURE__ */ jsx26(DragHandle, { size: 14 }),
1130
+ icon: /* @__PURE__ */ jsx13(DragHandle, { size: 14 }),
2606
1131
  label: "Drag to reorder",
2607
1132
  size: "sm",
2608
1133
  className: "cursor-grab rounded bg-base/80 p-1 shadow-sm text-base-contrast-light/80 hover:text-base-contrast active:cursor-grabbing",
2609
1134
  tabIndex: -1
2610
1135
  }
2611
1136
  ),
2612
- /* @__PURE__ */ jsx26(
1137
+ /* @__PURE__ */ jsx13(
2613
1138
  "button",
2614
1139
  {
2615
1140
  "aria-label": "Duplicate item",
2616
1141
  onClick: onDuplicate,
2617
1142
  className: "cursor-pointer rounded bg-base/80 p-1 shadow-sm text-base-contrast-light hover:text-primary",
2618
- children: /* @__PURE__ */ jsx26(CopyPlus, { size: 14 })
1143
+ children: /* @__PURE__ */ jsx13(CopyPlus, { size: 14 })
2619
1144
  }
2620
1145
  ),
2621
- /* @__PURE__ */ jsx26(
1146
+ /* @__PURE__ */ jsx13(
2622
1147
  IconButton,
2623
1148
  {
2624
- icon: /* @__PURE__ */ jsx26(DeleteIcon, { size: 14 }),
1149
+ icon: /* @__PURE__ */ jsx13(DeleteIcon, { size: 14 }),
2625
1150
  label: "Delete item",
2626
1151
  size: "sm",
2627
1152
  intent: "destructive",
@@ -2642,9 +1167,9 @@ function TrailingDropZone({
2642
1167
  setDragState,
2643
1168
  onReorder
2644
1169
  }) {
2645
- const ref = useRef8(null);
1170
+ const ref = useRef2(null);
2646
1171
  const trailingId = "__trailing__";
2647
- useEffect9(() => {
1172
+ useEffect3(() => {
2648
1173
  const el = ref.current;
2649
1174
  if (!el) return;
2650
1175
  let cleanup;
@@ -2671,7 +1196,7 @@ function TrailingDropZone({
2671
1196
  };
2672
1197
  }, [index, onReorder, setDragState]);
2673
1198
  const isDropTarget = dragState.targetId === trailingId && dragState.sourceId !== null;
2674
- return /* @__PURE__ */ jsx26(
1199
+ return /* @__PURE__ */ jsx13(
2675
1200
  "div",
2676
1201
  {
2677
1202
  ref,
@@ -2681,110 +1206,38 @@ function TrailingDropZone({
2681
1206
  }
2682
1207
 
2683
1208
  // src/components/sections/IconList/IconListSettings.tsx
2684
- import { useState as useState12 } from "react";
2685
-
2686
- // src/components/shared/Checkbox.tsx
2687
- import { useId as useId2 } from "react";
2688
- import { Check as Check3 } from "lucide-react";
2689
- import { jsx as jsx27, jsxs as jsxs14 } from "react/jsx-runtime";
2690
- function Checkbox({
2691
- checked,
2692
- onChange,
2693
- label,
2694
- description,
2695
- disabled,
2696
- className,
2697
- startAdornment,
2698
- align = "start"
2699
- }) {
2700
- const id = useId2();
2701
- return /* @__PURE__ */ jsxs14(
2702
- "label",
2703
- {
2704
- htmlFor: id,
2705
- className: cn(
2706
- "flex cursor-pointer gap-3 select-none hover:opacity-80",
2707
- align === "start" ? "items-start" : "items-center",
2708
- disabled && "cursor-not-allowed opacity-50",
2709
- className
2710
- ),
2711
- children: [
2712
- /* @__PURE__ */ jsxs14(
2713
- "span",
2714
- {
2715
- className: cn(
2716
- "relative flex h-5 w-5 shrink-0 items-center justify-center",
2717
- align === "start" && "mt-0.5"
2718
- ),
2719
- children: [
2720
- /* @__PURE__ */ jsx27(
2721
- "input",
2722
- {
2723
- id,
2724
- type: "checkbox",
2725
- checked,
2726
- disabled,
2727
- onChange: (e) => onChange(e.target.checked),
2728
- className: "sr-only"
2729
- }
2730
- ),
2731
- /* @__PURE__ */ jsx27(
2732
- "span",
2733
- {
2734
- "aria-hidden": "true",
2735
- className: cn(
2736
- "flex h-5 w-5 items-center justify-center rounded border transition-colors",
2737
- checked ? "border-primary bg-primary text-primary-contrast" : "border-base-300 bg-base hover:border-primary",
2738
- disabled && "pointer-events-none"
2739
- ),
2740
- children: checked && /* @__PURE__ */ jsx27(Check3, { size: 14, strokeWidth: 3 })
2741
- }
2742
- )
2743
- ]
2744
- }
2745
- ),
2746
- startAdornment,
2747
- /* @__PURE__ */ jsxs14("div", { className: "text-sm", children: [
2748
- /* @__PURE__ */ jsx27("span", { className: "font-medium text-base-contrast", children: label }),
2749
- description && /* @__PURE__ */ jsx27("span", { className: "block text-base-contrast-light", children: description })
2750
- ] })
2751
- ]
2752
- }
2753
- );
2754
- }
2755
-
2756
- // src/components/sections/IconList/IconListSettings.tsx
2757
- import { jsx as jsx28, jsxs as jsxs15 } from "react/jsx-runtime";
1209
+ import { useState as useState5 } from "react";
1210
+ import { jsx as jsx14, jsxs as jsxs6 } from "react/jsx-runtime";
2758
1211
  function IconListSettings({
2759
1212
  icon: initialIcon,
2760
1213
  showLabel: initialShowLabel,
2761
1214
  stackText: initialStackText,
2762
1215
  onChange
2763
1216
  }) {
2764
- const [icon, setIcon] = useState12(initialIcon);
2765
- const [showLabel, setShowLabel] = useState12(initialShowLabel);
2766
- const [stackText, setStackText] = useState12(initialStackText);
2767
- const [pickerOpen, setPickerOpen] = useState12(false);
1217
+ const [icon, setIcon] = useState5(initialIcon);
1218
+ const [showLabel, setShowLabel] = useState5(initialShowLabel);
1219
+ const [stackText, setStackText] = useState5(initialStackText);
1220
+ const [pickerOpen, setPickerOpen] = useState5(false);
2768
1221
  const emit = (overrides) => onChange({ content: {}, options: { icon, showLabel, stackText, ...overrides } });
2769
1222
  const showIcons = icon !== null;
2770
1223
  const iconEntry = icon ? getIcon(icon) : null;
2771
- return /* @__PURE__ */ jsxs15("div", { className: "space-y-4", children: [
2772
- /* @__PURE__ */ jsxs15("div", { className: "relative", children: [
2773
- /* @__PURE__ */ jsx28(FormLabel, { htmlFor: "icon-picker-btn", children: "Default Icon" }),
2774
- /* @__PURE__ */ jsxs15("div", { className: "flex items-center gap-2", children: [
2775
- /* @__PURE__ */ jsxs15(
1224
+ return /* @__PURE__ */ jsxs6("div", { className: "space-y-4", children: [
1225
+ /* @__PURE__ */ jsxs6("div", { className: "relative", children: [
1226
+ /* @__PURE__ */ jsx14(FormLabel, { htmlFor: "icon-picker-btn", children: "Default Icon" }),
1227
+ /* @__PURE__ */ jsxs6("div", { className: "flex items-center gap-2", children: [
1228
+ /* @__PURE__ */ jsxs6(
2776
1229
  "button",
2777
1230
  {
2778
1231
  id: "icon-picker-btn",
2779
1232
  className: "cursor-pointer flex items-center gap-2 rounded border border-base-200 px-3 py-1.5 text-sm text-base-contrast-light hover:border-primary hover:text-primary",
2780
1233
  onClick: () => setPickerOpen((prev) => !prev),
2781
1234
  children: [
2782
- iconEntry && /* @__PURE__ */ jsx28(iconEntry.icon, { size: 16 }),
2783
- /* @__PURE__ */ jsx28("span", { children: iconEntry?.label ?? "None" })
1235
+ iconEntry && /* @__PURE__ */ jsx14(iconEntry.icon, { size: 16 }),
1236
+ /* @__PURE__ */ jsx14("span", { children: iconEntry?.label ?? "None" })
2784
1237
  ]
2785
1238
  }
2786
1239
  ),
2787
- icon && /* @__PURE__ */ jsx28(
1240
+ icon && /* @__PURE__ */ jsx14(
2788
1241
  "button",
2789
1242
  {
2790
1243
  className: "cursor-pointer text-xs text-base-contrast-light hover:text-base-contrast",
@@ -2796,7 +1249,7 @@ function IconListSettings({
2796
1249
  }
2797
1250
  )
2798
1251
  ] }),
2799
- pickerOpen && /* @__PURE__ */ jsx28("div", { className: "absolute top-full left-0 z-50 mt-1", children: /* @__PURE__ */ jsx28(
1252
+ pickerOpen && /* @__PURE__ */ jsx14("div", { className: "absolute top-full left-0 z-50 mt-1", children: /* @__PURE__ */ jsx14(
2800
1253
  IconPicker,
2801
1254
  {
2802
1255
  selected: icon,
@@ -2811,7 +1264,7 @@ function IconListSettings({
2811
1264
  }
2812
1265
  ) })
2813
1266
  ] }),
2814
- /* @__PURE__ */ jsx28(
1267
+ /* @__PURE__ */ jsx14(
2815
1268
  Checkbox,
2816
1269
  {
2817
1270
  checked: showLabel,
@@ -2822,7 +1275,7 @@ function IconListSettings({
2822
1275
  label: "Show labels"
2823
1276
  }
2824
1277
  ),
2825
- showLabel && /* @__PURE__ */ jsx28(
1278
+ showLabel && /* @__PURE__ */ jsx14(
2826
1279
  Checkbox,
2827
1280
  {
2828
1281
  checked: stackText,
@@ -2837,24 +1290,30 @@ function IconListSettings({
2837
1290
  }
2838
1291
 
2839
1292
  // src/components/sections/IconList/index.tsx
2840
- import { jsx as jsx29 } from "react/jsx-runtime";
2841
- var schema8 = z8.object({
2842
- type: z8.literal("icon_list"),
2843
- content: z8.object({
2844
- items: z8.array(z8.object({ label: z8.string(), text: z8.string(), icon: z8.string().optional(), dodont: z8.enum(["do", "dont"]).optional() }))
1293
+ import { jsx as jsx15 } from "react/jsx-runtime";
1294
+ var IconListItemSchema = z5.object({
1295
+ label: z5.string(),
1296
+ text: z5.string(),
1297
+ icon: z5.string().optional(),
1298
+ dodont: z5.enum(["do", "dont"]).optional()
1299
+ });
1300
+ var schema5 = z5.object({
1301
+ type: z5.literal("icon_list"),
1302
+ content: z5.object({
1303
+ items: z5.array(IconListItemSchema)
2845
1304
  }),
2846
- options: z8.object({
2847
- icon: z8.string().nullable().optional(),
2848
- showLabel: z8.boolean().optional(),
2849
- stackText: z8.boolean().optional()
1305
+ options: z5.object({
1306
+ icon: z5.string().nullable().optional(),
1307
+ showLabel: z5.boolean().optional(),
1308
+ stackText: z5.boolean().optional()
2850
1309
  }).optional()
2851
1310
  });
2852
1311
  var IconList_default = defineSection({
2853
1312
  type: "icon_list",
2854
1313
  label: "Icon List",
2855
- icon: /* @__PURE__ */ jsx29(List, { size: 18 }),
2856
- schema: schema8,
2857
- component: ({ content, options, onChange }) => /* @__PURE__ */ jsx29(
1314
+ icon: /* @__PURE__ */ jsx15(List, { size: 18 }),
1315
+ schema: schema5,
1316
+ component: ({ content, options, onChange }) => /* @__PURE__ */ jsx15(
2858
1317
  IconList,
2859
1318
  {
2860
1319
  items: content.content.items,
@@ -2868,245 +1327,32 @@ var IconList_default = defineSection({
2868
1327
  type: "icon_list",
2869
1328
  content: { items: [{ label: "", text: "" }] }
2870
1329
  }),
2871
- getLabel: (content) => {
2872
- const n = content.content.items.length;
2873
- return `${n} icon${n === 1 ? "" : "s"}`;
2874
- },
1330
+ getLabel: (content) => pluralize(content.content.items.length, "icon"),
2875
1331
  settingsForm: IconListSettings
2876
1332
  });
2877
1333
 
2878
1334
  // src/components/sections/Container/index.tsx
2879
- import { z as z9 } from "zod";
1335
+ import { z as z6 } from "zod";
2880
1336
  import { LayoutTemplate } from "lucide-react";
2881
1337
 
2882
1338
  // src/components/sections/Container/Container.tsx
2883
1339
  import "react";
2884
1340
 
2885
1341
  // src/components/editor/ChildBlockWrapper.tsx
2886
- import { forwardRef as forwardRef6 } from "react";
1342
+ import { forwardRef } from "react";
2887
1343
  import { CopyPlus as CopyPlus2 } from "lucide-react";
2888
1344
 
2889
- // src/components/editor/DragHandle.tsx
2890
- import { forwardRef as forwardRef4 } from "react";
2891
-
2892
- // src/components/shared/Tooltip.tsx
2893
- import { jsx as jsx30, jsxs as jsxs16 } from "react/jsx-runtime";
2894
- function Tooltip({ content, children, className }) {
2895
- return /* @__PURE__ */ jsxs16("div", { className: cn("group/tooltip relative inline-flex", className), children: [
2896
- children,
2897
- /* @__PURE__ */ jsx30(
2898
- "span",
2899
- {
2900
- className: "pointer-events-none absolute left-1/2 top-full z-50 mt-1.5 -translate-x-1/2 opacity-0 transition-opacity delay-300 group-hover/tooltip:opacity-100",
2901
- role: "tooltip",
2902
- children: /* @__PURE__ */ jsx30("span", { className: "block whitespace-nowrap rounded bg-tooltip px-2 py-1 text-center text-xs leading-relaxed text-tooltip-muted", children: content })
2903
- }
2904
- )
2905
- ] });
2906
- }
2907
- function Kbd({ children }) {
2908
- return /* @__PURE__ */ jsx30("strong", { className: "text-tooltip-contrast", children });
2909
- }
2910
-
2911
- // src/components/editor/DragHandle.tsx
2912
- import { Fragment as Fragment5, jsx as jsx31, jsxs as jsxs17 } from "react/jsx-runtime";
2913
- var DragHandle2 = forwardRef4(function DragHandle3(_, ref) {
2914
- return /* @__PURE__ */ jsx31(Tooltip, { content: /* @__PURE__ */ jsxs17(Fragment5, { children: [
2915
- /* @__PURE__ */ jsx31(Kbd, { children: "Drag" }),
2916
- " to move"
2917
- ] }), children: /* @__PURE__ */ jsx31(
2918
- IconButton,
2919
- {
2920
- ref,
2921
- icon: /* @__PURE__ */ jsx31(DragHandle, { size: 18 }),
2922
- label: "Drag to reorder section",
2923
- className: "pointer-events-auto cursor-grab active:cursor-grabbing rounded-md text-base-contrast-light/80 hover:bg-base-contrast-light/10 hover:text-base-contrast",
2924
- tabIndex: -1
2925
- }
2926
- ) });
2927
- });
2928
-
2929
- // src/components/editor/DeleteButton.tsx
2930
- import { jsx as jsx32 } from "react/jsx-runtime";
2931
- function DeleteButton({ onDelete }) {
2932
- return /* @__PURE__ */ jsx32(
2933
- IconButton,
2934
- {
2935
- icon: /* @__PURE__ */ jsx32(DeleteIcon, { size: 16 }),
2936
- label: "Delete section",
2937
- intent: "destructive",
2938
- onClick: onDelete,
2939
- className: "pointer-events-auto"
2940
- }
2941
- );
2942
- }
2943
-
2944
- // src/components/editor/SettingsButton.tsx
2945
- import { jsx as jsx33 } from "react/jsx-runtime";
2946
- function SettingsButton({ onClick }) {
2947
- return /* @__PURE__ */ jsx33(
2948
- IconButton,
2949
- {
2950
- icon: /* @__PURE__ */ jsx33(SettingsIcon, { size: 16 }),
2951
- label: "Section settings",
2952
- onClick,
2953
- className: "pointer-events-auto"
2954
- }
2955
- );
2956
- }
2957
-
2958
1345
  // src/components/editor/ColSpanControl.tsx
2959
- import { useRef as useRef10, useState as useState14 } from "react";
1346
+ import { useRef as useRef3, useState as useState6 } from "react";
2960
1347
  import { Columns3 } from "lucide-react";
2961
-
2962
- // src/components/shared/Popover.tsx
2963
- import { useEffect as useEffect11, useLayoutEffect, useRef as useRef9, useState as useState13 } from "react";
2964
-
2965
- // src/hooks/useFocusTrap.ts
2966
- import { useEffect as useEffect10 } from "react";
2967
- var FOCUSABLE_SELECTOR = [
2968
- "a[href]",
2969
- "button:not([disabled])",
2970
- "input:not([disabled])",
2971
- "select:not([disabled])",
2972
- "textarea:not([disabled])",
2973
- '[tabindex]:not([tabindex="-1"])'
2974
- ].join(", ");
2975
- function useFocusTrap(ref, active) {
2976
- useEffect10(() => {
2977
- if (!active || !ref.current) return;
2978
- const container = ref.current;
2979
- const previouslyFocused = document.activeElement;
2980
- const getFocusable = () => Array.from(container.querySelectorAll(FOCUSABLE_SELECTOR));
2981
- const focusable = getFocusable();
2982
- if (focusable.length > 0) {
2983
- focusable[0].focus();
2984
- }
2985
- const handleKeyDown = (e) => {
2986
- if (e.key !== "Tab") return;
2987
- const elements = getFocusable();
2988
- if (elements.length === 0) return;
2989
- const first = elements[0];
2990
- const last = elements[elements.length - 1];
2991
- if (e.shiftKey) {
2992
- if (document.activeElement === first) {
2993
- e.preventDefault();
2994
- last.focus();
2995
- }
2996
- } else {
2997
- if (document.activeElement === last) {
2998
- e.preventDefault();
2999
- first.focus();
3000
- }
3001
- }
3002
- };
3003
- container.addEventListener("keydown", handleKeyDown);
3004
- return () => {
3005
- container.removeEventListener("keydown", handleKeyDown);
3006
- if (previouslyFocused && document.body.contains(previouslyFocused)) {
3007
- previouslyFocused.focus();
3008
- }
3009
- };
3010
- }, [active, ref]);
3011
- }
3012
-
3013
- // src/components/shared/Popover.tsx
3014
- import { jsx as jsx34 } from "react/jsx-runtime";
3015
- function Popover({
3016
- isOpen,
3017
- onClose,
3018
- anchorRef,
3019
- children,
3020
- align = "auto",
3021
- className
3022
- }) {
3023
- const panelRef = useRef9(null);
3024
- const [resolvedAlign, setResolvedAlign] = useState13("start");
3025
- useFocusTrap(panelRef, isOpen);
3026
- useLayoutEffect(() => {
3027
- if (!isOpen || align !== "auto") {
3028
- if (!isOpen) setResolvedAlign("start");
3029
- return;
3030
- }
3031
- const panel = panelRef.current;
3032
- if (!panel) return;
3033
- panel.style.left = "0";
3034
- panel.style.right = "auto";
3035
- const rect = panel.getBoundingClientRect();
3036
- if (rect.right > window.innerWidth) {
3037
- setResolvedAlign("end");
3038
- } else {
3039
- setResolvedAlign("start");
3040
- }
3041
- panel.style.left = "";
3042
- panel.style.right = "";
3043
- }, [isOpen, align]);
3044
- useEffect11(() => {
3045
- if (!isOpen) return;
3046
- function onKeyDown(e) {
3047
- if (e.key === "Escape") onClose();
3048
- }
3049
- function onMouseDown(e) {
3050
- const target = e.target;
3051
- if (panelRef.current?.contains(target)) return;
3052
- if (anchorRef.current?.contains(target)) return;
3053
- onClose();
3054
- }
3055
- document.addEventListener("keydown", onKeyDown);
3056
- document.addEventListener("mousedown", onMouseDown);
3057
- return () => {
3058
- document.removeEventListener("keydown", onKeyDown);
3059
- document.removeEventListener("mousedown", onMouseDown);
3060
- };
3061
- }, [isOpen, onClose, anchorRef]);
3062
- if (!isOpen) return null;
3063
- const effectiveAlign = align === "auto" ? resolvedAlign : align;
3064
- return /* @__PURE__ */ jsx34(
3065
- "div",
3066
- {
3067
- ref: panelRef,
3068
- role: "dialog",
3069
- "aria-modal": "false",
3070
- className: cn(
3071
- "absolute top-full z-50 mt-1 rounded-md border border-base-200 bg-base shadow-lg",
3072
- effectiveAlign === "end" ? "right-0" : "left-0",
3073
- className
3074
- ),
3075
- children
3076
- }
3077
- );
3078
- }
3079
-
3080
- // src/components/shared/PopoverItem.tsx
3081
- import { forwardRef as forwardRef5 } from "react";
3082
- import { jsx as jsx35 } from "react/jsx-runtime";
3083
- var PopoverItem = forwardRef5(
3084
- function PopoverItem2({ className, children, ...rest }, ref) {
3085
- return /* @__PURE__ */ jsx35(
3086
- "button",
3087
- {
3088
- ref,
3089
- type: "button",
3090
- className: cn(
3091
- "flex w-full cursor-pointer items-center gap-3 px-3 py-1.5 text-left text-xs hover:bg-base-accent",
3092
- className
3093
- ),
3094
- ...rest,
3095
- children
3096
- }
3097
- );
3098
- }
3099
- );
3100
-
3101
- // src/components/editor/ColSpanControl.tsx
3102
- import { jsx as jsx36, jsxs as jsxs18 } from "react/jsx-runtime";
1348
+ import { jsx as jsx16, jsxs as jsxs7 } from "react/jsx-runtime";
3103
1349
  function ColSpanControl({ colSpan, columns, onChange }) {
3104
- const buttonRef = useRef10(null);
3105
- const [open, setOpen] = useState14(false);
1350
+ const buttonRef = useRef3(null);
1351
+ const [open, setOpen] = useState6(false);
3106
1352
  if (columns <= 1) return null;
3107
1353
  const current = Math.min(Math.max(colSpan, 1), columns);
3108
- return /* @__PURE__ */ jsxs18("div", { className: "relative", children: [
3109
- /* @__PURE__ */ jsx36(
1354
+ return /* @__PURE__ */ jsxs7("div", { className: "relative", children: [
1355
+ /* @__PURE__ */ jsx16(
3110
1356
  "button",
3111
1357
  {
3112
1358
  ref: buttonRef,
@@ -3116,10 +1362,10 @@ function ColSpanControl({ colSpan, columns, onChange }) {
3116
1362
  "aria-expanded": open,
3117
1363
  onClick: () => setOpen((v) => !v),
3118
1364
  className: "pointer-events-auto inline-flex cursor-pointer items-center gap-1 rounded p-1 text-xs text-base-contrast-light hover:text-primary",
3119
- children: /* @__PURE__ */ jsx36(Columns3, { size: 14 })
1365
+ children: /* @__PURE__ */ jsx16(Columns3, { size: 14 })
3120
1366
  }
3121
1367
  ),
3122
- /* @__PURE__ */ jsx36(Popover, { isOpen: open, onClose: () => setOpen(false), anchorRef: buttonRef, className: "min-w-28", children: /* @__PURE__ */ jsx36("ul", { role: "list", className: "py-1", children: Array.from({ length: columns }, (_, i) => i + 1).map((span) => /* @__PURE__ */ jsx36("li", { children: /* @__PURE__ */ jsxs18(
1368
+ /* @__PURE__ */ jsx16(Popover, { isOpen: open, onClose: () => setOpen(false), anchorRef: buttonRef, className: "min-w-28", children: /* @__PURE__ */ jsx16("ul", { role: "list", className: "py-1", children: Array.from({ length: columns }, (_, i) => i + 1).map((span) => /* @__PURE__ */ jsx16("li", { children: /* @__PURE__ */ jsxs7(
3123
1369
  PopoverItem,
3124
1370
  {
3125
1371
  "aria-current": span === current,
@@ -3138,8 +1384,8 @@ function ColSpanControl({ colSpan, columns, onChange }) {
3138
1384
  }
3139
1385
 
3140
1386
  // src/components/editor/ChildBlockWrapper.tsx
3141
- import { jsx as jsx37, jsxs as jsxs19 } from "react/jsx-runtime";
3142
- var ChildBlockWrapper = forwardRef6(
1387
+ import { jsx as jsx17, jsxs as jsxs8 } from "react/jsx-runtime";
1388
+ var ChildBlockWrapper = forwardRef(
3143
1389
  function ChildBlockWrapper2({
3144
1390
  label,
3145
1391
  columns,
@@ -3154,7 +1400,7 @@ var ChildBlockWrapper = forwardRef6(
3154
1400
  onOpenSettings,
3155
1401
  children
3156
1402
  }, ref) {
3157
- return /* @__PURE__ */ jsxs19(
1403
+ return /* @__PURE__ */ jsxs8(
3158
1404
  "div",
3159
1405
  {
3160
1406
  ref,
@@ -3164,39 +1410,39 @@ var ChildBlockWrapper = forwardRef6(
3164
1410
  "hover:outline hover:outline-2 hover:outline-primary/30"
3165
1411
  ),
3166
1412
  children: [
3167
- /* @__PURE__ */ jsxs19("div", { className: "absolute top-0 left-0 -translate-x-full z-20 flex flex-col items-center gap-1 opacity-0 transition-opacity group-hover/childblock:opacity-100 group-focus-within/childblock:opacity-100 pointer-events-none", children: [
3168
- /* @__PURE__ */ jsx37("span", { className: "sr-only", children: label }),
3169
- /* @__PURE__ */ jsx37("div", { className: "pointer-events-auto", children: /* @__PURE__ */ jsx37(DragHandle2, { ref: dragHandleRef }) }),
3170
- /* @__PURE__ */ jsx37("div", { className: "pointer-events-auto", children: /* @__PURE__ */ jsx37(ColSpanControl, { colSpan, columns, onChange: onColSpanChange }) }),
3171
- hasSettings && onOpenSettings && /* @__PURE__ */ jsx37("div", { className: "pointer-events-auto", children: /* @__PURE__ */ jsx37(SettingsButton, { onClick: onOpenSettings }) }),
3172
- /* @__PURE__ */ jsx37("div", { className: "pointer-events-auto", children: /* @__PURE__ */ jsx37(
1413
+ /* @__PURE__ */ jsxs8("div", { className: "absolute top-0 left-0 -translate-x-full z-20 flex flex-col items-center gap-1 opacity-0 transition-opacity group-hover/childblock:opacity-100 group-focus-within/childblock:opacity-100 pointer-events-none", children: [
1414
+ /* @__PURE__ */ jsx17("span", { className: "sr-only", children: label }),
1415
+ /* @__PURE__ */ jsx17("div", { className: "pointer-events-auto", children: /* @__PURE__ */ jsx17(DragHandle2, { ref: dragHandleRef }) }),
1416
+ /* @__PURE__ */ jsx17("div", { className: "pointer-events-auto", children: /* @__PURE__ */ jsx17(ColSpanControl, { colSpan, columns, onChange: onColSpanChange }) }),
1417
+ hasSettings && onOpenSettings && /* @__PURE__ */ jsx17("div", { className: "pointer-events-auto", children: /* @__PURE__ */ jsx17(SettingsButton, { onClick: onOpenSettings }) }),
1418
+ /* @__PURE__ */ jsx17("div", { className: "pointer-events-auto", children: /* @__PURE__ */ jsx17(
3173
1419
  "button",
3174
1420
  {
3175
1421
  type: "button",
3176
1422
  "aria-label": "Duplicate block",
3177
1423
  onClick: onDuplicate,
3178
1424
  className: "cursor-pointer rounded p-1 text-base-contrast-light hover:text-primary",
3179
- children: /* @__PURE__ */ jsx37(CopyPlus2, { size: 16 })
1425
+ children: /* @__PURE__ */ jsx17(CopyPlus2, { size: 16 })
3180
1426
  }
3181
1427
  ) }),
3182
- /* @__PURE__ */ jsx37("div", { className: "pointer-events-auto", children: /* @__PURE__ */ jsx37(DeleteButton, { onDelete }) })
1428
+ /* @__PURE__ */ jsx17("div", { className: "pointer-events-auto", children: /* @__PURE__ */ jsx17(DeleteButton, { onDelete }) })
3183
1429
  ] }),
3184
- closestEdge === "left" && /* @__PURE__ */ jsx37(
1430
+ closestEdge === "left" && /* @__PURE__ */ jsx17(
3185
1431
  "div",
3186
1432
  {
3187
1433
  "data-drop-edge": "left",
3188
1434
  className: "absolute bottom-0 left-0 top-0 z-10 w-0.5 -translate-x-1 bg-primary"
3189
1435
  }
3190
1436
  ),
3191
- closestEdge === "right" && /* @__PURE__ */ jsx37(
1437
+ closestEdge === "right" && /* @__PURE__ */ jsx17(
3192
1438
  "div",
3193
1439
  {
3194
1440
  "data-drop-edge": "right",
3195
1441
  className: "absolute bottom-0 right-0 top-0 z-10 w-0.5 translate-x-1 bg-primary"
3196
1442
  }
3197
1443
  ),
3198
- closestEdge === "top" && /* @__PURE__ */ jsx37("div", { "data-drop-edge": "top", className: "absolute left-0 right-0 top-0 z-10 h-0.5 -translate-y-1 bg-primary" }),
3199
- closestEdge === "bottom" && /* @__PURE__ */ jsx37("div", { "data-drop-edge": "bottom", className: "absolute left-0 right-0 bottom-0 z-10 h-0.5 translate-y-1 bg-primary" }),
1444
+ closestEdge === "top" && /* @__PURE__ */ jsx17("div", { "data-drop-edge": "top", className: "absolute left-0 right-0 top-0 z-10 h-0.5 -translate-y-1 bg-primary" }),
1445
+ closestEdge === "bottom" && /* @__PURE__ */ jsx17("div", { "data-drop-edge": "bottom", className: "absolute left-0 right-0 bottom-0 z-10 h-0.5 translate-y-1 bg-primary" }),
3200
1446
  children
3201
1447
  ]
3202
1448
  }
@@ -3204,334 +1450,14 @@ var ChildBlockWrapper = forwardRef6(
3204
1450
  }
3205
1451
  );
3206
1452
 
3207
- // src/components/editor/SettingsForm.tsx
3208
- import { useId as useId4, useState as useState16 } from "react";
3209
-
3210
- // src/components/shared/Select.tsx
3211
- import { useId as useId3 } from "react";
3212
- import { ChevronDown } from "lucide-react";
3213
- import { jsx as jsx38, jsxs as jsxs20 } from "react/jsx-runtime";
3214
- function Select({
3215
- label,
3216
- value,
3217
- onChange,
3218
- options,
3219
- disabled,
3220
- className,
3221
- selectClassName
3222
- }) {
3223
- const id = useId3();
3224
- return /* @__PURE__ */ jsxs20("div", { className, children: [
3225
- label && /* @__PURE__ */ jsx38(FormLabel, { htmlFor: id, children: label }),
3226
- /* @__PURE__ */ jsxs20("div", { className: "relative", children: [
3227
- /* @__PURE__ */ jsx38(
3228
- "select",
3229
- {
3230
- id,
3231
- value,
3232
- onChange: (e) => onChange(e.target.value),
3233
- disabled,
3234
- className: cn(
3235
- "w-full appearance-none rounded border border-base-200 bg-base py-2 pr-8 pl-3 text-sm text-base-contrast",
3236
- "focus:border-base-contrast focus:outline-none focus:ring-1 focus:ring-base-contrast",
3237
- disabled && "cursor-not-allowed opacity-50",
3238
- selectClassName
3239
- ),
3240
- children: options.map((opt) => /* @__PURE__ */ jsx38("option", { value: opt.value, children: opt.label }, opt.value))
3241
- }
3242
- ),
3243
- /* @__PURE__ */ jsx38(
3244
- ChevronDown,
3245
- {
3246
- size: 14,
3247
- className: "pointer-events-none absolute right-2.5 top-1/2 -translate-y-1/2 text-base-contrast-light"
3248
- }
3249
- )
3250
- ] })
3251
- ] });
3252
- }
3253
-
3254
- // src/components/shared/LinkField.tsx
3255
- import { Fragment as Fragment6, jsx as jsx39, jsxs as jsxs21 } from "react/jsx-runtime";
3256
- var TARGET_OPTIONS = [
3257
- { label: "Same tab (_self)", value: "_self" },
3258
- { label: "New tab (_blank)", value: "_blank" }
3259
- ];
3260
- function LinkField({ label, value, onChange }) {
3261
- const { pages, getPageHeadings } = usePages();
3262
- const isInternal = value.kind === "internal";
3263
- function setMode(kind) {
3264
- if (kind === value.kind) return;
3265
- onChange(kind === "external" ? { kind: "external", href: "", target: value.target } : { kind: "internal", pageId: "", anchorSectionId: null, target: "_self" });
3266
- }
3267
- const pageId = isInternal ? value.pageId : "";
3268
- const headings = pageId ? getPageHeadings(pageId) : [];
3269
- return /* @__PURE__ */ jsxs21("div", { className: "flex flex-col gap-3", children: [
3270
- /* @__PURE__ */ jsx39(FormLabel, { children: label }),
3271
- /* @__PURE__ */ jsx39("div", { className: "flex gap-2", role: "tablist", children: ["external", "internal"].map((kind) => /* @__PURE__ */ jsx39(
3272
- "button",
3273
- {
3274
- type: "button",
3275
- role: "tab",
3276
- "aria-selected": value.kind === kind,
3277
- onClick: () => setMode(kind),
3278
- className: cn(
3279
- "cursor-pointer rounded border px-3 py-1 text-sm capitalize",
3280
- value.kind === kind ? "border-primary bg-primary text-primary-contrast" : "border-base-200 text-base-contrast"
3281
- ),
3282
- children: kind === "external" ? "External URL" : "Internal page"
3283
- },
3284
- kind
3285
- )) }),
3286
- value.kind === "external" ? /* @__PURE__ */ jsx39(
3287
- Input,
3288
- {
3289
- label: "URL",
3290
- value: value.href,
3291
- placeholder: "https://...",
3292
- onChange: (href) => onChange({ kind: "external", href, target: value.target })
3293
- }
3294
- ) : /* @__PURE__ */ jsxs21(Fragment6, { children: [
3295
- /* @__PURE__ */ jsx39(
3296
- Select,
3297
- {
3298
- label: "Page",
3299
- value: value.pageId,
3300
- options: [{ label: "Select a page\u2026", value: "" }, ...pages.map((p) => ({ label: p.title, value: p.id }))],
3301
- onChange: (pid) => onChange({ kind: "internal", pageId: pid, anchorSectionId: null, target: value.target })
3302
- }
3303
- ),
3304
- /* @__PURE__ */ jsx39(
3305
- Select,
3306
- {
3307
- label: "Section",
3308
- value: value.anchorSectionId ?? "",
3309
- disabled: !pageId,
3310
- options: [{ label: "None (top of page)", value: "" }, ...headings.map((h) => ({ label: h.label, value: h.id }))],
3311
- onChange: (anchor) => onChange({ kind: "internal", pageId, anchorSectionId: anchor || null, target: value.target })
3312
- }
3313
- )
3314
- ] }),
3315
- value.kind === "external" && /* @__PURE__ */ jsx39(
3316
- Select,
3317
- {
3318
- label: "Opens in",
3319
- value: value.target,
3320
- options: TARGET_OPTIONS,
3321
- onChange: (t) => onChange({ ...value, target: t })
3322
- }
3323
- )
3324
- ] });
3325
- }
3326
-
3327
- // src/components/shared/Tabs.tsx
3328
- import { useState as useState15 } from "react";
3329
- import { jsx as jsx40, jsxs as jsxs22 } from "react/jsx-runtime";
3330
- function Tabs({ tabs, defaultTabId, activeTabId, onTabChange, fullBleedTabBar }) {
3331
- const [uncontrolledId, setUncontrolledId] = useState15(defaultTabId ?? tabs[0]?.id);
3332
- const isControlled = activeTabId !== void 0;
3333
- const activeId = isControlled ? activeTabId : uncontrolledId;
3334
- const activeTab = tabs.find((t) => t.id === activeId) ?? tabs[0];
3335
- function handleSelect(id) {
3336
- if (!isControlled) setUncontrolledId(id);
3337
- onTabChange?.(id);
3338
- }
3339
- return /* @__PURE__ */ jsxs22("div", { className: "flex flex-1 flex-col overflow-hidden", children: [
3340
- /* @__PURE__ */ jsx40("div", { className: cn("border-b border-base-200", fullBleedTabBar && "-mx-6 px-6"), children: /* @__PURE__ */ jsx40("div", { className: "flex", role: "tablist", children: tabs.map((tab) => /* @__PURE__ */ jsx40(
3341
- "button",
3342
- {
3343
- type: "button",
3344
- role: "tab",
3345
- "aria-selected": activeTab?.id === tab.id,
3346
- onClick: () => handleSelect(tab.id),
3347
- className: cn(
3348
- "cursor-pointer px-4 py-2 text-sm font-medium border-b-2 -mb-px",
3349
- activeTab?.id === tab.id ? "border-brand text-brand" : "border-transparent text-base-contrast-light hover:text-base-contrast"
3350
- ),
3351
- children: tab.label
3352
- },
3353
- tab.id
3354
- )) }) }),
3355
- /* @__PURE__ */ jsx40("div", { role: "tabpanel", children: activeTab?.content })
3356
- ] });
3357
- }
3358
-
3359
- // src/components/editor/SettingsForm.tsx
3360
- import { jsx as jsx41, jsxs as jsxs23 } from "react/jsx-runtime";
3361
- function resolveValues(schema11, values) {
3362
- const resolved = {};
3363
- for (const [key, field] of Object.entries(schema11)) {
3364
- resolved[key] = values[key] ?? field.default;
3365
- }
3366
- return resolved;
3367
- }
3368
- function coerceValue(field, value) {
3369
- if (field.type === "select" && "coerce" in field && field.coerce === "number") {
3370
- return Number(value);
3371
- }
3372
- return value;
3373
- }
3374
- function normalizeEmpty(field, value) {
3375
- if (field.type === "select" && "emptyIsUndefined" in field && field.emptyIsUndefined && value === "") {
3376
- return void 0;
3377
- }
3378
- return value;
3379
- }
3380
- function splitByTarget(schema11, allValues) {
3381
- const content = {};
3382
- const options = {};
3383
- for (const [key, field] of Object.entries(schema11)) {
3384
- const value = normalizeEmpty(field, coerceValue(field, allValues[key]));
3385
- const target = field.target ?? "options";
3386
- if (target === "content") {
3387
- content[key] = value;
3388
- } else {
3389
- options[key] = value;
3390
- }
3391
- }
3392
- return { content, options };
3393
- }
3394
- function RangeField({
3395
- field,
3396
- value,
3397
- onChange
3398
- }) {
3399
- const id = useId4();
3400
- return /* @__PURE__ */ jsxs23("div", { children: [
3401
- /* @__PURE__ */ jsx41(FormLabel, { htmlFor: id, children: field.label }),
3402
- /* @__PURE__ */ jsx41(
3403
- "input",
3404
- {
3405
- id,
3406
- type: "range",
3407
- min: field.min,
3408
- max: field.max,
3409
- step: field.step,
3410
- value,
3411
- onChange: (e) => onChange(Number(e.target.value)),
3412
- className: cn(
3413
- "w-full accent-primary"
3414
- )
3415
- }
3416
- )
3417
- ] });
3418
- }
3419
- function renderField(key, field, value, handleFieldChange) {
3420
- switch (field.type) {
3421
- case "text":
3422
- return /* @__PURE__ */ jsx41(
3423
- Input,
3424
- {
3425
- label: field.label,
3426
- value: String(value ?? ""),
3427
- placeholder: field.placeholder,
3428
- onChange: (v) => handleFieldChange(key, v)
3429
- },
3430
- key
3431
- );
3432
- case "number":
3433
- return /* @__PURE__ */ jsx41(
3434
- Input,
3435
- {
3436
- label: field.label,
3437
- type: "number",
3438
- value: String(value ?? ""),
3439
- onChange: (v) => handleFieldChange(key, v === "" ? "" : Number(v))
3440
- },
3441
- key
3442
- );
3443
- case "checkbox":
3444
- return /* @__PURE__ */ jsx41(
3445
- Checkbox,
3446
- {
3447
- label: field.label,
3448
- checked: Boolean(value),
3449
- onChange: (checked) => handleFieldChange(key, checked)
3450
- },
3451
- key
3452
- );
3453
- case "select":
3454
- return /* @__PURE__ */ jsx41(
3455
- Select,
3456
- {
3457
- label: field.label,
3458
- value: String(value ?? ""),
3459
- options: field.options,
3460
- onChange: (v) => handleFieldChange(key, v)
3461
- },
3462
- key
3463
- );
3464
- case "range":
3465
- return /* @__PURE__ */ jsx41(
3466
- RangeField,
3467
- {
3468
- field,
3469
- value: Number(value ?? field.min),
3470
- onChange: (v) => handleFieldChange(key, v)
3471
- },
3472
- key
3473
- );
3474
- case "link":
3475
- return /* @__PURE__ */ jsx41(
3476
- LinkField,
3477
- {
3478
- label: field.label,
3479
- value: value ?? field.default,
3480
- onChange: (v) => handleFieldChange(key, v)
3481
- },
3482
- key
3483
- );
3484
- default:
3485
- return null;
3486
- }
3487
- }
3488
- function SettingsForm({ schema: schema11, values, onChange, tabs }) {
3489
- const [local, setLocal] = useState16(() => resolveValues(schema11, values));
3490
- function handleFieldChange(key, newValue) {
3491
- const next = { ...local, [key]: newValue };
3492
- setLocal(next);
3493
- onChange(splitByTarget(schema11, next));
3494
- }
3495
- if (tabs) {
3496
- const fields = (keys) => keys.filter((key) => key in schema11).map((key) => renderField(key, schema11[key], local[key], handleFieldChange));
3497
- const listed = new Set(tabs.flatMap((t) => t.fields));
3498
- const otherKeys = Object.keys(schema11).filter((key) => !listed.has(key));
3499
- const tabItems = [
3500
- ...tabs.map((t) => ({ id: t.label, label: t.label, content: /* @__PURE__ */ jsx41("div", { className: "flex flex-col gap-4", children: fields(t.fields) }) })),
3501
- ...otherKeys.length > 0 ? [{ id: "__other__", label: "Other", content: /* @__PURE__ */ jsx41("div", { className: "flex flex-col gap-4", children: fields(otherKeys) }) }] : []
3502
- ];
3503
- return /* @__PURE__ */ jsx41(Tabs, { tabs: tabItems, fullBleedTabBar: true });
3504
- }
3505
- return /* @__PURE__ */ jsx41("div", { className: "flex flex-col gap-4", children: Object.entries(schema11).map(([key, field]) => renderField(key, field, local[key], handleFieldChange)) });
3506
- }
3507
-
3508
- // src/hooks/useBlockDnd.ts
3509
- import { useEffect as useEffect12, useRef as useRef11, useState as useState17 } from "react";
3510
-
3511
- // src/lib/block-dnd.ts
3512
- var ROOT_CONTAINER_ID = "__root__";
3513
- function buildBlockDragData(args) {
3514
- return { dragType: "block", ...args };
3515
- }
3516
- function buildBlockDropData(args) {
3517
- return { ...args };
3518
- }
3519
- function isBlockDragData(data) {
3520
- return data.dragType === "block";
3521
- }
3522
- function canDropBlock(source, dropContainerId) {
3523
- if (source.blockId === dropContainerId) return false;
3524
- if (source.isContainer && dropContainerId !== ROOT_CONTAINER_ID) return false;
3525
- return true;
3526
- }
3527
-
3528
1453
  // src/hooks/useBlockDnd.ts
1454
+ import { useEffect as useEffect4, useRef as useRef4, useState as useState7 } from "react";
3529
1455
  function useBlockDnd({ blockId, containerId, index, isContainer, allowedEdges, enabled }) {
3530
- const dragRef = useRef11(null);
3531
- const handleRef = useRef11(null);
3532
- const [closestEdge, setClosestEdge] = useState17(null);
3533
- const [isDragging, setIsDragging] = useState17(false);
3534
- useEffect12(() => {
1456
+ const dragRef = useRef4(null);
1457
+ const handleRef = useRef4(null);
1458
+ const [closestEdge, setClosestEdge] = useState7(null);
1459
+ const [isDragging, setIsDragging] = useState7(false);
1460
+ useEffect4(() => {
3535
1461
  const element = dragRef.current;
3536
1462
  const handle = handleRef.current;
3537
1463
  if (!element || !enabled) return;
@@ -3587,9 +1513,9 @@ function useBlockDnd({ blockId, containerId, index, isContainer, allowedEdges, e
3587
1513
  return { dragRef, handleRef, closestEdge, isDragging };
3588
1514
  }
3589
1515
  function useBlockDropZone({ containerId, index, toColumn, allowedEdges, enabled }) {
3590
- const dropRef = useRef11(null);
3591
- const [closestEdge, setClosestEdge] = useState17(null);
3592
- useEffect12(() => {
1516
+ const dropRef = useRef4(null);
1517
+ const [closestEdge, setClosestEdge] = useState7(null);
1518
+ useEffect4(() => {
3593
1519
  const element = dropRef.current;
3594
1520
  if (!element || !enabled) return;
3595
1521
  let cleanup;
@@ -3686,7 +1612,7 @@ function trimTrailingSpacers(container) {
3686
1612
  }
3687
1613
 
3688
1614
  // src/components/sections/Container/Container.tsx
3689
- import { jsx as jsx42, jsxs as jsxs24 } from "react/jsx-runtime";
1615
+ import { jsx as jsx18, jsxs as jsxs9 } from "react/jsx-runtime";
3690
1616
  var ROW_EDGES = ["left", "right"];
3691
1617
  var COL_EDGES = ["top", "bottom"];
3692
1618
  function ContainerChild({
@@ -3717,14 +1643,14 @@ function ContainerChild({
3717
1643
  allowedEdges: flow === "column" ? COL_EDGES : ROW_EDGES,
3718
1644
  enabled: true
3719
1645
  });
3720
- return /* @__PURE__ */ jsx42(
1646
+ return /* @__PURE__ */ jsx18(
3721
1647
  "div",
3722
1648
  {
3723
1649
  ref: dragRef,
3724
1650
  className: cellClass,
3725
1651
  "data-child-index": index,
3726
1652
  "data-child-id": child.id,
3727
- children: /* @__PURE__ */ jsx42(
1653
+ children: /* @__PURE__ */ jsx18(
3728
1654
  ChildBlockWrapper,
3729
1655
  {
3730
1656
  label,
@@ -3738,7 +1664,7 @@ function ContainerChild({
3738
1664
  onDuplicate,
3739
1665
  onColSpanChange,
3740
1666
  onOpenSettings,
3741
- children: /* @__PURE__ */ jsx42(
1667
+ children: /* @__PURE__ */ jsx18(
3742
1668
  ChildComp,
3743
1669
  {
3744
1670
  content: child,
@@ -3766,7 +1692,7 @@ function EmptyColumnCell({
3766
1692
  enabled: true
3767
1693
  });
3768
1694
  const active = closestEdge !== null;
3769
- return /* @__PURE__ */ jsx42(
1695
+ return /* @__PURE__ */ jsx18(
3770
1696
  "div",
3771
1697
  {
3772
1698
  ref: dropRef,
@@ -3804,7 +1730,7 @@ function Container({ content, isEditMode, onChange, openModal }) {
3804
1730
  flow === "column" && "grid-flow-col"
3805
1731
  );
3806
1732
  if (!editable) {
3807
- return /* @__PURE__ */ jsx42("div", { className: "@container", children: /* @__PURE__ */ jsx42("div", { className: gridClass, children: children.map((child, index) => {
1733
+ return /* @__PURE__ */ jsx18("div", { className: "@container", children: /* @__PURE__ */ jsx18("div", { className: gridClass, children: children.map((child, index) => {
3808
1734
  const def = getSection(child.type);
3809
1735
  if (!def) return null;
3810
1736
  const Child = def.component;
@@ -3815,7 +1741,7 @@ function Container({ content, isEditMode, onChange, openModal }) {
3815
1741
  colSpanClass[columns]?.[Math.min(span, columns)] || void 0,
3816
1742
  child.type === "spacer" && spacerHiddenClass[columns]
3817
1743
  );
3818
- return /* @__PURE__ */ jsx42("div", { className: cellClass, children: /* @__PURE__ */ jsx42(Child, { content: child, options: mergedOptions, isEditMode }) }, child.id ?? index);
1744
+ return /* @__PURE__ */ jsx18("div", { className: cellClass, children: /* @__PURE__ */ jsx18(Child, { content: child, options: mergedOptions, isEditMode }) }, child.id ?? index);
3819
1745
  }) }) });
3820
1746
  }
3821
1747
  function columnStartOf(index) {
@@ -3829,7 +1755,7 @@ function Container({ content, isEditMode, onChange, openModal }) {
3829
1755
  const def = getSection(child.type);
3830
1756
  if (!def) return null;
3831
1757
  if (isSpacer(child)) {
3832
- return /* @__PURE__ */ jsx42(
1758
+ return /* @__PURE__ */ jsx18(
3833
1759
  EmptyColumnCell,
3834
1760
  {
3835
1761
  containerId: container.id,
@@ -3857,12 +1783,12 @@ function Container({ content, isEditMode, onChange, openModal }) {
3857
1783
  const CustomForm = def.settingsForm;
3858
1784
  openModal(
3859
1785
  `${def.label} Settings`,
3860
- /* @__PURE__ */ jsx42(CustomForm, { ...seed, onChange: applyResult })
1786
+ /* @__PURE__ */ jsx18(CustomForm, { ...seed, onChange: applyResult })
3861
1787
  );
3862
1788
  } else if (def.settings) {
3863
1789
  openModal(
3864
1790
  `${def.label} Settings`,
3865
- /* @__PURE__ */ jsx42(
1791
+ /* @__PURE__ */ jsx18(
3866
1792
  SettingsForm,
3867
1793
  {
3868
1794
  schema: def.settings,
@@ -3874,7 +1800,7 @@ function Container({ content, isEditMode, onChange, openModal }) {
3874
1800
  );
3875
1801
  }
3876
1802
  } : void 0;
3877
- return /* @__PURE__ */ jsx42(
1803
+ return /* @__PURE__ */ jsx18(
3878
1804
  ContainerChild,
3879
1805
  {
3880
1806
  containerId: container.id,
@@ -3900,9 +1826,9 @@ function Container({ content, isEditMode, onChange, openModal }) {
3900
1826
  }
3901
1827
  const occupied = occupiedColumns(children, columns);
3902
1828
  const emptyCols = occupied < columns ? columns - occupied : 0;
3903
- return /* @__PURE__ */ jsx42("div", { className: "@container", children: /* @__PURE__ */ jsxs24("div", { className: gridClass, children: [
1829
+ return /* @__PURE__ */ jsx18("div", { className: "@container", children: /* @__PURE__ */ jsxs9("div", { className: gridClass, children: [
3904
1830
  children.map(renderEditChild),
3905
- Array.from({ length: emptyCols }, (_, i) => /* @__PURE__ */ jsx42(
1831
+ Array.from({ length: emptyCols }, (_, i) => /* @__PURE__ */ jsx18(
3906
1832
  EmptyColumnCell,
3907
1833
  {
3908
1834
  containerId: container.id,
@@ -3915,8 +1841,8 @@ function Container({ content, isEditMode, onChange, openModal }) {
3915
1841
  }
3916
1842
 
3917
1843
  // src/components/sections/Container/ContainerSettingsForm.tsx
3918
- import { useState as useState18 } from "react";
3919
- import { jsx as jsx43, jsxs as jsxs25 } from "react/jsx-runtime";
1844
+ import { useState as useState8 } from "react";
1845
+ import { jsx as jsx19, jsxs as jsxs10 } from "react/jsx-runtime";
3920
1846
  function inheritableSchema(children) {
3921
1847
  const out = {};
3922
1848
  for (const child of children) {
@@ -3936,8 +1862,8 @@ function ContainerSettingsForm({
3936
1862
  children = [],
3937
1863
  onChange
3938
1864
  }) {
3939
- const [layout, setLayout] = useState18({ columns, flow });
3940
- const [defaults, setDefaults] = useState18(childDefaults);
1865
+ const [layout, setLayout] = useState8({ columns, flow });
1866
+ const [defaults, setDefaults] = useState8(childDefaults);
3941
1867
  const childSchema = inheritableSchema(children);
3942
1868
  const hasChildDefaults = Object.keys(childSchema).length > 0;
3943
1869
  function emit(nextLayout, nextDefaults) {
@@ -3966,10 +1892,10 @@ function ContainerSettingsForm({
3966
1892
  ]
3967
1893
  }
3968
1894
  };
3969
- return /* @__PURE__ */ jsxs25("div", { className: "flex flex-col gap-6", children: [
3970
- /* @__PURE__ */ jsxs25("fieldset", { className: "flex flex-col gap-4", children: [
3971
- /* @__PURE__ */ jsx43("legend", { className: "mb-2 text-sm font-semibold text-base-contrast", children: "Layout" }),
3972
- /* @__PURE__ */ jsx43(
1895
+ return /* @__PURE__ */ jsxs10("div", { className: "flex flex-col gap-6", children: [
1896
+ /* @__PURE__ */ jsxs10("fieldset", { className: "flex flex-col gap-4", children: [
1897
+ /* @__PURE__ */ jsx19("legend", { className: "mb-2 text-sm font-semibold text-base-contrast", children: "Layout" }),
1898
+ /* @__PURE__ */ jsx19(
3973
1899
  SettingsForm,
3974
1900
  {
3975
1901
  schema: layoutSchema,
@@ -3985,9 +1911,9 @@ function ContainerSettingsForm({
3985
1911
  }
3986
1912
  )
3987
1913
  ] }),
3988
- hasChildDefaults && /* @__PURE__ */ jsxs25("fieldset", { className: "flex flex-col gap-4 border-t border-base-200 pt-4", children: [
3989
- /* @__PURE__ */ jsx43("legend", { className: "mb-2 text-sm font-semibold text-base-contrast", children: "Apply to all items" }),
3990
- /* @__PURE__ */ jsx43(
1914
+ hasChildDefaults && /* @__PURE__ */ jsxs10("fieldset", { className: "flex flex-col gap-4 border-t border-base-200 pt-4", children: [
1915
+ /* @__PURE__ */ jsx19("legend", { className: "mb-2 text-sm font-semibold text-base-contrast", children: "Apply to all items" }),
1916
+ /* @__PURE__ */ jsx19(
3991
1917
  SettingsForm,
3992
1918
  {
3993
1919
  schema: childSchema,
@@ -4004,16 +1930,16 @@ function ContainerSettingsForm({
4004
1930
  }
4005
1931
 
4006
1932
  // src/components/sections/Container/index.tsx
4007
- import { jsx as jsx44 } from "react/jsx-runtime";
4008
- var schema9 = z9.object({
4009
- type: z9.literal("container"),
4010
- content: z9.object({
4011
- columns: z9.number().int().min(1).max(MAX_CONTAINER_COLUMNS).default(1),
4012
- flow: z9.enum(["row", "column"]).default("row"),
4013
- childDefaults: z9.record(z9.string(), z9.unknown()).optional(),
1933
+ import { jsx as jsx20 } from "react/jsx-runtime";
1934
+ var schema6 = z6.object({
1935
+ type: z6.literal("container"),
1936
+ content: z6.object({
1937
+ columns: z6.number().int().min(1).max(MAX_CONTAINER_COLUMNS).default(1),
1938
+ flow: z6.enum(["row", "column"]).default("row"),
1939
+ childDefaults: z6.record(z6.string(), z6.unknown()).optional(),
4014
1940
  // Recursive: children are full blocks. z.lazy defers evaluation to parse
4015
1941
  // time, by which point every section schema (incl. container) is registered.
4016
- children: z9.array(z9.lazy(() => getSectionSchema())).default([])
1942
+ children: z6.array(z6.lazy(() => getSectionSchema())).default([])
4017
1943
  }).transform((c) => ({
4018
1944
  ...c,
4019
1945
  children: c.children.map(
@@ -4024,42 +1950,39 @@ var schema9 = z9.object({
4024
1950
  var Container_default = defineSection({
4025
1951
  type: "container",
4026
1952
  label: "Container",
4027
- icon: /* @__PURE__ */ jsx44(LayoutTemplate, { size: 18 }),
4028
- schema: schema9,
4029
- component: ({ content, onChange, isEditMode, openModal }) => /* @__PURE__ */ jsx44(Container, { content, isEditMode: !!isEditMode, onChange, openModal }),
1953
+ icon: /* @__PURE__ */ jsx20(LayoutTemplate, { size: 18 }),
1954
+ schema: schema6,
1955
+ component: ({ content, onChange, isEditMode, openModal }) => /* @__PURE__ */ jsx20(Container, { content, isEditMode: !!isEditMode, onChange, openModal }),
4030
1956
  defaults: () => ({
4031
1957
  type: "container",
4032
1958
  content: { columns: 1, flow: "row", children: [] }
4033
1959
  }),
4034
- getLabel: (content) => {
4035
- const n = content.content.children.length;
4036
- return `Container (${n} item${n === 1 ? "" : "s"})`;
4037
- },
1960
+ getLabel: (content) => `Container (${pluralize(content.content.children.length, "item")})`,
4038
1961
  settingsForm: ContainerSettingsForm
4039
1962
  });
4040
1963
 
4041
1964
  // src/components/sections/Spacer/index.tsx
4042
- import { z as z10 } from "zod";
1965
+ import { z as z7 } from "zod";
4043
1966
  import { SquareDashed } from "lucide-react";
4044
1967
 
4045
1968
  // src/components/sections/Spacer/Spacer.tsx
4046
- import { jsx as jsx45 } from "react/jsx-runtime";
1969
+ import { jsx as jsx21 } from "react/jsx-runtime";
4047
1970
  function Spacer() {
4048
- return /* @__PURE__ */ jsx45("div", { "data-spacer": true, "aria-hidden": "true", className: "h-full" });
1971
+ return /* @__PURE__ */ jsx21("div", { "data-spacer": true, "aria-hidden": "true", className: "h-full" });
4049
1972
  }
4050
1973
 
4051
1974
  // src/components/sections/Spacer/index.tsx
4052
- import { jsx as jsx46 } from "react/jsx-runtime";
4053
- var schema10 = z10.object({
4054
- type: z10.literal("spacer"),
4055
- content: z10.object({}).default({})
1975
+ import { jsx as jsx22 } from "react/jsx-runtime";
1976
+ var schema7 = z7.object({
1977
+ type: z7.literal("spacer"),
1978
+ content: z7.object({}).default({})
4056
1979
  });
4057
1980
  var Spacer_default = defineSection({
4058
1981
  type: "spacer",
4059
1982
  label: "Spacer",
4060
- icon: /* @__PURE__ */ jsx46(SquareDashed, { size: 18 }),
4061
- schema: schema10,
4062
- component: () => /* @__PURE__ */ jsx46(Spacer, {}),
1983
+ icon: /* @__PURE__ */ jsx22(SquareDashed, { size: 18 }),
1984
+ schema: schema7,
1985
+ component: () => /* @__PURE__ */ jsx22(Spacer, {}),
4063
1986
  defaults: () => ({ type: "spacer", content: {} })
4064
1987
  });
4065
1988