@drawnagency/primitives 0.1.112 → 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 (491) 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/auth/types.d.ts +1 -0
  6. package/dist/auth/types.d.ts.map +1 -1
  7. package/dist/{chunk-GPPVYLEI.js → chunk-45FA7Z55.js} +1 -1
  8. package/dist/{chunk-REDDTTGJ.js → chunk-4Y2JQQZL.js} +19 -1
  9. package/dist/chunk-5GFACGSN.js +6 -0
  10. package/dist/chunk-5KSAS4VY.js +275 -0
  11. package/dist/{chunk-5KNXTMUC.js → chunk-7SAAJVDE.js} +408 -28
  12. package/dist/{chunk-S2L3BPLS.js → chunk-7SX42OP4.js} +1 -1
  13. package/dist/{chunk-GGXZZ77Z.js → chunk-AASEGXNT.js} +18 -1
  14. package/dist/{chunk-WKZ7OENC.js → chunk-BQ5TZZUE.js} +5 -1
  15. package/dist/chunk-DIMDRBFK.js +11 -0
  16. package/dist/{chunk-WUJ3XNFI.js → chunk-GGYY3JQJ.js} +19 -24
  17. package/dist/{chunk-VLPELTPI.js → chunk-IP2SEBAJ.js} +5 -1
  18. package/dist/{chunk-IINIZBQB.js → chunk-JCDZAFX2.js} +30 -10
  19. package/dist/chunk-JR24U7FJ.js +120 -0
  20. package/dist/{chunk-SW6PCQQD.js → chunk-KQA772WQ.js} +2 -1
  21. package/dist/{chunk-Z2LO4PSQ.js → chunk-N3G7DOU3.js} +609 -61
  22. package/dist/chunk-OAGJBCSH.js +67 -0
  23. package/dist/{chunk-VT3PYGVW.js → chunk-PLANHXS7.js} +191 -61
  24. package/dist/{chunk-OGGZ5AXT.js → chunk-PNSCDBU7.js} +38 -56
  25. package/dist/{chunk-HSXMOS5Q.js → chunk-PTY2RG2K.js} +5 -2
  26. package/dist/{chunk-EPWBPIV6.js → chunk-PZPGJZRG.js} +9 -8
  27. package/dist/{chunk-P3ZNCHW7.js → chunk-QHMKIODQ.js} +19 -5
  28. package/dist/{chunk-HBULBDGP.js → chunk-S37DZPHW.js} +1 -1
  29. package/dist/{chunk-4I5OO5UB.js → chunk-TEOAKGZA.js} +0 -1
  30. package/dist/chunk-VUYCRBJK.js +174 -0
  31. package/dist/{chunk-4RNF2DRG.js → chunk-XDEPXTBY.js} +0 -2
  32. package/dist/{chunk-3SZNUCXI.js → chunk-Y5QECJX7.js} +223 -16
  33. package/dist/collab/rebase.d.ts +2 -1
  34. package/dist/collab/rebase.d.ts.map +1 -1
  35. package/dist/collab/rebase.js +20 -5
  36. package/dist/components/brandguide/ColorsEditable.d.ts.map +1 -1
  37. package/dist/components/chat/ChatPanel.d.ts.map +1 -1
  38. package/dist/components/chat/ChatWidget.d.ts.map +1 -1
  39. package/dist/components/docfile/DocFilePane.d.ts.map +1 -1
  40. package/dist/components/editor/ChildBlockWrapper.d.ts.map +1 -1
  41. package/dist/components/editor/ConflictDialog.d.ts.map +1 -1
  42. package/dist/components/editor/DragHandle.d.ts +12 -2
  43. package/dist/components/editor/DragHandle.d.ts.map +1 -1
  44. package/dist/components/editor/DropEdgeIndicator.d.ts +15 -6
  45. package/dist/components/editor/DropEdgeIndicator.d.ts.map +1 -1
  46. package/dist/components/editor/NavGroupRow.d.ts +4 -1
  47. package/dist/components/editor/NavGroupRow.d.ts.map +1 -1
  48. package/dist/components/editor/PagesModal.d.ts.map +1 -1
  49. package/dist/components/editor/SectionWrapper.d.ts +1 -1
  50. package/dist/components/editor/SectionWrapper.d.ts.map +1 -1
  51. package/dist/components/editor/SettingsForm.d.ts.map +1 -1
  52. package/dist/components/editor/TagIndicator.d.ts +15 -0
  53. package/dist/components/editor/TagIndicator.d.ts.map +1 -0
  54. package/dist/components/editor/index.js +148 -77
  55. package/dist/components/primitives/DocumentDropZone.d.ts +1 -1
  56. package/dist/components/primitives/EditableGrid.d.ts.map +1 -1
  57. package/dist/components/primitives/EditableList.d.ts.map +1 -1
  58. package/dist/components/primitives/HeadingSection.d.ts +4 -1
  59. package/dist/components/primitives/HeadingSection.d.ts.map +1 -1
  60. package/dist/components/primitives/ResolvedMedia.d.ts +5 -1
  61. package/dist/components/primitives/ResolvedMedia.d.ts.map +1 -1
  62. package/dist/components/primitives/{defineHeadingSection.d.ts → define-heading-section.d.ts} +1 -1
  63. package/dist/components/primitives/define-heading-section.d.ts.map +1 -0
  64. package/dist/components/primitives/{gridDrop.d.ts → grid-drop.d.ts} +1 -1
  65. package/dist/components/primitives/grid-drop.d.ts.map +1 -0
  66. package/dist/components/primitives/index.d.ts +1 -1
  67. package/dist/components/primitives/index.d.ts.map +1 -1
  68. package/dist/components/primitives/index.js +17 -154
  69. package/dist/components/sections/Button/CTAButton.d.ts.map +1 -1
  70. package/dist/components/sections/DoDontMedia/DoDontMediaBlock.d.ts +2 -6
  71. package/dist/components/sections/DoDontMedia/DoDontMediaBlock.d.ts.map +1 -1
  72. package/dist/components/sections/DoDontMedia/index.d.ts +8 -0
  73. package/dist/components/sections/DoDontMedia/index.d.ts.map +1 -1
  74. package/dist/components/sections/IconList/IconList.d.ts +2 -1
  75. package/dist/components/sections/IconList/IconList.d.ts.map +1 -1
  76. package/dist/components/sections/IconList/IconListSettings.d.ts +2 -1
  77. package/dist/components/sections/IconList/IconListSettings.d.ts.map +1 -1
  78. package/dist/components/sections/IconList/index.d.ts +1 -0
  79. package/dist/components/sections/IconList/index.d.ts.map +1 -1
  80. package/dist/components/sections/Media/MediaBlock.d.ts +2 -1
  81. package/dist/components/sections/Media/MediaBlock.d.ts.map +1 -1
  82. package/dist/components/sections/Media/index.d.ts +8 -0
  83. package/dist/components/sections/Media/index.d.ts.map +1 -1
  84. package/dist/components/sections/Reference/ReferenceList.d.ts +35 -0
  85. package/dist/components/sections/Reference/ReferenceList.d.ts.map +1 -0
  86. package/dist/components/sections/Reference/ReferenceSettings.d.ts +12 -0
  87. package/dist/components/sections/Reference/ReferenceSettings.d.ts.map +1 -0
  88. package/dist/components/sections/Reference/index.d.ts +9 -0
  89. package/dist/components/sections/Reference/index.d.ts.map +1 -0
  90. package/dist/components/sections/SectionSummary/SectionSummaryBlock.d.ts +38 -0
  91. package/dist/components/sections/SectionSummary/SectionSummaryBlock.d.ts.map +1 -0
  92. package/dist/components/sections/SectionSummary/SectionSummarySettings.d.ts +20 -0
  93. package/dist/components/sections/SectionSummary/SectionSummarySettings.d.ts.map +1 -0
  94. package/dist/components/sections/SectionSummary/index.d.ts +33 -0
  95. package/dist/components/sections/SectionSummary/index.d.ts.map +1 -0
  96. package/dist/components/sections/all-sections.d.ts +53 -0
  97. package/dist/components/sections/all-sections.d.ts.map +1 -1
  98. package/dist/components/sections/brand-guide.d.ts +41 -2
  99. package/dist/components/sections/brand-guide.d.ts.map +1 -1
  100. package/dist/components/sections/brand-guide.js +12 -10
  101. package/dist/components/sections/core-sections.d.ts +14 -0
  102. package/dist/components/sections/core-sections.d.ts.map +1 -1
  103. package/dist/components/sections/register-schemas.js +19 -17
  104. package/dist/components/sections/register.js +19 -17
  105. package/dist/components/shared/BrandSwatchesContext.d.ts +9 -0
  106. package/dist/components/shared/BrandSwatchesContext.d.ts.map +1 -0
  107. package/dist/components/shared/Button.d.ts +11 -3
  108. package/dist/components/shared/Button.d.ts.map +1 -1
  109. package/dist/components/shared/Checkbox.d.ts +6 -0
  110. package/dist/components/shared/Checkbox.d.ts.map +1 -1
  111. package/dist/components/shared/ConfirmDialog.d.ts +25 -0
  112. package/dist/components/shared/ConfirmDialog.d.ts.map +1 -0
  113. package/dist/components/shared/ConfirmInline.d.ts +25 -0
  114. package/dist/components/shared/ConfirmInline.d.ts.map +1 -0
  115. package/dist/components/shared/ErrorBoundary.d.ts.map +1 -1
  116. package/dist/components/shared/Fab.d.ts +11 -2
  117. package/dist/components/shared/Fab.d.ts.map +1 -1
  118. package/dist/components/shared/FabStack.d.ts +22 -0
  119. package/dist/components/shared/FabStack.d.ts.map +1 -0
  120. package/dist/components/shared/HistoryChangesModal.d.ts.map +1 -1
  121. package/dist/components/shared/HistoryPopover.d.ts.map +1 -1
  122. package/dist/components/shared/ImageBackgroundField.d.ts +9 -0
  123. package/dist/components/shared/ImageBackgroundField.d.ts.map +1 -0
  124. package/dist/components/shared/{UploadRejectionAlert.d.ts → InlineAlert.d.ts} +4 -2
  125. package/dist/components/shared/InlineAlert.d.ts.map +1 -0
  126. package/dist/components/shared/Input.d.ts +10 -0
  127. package/dist/components/shared/Input.d.ts.map +1 -1
  128. package/dist/components/shared/Navigation.d.ts +1 -0
  129. package/dist/components/shared/Navigation.d.ts.map +1 -1
  130. package/dist/components/shared/Notice.d.ts +10 -0
  131. package/dist/components/shared/Notice.d.ts.map +1 -0
  132. package/dist/components/shared/Popover.d.ts.map +1 -1
  133. package/dist/components/shared/ReferenceSourcesContext.d.ts +25 -0
  134. package/dist/components/shared/ReferenceSourcesContext.d.ts.map +1 -0
  135. package/dist/components/shared/Select.d.ts +3 -2
  136. package/dist/components/shared/Select.d.ts.map +1 -1
  137. package/dist/components/shared/SplitButton.d.ts.map +1 -1
  138. package/dist/components/shared/SummaryAnchorsContext.d.ts +10 -0
  139. package/dist/components/shared/SummaryAnchorsContext.d.ts.map +1 -0
  140. package/dist/components/shared/Textarea.d.ts +7 -0
  141. package/dist/components/shared/Textarea.d.ts.map +1 -1
  142. package/dist/components/shared/ToolbarFrame.d.ts +17 -0
  143. package/dist/components/shared/ToolbarFrame.d.ts.map +1 -0
  144. package/dist/components/shared/copy.d.ts +13 -0
  145. package/dist/components/shared/copy.d.ts.map +1 -0
  146. package/dist/components/shared/icons.d.ts +0 -12
  147. package/dist/components/shared/icons.d.ts.map +1 -1
  148. package/dist/components/shell/AudienceDetailsPanel.d.ts +7 -1
  149. package/dist/components/shell/AudienceDetailsPanel.d.ts.map +1 -1
  150. package/dist/components/shell/AudienceRow.d.ts.map +1 -1
  151. package/dist/components/shell/BugReportFAB.d.ts +7 -1
  152. package/dist/components/shell/BugReportFAB.d.ts.map +1 -1
  153. package/dist/components/shell/CredentialList.d.ts.map +1 -1
  154. package/dist/components/shell/DocInspector.d.ts.map +1 -1
  155. package/dist/components/shell/DocumentEditorOverlay.d.ts.map +1 -1
  156. package/dist/components/shell/EditorContext.d.ts +4 -0
  157. package/dist/components/shell/EditorContext.d.ts.map +1 -1
  158. package/dist/components/shell/EditorLoginForm.d.ts.map +1 -1
  159. package/dist/components/shell/EditorModal.d.ts.map +1 -1
  160. package/dist/components/shell/EditorShell.d.ts.map +1 -1
  161. package/dist/components/shell/HistoryToolbar.d.ts.map +1 -1
  162. package/dist/components/shell/MediaFolderTile.d.ts +4 -1
  163. package/dist/components/shell/MediaFolderTile.d.ts.map +1 -1
  164. package/dist/components/shell/MediaLibraryModal.d.ts.map +1 -1
  165. package/dist/components/shell/RestoreModal.d.ts.map +1 -1
  166. package/dist/components/shell/SiteSettingsChatbot.d.ts.map +1 -1
  167. package/dist/components/shell/SiteSettingsDisplay.d.ts.map +1 -1
  168. package/dist/components/shell/SiteSettingsModal.d.ts +3 -1
  169. package/dist/components/shell/SiteSettingsModal.d.ts.map +1 -1
  170. package/dist/components/shell/SiteSettingsTags.d.ts +13 -0
  171. package/dist/components/shell/SiteSettingsTags.d.ts.map +1 -0
  172. package/dist/components/shell/SiteSettingsUsers.d.ts.map +1 -1
  173. package/dist/components/shell/ViewerLoginForm.d.ts.map +1 -1
  174. package/dist/components/shell/{blockMoveDispatch.d.ts → block-move-dispatch.d.ts} +1 -1
  175. package/dist/components/shell/block-move-dispatch.d.ts.map +1 -0
  176. package/dist/components/shell/reference-sources.d.ts +22 -0
  177. package/dist/components/shell/reference-sources.d.ts.map +1 -0
  178. package/dist/deck/index.d.ts +2 -2
  179. package/dist/deck/index.d.ts.map +1 -1
  180. package/dist/deck/index.js +1 -3
  181. package/dist/deck-builder/index.js +11 -18
  182. package/dist/document/index.d.ts +2 -2
  183. package/dist/document/index.d.ts.map +1 -1
  184. package/dist/hooks/useBlockDnd.d.ts +11 -0
  185. package/dist/hooks/useBlockDnd.d.ts.map +1 -1
  186. package/dist/hooks/useBuildStatus.d.ts +1 -1
  187. package/dist/hooks/useBuildStatus.d.ts.map +1 -1
  188. package/dist/hooks/useDocumentUploads.d.ts +1 -1
  189. package/dist/hooks/useDocumentUploads.d.ts.map +1 -1
  190. package/dist/hooks/useEditorPersistence.d.ts +5 -1
  191. package/dist/hooks/useEditorPersistence.d.ts.map +1 -1
  192. package/dist/hooks/useEditorPublish.d.ts +2 -2
  193. package/dist/hooks/useEditorPublish.d.ts.map +1 -1
  194. package/dist/hooks/useNotice.d.ts +19 -0
  195. package/dist/hooks/useNotice.d.ts.map +1 -0
  196. package/dist/hooks/useSortableNavRow.d.ts +11 -0
  197. package/dist/hooks/useSortableNavRow.d.ts.map +1 -1
  198. package/dist/hooks/useSortableRow.d.ts +11 -0
  199. package/dist/hooks/useSortableRow.d.ts.map +1 -1
  200. package/dist/hooks/useStackedEscape.d.ts +32 -0
  201. package/dist/hooks/useStackedEscape.d.ts.map +1 -0
  202. package/dist/index.js +67 -49
  203. package/dist/lib/audience-secret.js +2 -2
  204. package/dist/lib/block-dnd.d.ts +5 -0
  205. package/dist/lib/block-dnd.d.ts.map +1 -1
  206. package/dist/lib/block-move.d.ts.map +1 -1
  207. package/dist/lib/brand-swatches.d.ts +12 -0
  208. package/dist/lib/brand-swatches.d.ts.map +1 -0
  209. package/dist/lib/container-ops.d.ts +3 -2
  210. package/dist/lib/container-ops.d.ts.map +1 -1
  211. package/dist/lib/dexie.d.ts.map +1 -1
  212. package/dist/lib/dexie.js +9 -1
  213. package/dist/lib/env.d.ts +4 -0
  214. package/dist/lib/env.d.ts.map +1 -1
  215. package/dist/lib/env.js +5 -3
  216. package/dist/lib/events.d.ts +1 -2
  217. package/dist/lib/events.d.ts.map +1 -1
  218. package/dist/lib/image-background.d.ts +8 -0
  219. package/dist/lib/image-background.d.ts.map +1 -0
  220. package/dist/lib/index-ops.js +3 -3
  221. package/dist/lib/index.d.ts +6 -4
  222. package/dist/lib/index.d.ts.map +1 -1
  223. package/dist/lib/index.js +29 -21
  224. package/dist/lib/loader.d.ts +21 -7
  225. package/dist/lib/loader.d.ts.map +1 -1
  226. package/dist/lib/nav-tree.d.ts +1 -0
  227. package/dist/lib/nav-tree.d.ts.map +1 -1
  228. package/dist/lib/nav.d.ts +38 -0
  229. package/dist/lib/nav.d.ts.map +1 -1
  230. package/dist/lib/platform-broker.d.ts +4 -2
  231. package/dist/lib/platform-broker.d.ts.map +1 -1
  232. package/dist/lib/platform-broker.js +21 -13
  233. package/dist/lib/reference-type.d.ts +2 -0
  234. package/dist/lib/reference-type.d.ts.map +1 -0
  235. package/dist/lib/reference-validation.d.ts +31 -0
  236. package/dist/lib/reference-validation.d.ts.map +1 -0
  237. package/dist/lib/reference-validation.js +39 -0
  238. package/dist/lib/references.d.ts +74 -0
  239. package/dist/lib/references.d.ts.map +1 -0
  240. package/dist/lib/registry.d.ts +35 -0
  241. package/dist/lib/registry.d.ts.map +1 -1
  242. package/dist/lib/{safeRedirect.d.ts → safe-redirect.d.ts} +1 -1
  243. package/dist/lib/safe-redirect.d.ts.map +1 -0
  244. package/dist/lib/tags.d.ts +58 -0
  245. package/dist/lib/tags.d.ts.map +1 -0
  246. package/dist/lib/theme-vars.d.ts +9 -0
  247. package/dist/lib/theme-vars.d.ts.map +1 -1
  248. package/dist/media/index.d.ts +1 -1
  249. package/dist/media/index.d.ts.map +1 -1
  250. package/dist/media/index.js +8 -4
  251. package/dist/media/resolve.d.ts +2 -0
  252. package/dist/media/resolve.d.ts.map +1 -1
  253. package/dist/media/utils.d.ts +2 -0
  254. package/dist/media/utils.d.ts.map +1 -1
  255. package/dist/media/{videoPoster.d.ts → video-poster.d.ts} +1 -1
  256. package/dist/media/video-poster.d.ts.map +1 -0
  257. package/dist/schemas/audience.js +2 -2
  258. package/dist/schemas/index.d.ts +1 -0
  259. package/dist/schemas/index.d.ts.map +1 -1
  260. package/dist/schemas/index.js +14 -8
  261. package/dist/schemas/shared.d.ts +26 -0
  262. package/dist/schemas/shared.d.ts.map +1 -1
  263. package/dist/schemas/site-config.d.ts +7 -0
  264. package/dist/schemas/site-config.d.ts.map +1 -1
  265. package/dist/schemas/tags.d.ts +27 -0
  266. package/dist/schemas/tags.d.ts.map +1 -0
  267. package/dist/storage/index.js +1 -1
  268. package/dist/types/database.d.ts +8 -0
  269. package/dist/types/database.d.ts.map +1 -1
  270. package/package.json +6 -1
  271. package/src/auth/cookies.ts +111 -0
  272. package/src/auth/types.ts +1 -0
  273. package/src/collab/rebase.ts +26 -2
  274. package/src/components/brandguide/ColorSwatchSettings.tsx +1 -1
  275. package/src/components/brandguide/ColorsEditable.tsx +1 -0
  276. package/src/components/chat/ChatPanel.tsx +2 -3
  277. package/src/components/chat/ChatWidget.tsx +7 -7
  278. package/src/components/docfile/DocFilePane.tsx +4 -12
  279. package/src/components/editor/ChildBlockWrapper.tsx +13 -19
  280. package/src/components/editor/ConflictDialog.tsx +27 -35
  281. package/src/components/editor/DeleteButton.tsx +2 -2
  282. package/src/components/editor/DragHandle.tsx +16 -3
  283. package/src/components/editor/DropEdgeIndicator.tsx +36 -10
  284. package/src/components/editor/InsertButton.tsx +2 -2
  285. package/src/components/editor/NavGroupRow.tsx +43 -14
  286. package/src/components/editor/PagesModal.tsx +99 -26
  287. package/src/components/editor/SectionWrapper.tsx +64 -23
  288. package/src/components/editor/SettingsButton.tsx +2 -2
  289. package/src/components/editor/SettingsForm.tsx +19 -2
  290. package/src/components/editor/StatusIndicator.tsx +4 -4
  291. package/src/components/editor/TagIndicator.tsx +80 -0
  292. package/src/components/primitives/DocumentDropZone.tsx +1 -1
  293. package/src/components/primitives/EditableGrid.tsx +15 -14
  294. package/src/components/primitives/EditableList.tsx +27 -74
  295. package/src/components/primitives/HeadingSection.tsx +5 -2
  296. package/src/components/primitives/ResolvedMedia.tsx +13 -0
  297. package/src/components/primitives/{defineHeadingSection.tsx → define-heading-section.tsx} +1 -0
  298. package/src/components/primitives/index.ts +1 -1
  299. package/src/components/primitives/useEditablePlainText.ts +5 -5
  300. package/src/components/sections/Button/CTAButton.tsx +3 -1
  301. package/src/components/sections/DoDontMedia/DoDontMediaBlock.tsx +4 -4
  302. package/src/components/sections/DoDontMedia/index.tsx +12 -3
  303. package/src/components/sections/IconList/DoDontToggle.tsx +2 -2
  304. package/src/components/sections/IconList/IconList.tsx +70 -37
  305. package/src/components/sections/IconList/IconListSettings.tsx +55 -17
  306. package/src/components/sections/IconList/index.tsx +10 -3
  307. package/src/components/sections/LinkHeading/index.tsx +1 -1
  308. package/src/components/sections/Media/MediaBlock.tsx +13 -2
  309. package/src/components/sections/Media/index.tsx +12 -4
  310. package/src/components/sections/Reference/ReferenceList.tsx +214 -0
  311. package/src/components/sections/Reference/ReferenceSettings.tsx +159 -0
  312. package/src/components/sections/Reference/index.tsx +47 -0
  313. package/src/components/sections/SectionSummary/SectionSummaryBlock.tsx +476 -0
  314. package/src/components/sections/SectionSummary/SectionSummarySettings.tsx +126 -0
  315. package/src/components/sections/SectionSummary/index.tsx +149 -0
  316. package/src/components/sections/SubHeading/index.tsx +1 -1
  317. package/src/components/sections/SubSubHeading/index.tsx +1 -1
  318. package/src/components/sections/brand-guide.ts +4 -3
  319. package/src/components/sections/core-sections.ts +2 -0
  320. package/src/components/shared/BrandSwatchesContext.ts +13 -0
  321. package/src/components/shared/Button.tsx +47 -34
  322. package/src/components/shared/Checkbox.tsx +21 -12
  323. package/src/components/shared/ConfirmDialog.tsx +87 -0
  324. package/src/components/shared/ConfirmInline.tsx +90 -0
  325. package/src/components/shared/ErrorBoundary.tsx +6 -18
  326. package/src/components/shared/Fab.tsx +15 -5
  327. package/src/components/shared/FabStack.tsx +28 -0
  328. package/src/components/shared/HistoryChangesModal.tsx +43 -41
  329. package/src/components/shared/HistoryPopover.tsx +7 -20
  330. package/src/components/shared/ImageBackgroundField.tsx +92 -0
  331. package/src/components/shared/{UploadRejectionAlert.tsx → InlineAlert.tsx} +21 -5
  332. package/src/components/shared/Input.tsx +23 -7
  333. package/src/components/shared/Navigation.tsx +48 -35
  334. package/src/components/shared/Notice.tsx +59 -0
  335. package/src/components/shared/Popover.tsx +7 -11
  336. package/src/components/shared/ReferenceSourcesContext.ts +27 -0
  337. package/src/components/shared/Select.tsx +5 -2
  338. package/src/components/shared/SplitButton.tsx +11 -17
  339. package/src/components/shared/SummaryAnchorsContext.ts +11 -0
  340. package/src/components/shared/Textarea.tsx +17 -2
  341. package/src/components/shared/ToolbarFrame.tsx +28 -0
  342. package/src/components/shared/VideoSourceField.tsx +1 -1
  343. package/src/components/shared/copy.ts +18 -0
  344. package/src/components/shared/icons.tsx +1 -17
  345. package/src/components/shell/AudienceAddForm.tsx +2 -2
  346. package/src/components/shell/AudienceDetailsPanel.tsx +23 -5
  347. package/src/components/shell/AudienceRow.tsx +22 -15
  348. package/src/components/shell/BugReportFAB.tsx +50 -40
  349. package/src/components/shell/CredentialList.tsx +33 -13
  350. package/src/components/shell/DocInspector.tsx +3 -12
  351. package/src/components/shell/DocumentEditorOverlay.tsx +33 -23
  352. package/src/components/shell/EditorContext.tsx +7 -0
  353. package/src/components/shell/EditorLoginForm.tsx +26 -36
  354. package/src/components/shell/EditorModal.tsx +8 -10
  355. package/src/components/shell/EditorShell.tsx +499 -339
  356. package/src/components/shell/HistoryToolbar.tsx +24 -26
  357. package/src/components/shell/MediaFolderTile.tsx +8 -3
  358. package/src/components/shell/MediaLibraryModal.tsx +59 -60
  359. package/src/components/shell/ProcessingIndicator.tsx +2 -2
  360. package/src/components/shell/RestoreModal.tsx +17 -16
  361. package/src/components/shell/SetPasswordForm.tsx +3 -3
  362. package/src/components/shell/SiteSettingsChatbot.tsx +61 -42
  363. package/src/components/shell/SiteSettingsDisplay.tsx +20 -21
  364. package/src/components/shell/SiteSettingsModal.tsx +24 -2
  365. package/src/components/shell/SiteSettingsTags.tsx +143 -0
  366. package/src/components/shell/SiteSettingsUsers.tsx +85 -28
  367. package/src/components/shell/SiteSettingsViewerAccess.tsx +2 -2
  368. package/src/components/shell/ViewerLoginForm.tsx +5 -4
  369. package/src/components/shell/reference-sources.ts +36 -0
  370. package/src/deck/index.ts +2 -2
  371. package/src/document/index.ts +2 -2
  372. package/src/hooks/useBlockDnd.ts +11 -0
  373. package/src/hooks/useBuildStatus.ts +53 -6
  374. package/src/hooks/useDocumentUploads.ts +1 -1
  375. package/src/hooks/useEditorPersistence.ts +141 -35
  376. package/src/hooks/useEditorPublish.ts +18 -27
  377. package/src/hooks/useMediaPipeline.ts +1 -1
  378. package/src/hooks/useNotice.ts +45 -0
  379. package/src/hooks/useSortableNavRow.ts +11 -0
  380. package/src/hooks/useSortableRow.ts +22 -2
  381. package/src/hooks/useStackedEscape.ts +62 -0
  382. package/src/lib/block-dnd.ts +6 -1
  383. package/src/lib/block-move.ts +3 -0
  384. package/src/lib/brand-swatches.ts +32 -0
  385. package/src/lib/container-ops.ts +5 -3
  386. package/src/lib/dexie.ts +15 -1
  387. package/src/lib/env.ts +9 -0
  388. package/src/lib/events.ts +1 -2
  389. package/src/lib/image-background.ts +31 -0
  390. package/src/lib/index.ts +5 -4
  391. package/src/lib/loader.ts +54 -55
  392. package/src/lib/nav-tree.ts +71 -0
  393. package/src/lib/nav.ts +196 -36
  394. package/src/lib/platform-broker.ts +32 -14
  395. package/src/lib/reference-type.ts +3 -0
  396. package/src/lib/reference-validation.ts +69 -0
  397. package/src/lib/references.ts +130 -0
  398. package/src/lib/registry.ts +37 -0
  399. package/src/lib/tags.ts +148 -0
  400. package/src/lib/theme-vars.ts +23 -0
  401. package/src/media/index.ts +1 -1
  402. package/src/media/resolve.ts +3 -1
  403. package/src/media/utils.ts +18 -0
  404. package/src/media/{videoPoster.ts → video-poster.ts} +2 -2
  405. package/src/schemas/index.ts +1 -0
  406. package/src/schemas/shared.ts +24 -1
  407. package/src/schemas/site-config.ts +9 -1
  408. package/src/schemas/tags.ts +33 -0
  409. package/src/storage/types.ts +1 -1
  410. package/src/types/database.ts +6 -0
  411. package/dist/chunk-BR4MIPTM.js +0 -138
  412. package/dist/chunk-DNY72JZN.js +0 -86
  413. package/dist/chunk-F2LYQPXV.js +0 -77
  414. package/dist/chunk-TALRVOGO.js +0 -128
  415. package/dist/components/primitives/defineHeadingSection.d.ts.map +0 -1
  416. package/dist/components/primitives/gridDrop.d.ts.map +0 -1
  417. package/dist/components/shared/UploadRejectionAlert.d.ts.map +0 -1
  418. package/dist/components/shell/BuildStatusIndicator.d.ts +0 -7
  419. package/dist/components/shell/BuildStatusIndicator.d.ts.map +0 -1
  420. package/dist/components/shell/blockMoveDispatch.d.ts.map +0 -1
  421. package/dist/hooks/index.d.ts +0 -9
  422. package/dist/hooks/index.d.ts.map +0 -1
  423. package/dist/lib/grid.d.ts +0 -2
  424. package/dist/lib/grid.d.ts.map +0 -1
  425. package/dist/lib/safeRedirect.d.ts.map +0 -1
  426. package/dist/media/videoPoster.d.ts.map +0 -1
  427. package/src/components/brandguide/ColorSwatchSettings.d.ts.map +0 -1
  428. package/src/components/brandguide/Colors.d.ts.map +0 -1
  429. package/src/components/primitives/CustomParagraph.d.ts.map +0 -1
  430. package/src/components/primitives/EditableGrid.d.ts.map +0 -1
  431. package/src/components/primitives/EditablePlainText.d.ts.map +0 -1
  432. package/src/components/primitives/EditableRichText.d.ts.map +0 -1
  433. package/src/components/primitives/HeadingSection.d.ts.map +0 -1
  434. package/src/components/primitives/IconPicker.d.ts.map +0 -1
  435. package/src/components/primitives/LinkPopover.d.ts.map +0 -1
  436. package/src/components/primitives/MediaSettingsForms.d.ts.map +0 -1
  437. package/src/components/primitives/ResolvedMedia.d.ts.map +0 -1
  438. package/src/components/primitives/RichTextToolbar.d.ts.map +0 -1
  439. package/src/components/primitives/tiptap-presets.d.ts.map +0 -1
  440. package/src/components/primitives/useEditableCollection.d.ts.map +0 -1
  441. package/src/components/primitives/useEditablePlainText.d.ts.map +0 -1
  442. package/src/components/primitives/useEditableRichText.d.ts.map +0 -1
  443. package/src/components/sections/Button/CTAButton.d.ts.map +0 -1
  444. package/src/components/sections/Button/index.d.ts.map +0 -1
  445. package/src/components/sections/Colors/index.d.ts.map +0 -1
  446. package/src/components/sections/IconList/IconList.d.ts.map +0 -1
  447. package/src/components/sections/IconList/IconListSettings.d.ts.map +0 -1
  448. package/src/components/sections/IconList/index.d.ts.map +0 -1
  449. package/src/components/sections/LinkHeading/index.d.ts.map +0 -1
  450. package/src/components/sections/Prose/Prose.d.ts.map +0 -1
  451. package/src/components/sections/Prose/index.d.ts.map +0 -1
  452. package/src/components/sections/SubHeading/index.d.ts.map +0 -1
  453. package/src/components/sections/SubSubHeading/index.d.ts.map +0 -1
  454. package/src/components/sections/register-schemas.d.ts.map +0 -1
  455. package/src/components/shared/Button.d.ts.map +0 -1
  456. package/src/components/shared/Checkbox.d.ts.map +0 -1
  457. package/src/components/shared/FormLabel.d.ts.map +0 -1
  458. package/src/components/shared/IconButton.d.ts.map +0 -1
  459. package/src/components/shared/Input.d.ts.map +0 -1
  460. package/src/components/shared/Select.d.ts.map +0 -1
  461. package/src/components/shared/icons.d.ts.map +0 -1
  462. package/src/components/shell/BuildStatusIndicator.tsx +0 -59
  463. package/src/components/shell/MediaLibraryContext.d.ts.map +0 -1
  464. package/src/hooks/index.ts +0 -8
  465. package/src/hooks/useResolvedMedia.d.ts.map +0 -1
  466. package/src/lib/cn.d.ts.map +0 -1
  467. package/src/lib/grid.d.ts.map +0 -1
  468. package/src/lib/grid.ts +0 -7
  469. package/src/lib/icons.d.ts.map +0 -1
  470. package/src/lib/loader.d.ts.map +0 -1
  471. package/src/lib/nav.d.ts.map +0 -1
  472. package/src/lib/registry.d.ts.map +0 -1
  473. package/src/lib/sanitize.d.ts.map +0 -1
  474. package/src/media/github.d.ts.map +0 -1
  475. package/src/media/index.d.ts.map +0 -1
  476. package/src/media/queue.d.ts.map +0 -1
  477. package/src/media/resolve.d.ts.map +0 -1
  478. package/src/media/types.d.ts.map +0 -1
  479. package/src/media/utils.d.ts.map +0 -1
  480. package/src/media/videoPoster.d.ts.map +0 -1
  481. package/src/schemas/auth.d.ts.map +0 -1
  482. package/src/schemas/media-grid-options.d.ts.map +0 -1
  483. package/src/schemas/media.d.ts.map +0 -1
  484. package/src/schemas/sections.d.ts.map +0 -1
  485. package/src/schemas/shared.d.ts.map +0 -1
  486. package/src/schemas/site-config.d.ts.map +0 -1
  487. /package/dist/{chunk-B7VBSE5X.js → chunk-3JDTXOIG.js} +0 -0
  488. /package/dist/{chunk-CXZ5YJG4.js → chunk-L45X7DKX.js} +0 -0
  489. /package/src/components/primitives/{gridDrop.ts → grid-drop.ts} +0 -0
  490. /package/src/components/shell/{blockMoveDispatch.ts → block-move-dispatch.ts} +0 -0
  491. /package/src/lib/{safeRedirect.ts → safe-redirect.ts} +0 -0
