@drawnagency/primitives 0.1.108 → 0.1.110

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 (216) hide show
  1. package/dist/auth/cookies.d.ts +8 -0
  2. package/dist/auth/cookies.d.ts.map +1 -1
  3. package/dist/auth/index.d.ts +1 -1
  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 +11 -4
  7. package/dist/auth/types.d.ts.map +1 -1
  8. package/dist/{chunk-5IZ2KTMU.js → chunk-3GAG6ZXB.js} +55 -46
  9. package/dist/{chunk-PZJL2S3Z.js → chunk-3ZRQ4G5F.js} +1 -1
  10. package/dist/{chunk-FM4M4Q2W.js → chunk-4TZJZU2T.js} +23 -3
  11. package/dist/{chunk-LUAI5DWR.js → chunk-7MI3HCOF.js} +60 -23
  12. package/dist/{chunk-ZHHJVKU7.js → chunk-7WQJ57XG.js} +63 -204
  13. package/dist/{chunk-JIQBLG6Y.js → chunk-ALMEC3XN.js} +12 -13
  14. package/dist/{chunk-TZQPOR5A.js → chunk-DMCGN5AS.js} +10 -3
  15. package/dist/{chunk-D6TLUVOP.js → chunk-ELQ4C677.js} +48 -2
  16. package/dist/chunk-F2LYQPXV.js +77 -0
  17. package/dist/{chunk-5BM2Y3NJ.js → chunk-FRVO75DK.js} +14 -2
  18. package/dist/{chunk-VKAGMEKE.js → chunk-FTPETWXE.js} +1 -0
  19. package/dist/{chunk-3TO4RRHY.js → chunk-GPPVYLEI.js} +7 -0
  20. package/dist/{chunk-NLQ5F3RQ.js → chunk-HBULBDGP.js} +1 -1
  21. package/dist/{chunk-IPKZ5FU6.js → chunk-NXGIHSL5.js} +72 -55
  22. package/dist/{chunk-CPTFFLK2.js → chunk-OTDA6KWL.js} +10 -3
  23. package/dist/chunk-OUYB2GCW.js +194 -0
  24. package/dist/{chunk-72CG6AA2.js → chunk-RA5IOFPY.js} +10 -2
  25. package/dist/{chunk-GEENI3OC.js → chunk-SW6PCQQD.js} +4 -0
  26. package/dist/{chunk-BOZUEKWL.js → chunk-UVIYI4J6.js} +1 -1
  27. package/dist/{chunk-AB3XVXI3.js → chunk-VCCNKTFD.js} +37 -29
  28. package/dist/{chunk-XBYJQ5RS.js → chunk-XQMDWXD6.js} +21 -6
  29. package/dist/collab/rebase.d.ts +6 -0
  30. package/dist/collab/rebase.d.ts.map +1 -1
  31. package/dist/collab/rebase.js +12 -3
  32. package/dist/collab/recovery.d.ts +0 -20
  33. package/dist/collab/recovery.d.ts.map +1 -1
  34. package/dist/components/brandguide/ColorSwatchSettings.d.ts.map +1 -1
  35. package/dist/components/brandguide/ColorsView.d.ts.map +1 -1
  36. package/dist/components/brandguide/colors-shared.d.ts +10 -1
  37. package/dist/components/brandguide/colors-shared.d.ts.map +1 -1
  38. package/dist/components/chat/ChatPanel.d.ts.map +1 -1
  39. package/dist/components/docfile/DocFilePane.d.ts.map +1 -1
  40. package/dist/components/docfile/PdfCanvasViewer.d.ts.map +1 -1
  41. package/dist/components/docfile/doc-file-state.d.ts.map +1 -1
  42. package/dist/components/editor/IndicatorPill.d.ts.map +1 -1
  43. package/dist/components/editor/NavGroupRow.d.ts +7 -2
  44. package/dist/components/editor/NavGroupRow.d.ts.map +1 -1
  45. package/dist/components/editor/PagesModal.d.ts.map +1 -1
  46. package/dist/components/editor/index.js +14 -13
  47. package/dist/components/primitives/EditableRichText.d.ts.map +1 -1
  48. package/dist/components/primitives/LinkPopover.d.ts.map +1 -1
  49. package/dist/components/primitives/index.d.ts +2 -0
  50. package/dist/components/primitives/index.d.ts.map +1 -1
  51. package/dist/components/primitives/index.js +22 -13
  52. package/dist/components/primitives/useEditableCollection.d.ts.map +1 -1
  53. package/dist/components/primitives/useEditablePlainText.d.ts.map +1 -1
  54. package/dist/components/sections/IconList/index.d.ts.map +1 -1
  55. package/dist/components/sections/Prose/Prose.d.ts.map +1 -1
  56. package/dist/components/sections/brand-guide.js +10 -10
  57. package/dist/components/sections/register-schemas.js +16 -16
  58. package/dist/components/sections/register.js +16 -16
  59. package/dist/components/shared/FontPicker.d.ts.map +1 -1
  60. package/dist/components/shared/HistoryChangesModal.d.ts.map +1 -1
  61. package/dist/components/shared/HistoryPopover.d.ts.map +1 -1
  62. package/dist/components/shared/Navigation.d.ts.map +1 -1
  63. package/dist/components/shared/Popover.d.ts.map +1 -1
  64. package/dist/components/shell/AudienceAddForm.d.ts +1 -5
  65. package/dist/components/shell/AudienceAddForm.d.ts.map +1 -1
  66. package/dist/components/shell/AudienceRow.d.ts +2 -3
  67. package/dist/components/shell/AudienceRow.d.ts.map +1 -1
  68. package/dist/components/shell/BugReportFAB.d.ts.map +1 -1
  69. package/dist/components/shell/EditorShell.d.ts.map +1 -1
  70. package/dist/components/shell/MediaLibraryModal.d.ts.map +1 -1
  71. package/dist/components/shell/SiteSettingsUsers.d.ts.map +1 -1
  72. package/dist/components/shell/SiteSettingsViewerAccess.d.ts.map +1 -1
  73. package/dist/components/shell/view-branch.d.ts.map +1 -1
  74. package/dist/document/components/DocRail.d.ts.map +1 -1
  75. package/dist/document/components/DocViewer.d.ts.map +1 -1
  76. package/dist/document/components/measure.d.ts.map +1 -1
  77. package/dist/document/components/useDocPages.d.ts.map +1 -1
  78. package/dist/document/engine/blocks.d.ts.map +1 -1
  79. package/dist/document/engine/masonry.d.ts.map +1 -1
  80. package/dist/document/engine/paginate.d.ts.map +1 -1
  81. package/dist/document/engine/settings.d.ts +3 -0
  82. package/dist/document/engine/settings.d.ts.map +1 -1
  83. package/dist/document/index.js +79 -28
  84. package/dist/hooks/useBuildStatus.d.ts.map +1 -1
  85. package/dist/hooks/useDocumentUploads.d.ts +1 -0
  86. package/dist/hooks/useDocumentUploads.d.ts.map +1 -1
  87. package/dist/hooks/useEditorPersistence.d.ts.map +1 -1
  88. package/dist/hooks/useEditorPublish.d.ts.map +1 -1
  89. package/dist/hooks/useFocusTrap.d.ts +6 -0
  90. package/dist/hooks/useFocusTrap.d.ts.map +1 -1
  91. package/dist/hooks/useMediaPipeline.d.ts +3 -2
  92. package/dist/hooks/useMediaPipeline.d.ts.map +1 -1
  93. package/dist/hooks/useSanitizedHtml.d.ts +12 -0
  94. package/dist/hooks/useSanitizedHtml.d.ts.map +1 -0
  95. package/dist/index.js +20 -14
  96. package/dist/lib/block-move.d.ts.map +1 -1
  97. package/dist/lib/contrast.d.ts.map +1 -1
  98. package/dist/lib/dexie.d.ts +2 -8
  99. package/dist/lib/dexie.d.ts.map +1 -1
  100. package/dist/lib/dexie.js +21 -49
  101. package/dist/lib/index-ops.d.ts.map +1 -1
  102. package/dist/lib/index-ops.js +43 -23
  103. package/dist/lib/index.js +8 -8
  104. package/dist/lib/loader.d.ts.map +1 -1
  105. package/dist/lib/media-folders.d.ts +12 -4
  106. package/dist/lib/media-folders.d.ts.map +1 -1
  107. package/dist/lib/media-folders.js +12 -2
  108. package/dist/lib/migrate-sections-transform.js +1 -1
  109. package/dist/lib/nav-tree.d.ts.map +1 -1
  110. package/dist/lib/nav.d.ts.map +1 -1
  111. package/dist/lib/pages.d.ts.map +1 -1
  112. package/dist/lib/registry.d.ts.map +1 -1
  113. package/dist/lib/registry.js +1 -1
  114. package/dist/lib/sanitize.d.ts +6 -0
  115. package/dist/lib/sanitize.d.ts.map +1 -1
  116. package/dist/lib/timestamp.d.ts.map +1 -1
  117. package/dist/media/index.js +4 -4
  118. package/dist/media/queue.d.ts.map +1 -1
  119. package/dist/media/resolve.d.ts.map +1 -1
  120. package/dist/media/utils.d.ts.map +1 -1
  121. package/dist/media/videoPoster.d.ts.map +1 -1
  122. package/dist/schemas/audience.d.ts +5 -1
  123. package/dist/schemas/audience.d.ts.map +1 -1
  124. package/dist/schemas/audience.js +1 -1
  125. package/dist/schemas/index.js +9 -7
  126. package/dist/schemas/link.d.ts.map +1 -1
  127. package/dist/schemas/link.js +1 -1
  128. package/dist/schemas/sections.d.ts +12 -0
  129. package/dist/schemas/sections.d.ts.map +1 -1
  130. package/dist/storage/index.d.ts +1 -1
  131. package/dist/storage/index.d.ts.map +1 -1
  132. package/dist/storage/index.js +3 -1
  133. package/dist/storage/types.d.ts +9 -0
  134. package/dist/storage/types.d.ts.map +1 -1
  135. package/dist/types/database.d.ts +0 -3
  136. package/dist/types/database.d.ts.map +1 -1
  137. package/package.json +1 -1
  138. package/src/auth/cookies.ts +11 -0
  139. package/src/auth/index.ts +1 -1
  140. package/src/auth/types.ts +18 -4
  141. package/src/collab/rebase.ts +26 -1
  142. package/src/collab/recovery.ts +29 -3
  143. package/src/components/brandguide/ColorSwatchSettings.tsx +40 -13
  144. package/src/components/brandguide/ColorsEditable.tsx +7 -9
  145. package/src/components/brandguide/ColorsView.tsx +12 -11
  146. package/src/components/brandguide/colors-shared.ts +17 -1
  147. package/src/components/chat/ChatPanel.tsx +19 -0
  148. package/src/components/docfile/DocFilePane.tsx +11 -0
  149. package/src/components/docfile/PdfCanvasViewer.tsx +46 -5
  150. package/src/components/docfile/doc-file-state.ts +7 -0
  151. package/src/components/editor/IndicatorPill.tsx +5 -1
  152. package/src/components/editor/NavGroupRow.tsx +8 -3
  153. package/src/components/editor/PagesModal.tsx +10 -2
  154. package/src/components/primitives/EditableList.tsx +10 -2
  155. package/src/components/primitives/EditableRichText.tsx +4 -3
  156. package/src/components/primitives/LinkPopover.tsx +23 -1
  157. package/src/components/primitives/index.ts +5 -0
  158. package/src/components/primitives/useEditableCollection.ts +61 -41
  159. package/src/components/primitives/useEditablePlainText.ts +24 -3
  160. package/src/components/sections/IconList/index.tsx +8 -1
  161. package/src/components/sections/Prose/Prose.tsx +4 -2
  162. package/src/components/shared/FontPicker.tsx +16 -4
  163. package/src/components/shared/HistoryChangesModal.tsx +12 -1
  164. package/src/components/shared/HistoryPopover.tsx +18 -4
  165. package/src/components/shared/LinkField.tsx +1 -1
  166. package/src/components/shared/Navigation.tsx +20 -2
  167. package/src/components/shared/Popover.tsx +9 -5
  168. package/src/components/shell/AudienceAddForm.tsx +6 -26
  169. package/src/components/shell/AudienceRow.tsx +29 -39
  170. package/src/components/shell/BugReportFAB.tsx +20 -2
  171. package/src/components/shell/EditorShell.tsx +200 -50
  172. package/src/components/shell/MediaLibraryModal.tsx +52 -10
  173. package/src/components/shell/SiteSettingsUsers.tsx +3 -0
  174. package/src/components/shell/SiteSettingsViewerAccess.tsx +21 -16
  175. package/src/components/shell/view-branch.ts +4 -1
  176. package/src/document/components/DocRail.tsx +27 -2
  177. package/src/document/components/DocViewer.tsx +5 -1
  178. package/src/document/components/measure.tsx +22 -6
  179. package/src/document/components/useDocPages.ts +13 -3
  180. package/src/document/engine/blocks.ts +33 -4
  181. package/src/document/engine/masonry.ts +8 -2
  182. package/src/document/engine/paginate.ts +4 -1
  183. package/src/document/engine/settings.ts +40 -3
  184. package/src/hooks/useBuildStatus.ts +6 -0
  185. package/src/hooks/useDocumentUploads.ts +46 -3
  186. package/src/hooks/useEditorPersistence.ts +33 -19
  187. package/src/hooks/useEditorPublish.ts +83 -34
  188. package/src/hooks/useFocusTrap.ts +18 -0
  189. package/src/hooks/useMediaPipeline.ts +111 -38
  190. package/src/hooks/useSanitizedHtml.ts +29 -0
  191. package/src/lib/audience-secret.ts +1 -0
  192. package/src/lib/block-move.ts +13 -2
  193. package/src/lib/contrast.ts +14 -14
  194. package/src/lib/dexie.ts +46 -49
  195. package/src/lib/index-ops.ts +63 -20
  196. package/src/lib/loader.ts +41 -12
  197. package/src/lib/media-folders.ts +31 -5
  198. package/src/lib/migrate-sections-transform.ts +22 -2
  199. package/src/lib/nav-tree.ts +16 -2
  200. package/src/lib/nav.ts +10 -1
  201. package/src/lib/pages.ts +24 -6
  202. package/src/lib/registry.ts +3 -0
  203. package/src/lib/sanitize.ts +9 -0
  204. package/src/lib/timestamp.ts +5 -0
  205. package/src/media/queue.ts +4 -0
  206. package/src/media/resolve.ts +14 -1
  207. package/src/media/utils.ts +3 -1
  208. package/src/media/videoPoster.ts +14 -3
  209. package/src/schemas/audience.ts +10 -2
  210. package/src/schemas/link.ts +4 -1
  211. package/src/schemas/sections.ts +23 -1
  212. package/src/storage/index.ts +1 -1
  213. package/src/storage/types.ts +13 -0
  214. package/src/types/database.ts +0 -3
  215. package/dist/chunk-5THS4K5D.js +0 -123
  216. package/dist/chunk-ZSPCE6MZ.js +0 -70
