@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
@@ -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">
@@ -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 (
@@ -302,7 +292,7 @@ export function SiteSettingsDisplay({ siteConfig, onChange }: Props) {
302
292
  />
303
293
 
304
294
  {needsDarkHint && (
305
- <p className="text-xs text-amber-600">
295
+ <p className="text-xs text-warning">
306
296
  Add a dark variant or enable invert so the logo stays legible in dark mode.
307
297
  </p>
308
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
  }
@@ -163,27 +178,10 @@ export function SiteSettingsUsers({ currentUser }: Props) {
163
178
  <div className="flex items-center justify-end border-b border-base-200 py-2">
164
179
  {currentUser?.email === user.email ? (
165
180
  <span className="text-xs text-base-contrast-light">—</span>
166
- ) : confirmingRevoke === user.id ? (
167
- <span className="flex items-center gap-1">
168
- <Button
169
- type="button"
170
- variant="ghost"
171
- tone="destructive"
172
- onClick={() => confirmRevoke(user.id)}
173
- >
174
- Confirm
175
- </Button>
176
- <Button
177
- type="button"
178
- variant="ghost"
179
- onClick={() => setConfirmingRevoke(null)}
180
- >
181
- Cancel
182
- </Button>
183
- </span>
184
- ) : (
181
+ ) : confirmingRevoke === user.id ? null : (
185
182
  <Button
186
183
  type="button"
184
+ ref={getRemoveButtonRef(user.id)}
187
185
  variant="ghost"
188
186
  tone="destructive"
189
187
  onClick={() => handleRevoke(user.id)}
@@ -192,20 +190,32 @@ export function SiteSettingsUsers({ currentUser }: Props) {
192
190
  </Button>
193
191
  )}
194
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
+ )}
195
205
  </Fragment>
196
206
  ))}
197
207
  </div>
198
208
 
199
209
  <form onSubmit={handleInvite} className="grid grid-cols-[1fr_7rem_5rem] text-sm">
200
210
  <div className="pr-2">
201
- <input
211
+ <Input
212
+ label="Email address"
213
+ hideLabel
202
214
  type="email"
203
215
  placeholder="Email address"
204
216
  value={inviteEmail}
205
- onChange={(e) => setInviteEmail(e.target.value)}
217
+ onChange={setInviteEmail}
206
218
  required
207
- aria-label="Email address"
208
- 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"
209
219
  />
210
220
  </div>
211
221
  <div className="pr-2">
@@ -234,7 +244,7 @@ export function SiteSettingsUsers({ currentUser }: Props) {
234
244
  Role changes take effect the next time the user signs in, or within 24 hours.
235
245
  </p>
236
246
 
237
- {error && <p className="text-sm text-red-600">{error}</p>}
247
+ {error && <p className="text-sm text-danger">{error}</p>}
238
248
  </div>
239
249
  );
