@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
@@ -1,6 +1,7 @@
1
1
  import { useEffect, useState } from "react";
2
2
  import { EditorModal } from "../shell/EditorModal";
3
3
  import { Button } from "./Button";
4
+ import { ConfirmDialog } from "./ConfirmDialog";
4
5
 
5
6
  export interface ChangedSection {
6
7
  id: string;
@@ -165,33 +166,48 @@ export function HistoryChangesModal({
165
166
  <EditorModal
166
167
  isOpen={isOpen}
167
168
  onClose={onClose}
168
- title={confirmingRestore ? "Restore this version?" : title}
169
+ title={title}
169
170
  size="standard"
170
171
  tabbed
171
172
  noPadding
172
173
  >
173
174
  <div className="flex min-h-0 flex-1 flex-col">
174
- <div className="flex-1 overflow-y-auto px-6 py-4">
175
- {confirmingRestore ? (
176
- <>
177
- <p className="text-sm text-base-contrast-light">
178
- This will publish the content from this version as a new update.
179
- Your currently published content stays available in the history.
180
- </p>
181
- {restoreError && (
182
- <p role="alert" className="mt-3 text-sm font-medium text-red-600">
183
- {restoreError} Nothing was published — you can try again.
184
- </p>
185
- )}
186
- {hasUnsavedChanges && (
187
- <p className="mt-3 text-sm font-medium text-red-600">
188
- You have unsaved changes. Restoring discards them, and unlike
189
- published content they cannot be recovered from history.
190
- </p>
191
- )}
192
- </>
193
- ) : (
194
- <>
175
+ {confirmingRestore ? (
176
+ <div className="flex-1 overflow-y-auto px-6 py-4">
177
+ <ConfirmDialog
178
+ isOpen
179
+ embedded
180
+ title="Restore this version?"
181
+ body={
182
+ <>
183
+ <p className="text-sm text-base-contrast-light">
184
+ This will publish the content from this version as a new update.
185
+ Your currently published content stays available in the history.
186
+ </p>
187
+ {restoreError && (
188
+ <p role="alert" className="mt-3 text-sm font-medium text-danger">
189
+ {restoreError} Nothing was published — you can try again.
190
+ </p>
191
+ )}
192
+ {hasUnsavedChanges && (
193
+ <p className="mt-3 text-sm font-medium text-danger">
194
+ You have unsaved changes. Restoring discards them, and unlike
195
+ published content they cannot be recovered from history.
196
+ </p>
197
+ )}
198
+ </>
199
+ }
200
+ confirmLabel="Restore"
201
+ tone="destructive"
202
+ busy={isRestoring}
203
+ busyLabel="Restoring…"
204
+ onConfirm={onRestore}
205
+ onCancel={() => setConfirmingRestore(false)}
206
+ />
207
+ </div>
208
+ ) : (
209
+ <>
210
+ <div className="flex-1 overflow-y-auto px-6 py-4">
195
211
  {error && (
196
212
  <p className="py-6 text-center text-sm text-base-contrast-light">
197
213
  Couldn&rsquo;t load the change summary.
@@ -237,30 +253,16 @@ export function HistoryChangesModal({
237
253
  )}
238
254
  </>
239
255
  )}
240
- </>
241
- )}
242
- </div>
256
+ </div>
243
257
 
244
- <div className="flex shrink-0 justify-end gap-2 border-t border-base-200 px-6 py-3">
245
- {confirmingRestore ? (
246
- <>
247
- <Button variant="secondary" size="sm" disabled={isRestoring}
248
- onClick={() => setConfirmingRestore(false)}>
249
- Cancel
250
- </Button>
251
- <Button variant="destructive" size="sm" disabled={isRestoring} onClick={onRestore}>
252
- {isRestoring ? "Restoring…" : "Restore"}
253
- </Button>
254
- </>
255
- ) : (
256
- <>
258
+ <div className="flex shrink-0 justify-end gap-2 border-t border-base-200 px-6 py-3">
257
259
  <Button variant="destructive" size="sm" onClick={() => setConfirmingRestore(true)}>
258
260
  Restore&hellip;
259
261
  </Button>
260
262
  <Button size="sm" onClick={onBrowse}>Browse this version</Button>
261
- </>
262
- )}
263
- </div>
263
+ </div>
264
+ </>
265
+ )}
264
266
  </div>
265
267
  </EditorModal>
266
268
  );
@@ -1,4 +1,5 @@
1
1
  import { useState, useCallback, useEffect, useMemo, lazy, Suspense } from "react";
2
+ import { ChevronRight, ChevronLeft, MousePointer2 } from "lucide-react";
2
3
  import { cn } from "../../lib/cn";
3
4
  import { historyRestoreEvent, historyRestoreFailedEvent } from "../../lib/events";
4
5
  import {
@@ -26,26 +27,12 @@ interface Frame {
26
27
  node: HistoryNode | null;
27
28
  }
28
29
 
29
- const Chevron = () => (
30
- <svg width="9" height="9" viewBox="0 0 10 10" fill="none" stroke="currentColor"
31
- strokeWidth="1.7" aria-hidden="true">
32
- <path d="M3.5 1.5L7 5l-3.5 3.5" />
33
- </svg>
34
- );
30
+ const Chevron = () => <ChevronRight size={9} strokeWidth={1.7} aria-hidden="true" />;
35
31
 
36
- const BackArrow = () => (
37
- <svg width="11" height="11" viewBox="0 0 10 10" fill="none" stroke="currentColor"
38
- strokeWidth="1.7" aria-hidden="true">
39
- <path d="M6.5 1.5L3 5l3.5 3.5" />
40
- </svg>
41
- );
32
+ const BackArrow = () => <ChevronLeft size={11} strokeWidth={1.7} aria-hidden="true" />;
42
33
 
43
34
  /** Browse-to-this-version: a pointer cursor — "go look at the site as it was". */
44
- const BrowseCursor = () => (
45
- <svg width="13" height="13" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true">
46
- <path d="M3.6 1.9a.4.4 0 0 1 .66-.3l8 6.7a.4.4 0 0 1-.23.71l-3.4.2 1.94 3.5a.4.4 0 0 1-.17.54l-1.4.75a.4.4 0 0 1-.54-.18l-1.9-3.55-2.3 2.2a.4.4 0 0 1-.68-.29z" />
47
- </svg>
48
- );
35
+ const BrowseCursor = () => <MousePointer2 size={13} aria-hidden="true" />;
49
36
 
50
37
  function Volume({ count, max }: { count: number; max: number }) {
51
38
  // sqrt so a 200-commit year doesn't flatten every other bar to nothing.
@@ -89,7 +76,7 @@ export function HistoryPopover({ onSelectCommit, hasUnsavedChanges = false }: Hi
89
76
  const res = await fetch(`/api/history?page=${page}&per_page=${PER_PAGE}`, {
90
77
  signal: controller.signal,
91
78
  });
92
- if (!res.ok) throw new Error(`Failed to load history: ${res.status}`);
79
+ if (!res.ok) throw new Error(`Couldn't load history: ${res.status}`);
93
80
  const data = (await res.json()) as { items: HistoryCommit[] };
94
81
  all.push(...data.items);
95
82
  // Render each page as it lands. These requests are sequential, so waiting
@@ -105,7 +92,7 @@ export function HistoryPopover({ onSelectCommit, hasUnsavedChanges = false }: Hi
105
92
  if (!live || controller.signal.aborted) return;
106
93
  // A later page failing is not worth throwing away the pages that landed:
107
94
  // the error view replaces the tree entirely. Keep what we have instead.
108
- if (all.length === 0) setError(err instanceof Error ? err.message : "Failed to load history");
95
+ if (all.length === 0) setError(err instanceof Error ? err.message : "Couldn't load history");
109
96
  } finally {
110
97
  if (live) setLoading(false);
111
98
  }
@@ -139,7 +126,7 @@ export function HistoryPopover({ onSelectCommit, hasUnsavedChanges = false }: Hi
139
126
  return <div className="p-4 text-center text-xs text-base-contrast-light">Loading history…</div>;
140
127
  }
141
128
  if (error) {
142
- return <div className="p-4 text-center text-xs text-red-600">{error}</div>;
129
+ return <div className="p-4 text-center text-xs text-danger">{error}</div>;
143
130
  }
144
131
  if (commits.length === 0) {
145
132
  return <div className="p-4 text-center text-xs text-base-contrast-light">No history found</div>;
@@ -0,0 +1,92 @@
1
+ import type { ImageBackground, ImageBackgroundSurface } from "../../schemas/shared";
2
+ import { cn } from "../../lib/cn";
3
+ import { FormLabel } from "./FormLabel";
4
+ import { SegmentedControl } from "./SegmentedControl";
5
+ import { Select } from "./Select";
6
+ import { ColorPicker } from "./ColorPicker";
7
+ import { useBrandSwatches } from "./BrandSwatchesContext";
8
+
9
+ interface Props {
10
+ label: string;
11
+ value: ImageBackground | undefined;
12
+ onChange: (value: ImageBackground | undefined) => void;
13
+ }
14
+
15
+ const MODES = [
16
+ { value: "none", label: "None" },
17
+ { value: "theme", label: "Light / Dark" },
18
+ { value: "custom", label: "Custom" },
19
+ ];
20
+
21
+ const SURFACES: { value: ImageBackgroundSurface; label: string }[] = [
22
+ { value: "none", label: "None" },
23
+ { value: "light", label: "Light" },
24
+ { value: "dark", label: "Dark" },
25
+ ];
26
+
27
+ export function ImageBackgroundField({ label, value, onChange }: Props) {
28
+ const swatches = useBrandSwatches();
29
+ const mode = value?.mode ?? "none";
30
+
31
+ function setMode(next: string) {
32
+ if (next === "theme") onChange({ mode: "theme", light: "none", dark: "none" });
33
+ else if (next === "custom") onChange({ mode: "custom", color: "#ffffff" });
34
+ else onChange(undefined);
35
+ }
36
+
37
+ return (
38
+ <div className="flex flex-col gap-3">
39
+ <FormLabel>{label}</FormLabel>
40
+ <SegmentedControl options={MODES} value={mode} onChange={setMode} className="self-start" />
41
+
42
+ {value?.mode === "theme" && (
43
+ <div className="grid grid-cols-2 gap-3">
44
+ <Select
45
+ label="Light mode"
46
+ value={value.light}
47
+ options={SURFACES}
48
+ onChange={(light) => onChange({ ...value, light: light as ImageBackgroundSurface })}
49
+ />
50
+ <Select
51
+ label="Dark mode"
52
+ value={value.dark}
53
+ options={SURFACES}
54
+ onChange={(dark) => onChange({ ...value, dark: dark as ImageBackgroundSurface })}
55
+ />
56
+ </div>
57
+ )}
58
+
59
+ {value?.mode === "custom" && (
60
+ <div className="flex flex-col gap-2">
61
+ <div className="flex items-center gap-2">
62
+ <ColorPicker
63
+ label="Background color"
64
+ value={value.color}
65
+ onChange={(color) => onChange({ mode: "custom", color })}
66
+ />
67
+ <span className="font-mono text-xs text-base-contrast-light">{value.color}</span>
68
+ </div>
69
+ {swatches.length > 0 && (
70
+ <div role="group" aria-label="Brand colors" className="flex flex-wrap gap-2">
71
+ {swatches.map((s) => (
72
+ <button
73
+ key={s.hex}
74
+ type="button"
75
+ aria-label={s.name ? `${s.name} (${s.hex})` : s.hex}
76
+ aria-pressed={s.hex === value.color.toLowerCase()}
77
+ title={s.name ?? s.hex}
78
+ onClick={() => onChange({ mode: "custom", color: s.hex })}
79
+ className={cn(
80
+ "h-7 w-7 cursor-pointer rounded-full border border-base-200",
81
+ "aria-pressed:outline aria-pressed:outline-2 aria-pressed:outline-offset-2 aria-pressed:outline-base-contrast",
82
+ )}
83
+ style={{ backgroundColor: s.hex }}
84
+ />
85
+ ))}
86
+ </div>
87
+ )}
88
+ </div>
89
+ )}
90
+ </div>
91
+ );
92
+ }
@@ -1,3 +1,4 @@
1
+ import { cn } from "../../lib/cn";
1
2
  import { Button } from "./Button";
2
3
  import { formatFileSize } from "../../media/utils";
3
4
 
@@ -30,18 +31,33 @@ export function formatRejections(files: UploadRejection[], maxFileSize?: number)
30
31
  return `${files.length} files rejected (${parts.join(", ")}).`;
31
32
  }
32
33
 
33
- export function UploadRejectionAlert({
34
+ export type InlineAlertTone = "success" | "info" | "warning" | "error";
35
+
36
+ const toneClasses: Record<InlineAlertTone, string> = {
37
+ success: "border-success bg-success-bg text-success",
38
+ info: "border-base-200 bg-base-accent text-base-contrast",
39
+ warning: "border-warning bg-warning-bg text-warning",
40
+ error: "border-danger bg-danger-bg text-danger",
41
+ };
42
+
43
+ export function InlineAlert({
34
44
  message,
35
45
  onDismiss,
46
+ tone = "warning",
36
47
  }: {
37
48
  message: string;
38
49
  onDismiss: () => void;
50
+ tone?: InlineAlertTone;
39
51
  }) {
40
52
  return (
41
- <div role="alert" className="flex flex-col items-center rounded-lg border border-amber-200 bg-amber-50 px-4 py-3 lg:flex-row lg:justify-between dark:border-amber-900/50 dark:bg-amber-950/30">
42
- <p className="mb-2 text-center text-sm text-amber-800 lg:mb-0 lg:text-left dark:text-amber-200">
43
- {message}
44
- </p>
53
+ <div
54
+ role="alert"
55
+ className={cn(
56
+ "flex flex-col items-center rounded-lg border px-4 py-3 lg:flex-row lg:justify-between",
57
+ toneClasses[tone],
58
+ )}
59
+ >
60
+ <p className="mb-2 text-center text-sm lg:mb-0 lg:text-left">{message}</p>
45
61
  <div className="flex shrink-0 items-center">
46
62
  <Button variant="secondary" size="sm" type="button" onClick={onDismiss}>
47
63
  Dismiss
@@ -6,17 +6,28 @@ interface InputProps extends Omit<InputHTMLAttributes<HTMLInputElement>, "onChan
6
6
  label: string;
7
7
  onChange: (value: string) => void;
8
8
  error?: string;
9
+ /** Helper text below the field, styled like `error` but neutral — for
10
+ * guidance that isn't a validation failure. Not shown while `error` is set. */
11
+ hint?: string;
12
+ /** Keeps the field's accessible name (still a real associated `<label>`,
13
+ * not just `aria-label`) without a visible caption — for compact/inline
14
+ * fields (e.g. an editable row's title) where a caption would add layout
15
+ * a raw `<input>` with just `aria-label` never had. */
16
+ hideLabel?: boolean;
17
+ /** Escape hatch for one-off input sizing, matching `Select`'s `selectClassName`. */
18
+ inputClassName?: string;
9
19
  }
10
20
 
11
21
  export const Input = forwardRef<HTMLInputElement, InputProps>(function Input(
12
- { label, value, onChange, type = "text", className, disabled, error, ...rest },
22
+ { label, value, onChange, type = "text", className, disabled, error, hint, hideLabel, inputClassName, ...rest },
13
23
  ref,
14
24
  ) {
15
25
  const id = useId();
16
26
  const errorId = `${id}-error`;
27
+ const hintId = `${id}-hint`;
17
28
  return (
18
29
  <div className={className}>
19
- <FormLabel htmlFor={id}>{label}</FormLabel>
30
+ <FormLabel htmlFor={id} className={hideLabel ? "sr-only" : undefined}>{label}</FormLabel>
20
31
  <input
21
32
  ref={ref}
22
33
  id={id}
@@ -25,21 +36,26 @@ export const Input = forwardRef<HTMLInputElement, InputProps>(function Input(
25
36
  onChange={(e) => onChange(e.target.value)}
26
37
  disabled={disabled}
27
38
  aria-invalid={error ? true : undefined}
28
- aria-describedby={error ? errorId : undefined}
39
+ aria-describedby={error ? errorId : hint ? hintId : undefined}
29
40
  className={cn(
30
41
  "w-full rounded border bg-base px-3 py-2 text-sm text-base-contrast focus:outline-none focus:ring-1",
31
42
  error
32
- ? "border-red-600 focus:border-red-600 focus:ring-red-600"
43
+ ? "border-danger focus:border-danger focus:ring-danger"
33
44
  : "border-base-200 focus:border-base-contrast focus:ring-base-contrast",
34
45
  disabled && "cursor-not-allowed opacity-50",
46
+ inputClassName,
35
47
  )}
36
48
  {...rest}
37
49
  />
38
- {error && (
39
- <p id={errorId} className="mt-1 text-xs text-red-600">
50
+ {error ? (
51
+ <p id={errorId} className="mt-1 text-xs text-danger">
40
52
  {error}
41
53
  </p>
42
- )}
54
+ ) : hint ? (
55
+ <p id={hintId} className="mt-1 text-xs text-base-contrast-light">
56
+ {hint}
57
+ </p>
58
+ ) : null}
43
59
  </div>
44
60
  );
45
61
  });
@@ -1,5 +1,5 @@
1
1
  import { useState, useCallback, useEffect, useRef, type ReactNode } from "react";
2
- import { ChevronRight, Users } from "lucide-react";
2
+ import { ChevronRight, ChevronUp, Menu, X, Users } from "lucide-react";
3
3
  import { cn } from "../../lib/cn";
4
4
  import { Toggle } from "./Toggle";
5
5
  import type { SiteNav, NavItem, GroupNavItem, PageNavItem, DocGroupNavItem, DocNavItem } from "../../lib/nav";
@@ -93,6 +93,8 @@ export default function Navigation({ siteNav: initialNav, siteName, darkMode, la
93
93
 
94
94
  // /api/auth/sign-out clears BOTH the editor session and the audience cookie,
95
95
  // so one call serves either kind of session; only the destination differs.
96
+ // While an editor is impersonating it ends only that, and names the page to
97
+ // return to (#61).
96
98
  const handleLogout = useCallback(async () => {
97
99
  setLogoutError(null);
98
100
  setLoggingOut(true);
@@ -103,7 +105,9 @@ export default function Navigation({ siteNav: initialNav, siteName, darkMode, la
103
105
  setLoggingOut(false);
104
106
  return;
105
107
  }
106
- window.location.assign(identity?.kind === "editor" ? "/edit/login" : "/login");
108
+ const body = (await res.json().catch(() => null)) as { redirectTo?: unknown } | null;
109
+ const redirectTo = typeof body?.redirectTo === "string" ? body.redirectTo : null;
110
+ window.location.assign(redirectTo ?? (identity?.kind === "editor" ? "/edit/login" : "/login"));
107
111
  } catch {
108
112
  setLogoutError("Network error");
109
113
  setLoggingOut(false);
@@ -371,25 +375,25 @@ export default function Navigation({ siteNav: initialNav, siteName, darkMode, la
371
375
 
372
376
  return (
373
377
  <>
374
- <header className={cn("fixed top-0 left-0 right-0 z-50 bg-base", bp.hidden)}>
378
+ <header className={cn("bp-nav-header fixed top-0 left-0 right-0 z-50 bg-base", bp.hidden)}>
375
379
  <div className="mx-auto max-w-screen-xl flex h-16 items-center justify-between px-4">
376
380
  <div className="flex flex-col items-start gap-1">
377
381
  {renderLogo("h-8 w-auto")}
378
382
  {showTitle && <span className="text-lg font-bold text-primary">{currentSiteName}</span>}
379
383
  </div>
380
- <button onClick={() => setIsOpen(!isOpen)} className="cursor-pointer p-2 text-base-contrast" aria-label="Toggle navigation">
381
- <svg className="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true">
382
- {isOpen
383
- ? <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
384
- : <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 6h16M4 12h16M4 18h16" />}
385
- </svg>
384
+ <button onClick={() => setIsOpen(!isOpen)} className="cursor-pointer p-2 text-base-contrast" aria-label="Toggle navigation" aria-expanded={isOpen} aria-controls="bp-mobile-nav">
385
+ {isOpen ? (
386
+ <X className="h-6 w-6" aria-hidden="true" />
387
+ ) : (
388
+ <Menu className="h-6 w-6" aria-hidden="true" />
389
+ )}
386
390
  </button>
387
391
  </div>
388
392
  </header>
389
393
 
390
394
  {isOpen && <div className={cn("fixed inset-0 z-40 bg-black/50", bp.hidden)} onClick={() => setIsOpen(false)} />}
391
395
 
392
- <nav className={cn("fixed top-0 left-0 z-40 h-full w-64 flex flex-col overflow-y-auto border-r border-base-200 bg-base pt-16 transition-transform nav-sidebar",
396
+ <nav id="bp-mobile-nav" className={cn("fixed top-0 left-0 z-40 h-full w-64 flex flex-col overflow-y-auto border-r border-base-200 bg-base pt-16 transition-transform nav-sidebar",
393
397
  bp.leftAuto, bp.translate,
394
398
  isOpen ? "translate-x-0" : "-translate-x-full")}>
395
399
  {/* No horizontal padding on the wrapper: the logo's 80% is of the nav
@@ -467,11 +471,12 @@ export default function Navigation({ siteNav: initialNav, siteName, darkMode, la
467
471
  {/* The panel opens upward, so the caret points up when closed
468
472
  and flips when open — the affordance that this is a toggle
469
473
  rather than a label. */}
470
- <svg width="9" height="9" viewBox="0 0 10 10" fill="none" stroke="currentColor"
471
- strokeWidth="1.7" aria-hidden="true"
472
- className={cn("transition-transform", showHistory && "rotate-180")}>
473
- <path d="M1.5 6.5L5 3l3.5 3.5" strokeLinecap="round" strokeLinejoin="round" />
474
- </svg>
474
+ <ChevronUp
475
+ size={9}
476
+ strokeWidth={1.7}
477
+ aria-hidden="true"
478
+ className={cn("transition-transform", showHistory && "rotate-180")}
479
+ />
475
480
  </button>
476
481
  {/* Portalled: the sidebar sets overflow-y-auto, which computes
477
482
  overflow-x to auto, so a panel wider than the 16rem sidebar
@@ -506,10 +511,10 @@ export default function Navigation({ siteNav: initialNav, siteName, darkMode, la
506
511
  disabled={loggingOut}
507
512
  className="cursor-pointer underline-offset-2 transition-colors hover:text-primary hover:underline disabled:cursor-wait"
508
513
  >
509
- {loggingOut ? "Logging out..." : "Log out"}
514
+ {loggingOut ? "Logging out…" : "Log out"}
510
515
  </button>
511
516
  </p>
512
- {logoutError && <p className="mt-1 text-xs text-red-600">{logoutError}</p>}
517
+ {logoutError && <p className="mt-1 text-xs text-danger">{logoutError}</p>}
513
518
  </div>
514
519
  )}
515
520
  </div>
@@ -0,0 +1,59 @@
1
+ import type { ReactNode } from "react";
2
+ import { X } from "lucide-react";
3
+ import { cn } from "../../lib/cn";
4
+ import { IconButton } from "./IconButton";
5
+
6
+ export type NoticeTone = "success" | "info" | "warning" | "error";
7
+
8
+ const toneClasses: Record<NoticeTone, string> = {
9
+ success: "border-success bg-success-bg text-success",
10
+ info: "border-base-200 bg-base-accent text-base-contrast",
11
+ warning: "border-warning bg-warning-bg text-warning",
12
+ error: "border-danger bg-danger-bg text-danger",
13
+ };
14
+
15
+ // role="alert" interrupts assistive tech immediately (warning/error); role="status"
16
+ // only announces at the next polite opportunity (success/info) — matches how
17
+ // urgently each tone needs to reach a screen reader user.
18
+ const toneRole: Record<NoticeTone, "alert" | "status"> = {
19
+ success: "status",
20
+ info: "status",
21
+ warning: "alert",
22
+ error: "alert",
23
+ };
24
+
25
+ export interface NoticeProps {
26
+ tone: NoticeTone;
27
+ children: ReactNode;
28
+ onDismiss?: () => void;
29
+ placement: "inline" | "toast";
30
+ }
31
+
32
+ export function Notice({ tone, children, onDismiss, placement }: NoticeProps) {
33
+ return (
34
+ <div
35
+ role={toneRole[tone]}
36
+ className={cn(
37
+ "flex items-center gap-3 rounded-md border px-4 py-2 text-xs font-medium shadow-lg",
38
+ toneClasses[tone],
39
+ placement === "toast"
40
+ // top-16 clears the h-16 ToolbarFrame bar exactly; the extra 1rem
41
+ // (top-20) is the gap below it, so the toast never sits flush
42
+ // against — or, at top-14, partly under — the bar.
43
+ ? "fixed left-1/2 top-20 z-60 -translate-x-1/2"
44
+ : "w-full",
45
+ )}
46
+ >
47
+ <span className="flex-1">{children}</span>
48
+ {onDismiss && (
49
+ <IconButton
50
+ icon={<X size={14} />}
51
+ label="Dismiss"
52
+ size="sm"
53
+ onClick={onDismiss}
54
+ className="shrink-0 opacity-70 hover:opacity-100 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary"
55
+ />
56
+ )}
57
+ </div>
58
+ );
59
+ }
@@ -1,6 +1,7 @@
1
1
  import { useEffect, useLayoutEffect, useRef, useState, type ReactNode, type RefObject } from "react";
2
2
  import { createPortal } from "react-dom";
3
3
  import { useInitialFocus } from "../../hooks/useFocusTrap";
4
+ import { useStackedEscape } from "../../hooks/useStackedEscape";
4
5
  import { cn } from "../../lib/cn";
5
6
 
6
7
  interface PopoverProps {
@@ -106,17 +107,14 @@ export function Popover({
106
107
  panel.style.right = "";
107
108
  }, [isOpen, align]);
108
109
 
110
+ // A modal opened from inside this popover also closes on Escape. Without
111
+ // this the one keypress dismisses both, losing the navigator's zoom stack
112
+ // — inconsistent with the X button, and with the mousedown exemption below
113
+ // that exists for exactly this stacking.
114
+ useStackedEscape(onClose, { enabled: isOpen });
115
+
109
116
  useEffect(() => {
110
117
  if (!isOpen) return;
111
- function onKeyDown(e: KeyboardEvent) {
112
- if (e.key !== "Escape") return;
113
- // A modal opened from inside this popover also closes on Escape. Without
114
- // this the one keypress dismisses both, losing the navigator's zoom stack
115
- // — inconsistent with the X button, and with the mousedown exemption
116
- // below that exists for exactly this stacking.
117
- if (document.querySelector("[data-modal-layer]")) return;
118
- onClose();
119
- }
120
118
  function onMouseDown(e: MouseEvent) {
121
119
  const target = e.target as Node;
122
120
  if (panelRef.current?.contains(target)) return;
@@ -127,10 +125,8 @@ export function Popover({
127
125
  if (target instanceof Element && target.closest("[data-modal-layer]")) return;
128
126
  onClose();
129
127
  }
130
- document.addEventListener("keydown", onKeyDown);
131
128
  document.addEventListener("mousedown", onMouseDown);
132
129
  return () => {
133
- document.removeEventListener("keydown", onKeyDown);
134
130
  document.removeEventListener("mousedown", onMouseDown);
135
131
  };
136
132
  }, [isOpen, onClose, anchorRef]);
@@ -0,0 +1,27 @@
1
+ import { createContext } from "react";
2
+ import type { Audience } from "../../schemas/auth";
3
+ import type { SiteIndex } from "../../schemas/site-config";
4
+ import type { LoadedSection } from "../../lib/loader";
5
+ import type { Section } from "../../schemas/sections";
6
+ import type { MediaManifest } from "../../media/types";
7
+
8
+ /**
9
+ * The whole site's working copy, for the Reference section's editor list and
10
+ * settings picker — a Reference points across pages, so the page-scoped props
11
+ * a section gets aren't enough. EditorShell provides it above the modal root
12
+ * so the settings form sees it too. Absent (null) outside the editor.
13
+ */
14
+ export interface ReferenceSources {
15
+ index: SiteIndex;
16
+ sections: LoadedSection[];
17
+ /** Every audience on the site. */
18
+ audiences: Audience[];
19
+ mediaManifest: MediaManifest;
20
+ /** The page being edited's copies, per Reference id then source id, with
21
+ * the page-wide anchors the editor nav links to (pageReferenceCopies). */
22
+ copies: Record<string, Record<string, Section>>;
23
+ /** Open the section's page and scroll it into view. */
24
+ jumpToSection: (sectionId: string) => void;
25
+ }
26
+
27
+ export const ReferenceSourcesContext = createContext<ReferenceSources | null>(null);
@@ -1,4 +1,4 @@
1
- import { useId } from "react";
1
+ import { useId, type SelectHTMLAttributes } from "react";
2
2
  import { ChevronDown } from "lucide-react";
3
3
  import { cn } from "../../lib/cn";
4
4
  import { FormLabel } from "./FormLabel";
@@ -8,7 +8,8 @@ interface SelectOption {
8
8
  label: string;
9
9
  }
10
10
 
11
- interface SelectProps {
11
+ interface SelectProps
12
+ extends Omit<SelectHTMLAttributes<HTMLSelectElement>, "value" | "onChange" | "disabled" | "className" | "id"> {
12
13
  label?: string;
13
14
  value: string;
14
15
  onChange: (value: string) => void;
@@ -26,6 +27,7 @@ export function Select({
26
27
  disabled,
27
28
  className,
28
29
  selectClassName,
30
+ ...rest
29
31
  }: SelectProps) {
30
32
  const id = useId();
31
33
  return (
@@ -37,6 +39,7 @@ export function Select({
37
39
  value={value}
38
40
  onChange={(e) => onChange(e.target.value)}
39
41
  disabled={disabled}
42
+ {...rest}
40
43
  className={cn(
41
44
  "w-full appearance-none rounded border border-base-200 bg-base py-2 pr-8 pl-3 text-sm text-base-contrast",
42
45
  "focus:border-base-contrast focus:outline-none focus:ring-1 focus:ring-base-contrast",