@@ -0,0 +1,77 @@
1
+ import {
2
+ cn
3
+ } from "./chunk-7EOSDFS3.js";
4
+
5
+ // src/components/shared/Checkbox.tsx
6
+ import { useId } from "react";
7
+ import { Check } from "lucide-react";
8
+ import { jsx, jsxs } from "react/jsx-runtime";
9
+ function Checkbox({
10
+ checked,
11
+ onChange,
12
+ label,
13
+ description,
14
+ disabled,
15
+ className,
16
+ startAdornment,
17
+ align = "start"
18
+ }) {
19
+ const id = useId();
20
+ return /* @__PURE__ */ jsxs(
21
+ "label",
22
+ {
23
+ htmlFor: id,
24
+ className: cn(
25
+ "flex cursor-pointer gap-3 select-none hover:opacity-80",
26
+ align === "start" ? "items-start" : "items-center",
27
+ disabled && "cursor-not-allowed opacity-50",
28
+ className
29
+ ),
30
+ children: [
31
+ /* @__PURE__ */ jsxs(
32
+ "span",
33
+ {
34
+ className: cn(
35
+ "relative flex h-5 w-5 shrink-0 items-center justify-center",
36
+ align === "start" && "mt-0.5"
37
+ ),
38
+ children: [
39
+ /* @__PURE__ */ jsx(
40
+ "input",
41
+ {
42
+ id,
43
+ type: "checkbox",
44
+ checked,
45
+ disabled,
46
+ onChange: (e) => onChange(e.target.checked),
47
+ className: "sr-only"
48
+ }
49
+ ),
50
+ /* @__PURE__ */ jsx(
51
+ "span",
52
+ {
53
+ "aria-hidden": "true",
54
+ className: cn(
55
+ "flex h-5 w-5 items-center justify-center rounded border transition-colors",
56
+ checked ? "border-primary bg-primary text-primary-contrast" : "border-base-300 bg-base hover:border-primary",
57
+ disabled && "pointer-events-none"
58
+ ),
59
+ children: checked && /* @__PURE__ */ jsx(Check, { size: 14, strokeWidth: 3 })
60
+ }
61
+ )
62
+ ]
63
+ }
64
+ ),
65
+ startAdornment,
66
+ /* @__PURE__ */ jsxs("div", { className: "text-sm", children: [
67
+ /* @__PURE__ */ jsx("span", { className: "font-medium text-base-contrast", children: label }),
68
+ description && /* @__PURE__ */ jsx("span", { className: "block text-base-contrast-light", children: description })
69
+ ] })
70
+ ]
71
+ }
72
+ );
73
+ }
74
+
75
+ export {
76
+ Checkbox
77
+ };
@@ -26,7 +26,19 @@ function upgradeContainerChildren(section) {
26
26
  return up;
27
27
  });
