@drawnagency/primitives 0.1.113 → 0.1.114

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 (483) hide show
  1. package/README.md +1 -1
  2. package/dist/auth/cookies.d.ts +39 -0
  3. package/dist/auth/cookies.d.ts.map +1 -1
  4. package/dist/auth/index.js +3 -3
  5. package/dist/{chunk-GPPVYLEI.js → chunk-45FA7Z55.js} +1 -1
  6. package/dist/{chunk-REDDTTGJ.js → chunk-4Y2JQQZL.js} +19 -1
  7. package/dist/chunk-5GFACGSN.js +6 -0
  8. package/dist/chunk-5KSAS4VY.js +275 -0
  9. package/dist/{chunk-Z7W3DQTR.js → chunk-7SAAJVDE.js} +408 -28
  10. package/dist/{chunk-S2L3BPLS.js → chunk-7SX42OP4.js} +1 -1
  11. package/dist/{chunk-GGXZZ77Z.js → chunk-AASEGXNT.js} +18 -1
  12. package/dist/{chunk-WKZ7OENC.js → chunk-BQ5TZZUE.js} +5 -1
  13. package/dist/chunk-DIMDRBFK.js +11 -0
  14. package/dist/{chunk-WUJ3XNFI.js → chunk-GGYY3JQJ.js} +19 -24
  15. package/dist/{chunk-NHN2LSIU.js → chunk-IP2SEBAJ.js} +5 -1
  16. package/dist/{chunk-IINIZBQB.js → chunk-JCDZAFX2.js} +30 -10
  17. package/dist/chunk-JR24U7FJ.js +120 -0
  18. package/dist/{chunk-SW6PCQQD.js → chunk-KQA772WQ.js} +2 -1
  19. package/dist/{chunk-KR2MHTNJ.js → chunk-N3G7DOU3.js} +609 -61
  20. package/dist/chunk-OAGJBCSH.js +67 -0
  21. package/dist/{chunk-6BZ2KNTP.js → chunk-PLANHXS7.js} +191 -61
  22. package/dist/{chunk-VWWY5TL5.js → chunk-PNSCDBU7.js} +38 -56
  23. package/dist/{chunk-AHALSCQH.js → chunk-PTY2RG2K.js} +1 -1
  24. package/dist/{chunk-BDFTMGCL.js → chunk-PZPGJZRG.js} +9 -8
  25. package/dist/{chunk-FRE2CCLV.js → chunk-QHMKIODQ.js} +17 -4
  26. package/dist/{chunk-HBULBDGP.js → chunk-S37DZPHW.js} +1 -1
  27. package/dist/{chunk-4I5OO5UB.js → chunk-TEOAKGZA.js} +0 -1
  28. package/dist/chunk-VUYCRBJK.js +174 -0
  29. package/dist/{chunk-4RNF2DRG.js → chunk-XDEPXTBY.js} +0 -2
  30. package/dist/{chunk-DS4GCYV2.js → chunk-Y5QECJX7.js} +223 -16
  31. package/dist/collab/rebase.d.ts +2 -1
  32. package/dist/collab/rebase.d.ts.map +1 -1
  33. package/dist/collab/rebase.js +20 -5
  34. package/dist/components/brandguide/ColorsEditable.d.ts.map +1 -1
  35. package/dist/components/chat/ChatPanel.d.ts.map +1 -1
  36. package/dist/components/chat/ChatWidget.d.ts.map +1 -1
  37. package/dist/components/docfile/DocFilePane.d.ts.map +1 -1
  38. package/dist/components/editor/ChildBlockWrapper.d.ts.map +1 -1
  39. package/dist/components/editor/ConflictDialog.d.ts.map +1 -1
  40. package/dist/components/editor/DragHandle.d.ts +12 -2
  41. package/dist/components/editor/DragHandle.d.ts.map +1 -1
  42. package/dist/components/editor/DropEdgeIndicator.d.ts +15 -6
  43. package/dist/components/editor/DropEdgeIndicator.d.ts.map +1 -1
  44. package/dist/components/editor/NavGroupRow.d.ts +4 -1
  45. package/dist/components/editor/NavGroupRow.d.ts.map +1 -1
  46. package/dist/components/editor/PagesModal.d.ts.map +1 -1
  47. package/dist/components/editor/SectionWrapper.d.ts +1 -1
  48. package/dist/components/editor/SectionWrapper.d.ts.map +1 -1
  49. package/dist/components/editor/SettingsForm.d.ts.map +1 -1
  50. package/dist/components/editor/TagIndicator.d.ts +15 -0
  51. package/dist/components/editor/TagIndicator.d.ts.map +1 -0
  52. package/dist/components/editor/index.js +148 -77
  53. package/dist/components/primitives/DocumentDropZone.d.ts +1 -1
  54. package/dist/components/primitives/EditableGrid.d.ts.map +1 -1
  55. package/dist/components/primitives/EditableList.d.ts.map +1 -1
  56. package/dist/components/primitives/HeadingSection.d.ts +4 -1
  57. package/dist/components/primitives/HeadingSection.d.ts.map +1 -1
  58. package/dist/components/primitives/ResolvedMedia.d.ts +5 -1
  59. package/dist/components/primitives/ResolvedMedia.d.ts.map +1 -1
  60. package/dist/components/primitives/{defineHeadingSection.d.ts → define-heading-section.d.ts} +1 -1
  61. package/dist/components/primitives/define-heading-section.d.ts.map +1 -0
  62. package/dist/components/primitives/{gridDrop.d.ts → grid-drop.d.ts} +1 -1
  63. package/dist/components/primitives/grid-drop.d.ts.map +1 -0
  64. package/dist/components/primitives/index.d.ts +1 -1
  65. package/dist/components/primitives/index.d.ts.map +1 -1
  66. package/dist/components/primitives/index.js +17 -154
  67. package/dist/components/sections/Button/CTAButton.d.ts.map +1 -1
  68. package/dist/components/sections/DoDontMedia/DoDontMediaBlock.d.ts +2 -6
  69. package/dist/components/sections/DoDontMedia/DoDontMediaBlock.d.ts.map +1 -1
  70. package/dist/components/sections/DoDontMedia/index.d.ts +8 -0
  71. package/dist/components/sections/DoDontMedia/index.d.ts.map +1 -1
  72. package/dist/components/sections/IconList/IconList.d.ts +2 -1
  73. package/dist/components/sections/IconList/IconList.d.ts.map +1 -1
  74. package/dist/components/sections/IconList/IconListSettings.d.ts +2 -1
  75. package/dist/components/sections/IconList/IconListSettings.d.ts.map +1 -1
  76. package/dist/components/sections/IconList/index.d.ts +1 -0
  77. package/dist/components/sections/IconList/index.d.ts.map +1 -1
  78. package/dist/components/sections/Media/MediaBlock.d.ts +2 -1
  79. package/dist/components/sections/Media/MediaBlock.d.ts.map +1 -1
  80. package/dist/components/sections/Media/index.d.ts +8 -0
  81. package/dist/components/sections/Media/index.d.ts.map +1 -1
  82. package/dist/components/sections/Reference/ReferenceList.d.ts +35 -0
  83. package/dist/components/sections/Reference/ReferenceList.d.ts.map +1 -0
  84. package/dist/components/sections/Reference/ReferenceSettings.d.ts +12 -0
  85. package/dist/components/sections/Reference/ReferenceSettings.d.ts.map +1 -0
  86. package/dist/components/sections/Reference/index.d.ts +9 -0
  87. package/dist/components/sections/Reference/index.d.ts.map +1 -0
  88. package/dist/components/sections/SectionSummary/SectionSummaryBlock.d.ts +38 -0
  89. package/dist/components/sections/SectionSummary/SectionSummaryBlock.d.ts.map +1 -0
  90. package/dist/components/sections/SectionSummary/SectionSummarySettings.d.ts +20 -0
  91. package/dist/components/sections/SectionSummary/SectionSummarySettings.d.ts.map +1 -0
  92. package/dist/components/sections/SectionSummary/index.d.ts +33 -0
  93. package/dist/components/sections/SectionSummary/index.d.ts.map +1 -0
  94. package/dist/components/sections/all-sections.d.ts +53 -0
  95. package/dist/components/sections/all-sections.d.ts.map +1 -1
  96. package/dist/components/sections/brand-guide.d.ts +41 -2
  97. package/dist/components/sections/brand-guide.d.ts.map +1 -1
  98. package/dist/components/sections/brand-guide.js +12 -10
  99. package/dist/components/sections/core-sections.d.ts +14 -0
  100. package/dist/components/sections/core-sections.d.ts.map +1 -1
  101. package/dist/components/sections/register-schemas.js +19 -17
  102. package/dist/components/sections/register.js +19 -17
  103. package/dist/components/shared/BrandSwatchesContext.d.ts +9 -0
  104. package/dist/components/shared/BrandSwatchesContext.d.ts.map +1 -0
  105. package/dist/components/shared/Button.d.ts +11 -3
  106. package/dist/components/shared/Button.d.ts.map +1 -1
  107. package/dist/components/shared/Checkbox.d.ts +6 -0
  108. package/dist/components/shared/Checkbox.d.ts.map +1 -1
  109. package/dist/components/shared/ConfirmDialog.d.ts +25 -0
  110. package/dist/components/shared/ConfirmDialog.d.ts.map +1 -0
  111. package/dist/components/shared/ConfirmInline.d.ts +25 -0
  112. package/dist/components/shared/ConfirmInline.d.ts.map +1 -0
  113. package/dist/components/shared/ErrorBoundary.d.ts.map +1 -1
  114. package/dist/components/shared/Fab.d.ts +11 -2
  115. package/dist/components/shared/Fab.d.ts.map +1 -1
  116. package/dist/components/shared/FabStack.d.ts +22 -0
  117. package/dist/components/shared/FabStack.d.ts.map +1 -0
  118. package/dist/components/shared/HistoryChangesModal.d.ts.map +1 -1
  119. package/dist/components/shared/HistoryPopover.d.ts.map +1 -1
  120. package/dist/components/shared/ImageBackgroundField.d.ts +9 -0
  121. package/dist/components/shared/ImageBackgroundField.d.ts.map +1 -0
  122. package/dist/components/shared/{UploadRejectionAlert.d.ts → InlineAlert.d.ts} +4 -2
  123. package/dist/components/shared/InlineAlert.d.ts.map +1 -0
  124. package/dist/components/shared/Input.d.ts +10 -0
  125. package/dist/components/shared/Input.d.ts.map +1 -1
  126. package/dist/components/shared/Navigation.d.ts.map +1 -1
  127. package/dist/components/shared/Notice.d.ts +10 -0
  128. package/dist/components/shared/Notice.d.ts.map +1 -0
  129. package/dist/components/shared/Popover.d.ts.map +1 -1
  130. package/dist/components/shared/ReferenceSourcesContext.d.ts +25 -0
  131. package/dist/components/shared/ReferenceSourcesContext.d.ts.map +1 -0
  132. package/dist/components/shared/Select.d.ts +3 -2
  133. package/dist/components/shared/Select.d.ts.map +1 -1
  134. package/dist/components/shared/SplitButton.d.ts.map +1 -1
  135. package/dist/components/shared/SummaryAnchorsContext.d.ts +10 -0
  136. package/dist/components/shared/SummaryAnchorsContext.d.ts.map +1 -0
  137. package/dist/components/shared/Textarea.d.ts +7 -0
  138. package/dist/components/shared/Textarea.d.ts.map +1 -1
  139. package/dist/components/shared/ToolbarFrame.d.ts +17 -0
  140. package/dist/components/shared/ToolbarFrame.d.ts.map +1 -0
  141. package/dist/components/shared/copy.d.ts +13 -0
  142. package/dist/components/shared/copy.d.ts.map +1 -0
  143. package/dist/components/shared/icons.d.ts +0 -12
  144. package/dist/components/shared/icons.d.ts.map +1 -1
  145. package/dist/components/shell/AudienceDetailsPanel.d.ts +7 -1
  146. package/dist/components/shell/AudienceDetailsPanel.d.ts.map +1 -1
  147. package/dist/components/shell/AudienceRow.d.ts.map +1 -1
  148. package/dist/components/shell/BugReportFAB.d.ts +7 -1
  149. package/dist/components/shell/BugReportFAB.d.ts.map +1 -1
  150. package/dist/components/shell/CredentialList.d.ts.map +1 -1
  151. package/dist/components/shell/DocInspector.d.ts.map +1 -1
  152. package/dist/components/shell/DocumentEditorOverlay.d.ts.map +1 -1
  153. package/dist/components/shell/EditorContext.d.ts +4 -0
  154. package/dist/components/shell/EditorContext.d.ts.map +1 -1
  155. package/dist/components/shell/EditorLoginForm.d.ts.map +1 -1
  156. package/dist/components/shell/EditorModal.d.ts.map +1 -1
  157. package/dist/components/shell/EditorShell.d.ts.map +1 -1
  158. package/dist/components/shell/HistoryToolbar.d.ts.map +1 -1
  159. package/dist/components/shell/MediaFolderTile.d.ts +4 -1
  160. package/dist/components/shell/MediaFolderTile.d.ts.map +1 -1
  161. package/dist/components/shell/MediaLibraryModal.d.ts.map +1 -1
  162. package/dist/components/shell/RestoreModal.d.ts.map +1 -1
  163. package/dist/components/shell/SiteSettingsChatbot.d.ts.map +1 -1
  164. package/dist/components/shell/SiteSettingsDisplay.d.ts.map +1 -1
  165. package/dist/components/shell/SiteSettingsModal.d.ts +3 -1
  166. package/dist/components/shell/SiteSettingsModal.d.ts.map +1 -1
  167. package/dist/components/shell/SiteSettingsTags.d.ts +13 -0
  168. package/dist/components/shell/SiteSettingsTags.d.ts.map +1 -0
  169. package/dist/components/shell/SiteSettingsUsers.d.ts.map +1 -1
  170. package/dist/components/shell/ViewerLoginForm.d.ts.map +1 -1
  171. package/dist/components/shell/{blockMoveDispatch.d.ts → block-move-dispatch.d.ts} +1 -1
  172. package/dist/components/shell/block-move-dispatch.d.ts.map +1 -0
  173. package/dist/components/shell/reference-sources.d.ts +22 -0
  174. package/dist/components/shell/reference-sources.d.ts.map +1 -0
  175. package/dist/deck/index.d.ts +2 -2
  176. package/dist/deck/index.d.ts.map +1 -1
  177. package/dist/deck/index.js +1 -3
  178. package/dist/deck-builder/index.js +11 -18
  179. package/dist/document/index.d.ts +2 -2
  180. package/dist/document/index.d.ts.map +1 -1
  181. package/dist/hooks/useBlockDnd.d.ts +11 -0
  182. package/dist/hooks/useBlockDnd.d.ts.map +1 -1
  183. package/dist/hooks/useBuildStatus.d.ts +1 -1
  184. package/dist/hooks/useBuildStatus.d.ts.map +1 -1
  185. package/dist/hooks/useDocumentUploads.d.ts +1 -1
  186. package/dist/hooks/useDocumentUploads.d.ts.map +1 -1
  187. package/dist/hooks/useEditorPersistence.d.ts +5 -1
  188. package/dist/hooks/useEditorPersistence.d.ts.map +1 -1
  189. package/dist/hooks/useEditorPublish.d.ts +2 -2
  190. package/dist/hooks/useEditorPublish.d.ts.map +1 -1
  191. package/dist/hooks/useNotice.d.ts +19 -0
  192. package/dist/hooks/useNotice.d.ts.map +1 -0
  193. package/dist/hooks/useSortableNavRow.d.ts +11 -0
  194. package/dist/hooks/useSortableNavRow.d.ts.map +1 -1
  195. package/dist/hooks/useSortableRow.d.ts +11 -0
  196. package/dist/hooks/useSortableRow.d.ts.map +1 -1
  197. package/dist/hooks/useStackedEscape.d.ts +32 -0
  198. package/dist/hooks/useStackedEscape.d.ts.map +1 -0
  199. package/dist/index.js +67 -49
  200. package/dist/lib/audience-secret.js +2 -2
  201. package/dist/lib/block-dnd.d.ts +5 -0
  202. package/dist/lib/block-dnd.d.ts.map +1 -1
  203. package/dist/lib/block-move.d.ts.map +1 -1
  204. package/dist/lib/brand-swatches.d.ts +12 -0
  205. package/dist/lib/brand-swatches.d.ts.map +1 -0
  206. package/dist/lib/container-ops.d.ts +3 -2
  207. package/dist/lib/container-ops.d.ts.map +1 -1
  208. package/dist/lib/dexie.d.ts.map +1 -1
  209. package/dist/lib/dexie.js +9 -1
  210. package/dist/lib/env.d.ts +4 -0
  211. package/dist/lib/env.d.ts.map +1 -1
  212. package/dist/lib/env.js +5 -3
  213. package/dist/lib/events.d.ts +1 -2
  214. package/dist/lib/events.d.ts.map +1 -1
  215. package/dist/lib/image-background.d.ts +8 -0
  216. package/dist/lib/image-background.d.ts.map +1 -0
  217. package/dist/lib/index-ops.js +3 -3
  218. package/dist/lib/index.d.ts +6 -4
  219. package/dist/lib/index.d.ts.map +1 -1
  220. package/dist/lib/index.js +29 -21
  221. package/dist/lib/loader.d.ts +21 -7
  222. package/dist/lib/loader.d.ts.map +1 -1
  223. package/dist/lib/nav-tree.d.ts +1 -0
  224. package/dist/lib/nav-tree.d.ts.map +1 -1
  225. package/dist/lib/nav.d.ts +38 -0
  226. package/dist/lib/nav.d.ts.map +1 -1
  227. package/dist/lib/platform-broker.d.ts +4 -2
  228. package/dist/lib/platform-broker.d.ts.map +1 -1
  229. package/dist/lib/platform-broker.js +21 -13
  230. package/dist/lib/reference-type.d.ts +2 -0
  231. package/dist/lib/reference-type.d.ts.map +1 -0
  232. package/dist/lib/reference-validation.d.ts +31 -0
  233. package/dist/lib/reference-validation.d.ts.map +1 -0
  234. package/dist/lib/reference-validation.js +39 -0
  235. package/dist/lib/references.d.ts +74 -0
  236. package/dist/lib/references.d.ts.map +1 -0
  237. package/dist/lib/registry.d.ts +35 -0
  238. package/dist/lib/registry.d.ts.map +1 -1
  239. package/dist/lib/{safeRedirect.d.ts → safe-redirect.d.ts} +1 -1
  240. package/dist/lib/safe-redirect.d.ts.map +1 -0
  241. package/dist/lib/tags.d.ts +58 -0
  242. package/dist/lib/tags.d.ts.map +1 -0
  243. package/dist/lib/theme-vars.d.ts +9 -0
  244. package/dist/lib/theme-vars.d.ts.map +1 -1
  245. package/dist/media/index.d.ts +1 -1
  246. package/dist/media/index.d.ts.map +1 -1
  247. package/dist/media/index.js +8 -4
  248. package/dist/media/utils.d.ts +2 -0
  249. package/dist/media/utils.d.ts.map +1 -1
  250. package/dist/media/{videoPoster.d.ts → video-poster.d.ts} +1 -1
  251. package/dist/media/video-poster.d.ts.map +1 -0
  252. package/dist/schemas/audience.js +2 -2
  253. package/dist/schemas/index.d.ts +1 -0
  254. package/dist/schemas/index.d.ts.map +1 -1
  255. package/dist/schemas/index.js +14 -8
  256. package/dist/schemas/shared.d.ts +26 -0
  257. package/dist/schemas/shared.d.ts.map +1 -1
  258. package/dist/schemas/site-config.d.ts +6 -0
  259. package/dist/schemas/site-config.d.ts.map +1 -1
  260. package/dist/schemas/tags.d.ts +27 -0
  261. package/dist/schemas/tags.d.ts.map +1 -0
  262. package/dist/storage/index.js +1 -1
  263. package/dist/types/database.d.ts.map +1 -1
  264. package/package.json +6 -1
  265. package/src/auth/cookies.ts +111 -0
  266. package/src/collab/rebase.ts +26 -2
  267. package/src/components/brandguide/ColorSwatchSettings.tsx +1 -1
  268. package/src/components/brandguide/ColorsEditable.tsx +1 -0
  269. package/src/components/chat/ChatPanel.tsx +2 -3
  270. package/src/components/chat/ChatWidget.tsx +2 -3
  271. package/src/components/docfile/DocFilePane.tsx +4 -12
  272. package/src/components/editor/ChildBlockWrapper.tsx +13 -19
  273. package/src/components/editor/ConflictDialog.tsx +27 -35
  274. package/src/components/editor/DeleteButton.tsx +2 -2
  275. package/src/components/editor/DragHandle.tsx +16 -3
  276. package/src/components/editor/DropEdgeIndicator.tsx +36 -10
  277. package/src/components/editor/InsertButton.tsx +2 -2
  278. package/src/components/editor/NavGroupRow.tsx +43 -14
  279. package/src/components/editor/PagesModal.tsx +99 -26
  280. package/src/components/editor/SectionWrapper.tsx +64 -23
  281. package/src/components/editor/SettingsButton.tsx +2 -2
  282. package/src/components/editor/SettingsForm.tsx +19 -2
  283. package/src/components/editor/StatusIndicator.tsx +4 -4
  284. package/src/components/editor/TagIndicator.tsx +80 -0
  285. package/src/components/primitives/DocumentDropZone.tsx +1 -1
  286. package/src/components/primitives/EditableGrid.tsx +15 -14
  287. package/src/components/primitives/EditableList.tsx +27 -74
  288. package/src/components/primitives/HeadingSection.tsx +5 -2
  289. package/src/components/primitives/ResolvedMedia.tsx +13 -0
  290. package/src/components/primitives/{defineHeadingSection.tsx → define-heading-section.tsx} +1 -0
  291. package/src/components/primitives/index.ts +1 -1
  292. package/src/components/primitives/useEditablePlainText.ts +5 -5
  293. package/src/components/sections/Button/CTAButton.tsx +3 -1
  294. package/src/components/sections/DoDontMedia/DoDontMediaBlock.tsx +4 -4
  295. package/src/components/sections/DoDontMedia/index.tsx +12 -3
  296. package/src/components/sections/IconList/DoDontToggle.tsx +2 -2
  297. package/src/components/sections/IconList/IconList.tsx +70 -37
  298. package/src/components/sections/IconList/IconListSettings.tsx +55 -17
  299. package/src/components/sections/IconList/index.tsx +10 -3
  300. package/src/components/sections/LinkHeading/index.tsx +1 -1
  301. package/src/components/sections/Media/MediaBlock.tsx +13 -2
  302. package/src/components/sections/Media/index.tsx +12 -4
  303. package/src/components/sections/Reference/ReferenceList.tsx +214 -0
  304. package/src/components/sections/Reference/ReferenceSettings.tsx +159 -0
  305. package/src/components/sections/Reference/index.tsx +47 -0
  306. package/src/components/sections/SectionSummary/SectionSummaryBlock.tsx +476 -0
  307. package/src/components/sections/SectionSummary/SectionSummarySettings.tsx +126 -0
  308. package/src/components/sections/SectionSummary/index.tsx +149 -0
  309. package/src/components/sections/SubHeading/index.tsx +1 -1
  310. package/src/components/sections/SubSubHeading/index.tsx +1 -1
  311. package/src/components/sections/brand-guide.ts +4 -3
  312. package/src/components/sections/core-sections.ts +2 -0
  313. package/src/components/shared/BrandSwatchesContext.ts +13 -0
  314. package/src/components/shared/Button.tsx +47 -34
  315. package/src/components/shared/Checkbox.tsx +21 -12
  316. package/src/components/shared/ConfirmDialog.tsx +87 -0
  317. package/src/components/shared/ConfirmInline.tsx +90 -0
  318. package/src/components/shared/ErrorBoundary.tsx +6 -18
  319. package/src/components/shared/Fab.tsx +15 -5
  320. package/src/components/shared/FabStack.tsx +28 -0
  321. package/src/components/shared/HistoryChangesModal.tsx +43 -41
  322. package/src/components/shared/HistoryPopover.tsx +7 -20
  323. package/src/components/shared/ImageBackgroundField.tsx +92 -0
  324. package/src/components/shared/{UploadRejectionAlert.tsx → InlineAlert.tsx} +21 -5
  325. package/src/components/shared/Input.tsx +23 -7
  326. package/src/components/shared/Navigation.tsx +22 -17
  327. package/src/components/shared/Notice.tsx +59 -0
  328. package/src/components/shared/Popover.tsx +7 -11
  329. package/src/components/shared/ReferenceSourcesContext.ts +27 -0
  330. package/src/components/shared/Select.tsx +5 -2
  331. package/src/components/shared/SplitButton.tsx +11 -17
  332. package/src/components/shared/SummaryAnchorsContext.ts +11 -0
  333. package/src/components/shared/Textarea.tsx +17 -2
  334. package/src/components/shared/ToolbarFrame.tsx +28 -0
  335. package/src/components/shared/VideoSourceField.tsx +1 -1
  336. package/src/components/shared/copy.ts +18 -0
  337. package/src/components/shared/icons.tsx +1 -17
  338. package/src/components/shell/AudienceAddForm.tsx +2 -2
  339. package/src/components/shell/AudienceDetailsPanel.tsx +23 -5
  340. package/src/components/shell/AudienceRow.tsx +22 -15
  341. package/src/components/shell/BugReportFAB.tsx +50 -40
  342. package/src/components/shell/CredentialList.tsx +33 -13
  343. package/src/components/shell/DocInspector.tsx +3 -12
  344. package/src/components/shell/DocumentEditorOverlay.tsx +33 -23
  345. package/src/components/shell/EditorContext.tsx +7 -0
  346. package/src/components/shell/EditorLoginForm.tsx +26 -36
  347. package/src/components/shell/EditorModal.tsx +8 -10
  348. package/src/components/shell/EditorShell.tsx +499 -339
  349. package/src/components/shell/HistoryToolbar.tsx +24 -26
  350. package/src/components/shell/MediaFolderTile.tsx +8 -3
  351. package/src/components/shell/MediaLibraryModal.tsx +59 -60
  352. package/src/components/shell/ProcessingIndicator.tsx +2 -2
  353. package/src/components/shell/RestoreModal.tsx +17 -16
  354. package/src/components/shell/SetPasswordForm.tsx +3 -3
  355. package/src/components/shell/SiteSettingsChatbot.tsx +61 -42
  356. package/src/components/shell/SiteSettingsDisplay.tsx +10 -20
  357. package/src/components/shell/SiteSettingsModal.tsx +24 -2
  358. package/src/components/shell/SiteSettingsTags.tsx +143 -0
  359. package/src/components/shell/SiteSettingsUsers.tsx +36 -26
  360. package/src/components/shell/SiteSettingsViewerAccess.tsx +2 -2
  361. package/src/components/shell/ViewerLoginForm.tsx +5 -4
  362. package/src/components/shell/reference-sources.ts +36 -0
  363. package/src/deck/index.ts +2 -2
  364. package/src/document/index.ts +2 -2
  365. package/src/hooks/useBlockDnd.ts +11 -0
  366. package/src/hooks/useBuildStatus.ts +53 -6
  367. package/src/hooks/useDocumentUploads.ts +1 -1
  368. package/src/hooks/useEditorPersistence.ts +141 -35
  369. package/src/hooks/useEditorPublish.ts +18 -27
  370. package/src/hooks/useMediaPipeline.ts +1 -1
  371. package/src/hooks/useNotice.ts +45 -0
  372. package/src/hooks/useSortableNavRow.ts +11 -0
  373. package/src/hooks/useSortableRow.ts +22 -2
  374. package/src/hooks/useStackedEscape.ts +62 -0
  375. package/src/lib/block-dnd.ts +6 -1
  376. package/src/lib/block-move.ts +3 -0
  377. package/src/lib/brand-swatches.ts +32 -0
  378. package/src/lib/container-ops.ts +5 -3
  379. package/src/lib/dexie.ts +15 -1
  380. package/src/lib/env.ts +9 -0
  381. package/src/lib/events.ts +1 -2
  382. package/src/lib/image-background.ts +31 -0
  383. package/src/lib/index.ts +5 -4
  384. package/src/lib/loader.ts +54 -55
  385. package/src/lib/nav-tree.ts +71 -0
  386. package/src/lib/nav.ts +196 -36
  387. package/src/lib/platform-broker.ts +32 -14
  388. package/src/lib/reference-type.ts +3 -0
  389. package/src/lib/reference-validation.ts +69 -0
  390. package/src/lib/references.ts +130 -0
  391. package/src/lib/registry.ts +37 -0
  392. package/src/lib/tags.ts +148 -0
  393. package/src/lib/theme-vars.ts +23 -0
  394. package/src/media/index.ts +1 -1
  395. package/src/media/utils.ts +18 -0
  396. package/src/media/{videoPoster.ts → video-poster.ts} +2 -2
  397. package/src/schemas/index.ts +1 -0
  398. package/src/schemas/shared.ts +24 -1
  399. package/src/schemas/site-config.ts +6 -1
  400. package/src/schemas/tags.ts +33 -0
  401. package/src/storage/types.ts +1 -1
  402. package/src/types/database.ts +2 -0
  403. package/dist/chunk-DNY72JZN.js +0 -86
  404. package/dist/chunk-F2LYQPXV.js +0 -77
  405. package/dist/chunk-JH762VUH.js +0 -128
  406. package/dist/chunk-K7DYZXVU.js +0 -138
  407. package/dist/components/primitives/defineHeadingSection.d.ts.map +0 -1
  408. package/dist/components/primitives/gridDrop.d.ts.map +0 -1
  409. package/dist/components/shared/UploadRejectionAlert.d.ts.map +0 -1
  410. package/dist/components/shell/BuildStatusIndicator.d.ts +0 -7
  411. package/dist/components/shell/BuildStatusIndicator.d.ts.map +0 -1
  412. package/dist/components/shell/blockMoveDispatch.d.ts.map +0 -1
  413. package/dist/hooks/index.d.ts +0 -9
  414. package/dist/hooks/index.d.ts.map +0 -1
  415. package/dist/lib/grid.d.ts +0 -2
  416. package/dist/lib/grid.d.ts.map +0 -1
  417. package/dist/lib/safeRedirect.d.ts.map +0 -1
  418. package/dist/media/videoPoster.d.ts.map +0 -1
  419. package/src/components/brandguide/ColorSwatchSettings.d.ts.map +0 -1
  420. package/src/components/brandguide/Colors.d.ts.map +0 -1
  421. package/src/components/primitives/CustomParagraph.d.ts.map +0 -1
  422. package/src/components/primitives/EditableGrid.d.ts.map +0 -1
  423. package/src/components/primitives/EditablePlainText.d.ts.map +0 -1
  424. package/src/components/primitives/EditableRichText.d.ts.map +0 -1
  425. package/src/components/primitives/HeadingSection.d.ts.map +0 -1
  426. package/src/components/primitives/IconPicker.d.ts.map +0 -1
  427. package/src/components/primitives/LinkPopover.d.ts.map +0 -1
  428. package/src/components/primitives/MediaSettingsForms.d.ts.map +0 -1
  429. package/src/components/primitives/ResolvedMedia.d.ts.map +0 -1
  430. package/src/components/primitives/RichTextToolbar.d.ts.map +0 -1
  431. package/src/components/primitives/tiptap-presets.d.ts.map +0 -1
  432. package/src/components/primitives/useEditableCollection.d.ts.map +0 -1
  433. package/src/components/primitives/useEditablePlainText.d.ts.map +0 -1
  434. package/src/components/primitives/useEditableRichText.d.ts.map +0 -1
  435. package/src/components/sections/Button/CTAButton.d.ts.map +0 -1
  436. package/src/components/sections/Button/index.d.ts.map +0 -1
  437. package/src/components/sections/Colors/index.d.ts.map +0 -1
  438. package/src/components/sections/IconList/IconList.d.ts.map +0 -1
  439. package/src/components/sections/IconList/IconListSettings.d.ts.map +0 -1
  440. package/src/components/sections/IconList/index.d.ts.map +0 -1
  441. package/src/components/sections/LinkHeading/index.d.ts.map +0 -1
  442. package/src/components/sections/Prose/Prose.d.ts.map +0 -1
  443. package/src/components/sections/Prose/index.d.ts.map +0 -1
  444. package/src/components/sections/SubHeading/index.d.ts.map +0 -1
  445. package/src/components/sections/SubSubHeading/index.d.ts.map +0 -1
  446. package/src/components/sections/register-schemas.d.ts.map +0 -1
  447. package/src/components/shared/Button.d.ts.map +0 -1
  448. package/src/components/shared/Checkbox.d.ts.map +0 -1
  449. package/src/components/shared/FormLabel.d.ts.map +0 -1
  450. package/src/components/shared/IconButton.d.ts.map +0 -1
  451. package/src/components/shared/Input.d.ts.map +0 -1
  452. package/src/components/shared/Select.d.ts.map +0 -1
  453. package/src/components/shared/icons.d.ts.map +0 -1
  454. package/src/components/shell/BuildStatusIndicator.tsx +0 -59
  455. package/src/components/shell/MediaLibraryContext.d.ts.map +0 -1
  456. package/src/hooks/index.ts +0 -8
  457. package/src/hooks/useResolvedMedia.d.ts.map +0 -1
  458. package/src/lib/cn.d.ts.map +0 -1
  459. package/src/lib/grid.d.ts.map +0 -1
  460. package/src/lib/grid.ts +0 -7
  461. package/src/lib/icons.d.ts.map +0 -1
  462. package/src/lib/loader.d.ts.map +0 -1
  463. package/src/lib/nav.d.ts.map +0 -1
  464. package/src/lib/registry.d.ts.map +0 -1
  465. package/src/lib/sanitize.d.ts.map +0 -1
  466. package/src/media/github.d.ts.map +0 -1
  467. package/src/media/index.d.ts.map +0 -1
  468. package/src/media/queue.d.ts.map +0 -1
  469. package/src/media/resolve.d.ts.map +0 -1
  470. package/src/media/types.d.ts.map +0 -1
  471. package/src/media/utils.d.ts.map +0 -1
  472. package/src/media/videoPoster.d.ts.map +0 -1
  473. package/src/schemas/auth.d.ts.map +0 -1
  474. package/src/schemas/media-grid-options.d.ts.map +0 -1
  475. package/src/schemas/media.d.ts.map +0 -1
  476. package/src/schemas/sections.d.ts.map +0 -1
  477. package/src/schemas/shared.d.ts.map +0 -1
  478. package/src/schemas/site-config.d.ts.map +0 -1
  479. /package/dist/{chunk-B7VBSE5X.js → chunk-3JDTXOIG.js} +0 -0
  480. /package/dist/{chunk-CXZ5YJG4.js → chunk-L45X7DKX.js} +0 -0
  481. /package/src/components/primitives/{gridDrop.ts → grid-drop.ts} +0 -0
  482. /package/src/components/shell/{blockMoveDispatch.ts → block-move-dispatch.ts} +0 -0
  483. /package/src/lib/{safeRedirect.ts → safe-redirect.ts} +0 -0