@@ -5,6 +5,7 @@ import { ColorPicker } from "../shared/ColorPicker";
5
5
  import { FontPicker } from "../shared/FontPicker";
6
6
  import { Input } from "../shared/Input";
7
7
  import { Select } from "../shared/Select";
8
+ import { Textarea } from "../shared/Textarea";
8
9
  import { SubTabs } from "../shared/SubTabs";
9
10
  import { FormLabel } from "../shared/FormLabel";
10
11
  import { useMediaLibrary } from "./MediaLibraryContext";
@@ -12,22 +13,13 @@ import { contrastRatio, wcagLevel, suggestAccessibleOn } from "../../lib/contras
12
13
  import { cn } from "../../lib/cn";
13
14
  import { buildGoogleFontsUrl } from "../../lib/google-fonts";
14
15
  import type { SiteConfig } from "../../schemas/site-config";
15
- import type { MediaItem } from "../../media/types";
16
+ import { imageThumbnailUrl } from "../../media/utils";
16
17
 
17
18
  interface Props {
18
19
  siteConfig: SiteConfig;
19
20
  onChange: (config: SiteConfig) => void;
20
21
  }
21
22
 
22
- // Smallest generated variant for the editor-preview thumbnail (mirrors
23
- // DocInspector's thumbSrc) — avoids needing a MediaProvider in this context.
24
- function thumbSrc(imageId: string, item: MediaItem | undefined): string | null {
25
- if (!item || item.kind !== "image") return null;
26
- if (item.mimeType === "image/svg+xml") return `/api/media/${imageId}/original.svg`;
27
- const widths = item.variants.map((v) => v.width).sort((a, b) => a - b);
28
- if (widths.length === 0) return null;
29
- return `/api/media/${imageId}/${widths[0]}.webp`;
30
- }
31
23
 