28
28
  if (!changed) return section;
29
- return { ...section, content: { ...content, children: mapped } };
29
+ return {
30
+ ...section,
31
+ content: {
32
+ ...content,
33
+ layout: content?.layout ?? "grid",
34
+ columns: normalizeColumns(content?.columns),
35
+ equalizeRowHeight: content?.equalizeRowHeight ?? false,
36
+ children: mapped
37
+ }
38
+ };
39
+ }
40
+ function normalizeColumns(columns) {
41
+ return typeof columns === "number" && Number.isFinite(columns) ? columns : 1;
30
42
  }
31
43
  function buildRef(item, forceImage) {
32
44
  const type = forceImage ? "image" : item.type;
@@ -75,7 +87,7 @@ function upgradeGrid(section) {
75
87
  id,
76
88
  type: "container",
77
89
  content: {
78
- columns: content.columns,
90
+ columns: normalizeColumns(content.columns),
79
91
  layout: "grid",
80
92
  equalizeRowHeight: false,
81
93
  children: media.map((item, i) => mapRefItem(item, id, i))
@@ -10,6 +10,7 @@ function createRegistry() {
10
10
  registerSection(def) {
11
11
  sections.set(def.type, def);
12
12
  if (def.richTextFields) richText.set(def.type, def.richTextFields);
13
+ else richText.delete(def.type);
13
14
  },
14
15
  registerSchema(type, schema) {
15
16
  schemas.set(type, schema);
@@ -7,6 +7,12 @@ var StorageConflictError = class extends Error {
7
7
  }
8
8
  currentVersion;
9
9
  };
10
+ var NothingToPublishError = class extends Error {
11
+ constructor() {
12
+ super("No saved branch to publish");
13
+ this.name = "NothingToPublishError";
14
+ }
15
+ };
10
16
  var IdempotencyLookupUnavailableError = class extends Error {
11
17
  constructor(cause) {
12
18
  super("Could not search draft history for the idempotency key");
@@ -31,6 +37,7 @@ var DOCUMENT_CACHE_CONTROL = "private, max-age=31536000, immutable";
31
37
 
32
38
  export {
33
39
  StorageConflictError,
40
+ NothingToPublishError,
34
41
  IdempotencyLookupUnavailableError,
35
42
  IDEMPOTENCY_TRAILER,
36
43
  IDEMPOTENCY_KEY_RE,
@@ -27,7 +27,7 @@ var ViewerCredentialSchema = z.object({
27
27
  createdAt: z.string()
28
28
  });
29
29
  function slugifyAudienceName(input) {
30
- return input.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
30
+ return input.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 32).replace(/-+$/g, "");
31
31
  }
32
32
 
33
33
  export {
@@ -1,26 +1,26 @@
1
1
  import {
2
- Checkbox,
3
- FormLabel,
4
- Input
5
- } from "./chunk-5THS4K5D.js";
2
+ Checkbox
3
+ } from "./chunk-F2LYQPXV.js";
6
4
  import {
7
5
  EditableGrid,
8
6
  EditablePlainText,
9
7
  MediaBlock,
10
8
  containerGridClass,
11
9
  useEditableCollection
12
- } from "./chunk-5IZ2KTMU.js";
10
+ } from "./chunk-3GAG6ZXB.js";
13
11
  import {
14
12
  AddIcon,
15
13
  DeleteIcon,
16
14
  DragHandle,
15
+ FormLabel,
17
16
  IconButton,
17
+ Input,
18
18
  useMediaLibrary
19
- } from "./chunk-D6TLUVOP.js";
19
+ } from "./chunk-ELQ4C677.js";
20
20
  import {
21
21
  DEFAULT_LINK,
22
22
  LinkValueSchema
23
- } from "./chunk-PZJL2S3Z.js";
23
+ } from "./chunk-3ZRQ4G5F.js";
24
24
  import {
25
25
  DOCUMENT_MIME_TO_EXT
26
26
  } from "./chunk-BS5AQHZG.js";
@@ -36,7 +36,7 @@ import {
36
36
  } from "./chunk-7EOSDFS3.js";
37
37
  import {
38
38
  defineSection
39
- } from "./chunk-VKAGMEKE.js";
39
+ } from "./chunk-FTPETWXE.js";
40
40
  import {
41
41
  HexColorSchema,
42
42
  SingleMediaReferenceSchema
@@ -63,10 +63,17 @@ var ColorItemSchema = z.object({
63
63
  });
64
64
 
65
65
  // src/components/brandguide/ColorsView.tsx
66
- import { useState } from "react";
66
+ import { useEffect, useRef, useState } from "react";
67
67
  import { Eye, EyeOff } from "lucide-react";
68
68
 
69
69
  // src/components/brandguide/colors-shared.ts
70
+ var COLOR_SPACE_KEYS = ["hex", "rgb", "cmyk", "pantone"];
71
+ function orderedSpaceEntries(space) {
72
+ return COLOR_SPACE_KEYS.flatMap((key) => {
73
+ const value = space[key];
74
+ return value ? [[key, value]] : [];
75
+ });
76
+ }
70
77
  function getContrastClass(hex) {
71
78
  if (!hex) return "text-white";
72
79
  const h = hex.replace("#", "");
@@ -93,6 +100,10 @@ function ColorsView({
93
100
  }) {
94
101
  const [expanded, setExpanded] = useState(!collapsing);
95
102
  const [copiedIndex, setCopiedIndex] = useState(null);
103
+ const copiedTimerRef = useRef(null);
104
+ useEffect(() => () => {
105
+ if (copiedTimerRef.current) clearTimeout(copiedTimerRef.current);
106
+ }, []);
96
107
  const handleCopy = (value, colorIndex) => {
97
108
  if (navigator.clipboard?.writeText) {
98
109
  navigator.clipboard.writeText(value);
@@ -107,7 +118,8 @@ function ColorsView({
107
118
  document.body.removeChild(ta);
108
119
  }
109
120
  setCopiedIndex(colorIndex);
110
- setTimeout(() => setCopiedIndex(null), 1500);
121
+ if (copiedTimerRef.current) clearTimeout(copiedTimerRef.current);
122
+ copiedTimerRef.current = setTimeout(() => setCopiedIndex(null), 1500);
111
123
  };
112
124
  return /* @__PURE__ */ jsxs("div", { children: [
113
125
  (showLabel && !!label || collapsing) && /* @__PURE__ */ jsxs("div", { className: "mb-4 flex items-center gap-2", children: [
@@ -137,12 +149,10 @@ function ColorsView({
137
149
  ] }),
138
150
  expanded && // flex-1 so every swatch's label block grows to the tallest in its row.
139
151
  /* @__PURE__ */ jsx("div", { className: "flex-1 space-y-1 bg-base-accent p-3 text-sm", children: color.spaces.map(
140
- (space, j) => Object.entries(space).map(
141
- ([key, value]) => value ? /* @__PURE__ */ jsxs("button", { onClick: () => handleCopy(value, i), className: "cursor-pointer flex w-full justify-between hover:text-primary", children: [
142
- /* @__PURE__ */ jsx("span", { className: "font-medium uppercase", children: key }),
143
- /* @__PURE__ */ jsx("span", { children: value })
144
- ] }, `${j}-${key}`) : null
145
- )
152
+ (space, j) => orderedSpaceEntries(space).map(([key, value]) => /* @__PURE__ */ jsxs("button", { onClick: () => handleCopy(value, i), className: "cursor-pointer flex w-full justify-between hover:text-primary", children: [
153
+ /* @__PURE__ */ jsx("span", { className: "font-medium uppercase", children: key }),
154
+ /* @__PURE__ */ jsx("span", { children: value })
155
+ ] }, `${j}-${key}`))
146
156
  ) })
147
157
  ] }, i);
148
158
  }) }) })
@@ -150,28 +160,36 @@ function ColorsView({
150
160
  }
151
161
 
152
162
  // src/components/brandguide/ColorsEditable.tsx
153
- import { useState as useState3, useEffect } from "react";
163
+ import { useState as useState3, useEffect as useEffect2 } from "react";
154
164
  import { Eye as Eye2, EyeOff as EyeOff2 } from "lucide-react";
155
165
 
156
166
  // src/components/brandguide/ColorSwatchSettings.tsx
157
167
  import { useState as useState2 } from "react";
158
168
  import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
159
- function mergeSpaces(spaces) {
160
- return Object.assign({}, ...spaces);
169
+ var KEYS = COLOR_SPACE_KEYS;
170
+ function hasAnyValue(color) {
171
+ return color.spaces.some((s) => KEYS.some((k) => s[k]));
161
172
  }
162
173
  function ColorSwatchSettings({ color, onChange }) {
163
- const [localColor, setLocalColor] = useState2(() => ({
164
- ...color,
165
- spaces: [mergeSpaces(color.spaces)]
166
- }));
167
- const space = localColor.spaces[0] || {};
174
+ const [localColor, setLocalColor] = useState2(color);
175
+ const spaces = localColor.spaces.length > 0 ? localColor.spaces : [{}];
176
+ const valueOf = (key) => spaces.find((s) => s[key])?.[key] ?? "";
168
177
  const update = (updated) => {
169
178
  setLocalColor(updated);
170
- onChange(updated);
179
+ if (hasAnyValue(updated)) onChange(updated);
171
180
  };
172
181
  const updateSpace = (key, value) => {
173
- const newSpace = { ...space, [key]: value || void 0 };
174
- update({ ...localColor, spaces: [newSpace] });
182
+ const owner = spaces.findIndex((s) => s[key] !== void 0);
183
+ const target = owner === -1 ? 0 : owner;
184
+ const next = spaces.map((s, i) => {
185
+ if (i !== target) return s;
186
+ if (!value) {
187
+ const { [key]: _dropped, ...rest } = s;
188
+ return rest;
189
+ }
190
+ return { ...s, [key]: value };
191
+ }).filter((s, i) => i === 0 || Object.keys(s).length > 0);
192
+ update({ ...localColor, spaces: next });
175
193
  };
176
194
  return /* @__PURE__ */ jsxs2("div", { className: "space-y-3", children: [
177
195
  /* @__PURE__ */ jsx2(
@@ -182,15 +200,16 @@ function ColorSwatchSettings({ color, onChange }) {
182
200
  onChange: (name) => update({ ...localColor, name })
183
201
  }
184
202
  ),
185
- ["hex", "rgb", "cmyk", "pantone"].map((key) => /* @__PURE__ */ jsx2(
203
+ KEYS.map((key) => /* @__PURE__ */ jsx2(
186
204
  Input,
187
205
  {
188
206
  label: key.toUpperCase(),
189
- value: space[key] || "",
207
+ value: valueOf(key),
190
208
  onChange: (value) => updateSpace(key, value)
191
209
  },
192
210
  key
193
- ))
211
+ )),
212
+ !hasAnyValue(localColor) && /* @__PURE__ */ jsx2("p", { role: "alert", className: "mt-1 text-xs text-red-600", children: "Enter at least one value \u2014 this color won\u2019t be saved until you do." })
194
213
  ] });
195
214
  }
196
215
 
@@ -216,7 +235,7 @@ function ColorsEditable({
216
235
  }) {
217
236
  const [expanded, setExpanded] = useState3(!collapsing);
218
237
  const options = { label, columns, collapsing, showLabel };
219
- useEffect(() => {
238
+ useEffect2(() => {
220
239
  setExpanded(!collapsing);
221
240
  }, [collapsing]);
222
241
  const handleItemSettings = (index) => {
@@ -307,12 +326,10 @@ function ColorSwatchEditable({
307
326
  ) }),
308
327
  expanded && // flex-1 so every swatch's label block grows to the tallest in its row.
309
328
  /* @__PURE__ */ jsx3("div", { className: "flex-1 space-y-1 bg-base-accent p-3 text-sm", children: color.spaces.map(
310
- (space, j) => Object.entries(space).map(
311
- ([key, value]) => value ? /* @__PURE__ */ jsxs3("div", { className: "flex w-full justify-between", children: [
312
- /* @__PURE__ */ jsx3("span", { className: "font-medium uppercase", children: key }),
313
- /* @__PURE__ */ jsx3("span", { children: value })
314
- ] }, `${j}-${key}`) : null
315
- )
329
+ (space, j) => orderedSpaceEntries(space).map(([key, value]) => /* @__PURE__ */ jsxs3("div", { className: "flex w-full justify-between", children: [
330
+ /* @__PURE__ */ jsx3("span", { className: "font-medium uppercase", children: key }),
331
+ /* @__PURE__ */ jsx3("span", { children: value })
332
+ ] }, `${j}-${key}`))
316
333
  ) })
317
334
  ] });
318
335
  }
@@ -410,15 +427,15 @@ import { z as z3 } from "zod";
410
427
  import { List, Columns2 } from "lucide-react";
411
428
 
412
429
  // src/components/sections/IconList/IconList.tsx
413
- import { useRef as useRef3, useEffect as useEffect4, useState as useState4, useCallback, useId } from "react";
430
+ import { useRef as useRef4, useEffect as useEffect5, useState as useState4, useCallback, useId } from "react";
414
431
  import { Check as Check2, CopyPlus, Pencil, X as X2 } from "lucide-react";
415
432
 
416
433
  // src/components/primitives/IconPicker.tsx
417
- import { useEffect as useEffect2, useRef } from "react";
434
+ import { useEffect as useEffect3, useRef as useRef2 } from "react";
418
435
  import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
419
436
  function IconPicker({ selected, onSelect, onClose, showRemove = true }) {
420
- const panelRef = useRef(null);
421
- useEffect2(() => {
437
+ const panelRef = useRef2(null);
438
+ useEffect3(() => {
422
439
  function handleMouseDown(e) {
423
440
  if (panelRef.current && !panelRef.current.contains(e.target)) onClose();
424
441
  }
@@ -463,12 +480,12 @@ function IconPicker({ selected, onSelect, onClose, showRemove = true }) {
463
480
  }
464
481
 
465
482
  // src/components/sections/IconList/DoDontToggle.tsx
466
- import { useEffect as useEffect3, useRef as useRef2 } from "react";
483
+ import { useEffect as useEffect4, useRef as useRef3 } from "react";
467
484
  import { Check, X } from "lucide-react";
468
485
  import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
469
486
  function DoDontToggle({ value, onSelect, onClose }) {
470
- const panelRef = useRef2(null);
471
- useEffect3(() => {
487
+ const panelRef = useRef3(null);
488
+ useEffect4(() => {
472
489
  function handleMouseDown(e) {
473
490
  if (panelRef.current && !panelRef.current.contains(e.target)) onClose();
474
491
  }
@@ -677,9 +694,9 @@ function EditIconList({
677
694
  createItem: () => ({ label: "", text: "" })
678
695
  });
679
696
  const [dragState, setDragState] = useState4({ sourceId: null, targetId: null });
680
- const cfgRef = useRef3({ listId, onReorder, onRemove, onInsertItem });
697
+ const cfgRef = useRef4({ listId, onReorder, onRemove, onInsertItem });
681
698
  cfgRef.current = { listId, onReorder, onRemove, onInsertItem };
682
- useEffect4(() => {
699
+ useEffect5(() => {
683
700
  let cleanup;
684
701
  let cancelled = false;
685
702
  import("./adapter-HH47ZPGM.js").then(({ monitorForElements }) => {
@@ -798,9 +815,9 @@ function EditableRow({
798
815
  onUpdateItem,
799
816
  onDuplicate
800
817
  }) {
801
- const rowRef = useRef3(null);
802
- const handleRef = useRef3(null);
803
- const itemRef = useRef3(item);
818
+ const rowRef = useRef4(null);
819
+ const handleRef = useRef4(null);
820
+ const itemRef = useRef4(item);
804
821
  itemRef.current = item;
805
822
  const [iconHover, setIconHover] = useState4(false);
806
823
  const [pickerOpen, setPickerOpen] = useState4(false);
@@ -809,7 +826,7 @@ function EditableRow({
809
826
  const iconEntry = resolveIcon(item, defaultIcon, dodont);
810
827
  const effectiveIconId = item.icon ?? defaultIcon ?? null;
811
828
  const iconColor = resolveIconColor(item, iconClassName, dodont);
812
- useEffect4(() => {
829
+ useEffect5(() => {
813
830
  const row = rowRef.current;
814
831
  const handle = handleRef.current;
815
832
  if (!row || !handle) return;
@@ -1005,9 +1022,9 @@ function TrailingDropZone({
1005
1022
  dragState,
1006
1023
  setDragState
1007
1024
  }) {
1008
- const ref = useRef3(null);
1025
+ const ref = useRef4(null);
1009
1026
  const trailingId = "__trailing__";
1010
- useEffect4(() => {
1027
+ useEffect5(() => {
1011
1028
  const el = ref.current;
1012
1029
  if (!el) return;
1013
1030
  let cleanup;
@@ -1221,7 +1238,7 @@ var IconList_default = defineSection({
1221
1238
  showLabel: options?.showLabel,
1222
1239
  stackText: options?.stackText,
1223
1240
  dodont: options?.dodont,
1224
- onChange: onChange ? (c) => onChange(c) : void 0
1241
+ onChange: onChange ? (c) => onChange({ ...c, options: content.options }) : void 0
1225
1242
  }
1226
1243
  ),
1227
1244
  defaults: () => ({
@@ -1335,7 +1352,7 @@ var DoDontMedia_default = defineSection({
1335
1352
  });
1336
1353
 
1337
1354
  // src/components/sections/Document/index.tsx
1338
- import { useEffect as useEffect5, useState as useState7 } from "react";
1355
+ import { useEffect as useEffect6, useState as useState7 } from "react";
1339
1356
  import { z as z5 } from "zod";
1340
1357
  import { ExternalLink, FileText } from "lucide-react";
1341
1358
 
@@ -1563,7 +1580,7 @@ var schema4 = z5.object({
1563
1580
  });
1564
1581
  function useSiteDocument(sectionId) {
1565
1582
  const [docs, setDocs] = useState7(null);
1566
- useEffect5(() => {
1583
+ useEffect6(() => {
1567
1584
  setDocs(getSiteDocuments());
1568
1585
  return subscribeSiteDocuments(setDocs);
1569
1586
  }, []);
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  RESERVED_SLUGS
3
- } from "./chunk-BOZUEKWL.js";
3
+ } from "./chunk-UVIYI4J6.js";
4
4
 
5
5
  // src/lib/nav-tree.ts
6
6
  function reconcileNav(entries, pages) {
@@ -21,15 +21,22 @@ function reconcileNav(entries, pages) {
21
21
  continue;
22
22
  }
23
23
  if (entry?.kind !== "group") continue;
24
+ if (typeof entry.id !== "string" || entry.id === "") continue;
24
25
  if (seenGroupIds.has(entry.id)) continue;
25
26
  seenGroupIds.add(entry.id);
26
27
  const claimed = Array.isArray(entry.pageIds) ? entry.pageIds : [];
27
28
  const pageIds = claimed.filter((id) => {
28
- if (!validIds.has(id) || placed.has(id)) return false;
29
+ if (typeof id !== "string" || !validIds.has(id) || placed.has(id)) return false;
29
30
  placed.add(id);
30
31
  return true;
31
32
  });
32
- const group = { ...entry, pageIds };
33
+ const group = {
34
+ kind: "group",
35
+ id: entry.id,
36
+ label: typeof entry.label === "string" ? entry.label : "",
37
+ startCollapsed: entry.startCollapsed === true,
38
+ pageIds
39
+ };
33
40
  result.push(group);
34
41
  }
35
42
  for (const p of pageList) {
@@ -0,0 +1,194 @@
1
+ import {
2
+ FormLabel,
3
+ Input
4
+ } from "./chunk-ELQ4C677.js";
5
+ import {
6
+ cn
7
+ } from "./chunk-7EOSDFS3.js";
8
+
9
+ // src/components/shared/PagesContext.tsx
10
+ import { createContext, useContext } from "react";
11
+ var PagesContext = createContext({ pages: [], getPageHeadings: () => [] });
12
+ function usePages() {
13
+ return useContext(PagesContext);
14
+ }
15
+
16
+ // src/components/shared/Select.tsx
17
+ import { useId } from "react";
18
+ import { ChevronDown } from "lucide-react";
19
+ import { jsx, jsxs } from "react/jsx-runtime";
20
+ function Select({
21
+ label,
22
+ value,
23
+ onChange,
24
+ options,
25
+ disabled,
26
+ className,
27
+ selectClassName
28
+ }) {
29
+ const id = useId();
30
+ return /* @__PURE__ */ jsxs("div", { className, children: [
31
+ label && /* @__PURE__ */ jsx(FormLabel, { htmlFor: id, children: label }),
32
+ /* @__PURE__ */ jsxs("div", { className: "relative", children: [
33
+ /* @__PURE__ */ jsx(
34
+ "select",
35
+ {
36
+ id,
37
+ value,
38
+ onChange: (e) => onChange(e.target.value),
39
+ disabled,
40
+ className: cn(
41
+ "w-full appearance-none rounded border border-base-200 bg-base py-2 pr-8 pl-3 text-sm text-base-contrast",
42
+ "focus:border-base-contrast focus:outline-none focus:ring-1 focus:ring-base-contrast",
43
+ disabled && "cursor-not-allowed opacity-50",
44
+ selectClassName
45
+ ),
46
+ children: options.map((opt) => /* @__PURE__ */ jsx("option", { value: opt.value, children: opt.label }, opt.value))
47
+ }
48
+ ),
49
+ /* @__PURE__ */ jsx(
50
+ ChevronDown,
51
+ {
52
+ size: 14,
53
+ className: "pointer-events-none absolute right-2.5 top-1/2 -translate-y-1/2 text-base-contrast-light"
54
+ }
55
+ )
56
+ ] })
57
+ ] });
58
+ }
59
+
60
+ // src/components/shared/LinkField.tsx
61
+ import { Fragment, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
62
+ var TARGET_OPTIONS = [
63
+ { label: "Same tab (_self)", value: "_self" },
64
+ { label: "New tab (_blank)", value: "_blank" }
65
+ ];
66
+ function LinkField({ label, value, onChange }) {
67
+ const { pages, getPageHeadings } = usePages();
68
+ const isInternal = value.kind === "internal";
69
+ function setMode(kind) {
70
+ if (kind === value.kind) return;
71
+ onChange(kind === "external" ? { kind: "external", href: "", target: value.target } : { kind: "internal", pageId: "", anchorSectionId: null, target: "_self" });
72
+ }
73
+ const pageId = isInternal ? value.pageId : "";
74
+ const headings = pageId ? getPageHeadings(pageId) : [];
75
+ return /* @__PURE__ */ jsxs2("div", { className: "flex flex-col gap-3", children: [
76
+ /* @__PURE__ */ jsx2(FormLabel, { children: label }),
77
+ /* @__PURE__ */ jsx2("div", { className: "flex gap-2", role: "tablist", children: ["internal", "external"].map((kind) => /* @__PURE__ */ jsx2(
78
+ "button",
79
+ {
80
+ type: "button",
81
+ role: "tab",
82
+ "aria-selected": value.kind === kind,
83
+ onClick: () => setMode(kind),
84
+ className: cn(
85
+ "cursor-pointer rounded border px-3 py-1 text-sm capitalize",
86
+ value.kind === kind ? "border-primary bg-primary text-primary-contrast" : "border-base-200 text-base-contrast"
87
+ ),
88
+ children: kind === "external" ? "External URL" : "Internal page"
89
+ },
90
+ kind
91
+ )) }),
92
+ value.kind === "external" ? /* @__PURE__ */ jsx2(
93
+ Input,
94
+ {
95
+ label: "URL",
96
+ value: value.href,
97
+ placeholder: "https://...",
98
+ onChange: (href) => onChange({ kind: "external", href, target: value.target })
99
+ }
100
+ ) : /* @__PURE__ */ jsxs2(Fragment, { children: [
101
+ /* @__PURE__ */ jsx2(
102
+ Select,
103
+ {
104
+ label: "Page",
105
+ value: value.pageId,
106
+ options: [{ label: "Select a page\u2026", value: "" }, ...pages.map((p) => ({ label: p.title, value: p.id }))],
107
+ onChange: (pid) => onChange({ kind: "internal", pageId: pid, anchorSectionId: null, target: value.target })
108
+ }
109
+ ),
110
+ /* @__PURE__ */ jsx2(
111
+ Select,
112
+ {
113
+ label: "Section",
114
+ value: value.anchorSectionId ?? "",
115
+ disabled: !pageId,
116
+ options: [{ label: "None (top of page)", value: "" }, ...headings.map((h) => ({ label: h.label, value: h.id }))],
117
+ onChange: (anchor) => onChange({ kind: "internal", pageId, anchorSectionId: anchor || null, target: value.target })
118
+ }
119
+ )
120
+ ] }),
121
+ value.kind === "external" && /* @__PURE__ */ jsx2(
122
+ Select,
123
+ {
124
+ label: "Opens in",
125
+ value: value.target,
126
+ options: TARGET_OPTIONS,
127
+ onChange: (t) => onChange({ ...value, target: t })
128
+ }
129
+ )
130
+ ] });
131
+ }
132
+
133
+ // src/components/shared/Button.tsx
134
+ import { forwardRef } from "react";
135
+ import { jsx as jsx3 } from "react/jsx-runtime";
136
+ var boxSize = {
137
+ sm: "rounded px-3 py-1.5 text-xs font-medium",
138
+ md: "rounded px-4 py-2 text-sm font-medium"
139
+ };
140
+ var ghostSize = {
141
+ sm: "text-xs font-medium",
142
+ md: "text-sm font-medium"
143
+ };
144
+ var variantClasses = {
145
+ primary: "bg-base-contrast text-base-accent hover:bg-base-contrast/90",
146
+ brand: "bg-primary text-primary-contrast hover:opacity-90",
147
+ secondary: "border border-base-200 text-base-contrast hover:bg-base-accent",
148
+ destructive: "border border-red-600 text-red-600 hover:bg-red-600 hover:text-white"
149
+ };
150
+ var ghostToneClasses = {
151
+ neutral: "text-base-contrast-light hover:text-base-contrast",
152
+ destructive: "text-red-600 hover:text-red-800"
153
+ };
154
+ var Button = forwardRef(function Button2({
155
+ variant = "primary",
156
+ size = "sm",
157
+ tone = "neutral",
158
+ fullWidth = false,
159
+ isLoading = false,
160
+ loadingLabel,
161
+ disabled,
162
+ className,
163
+ children,
164
+ ...rest
165
+ }, ref) {
166
+ const isGhost = variant === "ghost";
167
+ const resolvedDisabled = disabled || isLoading;
168
+ const label = isLoading && loadingLabel ? loadingLabel : children;
169
+ return /* @__PURE__ */ jsx3(
170
+ "button",
171
+ {
172
+ ref,
173
+ disabled: resolvedDisabled,
174
+ className: cn(
175
+ "inline-flex items-center justify-center transition-colors",
176
+ isGhost ? ghostSize[size] : boxSize[size],
177
+ isGhost ? ghostToneClasses[tone] : variantClasses[variant],
178
+ fullWidth && "w-full",
179
+ resolvedDisabled ? "cursor-not-allowed opacity-50" : "cursor-pointer",
180
+ className
181
+ ),
182
+ ...rest,
183
+ children: label
184
+ }
185
+ );
186
+ });
187
+
188
+ export {
189
+ Button,
190
+ PagesContext,
191
+ usePages,
192
+ Select,
193
+ LinkField
194
+ };