@drawnagency/primitives 0.1.113 → 0.1.114

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (483) hide show
  1. package/README.md +1 -1
  2. package/dist/auth/cookies.d.ts +39 -0
  3. package/dist/auth/cookies.d.ts.map +1 -1
  4. package/dist/auth/index.js +3 -3
  5. package/dist/{chunk-GPPVYLEI.js → chunk-45FA7Z55.js} +1 -1
  6. package/dist/{chunk-REDDTTGJ.js → chunk-4Y2JQQZL.js} +19 -1
  7. package/dist/chunk-5GFACGSN.js +6 -0
  8. package/dist/chunk-5KSAS4VY.js +275 -0
  9. package/dist/{chunk-Z7W3DQTR.js → chunk-7SAAJVDE.js} +408 -28
  10. package/dist/{chunk-S2L3BPLS.js → chunk-7SX42OP4.js} +1 -1
  11. package/dist/{chunk-GGXZZ77Z.js → chunk-AASEGXNT.js} +18 -1
  12. package/dist/{chunk-WKZ7OENC.js → chunk-BQ5TZZUE.js} +5 -1
  13. package/dist/chunk-DIMDRBFK.js +11 -0
  14. package/dist/{chunk-WUJ3XNFI.js → chunk-GGYY3JQJ.js} +19 -24
  15. package/dist/{chunk-NHN2LSIU.js → chunk-IP2SEBAJ.js} +5 -1
  16. package/dist/{chunk-IINIZBQB.js → chunk-JCDZAFX2.js} +30 -10
  17. package/dist/chunk-JR24U7FJ.js +120 -0
  18. package/dist/{chunk-SW6PCQQD.js → chunk-KQA772WQ.js} +2 -1
  19. package/dist/{chunk-KR2MHTNJ.js → chunk-N3G7DOU3.js} +609 -61
  20. package/dist/chunk-OAGJBCSH.js +67 -0
  21. package/dist/{chunk-6BZ2KNTP.js → chunk-PLANHXS7.js} +191 -61
  22. package/dist/{chunk-VWWY5TL5.js → chunk-PNSCDBU7.js} +38 -56
  23. package/dist/{chunk-AHALSCQH.js → chunk-PTY2RG2K.js} +1 -1
  24. package/dist/{chunk-BDFTMGCL.js → chunk-PZPGJZRG.js} +9 -8
  25. package/dist/{chunk-FRE2CCLV.js → chunk-QHMKIODQ.js} +17 -4
  26. package/dist/{chunk-HBULBDGP.js → chunk-S37DZPHW.js} +1 -1
  27. package/dist/{chunk-4I5OO5UB.js → chunk-TEOAKGZA.js} +0 -1
  28. package/dist/chunk-VUYCRBJK.js +174 -0
  29. package/dist/{chunk-4RNF2DRG.js → chunk-XDEPXTBY.js} +0 -2
  30. package/dist/{chunk-DS4GCYV2.js → chunk-Y5QECJX7.js} +223 -16
  31. package/dist/collab/rebase.d.ts +2 -1
  32. package/dist/collab/rebase.d.ts.map +1 -1
  33. package/dist/collab/rebase.js +20 -5
  34. package/dist/components/brandguide/ColorsEditable.d.ts.map +1 -1
  35. package/dist/components/chat/ChatPanel.d.ts.map +1 -1
  36. package/dist/components/chat/ChatWidget.d.ts.map +1 -1
  37. package/dist/components/docfile/DocFilePane.d.ts.map +1 -1
  38. package/dist/components/editor/ChildBlockWrapper.d.ts.map +1 -1
  39. package/dist/components/editor/ConflictDialog.d.ts.map +1 -1
  40. package/dist/components/editor/DragHandle.d.ts +12 -2
  41. package/dist/components/editor/DragHandle.d.ts.map +1 -1
  42. package/dist/components/editor/DropEdgeIndicator.d.ts +15 -6
  43. package/dist/components/editor/DropEdgeIndicator.d.ts.map +1 -1
  44. package/dist/components/editor/NavGroupRow.d.ts +4 -1
  45. package/dist/components/editor/NavGroupRow.d.ts.map +1 -1
  46. package/dist/components/editor/PagesModal.d.ts.map +1 -1
  47. package/dist/components/editor/SectionWrapper.d.ts +1 -1
  48. package/dist/components/editor/SectionWrapper.d.ts.map +1 -1
  49. package/dist/components/editor/SettingsForm.d.ts.map +1 -1
  50. package/dist/components/editor/TagIndicator.d.ts +15 -0
  51. package/dist/components/editor/TagIndicator.d.ts.map +1 -0
  52. package/dist/components/editor/index.js +148 -77
  53. package/dist/components/primitives/DocumentDropZone.d.ts +1 -1
  54. package/dist/components/primitives/EditableGrid.d.ts.map +1 -1
  55. package/dist/components/primitives/EditableList.d.ts.map +1 -1
  56. package/dist/components/primitives/HeadingSection.d.ts +4 -1
  57. package/dist/components/primitives/HeadingSection.d.ts.map +1 -1
  58. package/dist/components/primitives/ResolvedMedia.d.ts +5 -1
  59. package/dist/components/primitives/ResolvedMedia.d.ts.map +1 -1
  60. package/dist/components/primitives/{defineHeadingSection.d.ts → define-heading-section.d.ts} +1 -1
  61. package/dist/components/primitives/define-heading-section.d.ts.map +1 -0
  62. package/dist/components/primitives/{gridDrop.d.ts → grid-drop.d.ts} +1 -1
  63. package/dist/components/primitives/grid-drop.d.ts.map +1 -0
  64. package/dist/components/primitives/index.d.ts +1 -1
  65. package/dist/components/primitives/index.d.ts.map +1 -1
  66. package/dist/components/primitives/index.js +17 -154
  67. package/dist/components/sections/Button/CTAButton.d.ts.map +1 -1
  68. package/dist/components/sections/DoDontMedia/DoDontMediaBlock.d.ts +2 -6
  69. package/dist/components/sections/DoDontMedia/DoDontMediaBlock.d.ts.map +1 -1
  70. package/dist/components/sections/DoDontMedia/index.d.ts +8 -0
  71. package/dist/components/sections/DoDontMedia/index.d.ts.map +1 -1
  72. package/dist/components/sections/IconList/IconList.d.ts +2 -1
  73. package/dist/components/sections/IconList/IconList.d.ts.map +1 -1
  74. package/dist/components/sections/IconList/IconListSettings.d.ts +2 -1
  75. package/dist/components/sections/IconList/IconListSettings.d.ts.map +1 -1
  76. package/dist/components/sections/IconList/index.d.ts +1 -0
  77. package/dist/components/sections/IconList/index.d.ts.map +1 -1
  78. package/dist/components/sections/Media/MediaBlock.d.ts +2 -1
  79. package/dist/components/sections/Media/MediaBlock.d.ts.map +1 -1
  80. package/dist/components/sections/Media/index.d.ts +8 -0
  81. package/dist/components/sections/Media/index.d.ts.map +1 -1
  82. package/dist/components/sections/Reference/ReferenceList.d.ts +35 -0
  83. package/dist/components/sections/Reference/ReferenceList.d.ts.map +1 -0
  84. package/dist/components/sections/Reference/ReferenceSettings.d.ts +12 -0
  85. package/dist/components/sections/Reference/ReferenceSettings.d.ts.map +1 -0
  86. package/dist/components/sections/Reference/index.d.ts +9 -0
  87. package/dist/components/sections/Reference/index.d.ts.map +1 -0
  88. package/dist/components/sections/SectionSummary/SectionSummaryBlock.d.ts +38 -0
  89. package/dist/components/sections/SectionSummary/SectionSummaryBlock.d.ts.map +1 -0
  90. package/dist/components/sections/SectionSummary/SectionSummarySettings.d.ts +20 -0
  91. package/dist/components/sections/SectionSummary/SectionSummarySettings.d.ts.map +1 -0
  92. package/dist/components/sections/SectionSummary/index.d.ts +33 -0
  93. package/dist/components/sections/SectionSummary/index.d.ts.map +1 -0
  94. package/dist/components/sections/all-sections.d.ts +53 -0
  95. package/dist/components/sections/all-sections.d.ts.map +1 -1
  96. package/dist/components/sections/brand-guide.d.ts +41 -2
  97. package/dist/components/sections/brand-guide.d.ts.map +1 -1
  98. package/dist/components/sections/brand-guide.js +12 -10
  99. package/dist/components/sections/core-sections.d.ts +14 -0
  100. package/dist/components/sections/core-sections.d.ts.map +1 -1
  101. package/dist/components/sections/register-schemas.js +19 -17
  102. package/dist/components/sections/register.js +19 -17
  103. package/dist/components/shared/BrandSwatchesContext.d.ts +9 -0
  104. package/dist/components/shared/BrandSwatchesContext.d.ts.map +1 -0
  105. package/dist/components/shared/Button.d.ts +11 -3
  106. package/dist/components/shared/Button.d.ts.map +1 -1
  107. package/dist/components/shared/Checkbox.d.ts +6 -0
  108. package/dist/components/shared/Checkbox.d.ts.map +1 -1
  109. package/dist/components/shared/ConfirmDialog.d.ts +25 -0
  110. package/dist/components/shared/ConfirmDialog.d.ts.map +1 -0
  111. package/dist/components/shared/ConfirmInline.d.ts +25 -0
  112. package/dist/components/shared/ConfirmInline.d.ts.map +1 -0
  113. package/dist/components/shared/ErrorBoundary.d.ts.map +1 -1
  114. package/dist/components/shared/Fab.d.ts +11 -2
  115. package/dist/components/shared/Fab.d.ts.map +1 -1
  116. package/dist/components/shared/FabStack.d.ts +22 -0
  117. package/dist/components/shared/FabStack.d.ts.map +1 -0
  118. package/dist/components/shared/HistoryChangesModal.d.ts.map +1 -1
  119. package/dist/components/shared/HistoryPopover.d.ts.map +1 -1
  120. package/dist/components/shared/ImageBackgroundField.d.ts +9 -0
  121. package/dist/components/shared/ImageBackgroundField.d.ts.map +1 -0
  122. package/dist/components/shared/{UploadRejectionAlert.d.ts → InlineAlert.d.ts} +4 -2
  123. package/dist/components/shared/InlineAlert.d.ts.map +1 -0
  124. package/dist/components/shared/Input.d.ts +10 -0
  125. package/dist/components/shared/Input.d.ts.map +1 -1
  126. package/dist/components/shared/Navigation.d.ts.map +1 -1
  127. package/dist/components/shared/Notice.d.ts +10 -0
  128. package/dist/components/shared/Notice.d.ts.map +1 -0
  129. package/dist/components/shared/Popover.d.ts.map +1 -1
  130. package/dist/components/shared/ReferenceSourcesContext.d.ts +25 -0
  131. package/dist/components/shared/ReferenceSourcesContext.d.ts.map +1 -0
  132. package/dist/components/shared/Select.d.ts +3 -2
  133. package/dist/components/shared/Select.d.ts.map +1 -1
  134. package/dist/components/shared/SplitButton.d.ts.map +1 -1
  135. package/dist/components/shared/SummaryAnchorsContext.d.ts +10 -0
  136. package/dist/components/shared/SummaryAnchorsContext.d.ts.map +1 -0
  137. package/dist/components/shared/Textarea.d.ts +7 -0
  138. package/dist/components/shared/Textarea.d.ts.map +1 -1
  139. package/dist/components/shared/ToolbarFrame.d.ts +17 -0
  140. package/dist/components/shared/ToolbarFrame.d.ts.map +1 -0
  141. package/dist/components/shared/copy.d.ts +13 -0
  142. package/dist/components/shared/copy.d.ts.map +1 -0
  143. package/dist/components/shared/icons.d.ts +0 -12
  144. package/dist/components/shared/icons.d.ts.map +1 -1
  145. package/dist/components/shell/AudienceDetailsPanel.d.ts +7 -1
  146. package/dist/components/shell/AudienceDetailsPanel.d.ts.map +1 -1
  147. package/dist/components/shell/AudienceRow.d.ts.map +1 -1
  148. package/dist/components/shell/BugReportFAB.d.ts +7 -1
  149. package/dist/components/shell/BugReportFAB.d.ts.map +1 -1
  150. package/dist/components/shell/CredentialList.d.ts.map +1 -1
  151. package/dist/components/shell/DocInspector.d.ts.map +1 -1
  152. package/dist/components/shell/DocumentEditorOverlay.d.ts.map +1 -1
  153. package/dist/components/shell/EditorContext.d.ts +4 -0
  154. package/dist/components/shell/EditorContext.d.ts.map +1 -1
  155. package/dist/components/shell/EditorLoginForm.d.ts.map +1 -1
  156. package/dist/components/shell/EditorModal.d.ts.map +1 -1
  157. package/dist/components/shell/EditorShell.d.ts.map +1 -1
  158. package/dist/components/shell/HistoryToolbar.d.ts.map +1 -1
  159. package/dist/components/shell/MediaFolderTile.d.ts +4 -1
  160. package/dist/components/shell/MediaFolderTile.d.ts.map +1 -1
  161. package/dist/components/shell/MediaLibraryModal.d.ts.map +1 -1
  162. package/dist/components/shell/RestoreModal.d.ts.map +1 -1
  163. package/dist/components/shell/SiteSettingsChatbot.d.ts.map +1 -1
  164. package/dist/components/shell/SiteSettingsDisplay.d.ts.map +1 -1
  165. package/dist/components/shell/SiteSettingsModal.d.ts +3 -1
  166. package/dist/components/shell/SiteSettingsModal.d.ts.map +1 -1
  167. package/dist/components/shell/SiteSettingsTags.d.ts +13 -0
  168. package/dist/components/shell/SiteSettingsTags.d.ts.map +1 -0
  169. package/dist/components/shell/SiteSettingsUsers.d.ts.map +1 -1
  170. package/dist/components/shell/ViewerLoginForm.d.ts.map +1 -1
  171. package/dist/components/shell/{blockMoveDispatch.d.ts → block-move-dispatch.d.ts} +1 -1
  172. package/dist/components/shell/block-move-dispatch.d.ts.map +1 -0
  173. package/dist/components/shell/reference-sources.d.ts +22 -0
  174. package/dist/components/shell/reference-sources.d.ts.map +1 -0
  175. package/dist/deck/index.d.ts +2 -2
  176. package/dist/deck/index.d.ts.map +1 -1
  177. package/dist/deck/index.js +1 -3
  178. package/dist/deck-builder/index.js +11 -18
  179. package/dist/document/index.d.ts +2 -2
  180. package/dist/document/index.d.ts.map +1 -1
  181. package/dist/hooks/useBlockDnd.d.ts +11 -0
  182. package/dist/hooks/useBlockDnd.d.ts.map +1 -1
  183. package/dist/hooks/useBuildStatus.d.ts +1 -1
  184. package/dist/hooks/useBuildStatus.d.ts.map +1 -1
  185. package/dist/hooks/useDocumentUploads.d.ts +1 -1
  186. package/dist/hooks/useDocumentUploads.d.ts.map +1 -1
  187. package/dist/hooks/useEditorPersistence.d.ts +5 -1
  188. package/dist/hooks/useEditorPersistence.d.ts.map +1 -1
  189. package/dist/hooks/useEditorPublish.d.ts +2 -2
  190. package/dist/hooks/useEditorPublish.d.ts.map +1 -1
  191. package/dist/hooks/useNotice.d.ts +19 -0
  192. package/dist/hooks/useNotice.d.ts.map +1 -0
  193. package/dist/hooks/useSortableNavRow.d.ts +11 -0
  194. package/dist/hooks/useSortableNavRow.d.ts.map +1 -1
  195. package/dist/hooks/useSortableRow.d.ts +11 -0
  196. package/dist/hooks/useSortableRow.d.ts.map +1 -1
  197. package/dist/hooks/useStackedEscape.d.ts +32 -0
  198. package/dist/hooks/useStackedEscape.d.ts.map +1 -0
  199. package/dist/index.js +67 -49
  200. package/dist/lib/audience-secret.js +2 -2
  201. package/dist/lib/block-dnd.d.ts +5 -0
  202. package/dist/lib/block-dnd.d.ts.map +1 -1
  203. package/dist/lib/block-move.d.ts.map +1 -1
  204. package/dist/lib/brand-swatches.d.ts +12 -0
  205. package/dist/lib/brand-swatches.d.ts.map +1 -0
  206. package/dist/lib/container-ops.d.ts +3 -2
  207. package/dist/lib/container-ops.d.ts.map +1 -1
  208. package/dist/lib/dexie.d.ts.map +1 -1
  209. package/dist/lib/dexie.js +9 -1
  210. package/dist/lib/env.d.ts +4 -0
  211. package/dist/lib/env.d.ts.map +1 -1
  212. package/dist/lib/env.js +5 -3
  213. package/dist/lib/events.d.ts +1 -2
  214. package/dist/lib/events.d.ts.map +1 -1
  215. package/dist/lib/image-background.d.ts +8 -0
  216. package/dist/lib/image-background.d.ts.map +1 -0
  217. package/dist/lib/index-ops.js +3 -3
  218. package/dist/lib/index.d.ts +6 -4
  219. package/dist/lib/index.d.ts.map +1 -1
  220. package/dist/lib/index.js +29 -21
  221. package/dist/lib/loader.d.ts +21 -7
  222. package/dist/lib/loader.d.ts.map +1 -1
  223. package/dist/lib/nav-tree.d.ts +1 -0
  224. package/dist/lib/nav-tree.d.ts.map +1 -1
  225. package/dist/lib/nav.d.ts +38 -0
  226. package/dist/lib/nav.d.ts.map +1 -1
  227. package/dist/lib/platform-broker.d.ts +4 -2
  228. package/dist/lib/platform-broker.d.ts.map +1 -1
  229. package/dist/lib/platform-broker.js +21 -13
  230. package/dist/lib/reference-type.d.ts +2 -0
  231. package/dist/lib/reference-type.d.ts.map +1 -0
  232. package/dist/lib/reference-validation.d.ts +31 -0
  233. package/dist/lib/reference-validation.d.ts.map +1 -0
  234. package/dist/lib/reference-validation.js +39 -0
  235. package/dist/lib/references.d.ts +74 -0
  236. package/dist/lib/references.d.ts.map +1 -0
  237. package/dist/lib/registry.d.ts +35 -0
  238. package/dist/lib/registry.d.ts.map +1 -1
  239. package/dist/lib/{safeRedirect.d.ts → safe-redirect.d.ts} +1 -1
  240. package/dist/lib/safe-redirect.d.ts.map +1 -0
  241. package/dist/lib/tags.d.ts +58 -0
  242. package/dist/lib/tags.d.ts.map +1 -0
  243. package/dist/lib/theme-vars.d.ts +9 -0
  244. package/dist/lib/theme-vars.d.ts.map +1 -1
  245. package/dist/media/index.d.ts +1 -1
  246. package/dist/media/index.d.ts.map +1 -1
  247. package/dist/media/index.js +8 -4
  248. package/dist/media/utils.d.ts +2 -0
  249. package/dist/media/utils.d.ts.map +1 -1
  250. package/dist/media/{videoPoster.d.ts → video-poster.d.ts} +1 -1
  251. package/dist/media/video-poster.d.ts.map +1 -0
  252. package/dist/schemas/audience.js +2 -2
  253. package/dist/schemas/index.d.ts +1 -0
  254. package/dist/schemas/index.d.ts.map +1 -1
  255. package/dist/schemas/index.js +14 -8
  256. package/dist/schemas/shared.d.ts +26 -0
  257. package/dist/schemas/shared.d.ts.map +1 -1
  258. package/dist/schemas/site-config.d.ts +6 -0
  259. package/dist/schemas/site-config.d.ts.map +1 -1
  260. package/dist/schemas/tags.d.ts +27 -0
  261. package/dist/schemas/tags.d.ts.map +1 -0
  262. package/dist/storage/index.js +1 -1
  263. package/dist/types/database.d.ts.map +1 -1
  264. package/package.json +6 -1
  265. package/src/auth/cookies.ts +111 -0
  266. package/src/collab/rebase.ts +26 -2
  267. package/src/components/brandguide/ColorSwatchSettings.tsx +1 -1
  268. package/src/components/brandguide/ColorsEditable.tsx +1 -0
  269. package/src/components/chat/ChatPanel.tsx +2 -3
  270. package/src/components/chat/ChatWidget.tsx +2 -3
  271. package/src/components/docfile/DocFilePane.tsx +4 -12
  272. package/src/components/editor/ChildBlockWrapper.tsx +13 -19
  273. package/src/components/editor/ConflictDialog.tsx +27 -35
  274. package/src/components/editor/DeleteButton.tsx +2 -2
  275. package/src/components/editor/DragHandle.tsx +16 -3
  276. package/src/components/editor/DropEdgeIndicator.tsx +36 -10
  277. package/src/components/editor/InsertButton.tsx +2 -2
  278. package/src/components/editor/NavGroupRow.tsx +43 -14
  279. package/src/components/editor/PagesModal.tsx +99 -26
  280. package/src/components/editor/SectionWrapper.tsx +64 -23
  281. package/src/components/editor/SettingsButton.tsx +2 -2
  282. package/src/components/editor/SettingsForm.tsx +19 -2
  283. package/src/components/editor/StatusIndicator.tsx +4 -4
  284. package/src/components/editor/TagIndicator.tsx +80 -0
  285. package/src/components/primitives/DocumentDropZone.tsx +1 -1
  286. package/src/components/primitives/EditableGrid.tsx +15 -14
  287. package/src/components/primitives/EditableList.tsx +27 -74
  288. package/src/components/primitives/HeadingSection.tsx +5 -2
  289. package/src/components/primitives/ResolvedMedia.tsx +13 -0
  290. package/src/components/primitives/{defineHeadingSection.tsx → define-heading-section.tsx} +1 -0
  291. package/src/components/primitives/index.ts +1 -1
  292. package/src/components/primitives/useEditablePlainText.ts +5 -5
  293. package/src/components/sections/Button/CTAButton.tsx +3 -1
  294. package/src/components/sections/DoDontMedia/DoDontMediaBlock.tsx +4 -4
  295. package/src/components/sections/DoDontMedia/index.tsx +12 -3
  296. package/src/components/sections/IconList/DoDontToggle.tsx +2 -2
  297. package/src/components/sections/IconList/IconList.tsx +70 -37
  298. package/src/components/sections/IconList/IconListSettings.tsx +55 -17
  299. package/src/components/sections/IconList/index.tsx +10 -3
  300. package/src/components/sections/LinkHeading/index.tsx +1 -1
  301. package/src/components/sections/Media/MediaBlock.tsx +13 -2
  302. package/src/components/sections/Media/index.tsx +12 -4
  303. package/src/components/sections/Reference/ReferenceList.tsx +214 -0
  304. package/src/components/sections/Reference/ReferenceSettings.tsx +159 -0
  305. package/src/components/sections/Reference/index.tsx +47 -0
  306. package/src/components/sections/SectionSummary/SectionSummaryBlock.tsx +476 -0
  307. package/src/components/sections/SectionSummary/SectionSummarySettings.tsx +126 -0
  308. package/src/components/sections/SectionSummary/index.tsx +149 -0
  309. package/src/components/sections/SubHeading/index.tsx +1 -1
  310. package/src/components/sections/SubSubHeading/index.tsx +1 -1
  311. package/src/components/sections/brand-guide.ts +4 -3
  312. package/src/components/sections/core-sections.ts +2 -0
  313. package/src/components/shared/BrandSwatchesContext.ts +13 -0
  314. package/src/components/shared/Button.tsx +47 -34
  315. package/src/components/shared/Checkbox.tsx +21 -12
  316. package/src/components/shared/ConfirmDialog.tsx +87 -0
  317. package/src/components/shared/ConfirmInline.tsx +90 -0
  318. package/src/components/shared/ErrorBoundary.tsx +6 -18
  319. package/src/components/shared/Fab.tsx +15 -5
  320. package/src/components/shared/FabStack.tsx +28 -0
  321. package/src/components/shared/HistoryChangesModal.tsx +43 -41
  322. package/src/components/shared/HistoryPopover.tsx +7 -20
  323. package/src/components/shared/ImageBackgroundField.tsx +92 -0
  324. package/src/components/shared/{UploadRejectionAlert.tsx → InlineAlert.tsx} +21 -5
  325. package/src/components/shared/Input.tsx +23 -7
  326. package/src/components/shared/Navigation.tsx +22 -17
  327. package/src/components/shared/Notice.tsx +59 -0
  328. package/src/components/shared/Popover.tsx +7 -11
  329. package/src/components/shared/ReferenceSourcesContext.ts +27 -0
  330. package/src/components/shared/Select.tsx +5 -2
  331. package/src/components/shared/SplitButton.tsx +11 -17
  332. package/src/components/shared/SummaryAnchorsContext.ts +11 -0
  333. package/src/components/shared/Textarea.tsx +17 -2
  334. package/src/components/shared/ToolbarFrame.tsx +28 -0
  335. package/src/components/shared/VideoSourceField.tsx +1 -1
  336. package/src/components/shared/copy.ts +18 -0
  337. package/src/components/shared/icons.tsx +1 -17
  338. package/src/components/shell/AudienceAddForm.tsx +2 -2
  339. package/src/components/shell/AudienceDetailsPanel.tsx +23 -5
  340. package/src/components/shell/AudienceRow.tsx +22 -15
  341. package/src/components/shell/BugReportFAB.tsx +50 -40
  342. package/src/components/shell/CredentialList.tsx +33 -13
  343. package/src/components/shell/DocInspector.tsx +3 -12
  344. package/src/components/shell/DocumentEditorOverlay.tsx +33 -23
  345. package/src/components/shell/EditorContext.tsx +7 -0
  346. package/src/components/shell/EditorLoginForm.tsx +26 -36
  347. package/src/components/shell/EditorModal.tsx +8 -10
  348. package/src/components/shell/EditorShell.tsx +499 -339
  349. package/src/components/shell/HistoryToolbar.tsx +24 -26
  350. package/src/components/shell/MediaFolderTile.tsx +8 -3
  351. package/src/components/shell/MediaLibraryModal.tsx +59 -60
  352. package/src/components/shell/ProcessingIndicator.tsx +2 -2
  353. package/src/components/shell/RestoreModal.tsx +17 -16
  354. package/src/components/shell/SetPasswordForm.tsx +3 -3
  355. package/src/components/shell/SiteSettingsChatbot.tsx +61 -42
  356. package/src/components/shell/SiteSettingsDisplay.tsx +10 -20
  357. package/src/components/shell/SiteSettingsModal.tsx +24 -2
  358. package/src/components/shell/SiteSettingsTags.tsx +143 -0
  359. package/src/components/shell/SiteSettingsUsers.tsx +36 -26
  360. package/src/components/shell/SiteSettingsViewerAccess.tsx +2 -2
  361. package/src/components/shell/ViewerLoginForm.tsx +5 -4
  362. package/src/components/shell/reference-sources.ts +36 -0
  363. package/src/deck/index.ts +2 -2
  364. package/src/document/index.ts +2 -2
  365. package/src/hooks/useBlockDnd.ts +11 -0
  366. package/src/hooks/useBuildStatus.ts +53 -6
  367. package/src/hooks/useDocumentUploads.ts +1 -1
  368. package/src/hooks/useEditorPersistence.ts +141 -35
  369. package/src/hooks/useEditorPublish.ts +18 -27
  370. package/src/hooks/useMediaPipeline.ts +1 -1
  371. package/src/hooks/useNotice.ts +45 -0
  372. package/src/hooks/useSortableNavRow.ts +11 -0
  373. package/src/hooks/useSortableRow.ts +22 -2
  374. package/src/hooks/useStackedEscape.ts +62 -0
  375. package/src/lib/block-dnd.ts +6 -1
  376. package/src/lib/block-move.ts +3 -0
  377. package/src/lib/brand-swatches.ts +32 -0
  378. package/src/lib/container-ops.ts +5 -3
  379. package/src/lib/dexie.ts +15 -1
  380. package/src/lib/env.ts +9 -0
  381. package/src/lib/events.ts +1 -2
  382. package/src/lib/image-background.ts +31 -0
  383. package/src/lib/index.ts +5 -4
  384. package/src/lib/loader.ts +54 -55
  385. package/src/lib/nav-tree.ts +71 -0
  386. package/src/lib/nav.ts +196 -36
  387. package/src/lib/platform-broker.ts +32 -14
  388. package/src/lib/reference-type.ts +3 -0
  389. package/src/lib/reference-validation.ts +69 -0
  390. package/src/lib/references.ts +130 -0
  391. package/src/lib/registry.ts +37 -0
  392. package/src/lib/tags.ts +148 -0
  393. package/src/lib/theme-vars.ts +23 -0
  394. package/src/media/index.ts +1 -1
  395. package/src/media/utils.ts +18 -0
  396. package/src/media/{videoPoster.ts → video-poster.ts} +2 -2
  397. package/src/schemas/index.ts +1 -0
  398. package/src/schemas/shared.ts +24 -1
  399. package/src/schemas/site-config.ts +6 -1
  400. package/src/schemas/tags.ts +33 -0
  401. package/src/storage/types.ts +1 -1
  402. package/src/types/database.ts +2 -0
  403. package/dist/chunk-DNY72JZN.js +0 -86
  404. package/dist/chunk-F2LYQPXV.js +0 -77
  405. package/dist/chunk-JH762VUH.js +0 -128
  406. package/dist/chunk-K7DYZXVU.js +0 -138
  407. package/dist/components/primitives/defineHeadingSection.d.ts.map +0 -1
  408. package/dist/components/primitives/gridDrop.d.ts.map +0 -1
  409. package/dist/components/shared/UploadRejectionAlert.d.ts.map +0 -1
  410. package/dist/components/shell/BuildStatusIndicator.d.ts +0 -7
  411. package/dist/components/shell/BuildStatusIndicator.d.ts.map +0 -1
  412. package/dist/components/shell/blockMoveDispatch.d.ts.map +0 -1
  413. package/dist/hooks/index.d.ts +0 -9
  414. package/dist/hooks/index.d.ts.map +0 -1
  415. package/dist/lib/grid.d.ts +0 -2
  416. package/dist/lib/grid.d.ts.map +0 -1
  417. package/dist/lib/safeRedirect.d.ts.map +0 -1
  418. package/dist/media/videoPoster.d.ts.map +0 -1
  419. package/src/components/brandguide/ColorSwatchSettings.d.ts.map +0 -1
  420. package/src/components/brandguide/Colors.d.ts.map +0 -1
  421. package/src/components/primitives/CustomParagraph.d.ts.map +0 -1
  422. package/src/components/primitives/EditableGrid.d.ts.map +0 -1
  423. package/src/components/primitives/EditablePlainText.d.ts.map +0 -1
  424. package/src/components/primitives/EditableRichText.d.ts.map +0 -1
  425. package/src/components/primitives/HeadingSection.d.ts.map +0 -1
  426. package/src/components/primitives/IconPicker.d.ts.map +0 -1
  427. package/src/components/primitives/LinkPopover.d.ts.map +0 -1
  428. package/src/components/primitives/MediaSettingsForms.d.ts.map +0 -1
  429. package/src/components/primitives/ResolvedMedia.d.ts.map +0 -1
  430. package/src/components/primitives/RichTextToolbar.d.ts.map +0 -1
  431. package/src/components/primitives/tiptap-presets.d.ts.map +0 -1
  432. package/src/components/primitives/useEditableCollection.d.ts.map +0 -1
  433. package/src/components/primitives/useEditablePlainText.d.ts.map +0 -1
  434. package/src/components/primitives/useEditableRichText.d.ts.map +0 -1
  435. package/src/components/sections/Button/CTAButton.d.ts.map +0 -1
  436. package/src/components/sections/Button/index.d.ts.map +0 -1
  437. package/src/components/sections/Colors/index.d.ts.map +0 -1
  438. package/src/components/sections/IconList/IconList.d.ts.map +0 -1
  439. package/src/components/sections/IconList/IconListSettings.d.ts.map +0 -1
  440. package/src/components/sections/IconList/index.d.ts.map +0 -1
  441. package/src/components/sections/LinkHeading/index.d.ts.map +0 -1
  442. package/src/components/sections/Prose/Prose.d.ts.map +0 -1
  443. package/src/components/sections/Prose/index.d.ts.map +0 -1
  444. package/src/components/sections/SubHeading/index.d.ts.map +0 -1
  445. package/src/components/sections/SubSubHeading/index.d.ts.map +0 -1
  446. package/src/components/sections/register-schemas.d.ts.map +0 -1
  447. package/src/components/shared/Button.d.ts.map +0 -1
  448. package/src/components/shared/Checkbox.d.ts.map +0 -1
  449. package/src/components/shared/FormLabel.d.ts.map +0 -1
  450. package/src/components/shared/IconButton.d.ts.map +0 -1
  451. package/src/components/shared/Input.d.ts.map +0 -1
  452. package/src/components/shared/Select.d.ts.map +0 -1
  453. package/src/components/shared/icons.d.ts.map +0 -1
  454. package/src/components/shell/BuildStatusIndicator.tsx +0 -59
  455. package/src/components/shell/MediaLibraryContext.d.ts.map +0 -1
  456. package/src/hooks/index.ts +0 -8
  457. package/src/hooks/useResolvedMedia.d.ts.map +0 -1
  458. package/src/lib/cn.d.ts.map +0 -1
  459. package/src/lib/grid.d.ts.map +0 -1
  460. package/src/lib/grid.ts +0 -7
  461. package/src/lib/icons.d.ts.map +0 -1
  462. package/src/lib/loader.d.ts.map +0 -1
  463. package/src/lib/nav.d.ts.map +0 -1
  464. package/src/lib/registry.d.ts.map +0 -1
  465. package/src/lib/sanitize.d.ts.map +0 -1
  466. package/src/media/github.d.ts.map +0 -1
  467. package/src/media/index.d.ts.map +0 -1
  468. package/src/media/queue.d.ts.map +0 -1
  469. package/src/media/resolve.d.ts.map +0 -1
  470. package/src/media/types.d.ts.map +0 -1
  471. package/src/media/utils.d.ts.map +0 -1
  472. package/src/media/videoPoster.d.ts.map +0 -1
  473. package/src/schemas/auth.d.ts.map +0 -1
  474. package/src/schemas/media-grid-options.d.ts.map +0 -1
  475. package/src/schemas/media.d.ts.map +0 -1
  476. package/src/schemas/sections.d.ts.map +0 -1
  477. package/src/schemas/shared.d.ts.map +0 -1
  478. package/src/schemas/site-config.d.ts.map +0 -1
  479. /package/dist/{chunk-B7VBSE5X.js → chunk-3JDTXOIG.js} +0 -0
  480. /package/dist/{chunk-CXZ5YJG4.js → chunk-L45X7DKX.js} +0 -0
  481. /package/src/components/primitives/{gridDrop.ts → grid-drop.ts} +0 -0
  482. /package/src/components/shell/{blockMoveDispatch.ts → block-move-dispatch.ts} +0 -0
  483. /package/src/lib/{safeRedirect.ts → safe-redirect.ts} +0 -0