32
24
  const darkModeOptions = [
33
25
  { label: "Light", value: "light" },
@@ -63,7 +55,7 @@ function ContrastRow({ label, entries }: { label: string; entries: ContrastEntry
63
55
  <span className="text-base-contrast-light">{label}</span>
64
56
  {entries.map((e) => {
65
57
  const level = wcagLevel(e.ratio);
66
- const tone = level.aa ? "text-green-600" : level.aaLarge ? "text-amber-600" : "text-red-600";
58
+ const tone = level.aa ? "text-success" : level.aaLarge ? "text-warning" : "text-danger";
67
59
  const mark = level.aa ? "AA" : level.aaLarge ? "large only" : "fail";
68
60
  return (
69
61
  <span key={e.mode} className="inline-flex items-center gap-1">
@@ -88,7 +80,7 @@ export function SiteSettingsDisplay({ siteConfig, onChange }: Props) {
88
80
 
89
81
  function pickLogo(which: "light" | "dark") {
90
82
  mediaLibrary?.openSelectModal((imageId) => {
91
- const base = siteConfig.logo ?? { light: { imageId: "" }, dark: null, invertInDark: false, showInNav: true };
83
+ const base = siteConfig.logo ?? { light: { imageId: "" }, dark: null, invertInDark: false, showInNav: true, hideTitle: true };
92
84
  update({ logo: { ...base, [which]: { imageId } } });
93
85
  });
94
86
  }
@@ -154,16 +146,14 @@ export function SiteSettingsDisplay({ siteConfig, onChange }: Props) {
154
146
  />
155
147
 
156
148
  <div>
157
- <FormLabel htmlFor="site-description">Description</FormLabel>
158
- <textarea
159
- id="site-description"
149
+ <Textarea
150
+ label="Description"
160
151
  aria-label="Site description"
161
152
  rows={2}
162
153
  maxLength={300}
163
154
  value={siteConfig.description ?? ""}
164
- onChange={(e) => update({ description: e.target.value || null })}
155
+ onChange={(value) => update({ description: value || null })}
165
156
  placeholder="Short summary for search results and social shares."
166
- className="w-full rounded border border-base-200 bg-base px-3 py-2 text-sm text-base-contrast"
167
157
  />
168
158
  <div className="mt-1 flex items-start justify-between gap-2">
169
159
  <p className="text-xs text-base-contrast/70">
@@ -214,13 +204,13 @@ export function SiteSettingsDisplay({ siteConfig, onChange }: Props) {
214
204
  )}
215
205
  </div>
216
206
  <p className="mt-1 text-xs text-base-contrast/70">SVG or PNG, up to 100KB. Shown in browser tabs.</p>
217
- {faviconError && <p className="mt-1 text-xs text-red-600">{faviconError}</p>}
207
+ {faviconError && <p className="mt-1 text-xs text-danger">{faviconError}</p>}
218
208
  </div>
219
209
 
220
210
  {(() => {
221
211
  const logo = siteConfig.logo;
222
- const lightSrc = logo ? thumbSrc(logo.light.imageId, mediaLibrary?.manifest.images[logo.light.imageId]) : null;
223
- const darkSrc = logo?.dark ? thumbSrc(logo.dark.imageId, mediaLibrary?.manifest.images[logo.dark.imageId]) : null;
212
+ const lightSrc = logo ? imageThumbnailUrl(mediaLibrary?.manifest.images[logo.light.imageId]) : null;
213
+ const darkSrc = logo?.dark ? imageThumbnailUrl(mediaLibrary?.manifest.images[logo.dark.imageId]) : null;
224
214
  const needsDarkHint = logo != null && siteConfig.darkMode !== "light" && !logo.dark && !logo.invertInDark;
225
215
 
226
216
  return (
@@ -292,8 +282,17 @@ export function SiteSettingsDisplay({ siteConfig, onChange }: Props) {
292
282
  label="Show in navigation"
293
283
  />
294
284
 
285
+ {/* Not gated on showInNav: the login page shows the logo either
286
+ way, so this has to stay reachable when the nav one is off. */}
287
+ <Checkbox
288
+ checked={logo.hideTitle}
289
+ onChange={(v) => update({ logo: { ...logo, hideTitle: v } })}
290
+ label="Hide site title"
291
+ description="The logo stands in for the site name in the navigation and on the login page. Turn this off to show both."
292
+ />
293
+
295
294
  {needsDarkHint && (
296
- <p className="text-xs text-amber-600">
295
+ <p className="text-xs text-warning">
297
296
  Add a dark variant or enable invert so the logo stays legible in dark mode.
298
297
  </p>
299
298
  )}
@@ -7,6 +7,8 @@ import { SiteSettingsDisplay } from "./SiteSettingsDisplay";
7
7
  import { SiteSettingsUsers } from "./SiteSettingsUsers";
8
8
  import { SiteSettingsChatbot } from "./SiteSettingsChatbot";
9
9
  import { SiteSettingsDecks } from "./SiteSettingsDecks";
10
+ import { SiteSettingsTags } from "./SiteSettingsTags";
11
+ import type { SiteTagging } from "../../lib/registry";
10
12
  import type { SiteConfig } from "../../schemas/site-config";
11
13
  import type { Audience } from "../../auth/types";
12
14
 
@@ -27,9 +29,13 @@ interface Props {
27
29
  passwordToggle: boolean;
28
30
  };
29
31
  currentUser: { email: string; role: "owner" | "editor" } | null;
32
+ tagging: SiteTagging;
30
33
  }
31
34
 
32
- export function SiteSettingsModal({ isOpen, onClose, siteConfig, onSiteConfigChange, onAudiencesChange, audiences, chatbotPlatformEnabled, capabilities, currentUser }: Props) {
35
+ export function SiteSettingsModal({
36
+ isOpen, onClose, siteConfig, onSiteConfigChange, onAudiencesChange, audiences, chatbotPlatformEnabled, capabilities, currentUser,
37
+ tagging,
38
+ }: Props) {
33
39
  const [signOutError, setSignOutError] = useState<string | null>(null);
34
40
 
35
41
  async function handleSignOut() {
@@ -92,6 +98,22 @@ export function SiteSettingsModal({ isOpen, onClose, siteConfig, onSiteConfigCha
92
98
  </div>
93
99
  ),
94
100
  },
101
+ {
102
+ id: "tags",
103
+ label: "Tags",
104
+ show: true,
105
+ content: (
106
+ <div data-testid="site-settings-tab-panel" className="flex flex-1 flex-col overflow-y-auto px-6 py-4">
107
+ <SiteSettingsTags
108
+ tags={tagging.tags}
109
+ editable={tagging.editable}
110
+ onCreate={(label) => tagging.onCreate(label)}
111
+ onRename={tagging.onRename}
112
+ onDelete={tagging.onDelete}
113
+ />
114
+ </div>
115
+ ),
116
+ },
95
117
  {
96
118
  id: "display",
97
119
  label: "Display",
@@ -130,7 +152,7 @@ export function SiteSettingsModal({ isOpen, onClose, siteConfig, onSiteConfigCha
130
152
  <span className="ml-1 capitalize">({currentUser.role})</span>
131
153
  )}
132
154
  {signOutError && (
133
- <span className="ml-2 text-red-600">— {signOutError}</span>
155
+ <span className="ml-2 text-danger">— {signOutError}</span>
134
156
  )}
135
157
  </div>
136
158
  <Button
@@ -0,0 +1,143 @@
1
+ import { useRef, useState, type FormEvent } from "react";
2
+ import { pluralize } from "../../lib/text";
3
+ import { findTagByLabel, tagDisplayLabel } from "../../lib/tags";
4
+ import type { TagEntry } from "../../schemas/tags";
5
+ import { Button } from "../shared/Button";
6
+ import { ConfirmInline } from "../shared/ConfirmInline";
7
+ import { Input } from "../shared/Input";
8
+
9
+ interface Props {
10
+ /** The site's tag registry, already reconciled (siteTags). */
11
+ tags: TagEntry[];
12
+ /** False when the registry was never loaded, or loaded degraded; the tab then edits nothing. */
13
+ editable: boolean;
14
+ onCreate: (label: string) => void;
15
+ onRename: (tagId: string, label: string) => void;
16
+ onDelete: (tagId: string) => void;
17
+ }
18
+
19
+ export function SiteSettingsTags({ tags, editable, onCreate, onRename, onDelete }: Props) {
20
+ const [draft, setDraft] = useState("");
21
+ const [error, setError] = useState<string | null>(null);
22
+
23
+ function submit(e: FormEvent) {
24
+ e.preventDefault();
25
+ const label = draft.trim();
26
+ if (!label) return;
27
+ if (findTagByLabel(tags, label)) {
28
+ setError(`A tag named "${label}" already exists.`);
29
+ return;
30
+ }
31
+ onCreate(label);
32
+ setDraft("");
33
+ setError(null);
34
+ }
35
+
36
+ const header = (
37
+ <div>
38
+ <h3 className="mb-1 text-sm font-medium text-base-contrast">Section tags</h3>
39
+ <p className="text-xs text-base-contrast-light">
40
+ Tags group related sections across pages. Put them on a section from the Tags pill above it. Viewers never see
41
+ tag names.
42
+ </p>
43
+ </div>
44
+ );
45
+
46
+ if (!editable) {
47
+ return (
48
+ <div className="space-y-4">
49
+ {header}
50
+ <p className="text-xs text-danger">Tags could not be loaded in full for this session, so they are read-only. Reload the editor to manage them.</p>
51
+ </div>
52
+ );
53
+ }
54
+
55
+ return (
56
+ <div className="space-y-4">
57
+ {header}
58
+
59
+ <form onSubmit={submit} className="flex items-start gap-2">
60
+ <Input
61
+ label="New tag"
62
+ hideLabel
63
+ className="flex-1"
64
+ value={draft}
65
+ placeholder="New tag name"
66
+ error={error ?? undefined}
67
+ onChange={(v) => { setDraft(v); setError(null); }}
68
+ />
69
+ <Button type="submit" variant="secondary">Add tag</Button>
70
+ </form>
71
+
72
+ {tags.length === 0 ? (
73
+ <p className="text-xs text-base-contrast-light">No tags yet.</p>
74
+ ) : (
75
+ <ul className="space-y-2">
76
+ {tags.map((t) => (
77
+ <TagRow
78
+ key={t.id}
79
+ tag={t}
80
+ duplicate={t.label.trim() !== "" && findTagByLabel(tags, t.label)?.id !== t.id}
81
+ onRename={(label) => onRename(t.id, label)}
82
+ onDelete={() => onDelete(t.id)}
83
+ />
84
+ ))}
85
+ </ul>
86
+ )}
87
+ </div>
88
+ );
89
+ }
90
+
91
+ function TagRow({ tag, duplicate, onRename, onDelete }: {
92
+ tag: TagEntry;
93
+ /** An earlier tag already has this name — inline creates would pick that one. */
94
+ duplicate: boolean;
95
+ onRename: (label: string) => void;
96
+ onDelete: () => void;
97
+ }) {
98
+ const [confirming, setConfirming] = useState(false);
99
+ const deleteRef = useRef<HTMLButtonElement>(null);
100
+ const count = tag.sectionIds.length;
101
+ const name = tagDisplayLabel(tag.label);
102
+
103
+ return (
104
+ <li className="rounded-md border border-base-200 px-3 py-2">
105
+ <div className="flex items-center gap-3">
106
+ <Input
107
+ label="Tag name"
108
+ hideLabel
109
+ className="min-w-0 flex-1"
110
+ value={tag.label}
111
+ placeholder="Untitled tag"
112
+ error={duplicate ? "Another tag already has this name." : undefined}
113
+ onChange={onRename}
114
+ />
115
+ <span className="shrink-0 text-xs text-base-contrast/60">{pluralize(count, "section")}</span>
116
+ {!confirming && (
117
+ <Button
118
+ ref={deleteRef}
119
+ variant="destructive"
120
+ size="sm"
121
+ aria-label={`Delete tag ${name}`}
122
+ onClick={() => (count === 0 ? onDelete() : setConfirming(true))}
123
+ >
124
+ Delete
125
+ </Button>
126
+ )}
127
+ </div>
128
+ {confirming && (
129
+ <div className="mt-2">
130
+ <ConfirmInline
131
+ message={`Delete "${name}"? It is removed from ${pluralize(count, "section")}. The sections themselves stay.`}
132
+ confirmLabel="Delete"
133
+ confirmAriaLabel="Confirm delete"
134
+ tone="destructive"
135
+ onConfirm={onDelete}
136
+ onCancel={() => setConfirming(false)}
137
+ returnFocusTo={deleteRef}
138
+ />
139
+ </div>
140
+ )}
141
+ </li>
142
+ );
143
+ }
@@ -1,7 +1,9 @@
1
- import { Fragment, useState, useEffect, type FormEvent } from "react";
1
+ import { Fragment, useRef, useState, useEffect, type FormEvent, type RefObject } from "react";
2
2
  import { ChevronDown } from "lucide-react";
3
3
  import type { Role } from "../../auth/types";
4
4
  import { Button } from "../shared/Button";
5
+ import { ConfirmInline } from "../shared/ConfirmInline";
6
+ import { Input } from "../shared/Input";
5
7
 
6
8
  interface User {
7
9
  id: string;
@@ -21,12 +23,25 @@ export function SiteSettingsUsers({ currentUser }: Props) {
21
23
  const [inviteRole, setInviteRole] = useState<Role>("editor");
22
24
  const [error, setError] = useState<string | null>(null);
23
25
  const [confirmingRevoke, setConfirmingRevoke] = useState<string | null>(null);
26
+ // One stable ref object per user, so a row's Remove button and its
27
+ // ConfirmInline share the same object across the swap — the button
28
+ // reattaches to it (layout phase) before ConfirmInline's cleanup effect
29
+ // (passive phase) reads it back to return focus.
30
+ const removeButtonRefs = useRef(new Map<string, RefObject<HTMLButtonElement | null>>());
31
+ function getRemoveButtonRef(userId: string): RefObject<HTMLButtonElement | null> {
32
+ let ref = removeButtonRefs.current.get(userId);
33
+ if (!ref) {
34
+ ref = { current: null };
35
+ removeButtonRefs.current.set(userId, ref);
36
+ }
37
+ return ref;
38
+ }
24
39
 
25
40
  async function loadUsers() {
26
41
  try {
27
42
  const res = await fetch("/api/auth/users");
28
43
  if (!res.ok) {
29
- setError("Failed to load users");
44
+ setError("Couldn't load users");
30
45
  setUsers([]);
31
46
  return;
32
47
  }
@@ -83,6 +98,29 @@ export function SiteSettingsUsers({ currentUser }: Props) {
83
98
  setConfirmingRevoke(userId);
84
99
  }
85
100
 
101
+ async function handleRoleChange(userId: string, role: Role) {
102
+ setError(null);
103
+ try {
104
+ const res = await fetch("/api/auth/users", {
105
+ method: "PATCH",
106
+ headers: { "Content-Type": "application/json" },
107
+ body: JSON.stringify({ userId, role }),
108
+ });
109
+ if (!res.ok) {
110
+ const data = await res.json().catch(() => ({}));
111
+ // Resync first: a rejected change would otherwise leave the select
112
+ // showing a role the user does not have. loadUsers() clears `error` on
113
+ // success, so the message has to be set after it, not before.
114
+ await loadUsers();
115
+ setError(data.error || "Role change failed");
116
+ return;
117
+ }
118
+ await loadUsers();
119
+ } catch {
120
+ setError("Network error while changing role");
121
+ }
122
+ }
123
+
86
124
  async function confirmRevoke(userId: string) {
87
125
  setConfirmingRevoke(null);
88
126
  try {
@@ -114,33 +152,36 @@ export function SiteSettingsUsers({ currentUser }: Props) {
114
152
  {users.map((user) => (
115
153
  <Fragment key={user.id}>
116
154
  <div className="flex items-center border-b border-base-200 py-2 pr-2 text-base-contrast">{user.email}</div>
117
- <div className="flex items-center border-b border-base-200 py-2 pr-2 capitalize text-base-contrast-light">
118
- {user.role}
155
+ <div className="flex items-center border-b border-base-200 py-2 pr-2 text-base-contrast-light">
156
+ {currentUser?.email === user.email ? (
157
+ // Owners can't change their own role — the same rule that makes
158
+ // this row's Remove control a dash.
159
+ <span className="capitalize">{user.role}</span>
160
+ ) : (
161
+ <div className="relative w-full">
162
+ <select
163
+ value={user.role}
164
+ onChange={(e) => handleRoleChange(user.id, e.target.value as Role)}
165
+ aria-label={`Role for ${user.email}`}
166
+ className="w-full appearance-none rounded border border-base-200 bg-base py-1 pr-7 pl-2 text-sm text-base-contrast focus:border-base-contrast focus:outline-none focus:ring-1 focus:ring-base-contrast"
167
+ >
168
+ <option value="editor">Editor</option>
169
+ <option value="owner">Owner</option>
170
+ </select>
171
+ <ChevronDown
172
+ size={14}
173
+ className="pointer-events-none absolute right-2 top-1/2 -translate-y-1/2 text-base-contrast-light"
174
+ />
175
+ </div>
176
+ )}
119
177
  </div>
120
178
  <div className="flex items-center justify-end border-b border-base-200 py-2">
121
179
  {currentUser?.email === user.email ? (
122
180
  <span className="text-xs text-base-contrast-light">—</span>
123
- ) : confirmingRevoke === user.id ? (
124
- <span className="flex items-center gap-1">
125
- <Button
126
- type="button"
127
- variant="ghost"
128
- tone="destructive"
129
- onClick={() => confirmRevoke(user.id)}
130
- >
131
- Confirm
132
- </Button>
133
- <Button
134
- type="button"
135
- variant="ghost"
136
- onClick={() => setConfirmingRevoke(null)}
137
- >
138
- Cancel
139
- </Button>
140
- </span>
141
- ) : (
181
+ ) : confirmingRevoke === user.id ? null : (
142
182
  <Button
143
183
  type="button"
184
+ ref={getRemoveButtonRef(user.id)}
144
185
  variant="ghost"
145
186
  tone="destructive"
146
187
  onClick={() => handleRevoke(user.id)}
@@ -149,20 +190,32 @@ export function SiteSettingsUsers({ currentUser }: Props) {
149
190
  </Button>
150
191
  )}
151
192
  </div>
193
+ {confirmingRevoke === user.id && (
194
+ <div className="col-span-3 pb-2">
195
+ <ConfirmInline
196
+ message={`Remove ${user.email}?`}
197
+ confirmLabel="Remove"
198
+ tone="destructive"
199
+ onConfirm={() => confirmRevoke(user.id)}
200
+ onCancel={() => setConfirmingRevoke(null)}
201
+ returnFocusTo={getRemoveButtonRef(user.id)}
202
+ />
203
+ </div>
204
+ )}
152
205
  </Fragment>
153
206
  ))}
154
207
  </div>
155
208
 
156
209
  <form onSubmit={handleInvite} className="grid grid-cols-[1fr_7rem_5rem] text-sm">
157
210
  <div className="pr-2">
158
- <input
211
+ <Input
212
+ label="Email address"
213
+ hideLabel
159
214
  type="email"
160
215
  placeholder="Email address"
161
216
  value={inviteEmail}
162
- onChange={(e) => setInviteEmail(e.target.value)}
217
+ onChange={setInviteEmail}
163
218
  required
164
- aria-label="Email address"
165
- className="w-full rounded border border-base-200 bg-base px-3 py-2 text-sm text-base-contrast focus:border-base-contrast focus:outline-none focus:ring-1 focus:ring-base-contrast"
166
219
  />
167
220
  </div>
168
221
  <div className="pr-2">
@@ -187,7 +240,11 @@ export function SiteSettingsUsers({ currentUser }: Props) {
187
240
  </Button>
188
241
  </form>
189
242
 
190
- {error && <p className="text-sm text-red-600">{error}</p>}
243
+ <p className="text-xs text-base-contrast-light">
244
+ Role changes take effect the next time the user signs in, or within 24 hours.
245
+ </p>
246
+
247
+ {error && <p className="text-sm text-danger">{error}</p>}
191
248
  </div>
192
249
  );
193
250
  }
@@ -153,7 +153,7 @@ export function SiteSettingsViewerAccess({ audienceManagement, passwordToggle, o
153
153
  // Deliberately a hard stop rather than an empty list: rendering the normal UI
154
154
  // here would show "no audiences" for a site that has them.
155
155
  if (loadError) {
156
- return <p className="text-sm text-red-600">{loadError}</p>;
156
+ return <p className="text-sm text-danger">{loadError}</p>;
157
157
  }
158
158
 
159
159
  return (
@@ -182,7 +182,7 @@ export function SiteSettingsViewerAccess({ audienceManagement, passwordToggle, o
182
182
  ? "Viewers must sign in with a username and password to see any content."
183
183
  : "Viewers must select an audience and enter a password to see any content."}
184
184
  </p>
185
- {toggleError && <p className="text-xs text-red-600">{toggleError}</p>}
185
+ {toggleError && <p className="text-xs text-danger">{toggleError}</p>}
186
186
  </div>
187
187
  )}
188
188
 
@@ -3,7 +3,8 @@ import { Input } from "../shared/Input";
3
3
  import { PasswordInput } from "../shared/PasswordInput";
4
4
  import { Button } from "../shared/Button";
5
5
  import { Select } from "../shared/Select";
6
- import { safeRedirect } from "../../lib/safeRedirect";
6
+ import { safeRedirect } from "../../lib/safe-redirect";
7
+ import { COPY } from "../shared/copy";
7
8
 
8
9
  interface Props {
9
10
  /** Audience mode only. Empty (and unused) in credential mode — the page must
@@ -55,7 +56,7 @@ export function ViewerLoginForm({ audiences, mode = "audience", next }: Props) {
55
56
 
56
57
  if (!response.ok) {
57
58
  const data = await response.json().catch(() => ({}));
58
- setError(data.error || "Access denied");
59
+ setError(data.error || COPY.auth.signInFallback);
59
60
  return;
60
61
  }
61
62
 
@@ -99,7 +100,7 @@ export function ViewerLoginForm({ audiences, mode = "audience", next }: Props) {
99
100
  )}
100
101
 
101
102
  {error && (
102
- <p className="text-sm text-red-600">{error}</p>
103
+ <p className="text-sm text-danger">{error}</p>
103
104
  )}
104
105
 
105
106
  <Button
@@ -108,7 +109,7 @@ export function ViewerLoginForm({ audiences, mode = "audience", next }: Props) {
108
109
  size="md"
109
110
  disabled={loading || (credentialMode ? username.trim().length === 0 : !audience)}
110
111
  isLoading={loading}
111
- loadingLabel="Verifying..."
112
+ loadingLabel="Verifying…"
112
113
  className="w-full"
113
114
  >
114
115
  Enter
@@ -0,0 +1,36 @@
1
+ import type { Audience } from "../../schemas/auth";
2
+ import type { SiteIndex } from "../../schemas/site-config";
3
+ import type { LoadedSection } from "../../lib/loader";
4
+ import type { MediaManifest } from "../../media/types";
5
+ import { pageById } from "../../lib/pages";
6
+ import { pageReferenceCopies } from "../../lib/nav";
7
+ import type { ReferenceSources } from "../shared/ReferenceSourcesContext";
8
+
9
+ /**
10
+ * What References resolve against in the Published preview: main's content
11
+ * (`viewIndex` once the history/view fetch lands, else the `mainIndex` the
12
+ * load reported), live sections only — the same content and page the viewer
13
+ * would expand. Null when there is no published view to resolve against, and
14
+ * the canvas falls back to the working copy.
15
+ */
16
+ export function publishedReferenceSources(opts: {
17
+ viewSections: LoadedSection[] | null;
18
+ viewIndex: SiteIndex | null;
19
+ mainIndex: SiteIndex | null;
20
+ activePageId: string | null;
21
+ audiences: Audience[];
22
+ mediaManifest: MediaManifest;
23
+ jumpToSection: (sectionId: string) => void;
24
+ }): ReferenceSources | null {
25
+ const { viewSections, activePageId, audiences, mediaManifest, jumpToSection } = opts;
26
+ const index = opts.viewIndex ?? opts.mainIndex;
27
+ if (!viewSections || !index || !activePageId) return null;
28
+ const byId = new Map(viewSections.map((s) => [s.section.id, s]));
29
+ const pageSections = (pageById(index, activePageId)?.order ?? [])
30
+ .flatMap((id) => byId.get(id) ?? [])
31
+ .filter((s) => s.meta.status === "live");
32
+ return {
33
+ index, sections: viewSections, audiences, mediaManifest, jumpToSection,
34
+ copies: pageReferenceCopies(pageSections, { index, allSections: viewSections }),
35
+ };
36
+ }
package/src/deck/index.ts CHANGED
@@ -25,6 +25,6 @@ export {
25
25
  type DeckMediaAsset,
26
26
  type DeckMediaResolver,
27
27
  } from "./media";
28
- export { DeckTextEditProvider, EditableText, useDeckTextEditor, type DeckTextEditor } from "./editing";
28
+ export { DeckTextEditProvider, EditableText, type DeckTextEditor } from "./editing";
29
29
  export { useStageScale } from "./useStageScale";
30
- export { deckAlign, surfaceInk, DECK_ELEVATION, SURFACE_BACKGROUND, type DeckAlign, type DeckSurface } from "./tokens";
30
+ export { deckAlign, surfaceInk, DECK_ELEVATION, SURFACE_BACKGROUND, type DeckSurface } from "./tokens";
@@ -6,7 +6,7 @@ export * from "./engine/settings";
6
6
  export { splitProseBlocks } from "./engine/blocks";
7
7
  export { harvest } from "./engine/harvest";
8
8
  export * from "./engine/paginate";
9
- export { layoutMasonryPage, type MasonryOpts, type MasonryBox, type Sized } from "./engine/masonry";
9
+ export { layoutMasonryPage, type MasonryOpts, type Sized } from "./engine/masonry";
10
10
 
11
11
  export { DocPage, type DocMeta } from "./components/DocPage";
12
12
  export { DocSectionPage, type DocHost } from "./components/DocSectionPage";
@@ -14,5 +14,5 @@ export { DocCoverPage } from "./components/DocCoverPage";
14
14
  export { useDocPages } from "./components/useDocPages";
15
15
 
16
16
  // Task 3 — shared document shell (presentation + editing).
17
- export { DocViewer, type DocViewerProps } from "./components/DocViewer";
17
+ export { DocViewer } from "./components/DocViewer";
18
18
  export { DocRail, entryId, type RailEntry } from "./components/DocRail";
@@ -15,6 +15,17 @@ export interface UseBlockDndArgs {
15
15
  }
16
16
 
17
17
  /**
18
+ * One of three sibling drag-and-drop position hooks, split by location type
19
+ * rather than unified behind a generic `useSortable({ axis })`: this one
20
+ * (block/container id + index — the section canvas, root and nested),
21
+ * `useSortableRow` (flat index — section-ordering lists), and
22
+ * `useSortableNavRow` (NavLocation — the Pages modal's group-aware nav tree).
23
+ * A fourth location type earns its own hook on the same shape (rowRef/
24
+ * handleRef, lazy atlaskit import, closest-edge state, a ref for the latest
25
+ * callback) rather than a parameter bag bolted onto an existing one — see
26
+ * docs/superpowers/specs/2026-09-04-editor-ui-primitives-consolidation.md
27
+ * § D6 for why a shared `useSortable` was deferred.
28
+ *
18
29
  * Wires a block as a unified drag source + drop target. Manages only this block's
19
30
  * local hover edge + dragging flag — the actual move dispatch is centralized in the
20
31
  * EditorContent monitor (a later task), so nested targets never double-fire. The edge