@@ -0,0 +1,32 @@
1
+ import type { Section } from "../schemas/sections";
2
+ import type { ColorItem } from "../components/sections/Colors/schema";
3
+
4
+ export interface BrandSwatch {
5
+ hex: string;
6
+ name: string | undefined;
7
+ }
8
+
9
+ type Block = { type?: unknown; content?: unknown };
10
+
11
+ /**
12
+ * Every hex value from every Colors section, walking container children, in
13
+ * page order and deduped case-insensitively. Swatches are offered as plain hex
14
+ * values: color items carry no stable id, so a picked swatch is copied, not linked.
15
+ */
16
+ export function collectBrandSwatches(sections: Section[]): BrandSwatch[] {
17
+ const seen = new Map<string, BrandSwatch>();
18
+ const visit = (block: Block) => {
19
+ const content = (block.content ?? {}) as { colors?: unknown; children?: unknown };
20
+ if (block.type === "colors" && Array.isArray(content.colors)) {
21
+ for (const item of content.colors as ColorItem[]) {
22
+ for (const space of item.spaces) {
23
+ const hex = space.hex?.toLowerCase();
24
+ if (hex && !seen.has(hex)) seen.set(hex, { hex, name: item.name });
25
+ }
26
+ }
27
+ }
28
+ if (Array.isArray(content.children)) (content.children as Block[]).forEach(visit);
29
+ };
30
+ sections.forEach(visit);
31
+ return [...seen.values()];
32
+ }
@@ -2,6 +2,7 @@ import type { ReactNode } from "react";
2
2
  import type { Section } from "../schemas/sections";