@@ -1,20 +1,46 @@
1
+ import type { CSSProperties } from "react";
2
+ import { cn } from "../../lib/cn";
3
+
1
4
  /**
2
- * The horizontal insertion line shown at the top or bottom edge of a row during
3
- * a closest-edge drag-to-reorder, paired with `useSortableRow`'s `closestEdge`.
5
+ * The insertion line shown at an edge of a row/cell during a closest-edge
6
+ * drag-to-reorder, paired with `useSortableRow`'s (or `useSortableNavRow`'s)
7
+ * `closestEdge`. `top`/`bottom` render a horizontal line spanning the row;
8
+ * `left`/`right` render a vertical one spanning its height.
4
9
  *
5
- * `inset` starts the line at the nested-row indent, which is the ONLY thing
6
- * distinguishing "drop inside this group" from "drop before this group" — and
10
+ * `inset` starts a horizontal line at the nested-row indent — the ONLY thing
11
+ * distinguishing "drop inside this group" from "drop before this group", and
7
12
  * a group's last member slot from the top-level slot that follows it.
13
+ *
14
+ * `style` is the escape hatch for a caller whose perpendicular offset differs
15
+ * from the default (a wider cell gutter, a caller-specific `top`) — it wins
16
+ * over the default Tailwind transform/position via normal inline-style
17
+ * precedence, so callers only need to override the property that differs.
8
18
  */