240
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
@@ -12,7 +12,7 @@ interface BuildStatusResponse {
12
12
  interface BuildStatusResult {
13
13
  state: BuildState;
14
14
  elapsedSeconds: number;
15
- startTracking: () => void;
15
+ startTracking: (commitSha?: string) => void;
16
16
  dismiss: () => void;
17
17
  }
18
18
 
@@ -40,6 +40,15 @@ export function useBuildStatus(): BuildStatusResult {
40
40
  const fadeRef = useRef<ReturnType<typeof setTimeout> | null>(null);
41
41
  const timerRef = useRef<ReturnType<typeof setInterval> | null>(null);
42
42
  const giveUpRef = useRef<ReturnType<typeof setTimeout> | null>(null);
43
+ const expectedCommit = useRef<string | null>(null);
44
+ // Commits already on the status row when tracking began (an earlier build,
45
+ // possibly still running) versus commits first seen building afterwards (a
46
+ // later publish by a peer or the MCP connector, which supersedes ours).
47
+ const firstTrackedPoll = useRef(false);
48
+ const preexistingCommits = useRef(new Set<string>());
49
+ const laterBuilds = useRef(new Set<string>());
50
+ const trackingGeneration = useRef(0);
51
+ const pollInFlight = useRef(false);
43
52
  const isPolling = useRef(false);
44
53
  const mountedRef = useRef(true);
45
54
 
@@ -80,7 +89,7 @@ export function useBuildStatus(): BuildStatusResult {
80
89
 
81
90
  const fetchStatus = useCallback(async (): Promise<BuildStatusResponse | null> => {
82
91
  try {
83
- const res = await fetch("/api/build-status");
92
+ const res = await fetch("/api/build-status", { signal: AbortSignal.timeout(10_000) });
84
93
  if (!res.ok) return null;
85
94
  const data = await res.json();
86
95
  const validStates = ["building", "ready", "error"];
@@ -107,6 +116,29 @@ export function useBuildStatus(): BuildStatusResult {
107
116
  return;
108
117
  }
109
118
 
119
+ const expected = expectedCommit.current;
120
+ if (expected && data.commitSha && data.commitSha !== expected) {
121
+ // Netlify builds serially, so a different commit is either an older
122
+ // build finishing late or a newer publish. No clock can tell them apart
123
+ // (the row is server time, this is browser time), so go by order seen:
124
+ // accept a different commit's result only if it began building after
125
+ // tracking did. A provider that reports no commit is accepted as-is.
126
+ const commit = data.commitSha;
127
+ const first = firstTrackedPoll.current;
128
+ firstTrackedPoll.current = false;
129
+ if (first || preexistingCommits.current.has(commit)) {
130
+ preexistingCommits.current.add(commit);
131
+ return;
132
+ }
133
+ if (data.state === "building") {
134
+ laterBuilds.current.add(commit);
135
+ return;
136
+ }
137
+ if (!laterBuilds.current.has(commit)) return;
138
+ } else if (expected) {
139
+ firstTrackedPoll.current = false;
140
+ }
141
+
110
142
  setState(data.state);
111
143
 
112
144
  if (data.state === "ready" || data.state === "error") {
@@ -133,8 +165,13 @@ export function useBuildStatus(): BuildStatusResult {
133
165
  isPolling.current = true;
134
166
 
135
167
  pollRef.current = setInterval(async () => {
136
- const data = await fetchStatus();
137
- handleStatusUpdate(data, false);
168
+ if (pollInFlight.current) return;
169
+ pollInFlight.current = true;
170
+ const generation = trackingGeneration.current;
171
+ try {
172
+ const data = await fetchStatus();
173
+ if (generation === trackingGeneration.current && isPolling.current) handleStatusUpdate(data, false);
174
+ } finally { pollInFlight.current = false; }
138
175
  }, POLL_INTERVAL);
139
176
  }, [fetchStatus, handleStatusUpdate]);
140
177
 
@@ -148,9 +185,10 @@ export function useBuildStatus(): BuildStatusResult {
148
185
  // real remount in production.
149
186
  mountedRef.current = true;
150
187
 
188
+ const initialGeneration = trackingGeneration.current;
151
189
  async function check() {
152
190
  const data = await fetchStatus();
153
- if (cancelled) return;
191
+ if (cancelled || initialGeneration !== trackingGeneration.current) return;
154
192
 
155
193
  if (data && data.state === "building") {
156
194
  const ageMs = Date.now() - Date.parse(data.updatedAt);
@@ -183,7 +221,13 @@ export function useBuildStatus(): BuildStatusResult {
183
221
  };
184
222
  }, [fetchStatus, handleStatusUpdate, startPolling, startTimer, stopPolling, stopTimer, armGiveUp, clearGiveUp]);
185
223
 
186
- const startTracking = useCallback(() => {
224
+ const startTracking = useCallback((commitSha?: string) => {
225
+ trackingGeneration.current++;
226
+ expectedCommit.current = commitSha ?? null;
227
+ firstTrackedPoll.current = true;
228
+ preexistingCommits.current = new Set();
229
+ laterBuilds.current = new Set();
230
+ if (fadeRef.current) { clearTimeout(fadeRef.current); fadeRef.current = null; }
187
231
  if (clearRef.current) { clearTimeout(clearRef.current); clearRef.current = null; }
188
232
  setState("building");
189
233
  startTimer();
@@ -192,6 +236,9 @@ export function useBuildStatus(): BuildStatusResult {
192
236
  }, [startPolling, startTimer, armGiveUp]);
193
237
 
194
238
  const dismiss = useCallback(() => {
239
+ trackingGeneration.current++;
240
+ expectedCommit.current = null;
241
+ if (fadeRef.current) { clearTimeout(fadeRef.current); fadeRef.current = null; }
195
242
  if (clearRef.current) { clearTimeout(clearRef.current); clearRef.current = null; }
196
243
  setState("idle");
197
244
  stopPolling();
@@ -32,7 +32,7 @@ interface UseDocumentUploadsInput {
32
32
  * invisible here, so without this the dedup below cannot tell a live
33
33
  * document from a doomed one. No-op for an id that isn't doomed. */
34
34
  undeleteDocument?: (id: string) => void;
35
- /** Same channel the media pipeline uses — surfaces in UploadRejectionAlert. */
35
+ /** Same channel the media pipeline uses — surfaces in InlineAlert. */
36
36
  onUploadError?: (message: string) => void;
37
37
  }
38
38