3
3
  import { getAllSections } from "./registry";
4
4
  import { sortInsertMenuOptions } from "./insert-menu";
5
+ import { REFERENCE_TYPE } from "./reference-type";
5
6
 
6
7
  interface ContainerContent {
7
8
  columns: number;
@@ -37,13 +38,14 @@ export function reorderChild(container: Section, from: number, to: number): Sect
37
38
 
38
39
  /**
39
40
  * Block types insertable as a container CHILD — excludes `container` itself
40
- * (v1 enforces one level of container nesting). Shared so the depth guard has a
41
- * single, testable source of truth (the container toolbar's "+" picker uses it).
41
+ * (v1 enforces one level of container nesting) and `reference`, which only
42
+ * expands at the top level of a page. Shared so the depth guard has a single,
43
+ * testable source of truth (the container toolbar's "+" picker uses it).
42
44
  */
43
45
  export function childInsertableTypes(): { type: string; label: string; icon: ReactNode }[] {
44
46
  return sortInsertMenuOptions(
45
47
  getAllSections()
46
- .filter((d) => d.type !== "container" && d.type !== "spacer")
48
+ .filter((d) => d.type !== "container" && d.type !== "spacer" && d.type !== REFERENCE_TYPE)
47
49
  .map((d) => ({ type: d.type, label: d.label, icon: d.icon })),
48
50
  );
49
51
  }
package/src/lib/dexie.ts CHANGED
@@ -1,7 +1,13 @@
1
+ // The editor's offline draft store (IndexedDB via Dexie): unsaved section
2
+ // edits, the working index/nav, site config, pending media, and a sha-keyed
3
+ // cache of the last loaded content. Nothing here is authoritative — GitHub is.
4
+ // Schema versions only ever append; an EMPTY `version(N)` bump is intentional
5
+ // (see the per-version notes in the constructor), never a leftover to remove.
1
6
  import Dexie from "dexie";
2
7
  import type { SectionContent } from "../schemas/sections";
3
8
  import type { SiteIndex, SectionMeta, SiteConfig, Page } from "../schemas/site-config";
4
9
  import type { NavEntry } from "../schemas/nav";
10
+ import type { TagEntry } from "../schemas/tags";
5
11
  import type { LoadedSection } from "./loader";
6
12
  import type { MediaManifest, MediaItem } from "../media/types";
7
13
  import type { DocumentManifest, DocumentManifestEntry } from "../schemas/documents";
@@ -19,6 +25,9 @@ interface SiteIndexRow {
19
25
  sections: Record<string, SectionMeta>;
20
26
  /** Optional: rows written before page groups shipped have no nav. */
21
27
  nav?: NavEntry[];
28
+ /** Optional: rows written before section tags shipped have none. Restored
29
+ * as absent (not []), which writers read as "leave tags.json alone". */
30
+ tags?: TagEntry[];
22
31
  deletedSections: string[];
23
32
  updatedAt: string;
24
33
  }
@@ -311,7 +320,10 @@ export async function restoreLocalChanges(): Promise<{
311
320
  const siteId = metaRow?.siteId || getDb().siteId;
312
321
  return {
313
322
  sections,
314
- siteIndex: { siteId, pages: indexRow.pages, sections: indexRow.sections, nav: indexRow.nav },
323
+ siteIndex: {
324
+ siteId, pages: indexRow.pages, sections: indexRow.sections, nav: indexRow.nav,
325
+ ...(indexRow.tags !== undefined ? { tags: indexRow.tags } : {}),
326
+ },
315
327
  siteConfig: configRow?.config,
316
328
  deletedSections: indexRow.deletedSections ?? [],
317
329
  };
@@ -345,6 +357,7 @@ export async function persistSiteIndex(index: SiteIndex, deletedSections: string
345
357
  pages: index.pages,
346
358
  sections: index.sections,
347
359
  nav: index.nav,
360
+ tags: index.tags,
348
361
  deletedSections,
349
362
  updatedAt: now,
350
363
  });
@@ -465,6 +478,7 @@ export async function persistAll(
465
478
  pages: siteIndex.pages,
466
479
  sections: siteIndex.sections,
467
480
  nav: siteIndex.nav,
481
+ tags: siteIndex.tags,
468
482
  // Record the deletion INTENT, not just its effect on the order. The
469
483
  // restore path reads this back; when it was hardcoded `[]` that path
470
484
  // had to re-derive deletions by diffing this snapshot against a fresh
package/src/lib/env.ts CHANGED
@@ -1,3 +1,5 @@
1
+ // env() is a byte-for-byte twin of packages/platform/src/env.ts: platform cannot
2
+ // import primitives without dragging it into apps/admin's bundle, so keep both.
1
3
  declare global {
2
4
  interface ImportMeta {
3
5
  readonly env?: Record<string, string | undefined>;
@@ -11,3 +13,10 @@ export function env(key: string): string {
11
13
  ""
12
14
  );
13
15
  }
16
+
17
+ /** Vite replaces `import.meta.env.PROD` with a boolean literal at build time
18
+ * (packages are `noExternal`, so that covers this file too), which is why the
19
+ * declared string type above is bypassed here. False under Node and vitest. */
20
+ export function isProd(): boolean {
21
+ return (import.meta.env?.PROD as unknown) === true;
22
+ }
package/src/lib/events.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { NavItem, SiteNav } from "./nav";
1
+ import type { SiteNav } from "./nav";
2
2
 
3
3
  export interface TypedEvent<T> {
4
4
  dispatch(detail: T): void;
@@ -19,7 +19,6 @@ export function createEvent<T>(name: string): TypedEvent<T> {
19
19
  }
20
20
 
21
21
  export const editModeEvent = createEvent<{ isEditMode: boolean }>("editmodechange");
22
- export const navChangeEvent = createEvent<NavItem[]>("sitenavchange");
23
22
  export const darkModeEvent = createEvent<string>("sitedarkmode");
24
23
  export const siteNameChangeEvent = createEvent<string>("sitenamechange");
25
24
  export const historySelectEvent = createEvent<{ sha: string; date: string }>("history-select");
@@ -0,0 +1,31 @@
1
+ import type { CSSProperties } from "react";
2
+ import type { ImageBackground, ImageBackgroundSurface } from "../schemas/shared";
3
+
4
+ // Class names are spelled out in full so Tailwind's source scan emits them.
5
+ // The surface-light/-dark tokens deliberately do NOT flip under `.dark`: a dark
6
+ // plate chosen for light mode must stay dark. The per-mode switch comes from the
7
+ // `dark:` variant instead, so it follows the theme toggle with no JS. An unset
8
+ // dark mode clears a light-mode plate rather than inheriting it.
9
+ const LIGHT_MODE: Record<ImageBackgroundSurface, string | undefined> = {
10
+ none: undefined,
11
+ light: "bg-surface-light",
12
+ dark: "bg-surface-dark",
13
+ };
14
+ const DARK_MODE: Record<ImageBackgroundSurface, string | undefined> = {
15
+ none: "dark:bg-transparent",
16
+ light: "dark:bg-surface-light",
17
+ dark: "dark:bg-surface-dark",
18
+ };
19
+
20
+ export interface ImageBackdrop {
21
+ className?: string;
22
+ style?: CSSProperties;
23
+ }
24
+
25
+ export function imageBackdrop(bg: ImageBackground | undefined): ImageBackdrop | undefined {
26
+ if (!bg) return undefined;
27
+ if (bg.mode === "custom") return { style: { backgroundColor: bg.color } };
28
+ const light = LIGHT_MODE[bg.light];
29
+ if (!light && bg.dark === "none") return undefined;
30
+ return { className: [light, DARK_MODE[bg.dark]].filter(Boolean).join(" ") };
31
+ }
package/src/lib/index.ts CHANGED
@@ -4,6 +4,9 @@ export { audiencePasses } from "./pages";
4
4
  export { resolveChatbotName, resolveThinkingPhrases, DEFAULT_CHATBOT_NAME } from "./chatbot";
5
5
  export { generateNavLinks, toSectionId, type NavItem } from "./nav";
6
6
  export { navEntries, pagesInNavOrder, groupOfPage, groupDisplayLabel, reconcileNav } from "./nav-tree";
7
+ export { siteTags, tagDisplayLabel, reconcileTags } from "./tags";
8
+ export { resolveReference, type ReferenceContent } from "./references";
9
+ export { REFERENCE_TYPE } from "./reference-type";
7
10
  export {
8
11
  deriveContrast,
9
12
  contrastColor,
@@ -15,12 +18,11 @@ export {
15
18
  } from "./contrast";
16
19
  export { primaryVarValue, accentVarValue, onPrimaryVarValue } from "./theme-vars";
17
20
  export { sanitizeHtml, ensureSanitizer } from "./sanitize";
18
- export { gridColsClass } from "./grid";
19
21
  export { getIcon, curatedIcons, type IconEntry } from "./icons";
20
22
  export { buildGoogleFontsUrl } from "./google-fonts";
21
23
  export { formatTimestamp } from "./timestamp";
22
- export { safeRedirect } from "./safeRedirect";
23
- export { createEvent, editModeEvent, navChangeEvent, darkModeEvent, type TypedEvent } from "./events";
24
+ export { safeRedirect } from "./safe-redirect";
25
+ export { createEvent, editModeEvent, darkModeEvent, type TypedEvent } from "./events";
24
26
  export {
25
27
  createRegistry,
26
28
  defineSection,
@@ -42,7 +44,6 @@ export {
42
44
  export {
43
45
  mergeSiteContent,
44
46
  loadStaticSiteContent,
45
- loadSiteContent,
46
47
  type LoadedSection,
47
48
  type SiteContent,
48
49
  } from "./loader";
package/src/lib/loader.ts CHANGED
@@ -4,10 +4,14 @@ import { getAllSchemas } from "./registry";
4
4
  import { blockDepth, MAX_BLOCK_DEPTH } from "../schemas/block";
5
5
  import { upgradeLegacySection } from "./migrate-sections-transform";
6
6
  import { NavEntrySchema, type NavEntry } from "../schemas/nav";
7
+ import { TagEntrySchema, type TagEntry } from "../schemas/tags";
7
8
 
8
9
  export interface LoadedSection {
9
10
  section: Section;
10
11
  meta: SectionMeta;
12
+ /** Set only on render-time copies a Reference pulled in (resolveReference):
13
+ * the id of that Reference. Never loaded or persisted. */
14
+ referencedBy?: string;
11
15
  }
12
16
 
13
17
  export interface SiteContent {
@@ -19,6 +23,9 @@ export interface SiteContent {
19
23
  * nav.json from a degraded read permanently destroys the entries this
20
24
  * version dropped. */
21
25
  navDegraded: boolean;
26
+ /** Same contract as navDegraded, for tags.json: a degraded read is
27
+ * read-only for tags. */
28
+ tagsDegraded: boolean;
22
29
  }
23
30
 
24
31
  /**
@@ -34,7 +41,7 @@ function parseNavFile(navFile: unknown): { entries: NavEntry[]; degraded: boolea
34
41
  if (!navFile) return { entries: [], degraded: false };
35
42
  const rawEntries = (navFile as { entries?: unknown }).entries;
36
43
  if (!Array.isArray(rawEntries)) {
37
- console.warn("Invalid nav.json, falling back to a flat navigation");
44
+ console.warn("[loader] Invalid nav.json, falling back to a flat navigation");
38
45
  return { entries: [], degraded: true };
39
46
  }
40
47
  const entries: NavEntry[] = [];
@@ -45,11 +52,46 @@ function parseNavFile(navFile: unknown): { entries: NavEntry[]; degraded: boolea
45
52
  else dropped++;
46
53
  }
47
54
  if (dropped > 0) {
48
- console.warn(`nav.json: dropped ${dropped} entr${dropped === 1 ? "y" : "ies"} this package version cannot represent; navigation is degraded`);
55
+ console.warn(`[loader] nav.json: dropped ${dropped} entr${dropped === 1 ? "y" : "ies"} this package version cannot represent; navigation is degraded`);
49
56
  }
50
57
  return { entries, degraded: dropped > 0 };
51
58
  }
52
59
 
60
+ /**
61
+ * Parse the tags.json sidecar with parseNavFile's per-entry tolerance and the
62
+ * same `degraded` contract. Assignments to deleted sections are NOT pruned
63
+ * here — that is reconcileTags' job on every read (siteTags), so the runtime
64
+ * field stays a faithful copy of the file.
65
+ */
66
+ function parseTagsFile(tagsFile: unknown): { tags: TagEntry[]; degraded: boolean } {
67
+ if (!tagsFile) return { tags: [], degraded: false };
68
+ const rawTags = (tagsFile as { tags?: unknown }).tags;
69
+ if (!Array.isArray(rawTags)) {
70
+ console.warn("[loader] Invalid tags.json, falling back to no tags");
71
+ return { tags: [], degraded: true };
72
+ }
73
+ const tags: TagEntry[] = [];
74
+ let dropped = 0;
75
+ for (const raw of rawTags) {
76
+ const result = TagEntrySchema.safeParse(raw);
77
+ if (result.success) tags.push(result.data);
78
+ else dropped++;
79
+ }
80
+ if (dropped > 0) {
81
+ console.warn(`[loader] tags.json: dropped ${dropped} entr${dropped === 1 ? "y" : "ies"} this package version cannot represent; tags are degraded`);
82
+ }
83
+ return { tags, degraded: dropped > 0 };
84
+ }
85
+
86
+ /**
87
+ * Whether a load looked for tags.json. Not loaded ⇒ no `index.tags` is attached
88
+ * and writers leave the file alone; loaded ⇒ `file` is its parsed contents,
89
+ * with null/undefined meaning absent (an empty registry). Unlike nav, the
90
+ * difference is load-bearing — an attached `[]` that a writer saves back
91
+ * deletes every tag — hence an explicit flag rather than undefined-vs-null.
92
+ */
93
+ export type TagsSidecar = { loaded: false } | { loaded: true; file: unknown };
94
+
53
95
  /**
54
96
  * Merge a validated index with raw section file contents.
55
97
  * Validates each section file against its Zod schema.
@@ -59,6 +101,7 @@ export function mergeSiteContent(
59
101
  index: SiteIndex,
60
102
  sectionFiles: Record<string, unknown>,
61
103
  navFile?: unknown,
104
+ tagsSidecar: TagsSidecar = { loaded: false },
62
105
  ): SiteContent {
63
106
  const sections: LoadedSection[] = [];
64
107
 
@@ -112,7 +155,13 @@ export function mergeSiteContent(
112
155
  }
113
156
 
114
157
  const nav = parseNavFile(navFile);
115
- return { sections, index: { ...index, nav: nav.entries }, navDegraded: nav.degraded };
158
+ const tags = tagsSidecar.loaded ? parseTagsFile(tagsSidecar.file) : null;
159
+ return {
160
+ sections,
161
+ index: { ...index, nav: nav.entries, ...(tags ? { tags: tags.tags } : {}) },
162
+ navDegraded: nav.degraded,
163
+ tagsDegraded: tags?.degraded ?? false,
164
+ };
116
165
  }
117
166
 
118
167
  /**
@@ -124,6 +173,7 @@ export function loadStaticSiteContent(
124
173
  index: SiteIndex,
125
174
  sectionGlob: Record<string, unknown>,
126
175
  navFile?: unknown,
176
+ tagsSidecar: TagsSidecar = { loaded: false },
127
177
  ): SiteContent {
128
178
  const sectionFiles: Record<string, unknown> = {};
129
179
  for (const [path, data] of Object.entries(sectionGlob)) {
@@ -131,56 +181,5 @@ export function loadStaticSiteContent(
131
181
  const id = filename.replace(".json", "");
132
182
  sectionFiles[id] = data;
133
183
  }
134
- return mergeSiteContent(index, sectionFiles, navFile);
135
- }
136
-
137
- /**
138
- * Load site content from the filesystem.
139
- * For use in scripts and tests — Astro pages should use import.meta.glob instead.
140
- */
141
- export async function loadSiteContent(contentDir: string): Promise<SiteContent> {
142
- const fs = await import("node:fs/promises");
143
- const path = await import("node:path");
144
-
145
- const indexPath = path.join(contentDir, "index.json");
146
- const indexRaw = JSON.parse(await fs.readFile(indexPath, "utf-8"));
147
- const index = IndexSchema.parse(indexRaw);
148
-
149
- const sectionFiles: Record<string, unknown> = {};
150
- for (const id of index.pages.flatMap((p: SiteIndex["pages"][number]) => p.order)) {
151
- const sectionPath = path.join(contentDir, "sections", `${id}.json`);
152
- sectionFiles[id] = JSON.parse(await fs.readFile(sectionPath, "utf-8"));
153
- }
154
-
155
- // nav.json is an optional sidecar — absent (every portal predating page
156
- // groups) or unreadable must degrade to a flat nav, never fail the load.
157
- //
158
- // ABSENT and UNREADABLE are not the same answer, though: absent is the normal
159
- // case and safe to overwrite, while a present-but-unparseable sidecar holds
160
- // groups this load cannot see. Collapsing both to `undefined` reported
161
- // navDegraded: false, so the next nav-touching save wholesale-wrote a flat
162
- // nav.json over a file that was still hand-recoverable. Mirrors the
163
- // GitHub-backed loader's navRaw/navFile split in @drawnagency/core.
164
- let navRaw: string | undefined;
165
- let readFailed = false;
166
- try {
167
- navRaw = await fs.readFile(path.join(contentDir, "nav.json"), "utf-8");
168
- } catch (err) {
169
- // Only a genuinely missing file is "absent". A permissions/IO error leaves
170
- // the sidecar's contents unknown, which must not license overwriting it.
171
- readFailed = (err as { code?: string })?.code !== "ENOENT";
172
- }
173
-
174
- let navFile: unknown;
175
- let parseFailed = false;
176
- if (navRaw !== undefined) {
177
- try {
178
- navFile = JSON.parse(navRaw);
179
- } catch {
180
- parseFailed = true;
181
- }
182
- }
183
-
184
- const content = mergeSiteContent(index, sectionFiles, navFile);
185
- return readFailed || parseFailed ? { ...content, navDegraded: true } : content;
184
+ return mergeSiteContent(index, sectionFiles, navFile, tagsSidecar);
186
185
  }
@@ -204,3 +204,74 @@ export function moveNavEntry(index: SiteIndex, from: NavLocation, to: NavLocatio
204
204
 
205
205
  return withNav(index, entries);
206
206
  }
207
+
208
+ /**
209
+ * "Moved <label> to position N of M" for the aria-live reorder announcement,
210
+ * covering both `moveNavEntry` callers: drag (PagesModal/NavGroupRow) and the
211
+ * Move up/down buttons. `prevIndex` resolves WHO moved and its label (read
212
+ * before the move, since `from` is a position, not an id); `nextIndex` (the
213
+ * result of `moveNavEntry(prevIndex, from, to)`) is searched by id for where
214
+ * it actually landed — robust to `moveNavEntry`'s own clamping, so the caller
215
+ * never has to replicate that math. Returns null if `from` didn't resolve to
216
+ * a real row (defensive; ordinary callers always pass a live location).
217
+ */
218
+ /** A top-level entry counts toward "N of M" iff it's a group (always visible)
219
+ * or a page that isn't archived — mirrors the PagesModal Move up/down rows,
220
+ * which skip archived pages entirely rather than render an inert slot. */
221
+ function isLiveTopEntry(index: SiteIndex, entry: NavEntry): boolean {
222
+ if (entry.kind === "group") return true;
223
+ const page = index.pages.find((p) => p.id === entry.pageId);
224
+ return page?.status === "live";
225
+ }
226
+
227
+ export function describeNavMove(prevIndex: SiteIndex, nextIndex: SiteIndex, from: NavLocation): string | null {
228
+ const prevEntries = navEntries(prevIndex);
229
+ let label: string;
230
+ let pageId: string | null = null;
231
+ let groupId: string | null = null;
232
+
233
+ if (from.groupId === null) {
234
+ const entry = prevEntries[from.index];
235
+ if (!entry) return null;
236
+ if (entry.kind === "group") {
237
+ label = groupDisplayLabel(entry.label);
238
+ groupId = entry.id;
239
+ } else {
240
+ const page = prevIndex.pages.find((p) => p.id === entry.pageId);
241
+ if (!page) return null;
242
+ label = page.title || "Untitled";
243
+ pageId = page.id;
244
+ }
245
+ } else {
246
+ const group = prevEntries.find((e) => e.kind === "group" && e.id === from.groupId);
247
+ if (!group || group.kind !== "group") return null;
248
+ const id = group.pageIds[from.index];
249
+ if (!id) return null;
250
+ const page = prevIndex.pages.find((p) => p.id === id);
251
+ if (!page) return null;
252
+ label = page.title || "Untitled";
253
+ pageId = page.id;
254
+ }
255
+
256
+ const nextEntries = navEntries(nextIndex);
257
+ const liveTopEntries = nextEntries.filter((e) => isLiveTopEntry(nextIndex, e));
258
+
259
+ if (groupId !== null) {
260
+ const i = liveTopEntries.findIndex((e) => e.kind === "group" && e.id === groupId);
261
+ if (i === -1) return null;
262
+ return `Moved ${label} to position ${i + 1} of ${liveTopEntries.length}`;
263
+ }
264
+
265
+ if (pageId === null) return null;
266
+
267
+ const topIndex = liveTopEntries.findIndex((e) => e.kind === "page" && e.pageId === pageId);
268
+ if (topIndex !== -1) return `Moved ${label} to position ${topIndex + 1} of ${liveTopEntries.length}`;
269
+ for (const e of nextEntries) {
270
+ if (e.kind === "group") {
271
+ const liveMemberIds = e.pageIds.filter((id) => nextIndex.pages.find((p) => p.id === id)?.status === "live");
272
+ const gi = liveMemberIds.indexOf(pageId);
273
+ if (gi !== -1) return `Moved ${label} to position ${gi + 1} of ${liveMemberIds.length}`;
274
+ }
275
+ }
276
+ return null;
277
+ }