9
- export function DropEdgeIndicator({ edge, inset = false }: { edge: "top" | "bottom" | null; inset?: boolean }) {
19
+ export function DropEdgeIndicator({
20
+ edge,
21
+ inset = false,
22
+ style,
23
+ }: {
24
+ edge: "top" | "bottom" | "left" | "right" | null;
25
+ inset?: boolean;
26
+ style?: CSSProperties;
27
+ }) {
10
28
  if (!edge) return null;
29
+ const horizontal = edge === "top" || edge === "bottom";
11
30
  return (
12
31
  <div
13
- className={[
14
- "absolute right-0 z-10 h-0.5 bg-primary",
15
- inset ? "left-6" : "left-0",
16
- edge === "top" ? "top-0 -translate-y-1/2" : "bottom-0 translate-y-1/2",
17
- ].join(" ")}
32
+ data-drop-edge={edge}
33
+ className={cn(
34
+ "absolute z-10 bg-primary",
35
+ horizontal
36
+ ? cn("right-0 h-0.5", inset ? "left-6" : "left-0")
37
+ : "top-0 bottom-0 w-0.5",
38
+ edge === "top" && "top-0 -translate-y-1/2",
39
+ edge === "bottom" && "bottom-0 translate-y-1/2",
40
+ edge === "left" && "left-0 -translate-x-1",
41
+ edge === "right" && "right-0 translate-x-1",
42
+ )}
43
+ style={style}
18
44
  />
19
45
  );
20
46
  }
@@ -1,4 +1,4 @@
1
- import { AddIcon } from "../shared/icons";
1
+ import { Plus } from "lucide-react";
2
2
  import { IconButton } from "../shared/IconButton";
3
3
  import { Tooltip, Kbd } from "../shared/Tooltip";
4
4
 
@@ -11,7 +11,7 @@ export function InsertButton({ index, onInsert }: InsertButtonProps) {
11
11
  return (
12
12
  <Tooltip content={<><Kbd>Click</Kbd> to add below<br /><Kbd>Alt+Click</Kbd> to add above</>}>
13
13
  <IconButton
14
- icon={<AddIcon size={18} />}
14
+ icon={<Plus size={18} />}
15
15
  label="Add section"
16
16
  className="pointer-events-auto rounded-md text-base-contrast-light/80 hover:bg-base-contrast-light/10 hover:text-base-contrast"
17
17
  tabIndex={-1}
@@ -1,11 +1,13 @@
1
- import { useEffect, useState } from "react";
2
- import { ChevronDown } from "lucide-react";
1
+ import { useEffect, useId, useRef, useState } from "react";
2
+ import { ChevronDown, ChevronUp } from "lucide-react";
3
3
  import type { NavGroupEntry } from "../../schemas/nav";
4
4
  import type { NavLocation } from "../../lib/nav-tree";
5
5
  import { groupDisplayLabel } from "../../lib/nav-tree";
6
6
  import { pluralize } from "../../lib/text";
7
7
  import { cn } from "../../lib/cn";
8
8
  import { Button } from "../shared/Button";
9
+ import { ConfirmInline } from "../shared/ConfirmInline";
10
+ import { IconButton } from "../shared/IconButton";
9
11
  import { Input } from "../shared/Input";
10
12
  import { Toggle } from "../shared/Toggle";
11
13
  import { DragHandle } from "./DragHandle";
@@ -15,6 +17,9 @@ import { useSortableNavRow } from "../../hooks/useSortableNavRow";
15
17
  interface Props {
16
18
  group: NavGroupEntry;
17
19
  entryIndex: number;
20
+ /** Total top-level entries — groups move among these only (never into a
21
+ * group, unlike a page). */
22
+ entryCount: number;
18
23
  memberCount: number;
19
24
  /** Opens the settings panel on mount. The `+ Add group` flow sets this so a
20
25
  * brand-new group lands with its label input focused. */
@@ -35,17 +40,21 @@ interface Props {
35
40
  }
36
41
 
37
42
  export function NavGroupRow({
38
- group, entryIndex, memberCount, autoOpenSettings = false,
43
+ group, entryIndex, entryCount, memberCount, autoOpenSettings = false,
39
44
  onRename, onSetStartCollapsed, onDelete, onMove, onDragStateChange,
40
45
  }: Props) {
41
46
  const [settingsOpen, setSettingsOpen] = useState(autoOpenSettings);
42
47
  const [confirmingDelete, setConfirmingDelete] = useState(false);
43
48
  const [showError, setShowError] = useState(false);
49
+ const deleteButtonRef = useRef<HTMLButtonElement>(null);
44
50
 
45
51
  const { rowRef, handleRef, isDragging, closestEdge } = useSortableNavRow<HTMLDivElement, HTMLButtonElement>({
46
52
  row: { kind: "group", groupId: group.id, index: entryIndex },
47
53
  entryIndex, kind: "group", onMove,
48
54
  });
55
+ const dragHintId = useId();
56
+ const moveTo = (delta: 1 | -1) =>
57
+ onMove({ groupId: null, index: entryIndex }, { groupId: null, index: entryIndex + delta });
49
58
 
50
59
  // Surface the hook's drag state upward so the modal can collapse this group
51
60
  // while it is the one being dragged.
@@ -65,8 +74,27 @@ export function NavGroupRow({
65
74
  return (
66
75
  <div ref={rowRef} data-testid={`nav-row-group-${group.id}`} className={cn("relative flex items-start gap-1", isDragging && "opacity-50")}>
67
76
  <DropEdgeIndicator edge={closestEdge} />
68
- <div className="flex shrink-0 items-center py-2 [&_[role=tooltip]]:hidden">
69
- <DragHandle ref={handleRef} />
77
+ <div className="flex shrink-0 items-center gap-1 py-2 [&_[role=tooltip]]:hidden">
78
+ <DragHandle ref={handleRef} tabIndex={0} aria-describedby={dragHintId} />
79
+ <div className="flex flex-col">
80
+ <IconButton
81
+ icon={<ChevronUp size={12} />}
82
+ label="Move up"
83
+ size="sm"
84
+ disabled={entryIndex === 0}
85
+ onClick={() => moveTo(-1)}
86
+ />
87
+ <IconButton
88
+ icon={<ChevronDown size={12} />}
89
+ label="Move down"
90
+ size="sm"
91
+ disabled={entryIndex === entryCount - 1}
92
+ onClick={() => moveTo(1)}
93
+ />
94
+ </div>
95
+ <span id={dragHintId} className="sr-only">
96
+ Use Move up / Move down to reorder with the keyboard.
97
+ </span>
70
98
  </div>
71
99
  <div className="min-w-0 flex-1 rounded-md border border-base-200 bg-base">
72
100
  <div className="flex items-center">
@@ -106,17 +134,18 @@ export function NavGroupRow({
106
134
  </div>
107
135
 
108
136
  {confirmingDelete ? (
109
- <div className="flex flex-col gap-2 rounded-inner border border-base-200 p-3">
110
- <p className="text-sm text-base-contrast">
111
- Delete this group? {pluralize(memberCount, "page")} will move to the top level.
112
- </p>
113
- <div className="flex justify-end gap-2">
114
- <Button variant="secondary" size="sm" onClick={() => setConfirmingDelete(false)}>Cancel</Button>
115
- <Button variant="destructive" size="sm" aria-label="Confirm delete" onClick={onDelete}>Delete</Button>
116
- </div>
117
- </div>
137
+ <ConfirmInline
138
+ message={`Delete this group? ${pluralize(memberCount, "page")} will move to the top level.`}
139
+ confirmLabel="Delete"
140
+ confirmAriaLabel="Confirm delete"
141
+ tone="neutral"
142
+ onConfirm={onDelete}
143
+ onCancel={() => setConfirmingDelete(false)}
144
+ returnFocusTo={deleteButtonRef}
145
+ />
118
146
  ) : (
119
147
  <Button
148
+ ref={deleteButtonRef}
120
149
  variant="destructive" size="sm" aria-label="Delete group" className="self-start"
121
150
  onClick={() => (memberCount === 0 ? onDelete() : setConfirmingDelete(true))}
122
151
  >
@@ -1,5 +1,5 @@
1
- import { useCallback, useState, useEffect } from "react";
2
- import { Check, ChevronDown, Home } from "lucide-react";
1
+ import { useCallback, useState, useEffect, useId } from "react";
2
+ import { Check, ChevronDown, ChevronUp, Home } from "lucide-react";
3
3
  import type { SiteIndex, Page } from "../../schemas/site-config";
4
4
  import type { Audience } from "../../auth/types";
5
5
  // Sanctioned outside->inside import: document/ is a sealed subtree, but the
@@ -7,6 +7,8 @@ import type { Audience } from "../../auth/types";
7
7
  // sanctioned import in schemas/site-config.ts and lib/pages.ts).
8
8
  import type { DocumentOptions } from "../../document/engine/settings";
9
9
  import { Button } from "../shared/Button";
10
+ import { ConfirmDialog } from "../shared/ConfirmDialog";
11
+ import { IconButton } from "../shared/IconButton";
10
12
  import { Input } from "../shared/Input";
11
13
  import { Toggle } from "../shared/Toggle";
12
14
  import { AudienceIndicator } from "./AudienceIndicator";
@@ -80,7 +82,17 @@ export function PagesModal(props: PagesModalProps) {
80
82
 
81
83
  // `location` is optional: the archived flat block renders inert rows with no
82
84
  // nav position, exactly as it did when draggability keyed off `dragIndex`.
83
- const renderPage = (page: Page, location?: NavLocation, entryIndex?: number) => (
85
+ // `liveSiblingRawIndexes` is the ordered list of RAW positions (within the
86
+ // group's `pageIds`, or within the top-level `entries`) that Move up/down
87
+ // may cross into — i.e. every LIVE sibling's raw slot, archived ones
88
+ // excluded. `location.index` is itself a raw position (drag needs it to
89
+ // splice correctly — see the ORIGINAL pageIds index comment below), so
90
+ // comparing it against a raw-space list (not a live COUNT) is what keeps an
91
+ // archived page sitting between two live ones from producing a wrongly
92
+ // disabled button or a click that silently swaps past the archived page
93
+ // with no visible reorder (drag can cross a group boundary; the keyboard
94
+ // pair deliberately cannot).
95
+ const renderPage = (page: Page, location?: NavLocation, entryIndex?: number, liveSiblingRawIndexes?: number[]) => (
84
96
  <PageRow
85
97
  key={page.id}
86
98
  page={page}
@@ -90,6 +102,7 @@ export function PagesModal(props: PagesModalProps) {
90
102
  otherSlugs={index.pages.filter((p) => p.id !== page.id).map((p) => p.slug)}
91
103
  location={location}
92
104
  entryIndex={entryIndex}
105
+ liveSiblingRawIndexes={liveSiblingRawIndexes}
93
106
  onMove={props.onMoveNavEntry}
94
107
  onToggleExpand={() => toggleExpand(page.id)}
95
108
  onSetHome={() => props.onSetHome(page.id)}
@@ -108,14 +121,28 @@ export function PagesModal(props: PagesModalProps) {
108
121
  />
109
122
  );
110
123
 
124
+ const entries = navEntries(index);
125
+ const entryCount = entries.length;
126
+ // Raw top-level positions that are "live" for Move up/down purposes: every
127
+ // group (always visible) plus every non-archived page — mirrors
128
+ // isLiveTopEntry in lib/nav-tree.ts, which describeNavMove's "N of M" uses.
129
+ const liveTopIndexes = entries
130
+ .map((entry, i) => ({ entry, i }))
131
+ .filter(({ entry }) => {
132
+ if (entry.kind === "group") return true;
133
+ const page = index.pages.find((p) => p.id === entry.pageId);
134
+ return page?.status === "live";
135
+ })
136
+ .map(({ i }) => i);
137
+
111
138
  return (
112
139
  <div className="flex flex-col gap-2 overflow-y-auto">
113
140
  <div className="flex flex-col gap-2">
114
- {navEntries(index).map((entry, entryIndex) => {
141
+ {entries.map((entry, entryIndex) => {
115
142
  if (entry.kind === "page") {
116
143
  const page = index.pages.find((p) => p.id === entry.pageId);
117
144
  if (!page || page.status !== "live") return null;
118
- return renderPage(page, { groupId: null, index: entryIndex }, entryIndex);
145
+ return renderPage(page, { groupId: null, index: entryIndex }, entryIndex, liveTopIndexes);
119
146
  }
120
147
 
121
148
  // CRITICAL: carry the ORIGINAL pageIds index (`pi`) through the filter.
@@ -134,6 +161,7 @@ export function PagesModal(props: PagesModalProps) {
134
161
  <NavGroupRow
135
162
  group={entry}
136
163
  entryIndex={entryIndex}
164
+ entryCount={entryCount}
137
165
  memberCount={members.length}
138
166
  autoOpenSettings={newGroupId === entry.id}
139
167
  onRename={(label) => props.onRenameGroup(entry.id, label)}
@@ -146,7 +174,7 @@ export function PagesModal(props: PagesModalProps) {
146
174
  <div data-testid={`nav-group-members-${entry.id}`} className="ml-6 flex flex-col gap-2">
147
175
  {members.length === 0
148
176
  ? <EmptyGroupDropzone groupId={entry.id} onMove={props.onMoveNavEntry} />
149
- : members.map(({ page, pi }) => renderPage(page, { groupId: entry.id, index: pi }, entryIndex))}
177
+ : members.map(({ page, pi }) => renderPage(page, { groupId: entry.id, index: pi }, entryIndex, members.map((m) => m.pi)))}
150
178
  </div>
151
179
  )}
152
180
  </div>
@@ -156,7 +184,7 @@ export function PagesModal(props: PagesModalProps) {
156
184
 
157
185
  {/* The only target for "after the last entry" when that entry is a group:
158
186
  every edge of a trailing group resolves to a position inside it. */}
159
- <TailDropzone entryCount={navEntries(index).length} onMove={props.onMoveNavEntry} />
187
+ <TailDropzone entryCount={entryCount} onMove={props.onMoveNavEntry} />
160
188
 
161
189
  <div className="mt-2 flex gap-2">
162
190
  <Button
@@ -194,11 +222,16 @@ export function PagesModal(props: PagesModalProps) {
194
222
  }
195
223
 
196
224
  function PageRow({
197
- page, audiences, expanded, isNew, otherSlugs, location, entryIndex, onMove, onToggleExpand, onSetHome, onSetArchived, onSetFields, onSetAudience, onSetDocument, onRequestDelete,
225
+ page, audiences, expanded, isNew, otherSlugs, location, entryIndex, liveSiblingRawIndexes, onMove, onToggleExpand, onSetHome, onSetArchived, onSetFields, onSetAudience, onSetDocument, onRequestDelete,
198
226
  }: {
199
227
  page: Page; audiences: Audience[]; expanded: boolean;
200
228
  isNew: boolean; otherSlugs: string[];
201
229
  location?: NavLocation; entryIndex?: number;
230
+ /** Ordered RAW positions of every LIVE sibling (the enclosing group's
231
+ * `pageIds`, or the top-level `entries`, whichever `location.groupId`
232
+ * selects) — see the comment on `renderPage` in PagesModal. Drag can cross
233
+ * a group boundary; the keyboard pair deliberately cannot. */
234
+ liveSiblingRawIndexes?: number[];
202
235
  onMove: (from: NavLocation, to: NavLocation) => void;
203
236
  onToggleExpand: () => void; onSetHome: () => void; onSetArchived: (v: boolean) => void;
204
237
  onSetFields: (patch: Partial<Pick<Page, "title" | "slug" | "showInNav">>) => void;
@@ -211,6 +244,19 @@ function PageRow({
211
244
  row: { kind: "page", pageId: page.id, groupId: location?.groupId ?? null, index: location?.index ?? 0 },
212
245
  entryIndex: entryIndex ?? 0, kind: "page", onMove, enabled: draggable,
213
246
  });
247
+ const dragHintId = useId();
248
+ // Neighbours are resolved among LIVE siblings only, then translated back to
249
+ // the neighbour's own raw index — never `location.index + delta`, which
250
+ // would step into an archived page's raw slot instead of the next visible
251
+ // row (see the `liveSiblingRawIndexes` comment on PagesModal's renderPage).
252
+ const liveIndex = liveSiblingRawIndexes?.indexOf(location?.index ?? -1) ?? -1;
253
+ const canMoveUp = draggable && liveIndex > 0;
254
+ const canMoveDown = draggable && liveSiblingRawIndexes !== undefined && liveIndex !== -1 && liveIndex < liveSiblingRawIndexes.length - 1;
255
+ const moveTo = (delta: 1 | -1) => {
256
+ const neighbourRawIndex = liveSiblingRawIndexes?.[liveIndex + delta];
257
+ if (neighbourRawIndex === undefined) return;
258
+ onMove(location!, { groupId: location!.groupId, index: neighbourRawIndex });
259
+ };
214
260
 
215
261
  // The slug input edits a local draft; only valid values commit to the index,
216
262
  // which must always hold a non-empty, unique, non-reserved slug.
@@ -297,8 +343,27 @@ function PageRow({
297
343
  >
298
344
  <DropEdgeIndicator edge={closestEdge} inset={location?.groupId != null} />
299
345
  {draggable && (
300
- <div className="flex shrink-0 items-center py-2 [&_[role=tooltip]]:hidden">
301
- <DragHandle ref={handleRef} />
346
+ <div className="flex shrink-0 items-center gap-1 py-2 [&_[role=tooltip]]:hidden">
347
+ <DragHandle ref={handleRef} tabIndex={0} aria-describedby={dragHintId} />
348
+ <div className="flex flex-col">
349
+ <IconButton
350
+ icon={<ChevronUp size={12} />}
351
+ label="Move up"
352
+ size="sm"
353
+ disabled={!canMoveUp}
354
+ onClick={() => moveTo(-1)}
355
+ />
356
+ <IconButton
357
+ icon={<ChevronDown size={12} />}
358
+ label="Move down"
359
+ size="sm"
360
+ disabled={!canMoveDown}
361
+ onClick={() => moveTo(1)}
362
+ />
363
+ </div>
364
+ <span id={dragHintId} className="sr-only">
365
+ Use Move up / Move down to reorder with the keyboard.
366
+ </span>
302
367
  </div>
303
368
  )}
304
369
  <div className={cn("min-w-0 flex-1 rounded-md border border-base-200 bg-base", isDragging && "outline outline-2 outline-primary/50")}>
@@ -455,21 +520,29 @@ function DeleteConfirm({ page, onCancel, onConfirm }: { page: Page; onCancel: ()
455
520
  // type-the-name guard pre-satisfied ("" === "").
456
521
  const confirmName = pageDisplayTitle(page.title);
457
522
  return (
458
- <div className="flex flex-col gap-3">
459
- <p className="text-sm text-base-contrast">
460
- Deleting <strong>{confirmName}</strong> permanently removes the page and its{" "}
461
- <strong>{pluralize(n, "section")}</strong>. This is recoverable only via GitHub version history.
462
- </p>
463
- <Input
464
- label={`Type the page name "${confirmName}" to confirm`}
465
- value={typed}
466
- onChange={setTyped}
467
- placeholder={confirmName}
468
- />
469
- <div className="flex justify-end gap-3">
470
- <Button variant="secondary" size="md" onClick={onCancel}>Cancel</Button>
471
- <Button variant="destructive" size="md" disabled={typed !== confirmName} onClick={onConfirm}>Delete</Button>
472
- </div>
473
- </div>
523
+ <ConfirmDialog
524
+ isOpen
525
+ embedded
526
+ title="Delete this page?"
527
+ body={
528
+ <div className="flex flex-col gap-3">
529
+ <p className="text-sm text-base-contrast">
530
+ Deleting <strong>{confirmName}</strong> permanently removes the page and its{" "}
531
+ <strong>{pluralize(n, "section")}</strong>. This is recoverable only via GitHub version history.
532
+ </p>
533
+ <Input
534
+ label={`Type the page name "${confirmName}" to confirm`}
535
+ value={typed}
536
+ onChange={setTyped}
537
+ placeholder={confirmName}
538
+ />
539
+ </div>
540
+ }
541
+ confirmLabel="Delete"
542
+ tone="destructive"
543
+ confirmDisabled={typed !== confirmName}
544
+ onConfirm={onConfirm}
545
+ onCancel={onCancel}
546
+ />
474
547
  );
475
548
  }
@@ -1,14 +1,16 @@
1
- import { useState, useRef, useEffect } from "react";
2
- import { ArrowRightLeft, Plus } from "lucide-react";
1
+ import { useState, useRef, useEffect, useId } from "react";
2
+ import { ArrowRightLeft, Plus, ChevronUp, ChevronDown } from "lucide-react";
3
3
  import type { Edge } from "@atlaskit/pragmatic-drag-and-drop-hitbox/closest-edge";
4
4
  import { DragHandle } from "./DragHandle";
5
+ import { DropEdgeIndicator } from "./DropEdgeIndicator";
5
6
  import { InsertButton } from "./InsertButton";
6
7
  import { DeleteButton } from "./DeleteButton";
7
8
  import { SettingsButton } from "./SettingsButton";
8
9
  import { IconButton } from "../shared/IconButton";
9
- import { Tooltip, Kbd } from "../shared/Tooltip";
10
+ import { Tooltip } from "../shared/Tooltip";
10
11
  import { StatusIndicator } from "./StatusIndicator";
11
12
  import { AudienceIndicator } from "./AudienceIndicator";
13
+ import { TagIndicator } from "./TagIndicator";
12
14
  import { LockBadge } from "./LockBadge";
13
15
  import { SettingsForm } from "./SettingsForm";
14
16
  import { useEditorContext } from "../shell/EditorContext";
@@ -16,6 +18,7 @@ import { useEditorModal } from "../shell/EditorModalContext";
16
18
  import type { WrapperProps } from "../../lib/registry";
17
19
  import { buildBlockDragData, buildBlockDropData, isBlockDragData, canDropBlock } from "../../lib/block-dnd";
18
20
  import { cn } from "../../lib/cn";
21
+ import { REFERENCE_TYPE } from "../../lib/reference-type";
19
22
 
20
23
  export function SectionWrapper({
21
24
  sectionId,
@@ -24,6 +27,7 @@ export function SectionWrapper({
24
27
  dirty,
25
28
  index,
26
29
  isLast,
30
+ sectionCount,
27
31
  containerId,
28
32
  isContainerBlock,
29
33
  definition,
@@ -31,6 +35,7 @@ export function SectionWrapper({
31
35
  audiences,
32
36
  access,
33
37
  onAccessChange,
38
+ tagging,
34
39
  onStatusChange,
35
40
  onSectionChange,
36
41
  onReorder,
@@ -64,6 +69,7 @@ export function SectionWrapper({
64
69
  const handleRef = useRef<HTMLButtonElement>(null);
65
70
  const [isDragging, setIsDragging] = useState(false);
66
71
  const [closestEdge, setClosestEdge] = useState<Edge | null>(null);
72
+ const dragHintId = useId();
67
73
 
68
74
  const hasSettings = !!(
69
75
  (definition.settings || definition.settingsForm) &&
@@ -120,6 +126,7 @@ export function SectionWrapper({
120
126
  containerId,
121
127
  index,
122
128
  isContainer: isContainerBlock,
129
+ isReference: sectionType === REFERENCE_TYPE,
123
130
  }),
124
131
  onGenerateDragPreview: () => {
125
132
  dropElement.style.opacity = "0.4";
@@ -182,7 +189,7 @@ export function SectionWrapper({
182
189
  // be torn down on lock-acquire (cleanup above) and re-run against the fresh
183
190
  // handle node on lock-release — without it, a locked-then-released section's
184
191
  // registration stays bound to the old unmounted handle and reorder breaks.
185
- }, [sectionId, index, containerId, isContainerBlock, isEditMode, isLocked]);
192
+ }, [sectionId, sectionType, index, containerId, isContainerBlock, isEditMode, isLocked]);
186
193
 
187
194
  function handleSettingsClick() {
188
195
  if (!onSectionChange) return;
@@ -278,11 +285,14 @@ export function SectionWrapper({
278
285
  <div
279
286
  className={cn(
280
287
  "absolute left-0 bottom-full z-30 hover:z-[60] mb-1 flex items-center gap-1.5 transition-opacity",
281
- alwaysShow ? "opacity-100" : "opacity-0 group-hover:opacity-100",
288
+ // group-focus-within: this cluster's own tab stops (DragHandle,
289
+ // Move up/down) would otherwise be focusable but invisible —
290
+ // hover alone doesn't cover keyboard navigation.
291
+ alwaysShow ? "opacity-100" : "opacity-0 group-hover:opacity-100 group-focus-within:opacity-100",
282
292
  )}
283
293
  >
284
294
  <div className="relative -ml-[34px] flex flex-col items-center">
285
- <DragHandle ref={handleRef} />
295
+ <DragHandle ref={handleRef} tabIndex={0} aria-describedby={dragHintId} />
286
296
  {onRequestInsert && !isDragging && chromeReady && (
287
297
  // Below xl the gutter can't fit the plus beside the handle, so it
288
298
  // hangs below it (absolute → no row-height contribution: the handle
@@ -295,6 +305,27 @@ export function SectionWrapper({
295
305
  <span className="pointer-events-none select-none whitespace-nowrap text-sm capitalize text-base-contrast-light/80">
296
306
  {definition.label}
297
307
  </span>
308
+ {onReorder && (
309
+ <div className="pointer-events-auto flex items-center">
310
+ <IconButton
311
+ icon={<ChevronUp size={14} />}
312
+ label={sectionCount ? `Move up (position ${index + 1} of ${sectionCount})` : "Move up"}
313
+ size="sm"
314
+ disabled={index === 0}
315
+ onClick={() => onReorder(index, index - 1)}
316
+ />
317
+ <IconButton
318
+ icon={<ChevronDown size={14} />}
319
+ label={sectionCount ? `Move down (position ${index + 1} of ${sectionCount})` : "Move down"}
320
+ size="sm"
321
+ disabled={isLast}
322
+ onClick={() => onReorder(index, index + 1)}
323
+ />
324
+ </div>
325
+ )}
326
+ <span id={dragHintId} className="sr-only">
327
+ Use Move up / Move down to reorder with the keyboard.
328
+ </span>
298
329
  </div>
299
330
  )}
300
331
 
@@ -331,9 +362,18 @@ export function SectionWrapper({
331
362
  onChange={(newAccess) => onAccessChange?.(newAccess)}
332
363
  />
333
364
  </div>
365
+ <div className="pointer-events-auto">
366
+ <TagIndicator
367
+ tags={tagging.tags}
368
+ assignedIds={tagging.assignedIds}
369
+ disabled={isLocked || !tagging.onToggle || !tagging.onCreate}
370
+ onToggle={(tagId, tagged) => tagging.onToggle?.(tagId, tagged)}
371
+ onCreate={(label) => tagging.onCreate?.(label)}
372
+ />
373
+ </div>
334
374
 
335
375
  {!isLocked && onAddChild && (
336
- <Tooltip className="pointer-events-auto" content={<><Kbd>Add</Kbd> a block</>}>
376
+ <Tooltip className="pointer-events-auto" content="Add a block">
337
377
  <IconButton
338
378
  icon={<Plus size={16} />}
339
379
  label="Add block"
@@ -344,7 +384,7 @@ export function SectionWrapper({
344
384
  )}
345
385
 
346
386
  {!isLocked && onMoveSection && (
347
- <Tooltip className="pointer-events-auto" content={<><Kbd>Move</Kbd> to page</>}>
387
+ <Tooltip className="pointer-events-auto" content="Move to page">
348
388
  <IconButton
349
389
  icon={<ArrowRightLeft size={16} />}
350
390
  label="Move to page"
@@ -355,31 +395,32 @@ export function SectionWrapper({
355
395
  )}
356
396
 
357
397
  {!isLocked && hasSettings && (
358
- <Tooltip className="pointer-events-auto" content={<><Kbd>Edit</Kbd> settings</>}>
398
+ <Tooltip className="pointer-events-auto" content="Edit settings">
359
399
  <SettingsButton onClick={handleSettingsClick} />
360
400
  </Tooltip>
361
401
  )}
362
402
 
363
403
  {!isLocked && onDelete && (
364
- <Tooltip className="pointer-events-auto" content={<><Kbd>Delete</Kbd> section</>}>
404
+ <Tooltip className="pointer-events-auto" content="Delete section">
365
405
  <DeleteButton onDelete={onDelete} />
366
406
  </Tooltip>
367
407
  )}
368
408
  </div>
369
409
 
370
- {/* Drop indicator — centered in the section's own padding-top */}
371
- {closestEdge === "top" && (
372
- <div
373
- className="absolute right-0 left-0 z-10 h-0.5 bg-primary"
374
- style={{
375
- top: "calc(var(--section-gap) / 2 * -1)",
376
- transform: "translateY(-50%)",
377
- }}
378
- />
379
- )}
380
- {closestEdge === "bottom" && isLast && (
381
- <div className="absolute right-0 bottom-0 left-0 z-10 h-0.5 bg-primary" />
382
- )}
410
+ {/* Drop indicator — centered in the section's own padding-top. Bottom
411
+ only ever shows on the last section: every other slot's "insert
412
+ after me" is the next section's own "top" line, so showing both
413
+ would double the line. */}
414
+ <DropEdgeIndicator
415
+ edge={closestEdge === "bottom" && !isLast ? null : closestEdge}
416
+ style={
417
+ closestEdge === "top"
418
+ ? { top: "calc(var(--section-gap) / 2 * -1)" }
419
+ : closestEdge === "bottom"
420
+ ? { transform: "none" }
421
+ : undefined
422
+ }
423
+ />
383
424
 
384
425
  {isLocked ? (
385
426
  // React 19 supports the boolean `inert` prop — blocks focus, pointer,
@@ -1,4 +1,4 @@
1
- import { SettingsIcon } from "../shared/icons";
1
+ import { Settings } from "lucide-react";
2
2
  import { IconButton } from "../shared/IconButton";
3
3
 
4
4
  interface SettingsButtonProps {
@@ -8,7 +8,7 @@ interface SettingsButtonProps {
8
8
  export function SettingsButton({ onClick }: SettingsButtonProps) {
9
9
  return (
10
10
  <IconButton
11
- icon={<SettingsIcon size={16} />}
11
+ icon={<Settings size={16} />}
12
12
  label="Section settings"
13
13
  onClick={onClick}
14
14
  className="pointer-events-auto"
@@ -7,8 +7,10 @@ import { FormLabel } from "../shared/FormLabel";
7
7
  import { LinkField } from "../shared/LinkField";
8
8
  import { VideoSourceField } from "../shared/VideoSourceField";
9
9
  import { DocumentSourceField, type DocumentRef } from "../shared/DocumentSourceField";
10
+ import { ImageBackgroundField } from "../shared/ImageBackgroundField";
10
11
  import { Tabs } from "../shared/Tabs";
11
12
  import type { LinkValue } from "../../schemas/link";
13
+ import type { ImageBackground } from "../../schemas/shared";
12
14
  import type { VideoSource } from "../sections/Video/VideoBlock";
13
15
  import { cn } from "../../lib/cn";
14
16
 
@@ -101,6 +103,7 @@ function renderField(
101
103
  field: SettingsFieldDef,
102
104
  value: unknown,
103
105
  handleFieldChange: (key: string, newValue: unknown) => void,
106
+ values: Record<string, unknown>,
104
107
  ) {
105
108
  switch (field.type) {
106
109
  case "text":
@@ -184,6 +187,17 @@ function renderField(
184
187
  />
185
188
  );
186
189
 
190
+ case "image-background":
191
+ if (field.hidden?.(values)) return null;
192
+ return (
193
+ <ImageBackgroundField
194
+ key={key}
195
+ label={field.label}
196
+ value={value as ImageBackground | undefined}
197
+ onChange={(v) => handleFieldChange(key, v)}
198
+ />
199
+ );
200
+
187
201
  default:
188
202
  return null;
189
203
  }
@@ -192,6 +206,9 @@ function renderField(
192
206
  export function SettingsForm({ schema, values, onChange, tabs }: SettingsFormProps) {
193
207
  const [local, setLocal] = useState(() => resolveValues(schema, values));
194
208
 
209
+ // `local` holds only schema keys; `hidden` predicates may read others (e.g. `ref`).
210
+ const allValues = { ...values, ...local };
211
+
195
212
  function handleFieldChange(key: string, newValue: unknown) {
196
213
  const next = { ...local, [key]: newValue };
197
214
  setLocal(next);
@@ -200,7 +217,7 @@ export function SettingsForm({ schema, values, onChange, tabs }: SettingsFormPro
200
217
 
201
218
  if (tabs) {
202
219
  const fields = (keys: string[]) =>
203
- keys.filter((key) => key in schema).map((key) => renderField(key, schema[key], local[key], handleFieldChange));
220
+ keys.filter((key) => key in schema).map((key) => renderField(key, schema[key], local[key], handleFieldChange, allValues));
204
221
 
205
222
  const listed = new Set(tabs.flatMap((t) => t.fields));
206
223
  const otherKeys = Object.keys(schema).filter((key) => !listed.has(key));
@@ -216,7 +233,7 @@ export function SettingsForm({ schema, values, onChange, tabs }: SettingsFormPro
216
233
 
217
234
  return (
218
235
  <div className="flex flex-col gap-4">
219
- {Object.entries(schema).map(([key, field]) => renderField(key, field, local[key], handleFieldChange))}
236
+ {Object.entries(schema).map(([key, field]) => renderField(key, field, local[key], handleFieldChange, allValues))}
220
237
  </div>
221
238
  );
222
239
  }