canopycms 0.0.68-int.98 → 0.1.0

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 (688) hide show
  1. package/CHANGELOG.md +2131 -0
  2. package/README.md +47 -63
  3. package/dist/ai/generate.d.ts +5 -0
  4. package/dist/ai/generate.js +244 -115
  5. package/dist/ai/index.d.ts +1 -1
  6. package/dist/ai/json-to-markdown.d.ts +21 -1
  7. package/dist/ai/json-to-markdown.js +120 -57
  8. package/dist/ai/references.d.ts +72 -0
  9. package/dist/ai/references.js +223 -0
  10. package/dist/ai/resolve-branch.js +9 -2
  11. package/dist/ai/to-plain-text.d.ts +17 -8
  12. package/dist/ai/to-plain-text.js +18 -15
  13. package/dist/ai/types.d.ts +84 -3
  14. package/dist/api/admin-branch-health.d.ts +24 -1
  15. package/dist/api/admin-branch-health.js +52 -20
  16. package/dist/api/admin.d.ts +23 -5
  17. package/dist/api/admin.js +36 -1
  18. package/dist/api/assets.js +88 -78
  19. package/dist/api/branch-create-window.d.ts +12 -0
  20. package/dist/api/branch-create-window.js +13 -0
  21. package/dist/api/branch-merge.d.ts +1 -1
  22. package/dist/api/branch-merge.js +1 -1
  23. package/dist/api/branch-review.d.ts +3 -3
  24. package/dist/api/branch-review.js +16 -7
  25. package/dist/api/branch-status.d.ts +16 -6
  26. package/dist/api/branch-status.js +57 -12
  27. package/dist/api/branch-withdraw.d.ts +2 -2
  28. package/dist/api/branch-withdraw.js +8 -3
  29. package/dist/api/branch.d.ts +25 -12
  30. package/dist/api/branch.js +164 -103
  31. package/dist/api/client.d.ts +78 -58
  32. package/dist/api/client.js +150 -117
  33. package/dist/api/comments.d.ts +12 -3
  34. package/dist/api/comments.js +118 -9
  35. package/dist/api/content.d.ts +9 -9
  36. package/dist/api/content.js +131 -22
  37. package/dist/api/entries-constants.d.ts +14 -3
  38. package/dist/api/entries-constants.js +14 -3
  39. package/dist/api/entries.d.ts +40 -2
  40. package/dist/api/entries.js +102 -6
  41. package/dist/api/github-sync.d.ts +19 -0
  42. package/dist/api/github-sync.js +50 -3
  43. package/dist/api/groups.d.ts +4 -4
  44. package/dist/api/groups.js +2 -1
  45. package/dist/api/guards.js +23 -3
  46. package/dist/api/index.d.ts +3 -1
  47. package/dist/api/index.js +1 -0
  48. package/dist/api/permissions.d.ts +21 -2
  49. package/dist/api/permissions.js +50 -3
  50. package/dist/api/request-deadline.d.ts +59 -0
  51. package/dist/api/request-deadline.js +73 -0
  52. package/dist/api/resolve-references.js +8 -7
  53. package/dist/api/route-builder.d.ts +14 -0
  54. package/dist/api/route-builder.js +14 -1
  55. package/dist/api/schema.d.ts +11 -9
  56. package/dist/api/schema.js +2 -0
  57. package/dist/api/settings-helpers.d.ts +5 -1
  58. package/dist/api/settings-helpers.js +10 -2
  59. package/dist/api/types.d.ts +18 -0
  60. package/dist/api/users-constants.d.ts +6 -0
  61. package/dist/api/users-constants.js +6 -0
  62. package/dist/assets/asset-url.d.ts +28 -16
  63. package/dist/assets/asset-url.js +51 -25
  64. package/dist/assets/finalize.js +3 -0
  65. package/dist/assets/index.d.ts +1 -1
  66. package/dist/assets/materialize.d.ts +153 -0
  67. package/dist/assets/materialize.js +402 -0
  68. package/dist/assets/store-local.d.ts +19 -5
  69. package/dist/assets/store-local.js +69 -10
  70. package/dist/assets/store-s3.d.ts +38 -7
  71. package/dist/assets/store-s3.js +128 -22
  72. package/dist/assets/transform-directives.d.ts +52 -13
  73. package/dist/assets/transform-directives.js +87 -27
  74. package/dist/assets/transform.d.ts +3 -3
  75. package/dist/assets/transform.js +13 -8
  76. package/dist/assets/types.d.ts +31 -4
  77. package/dist/auth/cache.d.ts +1 -1
  78. package/dist/auth/cache.js +1 -1
  79. package/dist/auth/caching-auth-plugin.d.ts +2 -0
  80. package/dist/auth/caching-auth-plugin.js +12 -2
  81. package/dist/auth/file-based-auth-cache.d.ts +8 -0
  82. package/dist/auth/file-based-auth-cache.js +18 -8
  83. package/dist/auth/person-name.d.ts +5 -0
  84. package/dist/auth/person-name.js +8 -0
  85. package/dist/auth/plugin.d.ts +10 -1
  86. package/dist/auth/types.d.ts +2 -0
  87. package/dist/auth/user-metadata-lookup.d.ts +21 -0
  88. package/dist/auth/user-metadata-lookup.js +116 -0
  89. package/dist/authorization/branch.d.ts +1 -1
  90. package/dist/authorization/branch.js +12 -13
  91. package/dist/authorization/groups/schema.d.ts +4 -4
  92. package/dist/authorization/helpers.d.ts +8 -0
  93. package/dist/authorization/helpers.js +8 -0
  94. package/dist/authorization/index.d.ts +1 -1
  95. package/dist/authorization/index.js +1 -1
  96. package/dist/authorization/protected-branch.d.ts +5 -4
  97. package/dist/authorization/protected-branch.js +7 -5
  98. package/dist/branch-health.d.ts +35 -8
  99. package/dist/branch-health.js +54 -18
  100. package/dist/branch-metadata-error.d.ts +27 -0
  101. package/dist/branch-metadata-error.js +23 -0
  102. package/dist/branch-metadata-file.d.ts +205 -24
  103. package/dist/branch-metadata-file.js +126 -25
  104. package/dist/branch-metadata.d.ts +45 -6
  105. package/dist/branch-metadata.js +142 -50
  106. package/dist/branch-provisioning.d.ts +167 -0
  107. package/dist/branch-provisioning.js +501 -0
  108. package/dist/branch-registry.d.ts +8 -0
  109. package/dist/branch-registry.js +61 -9
  110. package/dist/branch-schema-cache.d.ts +29 -12
  111. package/dist/branch-schema-cache.js +64 -21
  112. package/dist/branch-sparse.d.ts +23 -0
  113. package/dist/branch-sparse.js +118 -0
  114. package/dist/branch-workspace.d.ts +42 -3
  115. package/dist/branch-workspace.js +239 -100
  116. package/dist/build/asset-refs.d.ts +94 -0
  117. package/dist/build/asset-refs.js +267 -0
  118. package/dist/build/generate-ai-content.js +5 -0
  119. package/dist/build/inline-flight.d.ts +37 -0
  120. package/dist/build/inline-flight.js +142 -0
  121. package/dist/cli/asset-refs.d.ts +52 -0
  122. package/dist/cli/asset-refs.js +194 -0
  123. package/dist/cli/cli.d.ts +15 -1
  124. package/dist/cli/cli.js +8543 -3193
  125. package/dist/cli/configured-asset-store.d.ts +12 -0
  126. package/dist/cli/configured-asset-store.js +46 -0
  127. package/dist/cli/generate-ai-content.js +4244 -1072
  128. package/dist/cli/init.js +217 -126
  129. package/dist/cli/project-detect.d.ts +2 -0
  130. package/dist/cli/project-detect.js +2 -0
  131. package/dist/cli/template-files/Dockerfile.cms.template +13 -5
  132. package/dist/cli/template-files/cms-stack.ts.template +16 -13
  133. package/dist/client.d.ts +1 -2
  134. package/dist/client.js +1 -2
  135. package/dist/comment-store.d.ts +25 -0
  136. package/dist/comment-store.js +43 -8
  137. package/dist/config/flatten.js +2 -0
  138. package/dist/config/helpers.js +2 -2
  139. package/dist/config/index.d.ts +1 -1
  140. package/dist/config/schemas/config.d.ts +12 -29
  141. package/dist/config/schemas/config.js +9 -12
  142. package/dist/config/schemas/field.d.ts +3 -0
  143. package/dist/config/schemas/field.js +58 -4
  144. package/dist/config/schemas/media.d.ts +4 -13
  145. package/dist/config/schemas/media.js +5 -8
  146. package/dist/config/schemas/url.d.ts +2 -20
  147. package/dist/config/schemas/url.js +2 -25
  148. package/dist/config/types.d.ts +116 -22
  149. package/dist/config/types.js +3 -9
  150. package/dist/config/validation.d.ts +25 -4
  151. package/dist/config/validation.js +116 -3
  152. package/dist/content-id-index.js +2 -2
  153. package/dist/content-index-generation.d.ts +1 -1
  154. package/dist/content-index-generation.js +1 -1
  155. package/dist/content-listing.d.ts +10 -1
  156. package/dist/content-listing.js +22 -5
  157. package/dist/content-reader.d.ts +9 -0
  158. package/dist/content-reader.js +16 -11
  159. package/dist/content-store.d.ts +75 -12
  160. package/dist/content-store.js +249 -86
  161. package/dist/content-tree.js +2 -2
  162. package/dist/context.js +5 -2
  163. package/dist/dangling-reference-log.d.ts +17 -0
  164. package/dist/dangling-reference-log.js +29 -0
  165. package/dist/dev-content-watcher.d.ts +1 -1
  166. package/dist/dev-content-watcher.js +1 -1
  167. package/dist/editor/BranchManager.d.ts +23 -7
  168. package/dist/editor/BranchManager.js +119 -54
  169. package/dist/editor/CanopyEditor.d.ts +1 -1
  170. package/dist/editor/CanopyEditor.js +1 -2
  171. package/dist/editor/CommentsPanel.d.ts +14 -0
  172. package/dist/editor/CommentsPanel.js +68 -43
  173. package/dist/editor/Editor.d.ts +11 -5
  174. package/dist/editor/Editor.js +891 -218
  175. package/dist/editor/EditorAuthGate.d.ts +7 -1
  176. package/dist/editor/EditorAuthGate.js +41 -9
  177. package/dist/editor/EntryNavigator.d.ts +22 -0
  178. package/dist/editor/EntryNavigator.js +353 -105
  179. package/dist/editor/FormHeader.d.ts +25 -0
  180. package/dist/editor/FormHeader.js +82 -0
  181. package/dist/editor/FormRenderer.d.ts +27 -3
  182. package/dist/editor/FormRenderer.js +171 -76
  183. package/dist/editor/PreviewFrame.d.ts +40 -0
  184. package/dist/editor/PreviewFrame.js +215 -0
  185. package/dist/editor/PreviewPane.d.ts +47 -0
  186. package/dist/editor/PreviewPane.js +147 -0
  187. package/dist/editor/PreviewToolbarTools.d.ts +22 -0
  188. package/dist/editor/PreviewToolbarTools.js +73 -0
  189. package/dist/editor/admin/SystemHealthPanel.d.ts +2 -1
  190. package/dist/editor/admin/SystemHealthPanel.js +163 -33
  191. package/dist/editor/admin/useSystemHealth.d.ts +18 -0
  192. package/dist/editor/admin/useSystemHealth.js +70 -1
  193. package/dist/editor/branch-status.d.ts +17 -0
  194. package/dist/editor/branch-status.js +29 -0
  195. package/dist/editor/canopy-path.d.ts +9 -6
  196. package/dist/editor/canopy-path.js +8 -11
  197. package/dist/editor/client-reference-resolver.d.ts +43 -16
  198. package/dist/editor/client-reference-resolver.js +155 -64
  199. package/dist/editor/comments/BranchComments.d.ts +1 -1
  200. package/dist/editor/comments/BranchComments.js +2 -2
  201. package/dist/editor/comments/CloseRefusedNotice.d.ts +4 -0
  202. package/dist/editor/comments/CloseRefusedNotice.js +7 -0
  203. package/dist/editor/comments/CommentThreadsPopover.d.ts +47 -0
  204. package/dist/editor/comments/CommentThreadsPopover.js +274 -0
  205. package/dist/editor/comments/CommentWriteAlert.d.ts +11 -0
  206. package/dist/editor/comments/CommentWriteAlert.js +13 -0
  207. package/dist/editor/comments/EntryComments.d.ts +10 -4
  208. package/dist/editor/comments/EntryComments.js +14 -17
  209. package/dist/editor/comments/FieldWrapper.d.ts +18 -4
  210. package/dist/editor/comments/FieldWrapper.js +62 -28
  211. package/dist/editor/comments/InlineCommentThread.d.ts +9 -2
  212. package/dist/editor/comments/InlineCommentThread.js +11 -47
  213. package/dist/editor/comments/ThreadCarousel.d.ts +1 -1
  214. package/dist/editor/comments/ThreadCarousel.js +18 -21
  215. package/dist/editor/comments/ThreadReplyControls.d.ts +28 -0
  216. package/dist/editor/comments/ThreadReplyControls.js +92 -0
  217. package/dist/editor/comments/field-comment-context.d.ts +14 -0
  218. package/dist/editor/comments/field-comment-context.js +14 -0
  219. package/dist/editor/comments/form-comments.d.ts +48 -0
  220. package/dist/editor/comments/form-comments.js +178 -0
  221. package/dist/editor/comments/item-anchor-copy.d.ts +6 -0
  222. package/dist/editor/comments/item-anchor-copy.js +6 -0
  223. package/dist/editor/comments/move-focus-if-unmoved.d.ts +6 -0
  224. package/dist/editor/comments/move-focus-if-unmoved.js +11 -0
  225. package/dist/editor/comments/popover-placement.d.ts +18 -0
  226. package/dist/editor/comments/popover-placement.js +58 -0
  227. package/dist/editor/comments/use-tracked-writes.d.ts +16 -0
  228. package/dist/editor/comments/use-tracked-writes.js +53 -0
  229. package/dist/editor/components/BranchAccessList.d.ts +15 -0
  230. package/dist/editor/components/BranchAccessList.js +52 -0
  231. package/dist/editor/components/BranchLockBanner.d.ts +22 -0
  232. package/dist/editor/components/BranchLockBanner.js +39 -0
  233. package/dist/editor/components/BranchPill.d.ts +32 -0
  234. package/dist/editor/components/BranchPill.js +33 -0
  235. package/dist/editor/components/BranchesDrawer.d.ts +11 -0
  236. package/dist/editor/components/BranchesDrawer.js +5 -0
  237. package/dist/editor/components/ConfirmDeleteModal.d.ts +6 -1
  238. package/dist/editor/components/ConfirmDeleteModal.js +3 -2
  239. package/dist/editor/components/DestructiveMenuItem.d.ts +7 -0
  240. package/dist/editor/components/DestructiveMenuItem.js +8 -0
  241. package/dist/editor/components/EditorCrashScreen.d.ts +17 -0
  242. package/dist/editor/components/EditorCrashScreen.js +22 -0
  243. package/dist/editor/components/EditorErrorBoundary.d.ts +38 -0
  244. package/dist/editor/components/EditorErrorBoundary.js +40 -0
  245. package/dist/editor/components/EditorHeader.d.ts +56 -106
  246. package/dist/editor/components/EditorHeader.js +130 -131
  247. package/dist/editor/components/EditorRail.d.ts +49 -0
  248. package/dist/editor/components/EditorRail.js +65 -0
  249. package/dist/editor/components/EntryBreadcrumb.d.ts +33 -0
  250. package/dist/editor/components/EntryBreadcrumb.js +56 -0
  251. package/dist/editor/components/EntryCreateModal.d.ts +12 -7
  252. package/dist/editor/components/EntryCreateModal.js +51 -57
  253. package/dist/editor/components/NoEditPermissionNotice.d.ts +6 -5
  254. package/dist/editor/components/NoEditPermissionNotice.js +5 -4
  255. package/dist/editor/components/QuickOpenPalette.d.ts +22 -0
  256. package/dist/editor/components/QuickOpenPalette.js +66 -0
  257. package/dist/editor/components/ReadOnlyDraftNotice.d.ts +11 -0
  258. package/dist/editor/components/ReadOnlyDraftNotice.js +12 -0
  259. package/dist/editor/components/ReasonedMenuItem.d.ts +15 -0
  260. package/dist/editor/components/ReasonedMenuItem.js +15 -0
  261. package/dist/editor/components/ReferencedByList.d.ts +11 -0
  262. package/dist/editor/components/ReferencedByList.js +28 -0
  263. package/dist/editor/components/RelativeTime.d.ts +10 -0
  264. package/dist/editor/components/RelativeTime.js +10 -0
  265. package/dist/editor/components/RenameEntryModal.d.ts +9 -1
  266. package/dist/editor/components/RenameEntryModal.js +19 -31
  267. package/dist/editor/components/RetryToastMessage.d.ts +9 -0
  268. package/dist/editor/components/RetryToastMessage.js +6 -0
  269. package/dist/editor/components/SaveAllResult.d.ts +25 -0
  270. package/dist/editor/components/SaveAllResult.js +86 -0
  271. package/dist/editor/components/SaveSplitButton.d.ts +30 -0
  272. package/dist/editor/components/SaveSplitButton.js +108 -0
  273. package/dist/editor/components/StagedChangesDrawer.d.ts +39 -0
  274. package/dist/editor/components/StagedChangesDrawer.js +92 -0
  275. package/dist/editor/components/UnavailableEntryNotice.d.ts +6 -0
  276. package/dist/editor/components/UnavailableEntryNotice.js +9 -0
  277. package/dist/editor/components/UnavailableTypeMessage.d.ts +6 -0
  278. package/dist/editor/components/UnavailableTypeMessage.js +9 -0
  279. package/dist/editor/components/UndoToastMessage.d.ts +18 -0
  280. package/dist/editor/components/UndoToastMessage.js +10 -0
  281. package/dist/editor/components/UserBadge.d.ts +2 -5
  282. package/dist/editor/components/UserBadge.js +62 -33
  283. package/dist/editor/components/ValidationSummary.d.ts +14 -0
  284. package/dist/editor/components/ValidationSummary.js +24 -0
  285. package/dist/editor/components/index.d.ts +2 -2
  286. package/dist/editor/components/index.js +2 -2
  287. package/dist/editor/context/ApiClientContext.d.ts +8 -2
  288. package/dist/editor/context/ApiClientContext.js +19 -8
  289. package/dist/editor/context/AssetContext.d.ts +13 -35
  290. package/dist/editor/context/AssetContext.js +12 -31
  291. package/dist/editor/context/EditorStateContext.d.ts +4 -4
  292. package/dist/editor/context/EditorStateContext.js +4 -4
  293. package/dist/editor/context/SWRProvider.d.ts +3 -3
  294. package/dist/editor/context/SiteMdxAllowContext.d.ts +4 -0
  295. package/dist/editor/context/SiteMdxAllowContext.js +7 -0
  296. package/dist/editor/context/index.d.ts +2 -1
  297. package/dist/editor/context/index.js +2 -1
  298. package/dist/editor/contrast.d.ts +19 -0
  299. package/dist/editor/contrast.js +41 -0
  300. package/dist/editor/copy.d.ts +18 -0
  301. package/dist/editor/copy.js +18 -0
  302. package/dist/editor/editor-config.js +16 -0
  303. package/dist/editor/editor-utils.d.ts +34 -8
  304. package/dist/editor/editor-utils.js +42 -20
  305. package/dist/editor/entry-search.d.ts +20 -0
  306. package/dist/editor/entry-search.js +34 -0
  307. package/dist/editor/field-breadcrumb.d.ts +12 -0
  308. package/dist/editor/field-breadcrumb.js +85 -0
  309. package/dist/editor/field-props.d.ts +86 -0
  310. package/dist/editor/field-props.js +27 -0
  311. package/dist/editor/field-target.d.ts +30 -0
  312. package/dist/editor/field-target.js +77 -0
  313. package/dist/editor/fields/BlockField.d.ts +12 -0
  314. package/dist/editor/fields/BlockField.js +89 -73
  315. package/dist/editor/fields/CodeField.d.ts +2 -0
  316. package/dist/editor/fields/CodeField.js +6 -2
  317. package/dist/editor/fields/DateTimeField.d.ts +4 -29
  318. package/dist/editor/fields/DateTimeField.js +79 -41
  319. package/dist/editor/fields/ExpandAllButton.d.ts +6 -0
  320. package/dist/editor/fields/ExpandAllButton.js +4 -0
  321. package/dist/editor/fields/FieldCrashFallback.d.ts +17 -0
  322. package/dist/editor/fields/FieldCrashFallback.js +30 -0
  323. package/dist/editor/fields/FieldLabel.d.ts +37 -0
  324. package/dist/editor/fields/FieldLabel.js +19 -0
  325. package/dist/editor/fields/ImageField.d.ts +4 -0
  326. package/dist/editor/fields/ImageField.js +29 -14
  327. package/dist/editor/fields/InlineGroupField.js +4 -3
  328. package/dist/editor/fields/ItemList.d.ts +42 -0
  329. package/dist/editor/fields/ItemList.js +130 -0
  330. package/dist/editor/fields/MarkdownField.d.ts +32 -0
  331. package/dist/editor/fields/MarkdownField.js +223 -46
  332. package/dist/editor/fields/MdxImageDialog.js +1 -1
  333. package/dist/editor/fields/NumberField.d.ts +2 -0
  334. package/dist/editor/fields/NumberField.js +8 -5
  335. package/dist/editor/fields/NumberListField.d.ts +2 -0
  336. package/dist/editor/fields/NumberListField.js +19 -16
  337. package/dist/editor/fields/ObjectField.d.ts +2 -7
  338. package/dist/editor/fields/ObjectField.js +15 -5
  339. package/dist/editor/fields/ObjectListField.d.ts +29 -0
  340. package/dist/editor/fields/ObjectListField.js +57 -0
  341. package/dist/editor/fields/ReferenceField.d.ts +2 -0
  342. package/dist/editor/fields/ReferenceField.js +34 -9
  343. package/dist/editor/fields/SelectField.d.ts +2 -0
  344. package/dist/editor/fields/SelectField.js +11 -2
  345. package/dist/editor/fields/StringListField.d.ts +4 -1
  346. package/dist/editor/fields/StringListField.js +22 -8
  347. package/dist/editor/fields/TextField.d.ts +2 -0
  348. package/dist/editor/fields/TextField.js +6 -2
  349. package/dist/editor/fields/ToggleField.d.ts +2 -0
  350. package/dist/editor/fields/ToggleField.js +8 -3
  351. package/dist/editor/fields/entry-link/InsertEntryLink.d.ts +1 -1
  352. package/dist/editor/fields/entry-link/InsertEntryLink.js +1 -1
  353. package/dist/editor/fields/form-expansion.d.ts +9 -0
  354. package/dist/editor/fields/form-expansion.js +2 -0
  355. package/dist/editor/fields/item-expansion.d.ts +15 -0
  356. package/dist/editor/fields/item-expansion.js +60 -0
  357. package/dist/editor/fields/list-item-keys.d.ts +8 -0
  358. package/dist/editor/fields/list-item-keys.js +50 -0
  359. package/dist/editor/fields/list-item-title.d.ts +20 -0
  360. package/dist/editor/fields/list-item-title.js +85 -0
  361. package/dist/editor/fields/markdown-export-options.d.ts +2 -0
  362. package/dist/editor/fields/markdown-export-options.js +8 -0
  363. package/dist/editor/fields/markdown-fidelity-visitors.d.ts +11 -0
  364. package/dist/editor/fields/markdown-fidelity-visitors.js +160 -0
  365. package/dist/editor/fields/mdx-jsx-support.d.ts +5 -1
  366. package/dist/editor/fields/mdx-jsx-support.js +157 -69
  367. package/dist/editor/fields/rich-text-failures.d.ts +5 -0
  368. package/dist/editor/fields/rich-text-failures.js +26 -0
  369. package/dist/editor/fields/toolbar-wrap.d.ts +7 -0
  370. package/dist/editor/fields/toolbar-wrap.js +35 -0
  371. package/dist/editor/form-outline.d.ts +13 -0
  372. package/dist/editor/form-outline.js +70 -0
  373. package/dist/editor/group-manager/GroupCard.d.ts +4 -1
  374. package/dist/editor/group-manager/GroupCard.js +12 -3
  375. package/dist/editor/group-manager/GroupForm.js +1 -1
  376. package/dist/editor/group-manager/InternalGroupsTab.d.ts +2 -1
  377. package/dist/editor/group-manager/InternalGroupsTab.js +4 -4
  378. package/dist/editor/group-manager/MemberList.d.ts +2 -1
  379. package/dist/editor/group-manager/MemberList.js +15 -4
  380. package/dist/editor/group-manager/hooks/useGroupState.d.ts +5 -0
  381. package/dist/editor/group-manager/hooks/useGroupState.js +30 -7
  382. package/dist/editor/group-manager/index.js +34 -5
  383. package/dist/editor/group-manager/types.d.ts +5 -2
  384. package/dist/editor/hooks/create-branch-request.d.ts +20 -0
  385. package/dist/editor/hooks/create-branch-request.js +54 -0
  386. package/dist/editor/hooks/save-all.d.ts +68 -0
  387. package/dist/editor/hooks/save-all.js +61 -0
  388. package/dist/editor/hooks/useBranchActions.d.ts +11 -1
  389. package/dist/editor/hooks/useBranchActions.js +42 -19
  390. package/dist/editor/hooks/useBranchManager.d.ts +21 -0
  391. package/dist/editor/hooks/useBranchManager.js +321 -125
  392. package/dist/editor/hooks/useBranchesData.d.ts +8 -4
  393. package/dist/editor/hooks/useBranchesData.js +9 -5
  394. package/dist/editor/hooks/useCommentSystem.d.ts +14 -2
  395. package/dist/editor/hooks/useCommentSystem.js +79 -55
  396. package/dist/editor/hooks/useCommentsData.d.ts +11 -7
  397. package/dist/editor/hooks/useCommentsData.js +20 -19
  398. package/dist/editor/hooks/useDraftManager.d.ts +77 -3
  399. package/dist/editor/hooks/useDraftManager.js +887 -94
  400. package/dist/editor/hooks/useEditorLayout.d.ts +53 -7
  401. package/dist/editor/hooks/useEditorLayout.js +98 -20
  402. package/dist/editor/hooks/useEntriesData.d.ts +2 -1
  403. package/dist/editor/hooks/useEntriesData.js +18 -4
  404. package/dist/editor/hooks/useEntryManager.d.ts +41 -2
  405. package/dist/editor/hooks/useEntryManager.js +227 -41
  406. package/dist/editor/hooks/useGroupManager.js +13 -16
  407. package/dist/editor/hooks/usePermissionManager.js +14 -6
  408. package/dist/editor/hooks/usePreviewMarks.d.ts +21 -0
  409. package/dist/editor/hooks/usePreviewMarks.js +33 -0
  410. package/dist/editor/hooks/useReferenceResolution.d.ts +10 -3
  411. package/dist/editor/hooks/useReferenceResolution.js +34 -177
  412. package/dist/editor/hooks/useSchemaManager.d.ts +25 -6
  413. package/dist/editor/hooks/useSchemaManager.js +97 -39
  414. package/dist/editor/hooks/useUserMetadata.d.ts +14 -4
  415. package/dist/editor/hooks/useUserMetadata.js +76 -47
  416. package/dist/editor/hooks/user-metadata-batcher.d.ts +8 -0
  417. package/dist/editor/hooks/user-metadata-batcher.js +33 -0
  418. package/dist/editor/media/AssetCard.d.ts +6 -7
  419. package/dist/editor/media/AssetCard.js +24 -31
  420. package/dist/editor/media/AssetListRow.d.ts +16 -0
  421. package/dist/editor/media/AssetListRow.js +41 -0
  422. package/dist/editor/media/CropStep.js +6 -1
  423. package/dist/editor/media/MediaLibrary.d.ts +4 -3
  424. package/dist/editor/media/MediaLibrary.js +11 -4
  425. package/dist/editor/media/MediaLibraryBody.d.ts +3 -3
  426. package/dist/editor/media/MediaLibraryBody.js +195 -80
  427. package/dist/editor/media/MediaLibrarySkeleton.d.ts +6 -0
  428. package/dist/editor/media/MediaLibrarySkeleton.js +14 -0
  429. package/dist/editor/media/crop-math.d.ts +3 -4
  430. package/dist/editor/media/crop-math.js +11 -22
  431. package/dist/editor/media/editor-image-src.d.ts +8 -0
  432. package/dist/editor/media/editor-image-src.js +10 -0
  433. package/dist/editor/media/format-bytes.d.ts +2 -0
  434. package/dist/editor/media/format-bytes.js +23 -0
  435. package/dist/editor/media/media-library-prefs.d.ts +23 -0
  436. package/dist/editor/media/media-library-prefs.js +49 -0
  437. package/dist/editor/media/media-library-view.d.ts +34 -0
  438. package/dist/editor/media/media-library-view.js +40 -0
  439. package/dist/editor/media/useAssetLibrary.d.ts +30 -0
  440. package/dist/editor/media/useAssetLibrary.js +126 -0
  441. package/dist/editor/media/useStableAssetOrder.d.ts +11 -0
  442. package/dist/editor/media/useStableAssetOrder.js +37 -0
  443. package/dist/editor/media/useThumbnailFailure.d.ts +6 -0
  444. package/dist/editor/media/useThumbnailFailure.js +16 -0
  445. package/dist/editor/permission-manager/GroupSelector.js +2 -2
  446. package/dist/editor/permission-manager/PermissionEditor.d.ts +1 -1
  447. package/dist/editor/permission-manager/PermissionEditor.js +15 -4
  448. package/dist/editor/permission-manager/PermissionLevelBadge.js +1 -1
  449. package/dist/editor/permission-manager/PermissionTree.d.ts +1 -1
  450. package/dist/editor/permission-manager/UserSelector.d.ts +1 -1
  451. package/dist/editor/permission-manager/UserSelector.js +3 -3
  452. package/dist/editor/permission-manager/index.js +12 -3
  453. package/dist/editor/permission-manager/types.d.ts +5 -2
  454. package/dist/editor/platform.d.ts +3 -0
  455. package/dist/editor/platform.js +9 -0
  456. package/dist/editor/preview-asset-base.d.ts +9 -0
  457. package/dist/editor/preview-asset-base.js +17 -0
  458. package/dist/editor/preview-bridge.d.ts +51 -27
  459. package/dist/editor/preview-bridge.js +103 -144
  460. package/dist/editor/preview-marks.d.ts +15 -0
  461. package/dist/editor/preview-marks.js +74 -0
  462. package/dist/editor/preview-path.d.ts +12 -0
  463. package/dist/editor/preview-path.js +45 -1
  464. package/dist/editor/preview-scale.d.ts +27 -0
  465. package/dist/editor/preview-scale.js +34 -0
  466. package/dist/editor/raw-asset-base.d.ts +5 -0
  467. package/dist/editor/raw-asset-base.js +14 -0
  468. package/dist/editor/relative-time.d.ts +4 -4
  469. package/dist/editor/relative-time.js +16 -8
  470. package/dist/editor/schema-editor/CollectionEditor.d.ts +20 -3
  471. package/dist/editor/schema-editor/CollectionEditor.js +361 -62
  472. package/dist/editor/schema-editor/EntryTypeEditor.d.ts +5 -1
  473. package/dist/editor/schema-editor/EntryTypeEditor.js +11 -6
  474. package/dist/editor/schema-editor/collection-draft.d.ts +22 -0
  475. package/dist/editor/schema-editor/collection-draft.js +40 -0
  476. package/dist/editor/schema-editor/index.d.ts +1 -1
  477. package/dist/editor/schema-label.d.ts +10 -0
  478. package/dist/editor/schema-label.js +17 -0
  479. package/dist/editor/settings-conflict.d.ts +5 -0
  480. package/dist/editor/settings-conflict.js +8 -0
  481. package/dist/editor/shell/ActivityRail.d.ts +37 -0
  482. package/dist/editor/shell/ActivityRail.js +91 -0
  483. package/dist/editor/shell/EditorShell.d.ts +17 -0
  484. package/dist/editor/shell/EditorShell.js +31 -0
  485. package/dist/editor/shell/PaneLayout.d.ts +28 -0
  486. package/dist/editor/shell/PaneLayout.js +116 -0
  487. package/dist/editor/shell/SidePanelHost.d.ts +35 -0
  488. package/dist/editor/shell/SidePanelHost.js +184 -0
  489. package/dist/editor/shell/TopBar.d.ts +12 -0
  490. package/dist/editor/shell/TopBar.js +14 -0
  491. package/dist/editor/shell/host-chrome.d.ts +16 -0
  492. package/dist/editor/shell/host-chrome.js +69 -0
  493. package/dist/editor/slug.d.ts +16 -0
  494. package/dist/editor/slug.js +43 -0
  495. package/dist/editor/theme.d.ts +2 -1
  496. package/dist/editor/theme.js +314 -13
  497. package/dist/editor/unanswered.d.ts +17 -0
  498. package/dist/editor/unanswered.js +26 -0
  499. package/dist/editor/unavailable-entry-type.d.ts +20 -0
  500. package/dist/editor/unavailable-entry-type.js +28 -0
  501. package/dist/editor/undo-removal.d.ts +57 -0
  502. package/dist/editor/undo-removal.js +163 -0
  503. package/dist/editor/user-display.d.ts +4 -0
  504. package/dist/editor/user-display.js +5 -0
  505. package/dist/editor/utils/confirm-modal.d.ts +8 -0
  506. package/dist/editor/utils/confirm-modal.js +12 -0
  507. package/dist/editor/utils/editor-errors.d.ts +19 -0
  508. package/dist/editor/utils/editor-errors.js +32 -0
  509. package/dist/editor/utils/focus-return.d.ts +56 -0
  510. package/dist/editor/utils/focus-return.js +106 -0
  511. package/dist/entry-link-resolver.d.ts +11 -1
  512. package/dist/entry-link-resolver.js +65 -5
  513. package/dist/entry-schema-registry.d.ts +3 -3
  514. package/dist/entry-schema-registry.js +7 -4
  515. package/dist/entry-schema.d.ts +49 -3
  516. package/dist/entry-schema.js +18 -0
  517. package/dist/git-manager.d.ts +172 -10
  518. package/dist/git-manager.js +449 -107
  519. package/dist/github-service.d.ts +39 -2
  520. package/dist/github-service.js +101 -7
  521. package/dist/http/handler.js +74 -9
  522. package/dist/http/types.d.ts +2 -0
  523. package/dist/http/worker-not-ready.d.ts +9 -4
  524. package/dist/http/worker-not-ready.js +59 -6
  525. package/dist/index.d.ts +2 -0
  526. package/dist/index.js +3 -0
  527. package/dist/item-keys.d.ts +46 -0
  528. package/dist/item-keys.js +235 -0
  529. package/dist/operating-mode/editor-mode-check.d.ts +15 -0
  530. package/dist/operating-mode/editor-mode-check.js +24 -0
  531. package/dist/operating-mode/mode-env.d.ts +13 -14
  532. package/dist/operating-mode/mode-env.js +13 -14
  533. package/dist/operating-mode/types.d.ts +2 -2
  534. package/dist/paths/branch.d.ts +0 -1
  535. package/dist/paths/branch.js +1 -8
  536. package/dist/paths/index.d.ts +1 -1
  537. package/dist/paths/index.js +1 -1
  538. package/dist/paths/validation.js +0 -2
  539. package/dist/preview.d.ts +8 -0
  540. package/dist/preview.js +7 -0
  541. package/dist/reference-resolver.js +1 -0
  542. package/dist/resolve-canopy-user.d.ts +1 -1
  543. package/dist/resolve-canopy-user.js +1 -1
  544. package/dist/schema/meta-loader.d.ts +18 -8
  545. package/dist/schema/meta-loader.js +42 -9
  546. package/dist/schema/registry-fingerprint.d.ts +14 -0
  547. package/dist/schema/registry-fingerprint.js +63 -0
  548. package/dist/schema/resolver.d.ts +7 -4
  549. package/dist/schema/resolver.js +7 -6
  550. package/dist/schema/schema-store.d.ts +7 -10
  551. package/dist/schema/schema-store.js +13 -11
  552. package/dist/schema/schema-unavailable-error.d.ts +11 -0
  553. package/dist/schema/schema-unavailable-error.js +15 -0
  554. package/dist/schema/types.d.ts +22 -0
  555. package/dist/schema-registry-record.d.ts +35 -0
  556. package/dist/schema-registry-record.js +134 -0
  557. package/dist/server.d.ts +79 -9
  558. package/dist/server.js +62 -8
  559. package/dist/services.d.ts +33 -5
  560. package/dist/services.js +155 -37
  561. package/dist/settings-workspace.d.ts +23 -3
  562. package/dist/settings-workspace.js +159 -45
  563. package/dist/stamp-on-publish.d.ts +36 -0
  564. package/dist/stamp-on-publish.js +71 -0
  565. package/dist/static/index.d.ts +58 -5
  566. package/dist/static/index.js +133 -3
  567. package/dist/static/seo.d.ts +5 -5
  568. package/dist/static/seo.js +5 -5
  569. package/dist/submission-attribution.d.ts +8 -0
  570. package/dist/submission-attribution.js +22 -0
  571. package/dist/system-users.d.ts +12 -0
  572. package/dist/system-users.js +12 -0
  573. package/dist/task-queue/cms-task-queue.d.ts +1 -1
  574. package/dist/task-queue/cms-task-queue.js +1 -1
  575. package/dist/task-queue/index.d.ts +1 -1
  576. package/dist/task-queue/index.js +1 -1
  577. package/dist/task-queue/task-queue.d.ts +6 -0
  578. package/dist/task-queue/task-queue.js +29 -0
  579. package/dist/task-queue/worker-status.d.ts +35 -9
  580. package/dist/task-queue/worker-status.js +84 -17
  581. package/dist/types.d.ts +90 -9
  582. package/dist/url-collision.js +3 -3
  583. package/dist/url-exclusivity-fixtures.d.ts +8 -1
  584. package/dist/url-exclusivity-fixtures.js +11 -1
  585. package/dist/user.js +2 -1
  586. package/dist/utils/atomic-write.js +2 -1
  587. package/dist/utils/base-branch.d.ts +13 -0
  588. package/dist/utils/base-branch.js +23 -0
  589. package/dist/utils/content-serialize.d.ts +15 -7
  590. package/dist/utils/content-serialize.js +353 -51
  591. package/dist/utils/content-write-lock.d.ts +6 -1
  592. package/dist/utils/content-write-lock.js +21 -6
  593. package/dist/utils/debug.d.ts +1 -1
  594. package/dist/utils/debug.js +5 -3
  595. package/dist/utils/error.js +13 -2
  596. package/dist/utils/escape-text.d.ts +7 -0
  597. package/dist/utils/escape-text.js +27 -0
  598. package/dist/utils/fs.d.ts +12 -4
  599. package/dist/utils/fs.js +18 -5
  600. package/dist/utils/git.d.ts +21 -1
  601. package/dist/utils/git.js +63 -8
  602. package/dist/utils/json-source-splice.d.ts +14 -0
  603. package/dist/utils/json-source-splice.js +120 -0
  604. package/dist/utils/markdown-body-splice.d.ts +39 -0
  605. package/dist/utils/markdown-body-splice.js +421 -0
  606. package/dist/utils/occ-json-write.js +18 -12
  607. package/dist/utils/provision-log.d.ts +25 -0
  608. package/dist/utils/provision-log.js +49 -0
  609. package/dist/utils/provisioning-lock.d.ts +32 -1
  610. package/dist/utils/provisioning-lock.js +78 -32
  611. package/dist/utils/sanitize-href.d.ts +9 -23
  612. package/dist/utils/sanitize-href.js +14 -28
  613. package/dist/utils/title-field.d.ts +10 -0
  614. package/dist/utils/title-field.js +47 -0
  615. package/dist/utils/yaml-source-splice.d.ts +58 -0
  616. package/dist/utils/yaml-source-splice.js +515 -0
  617. package/dist/validation/block-structural-keys.d.ts +1 -1
  618. package/dist/validation/block-structural-keys.js +1 -1
  619. package/dist/validation/deletion-checker.d.ts +21 -29
  620. package/dist/validation/deletion-checker.js +41 -117
  621. package/dist/validation/entry-validator.d.ts +6 -10
  622. package/dist/validation/entry-validator.js +19 -26
  623. package/dist/validation/field-traversal.d.ts +22 -0
  624. package/dist/validation/field-traversal.js +39 -8
  625. package/dist/validation/markdown-safety.d.ts +68 -0
  626. package/dist/validation/markdown-safety.js +596 -0
  627. package/dist/validation/mdx-allowlist.d.ts +55 -0
  628. package/dist/validation/mdx-allowlist.js +183 -0
  629. package/dist/validation/reference-validator.d.ts +2 -0
  630. package/dist/validation/reference-validator.js +3 -0
  631. package/dist/version.d.ts +1 -1
  632. package/dist/version.js +1 -1
  633. package/dist/worker/branch-policy.d.ts +124 -0
  634. package/dist/worker/branch-policy.js +243 -0
  635. package/dist/worker/bundle-header.d.ts +117 -0
  636. package/dist/worker/bundle-header.js +253 -0
  637. package/dist/worker/cms-worker.d.ts +236 -142
  638. package/dist/worker/cms-worker.js +772 -285
  639. package/dist/worker/gateway-egress.d.ts +43 -0
  640. package/dist/worker/gateway-egress.js +209 -0
  641. package/dist/worker/gateway-instance.d.ts +42 -0
  642. package/dist/worker/gateway-instance.js +129 -0
  643. package/dist/worker/git-process.d.ts +81 -0
  644. package/dist/worker/git-process.js +234 -0
  645. package/dist/worker/git-sync.d.ts +34 -17
  646. package/dist/worker/git-sync.js +394 -131
  647. package/dist/worker/github-auth.d.ts +9 -9
  648. package/dist/worker/github-auth.js +13 -15
  649. package/dist/worker/github-gateway-http.d.ts +62 -0
  650. package/dist/worker/github-gateway-http.js +977 -0
  651. package/dist/worker/github-gateway-wire.d.ts +590 -0
  652. package/dist/worker/github-gateway-wire.js +488 -0
  653. package/dist/worker/github-gateway.d.ts +238 -0
  654. package/dist/worker/github-gateway.js +726 -0
  655. package/dist/worker/github-mirror-upkeep.d.ts +75 -0
  656. package/dist/worker/github-mirror-upkeep.js +266 -0
  657. package/dist/worker/github-mirror.d.ts +364 -0
  658. package/dist/worker/github-mirror.js +1030 -0
  659. package/dist/worker/github-transfer.d.ts +56 -0
  660. package/dist/worker/github-transfer.js +444 -0
  661. package/dist/worker/history-rewrite.d.ts +4 -5
  662. package/dist/worker/history-rewrite.js +12 -17
  663. package/dist/worker/provisioned-workspace.d.ts +5 -5
  664. package/dist/worker/provisioned-workspace.js +5 -5
  665. package/dist/worker/rebase.d.ts +1 -1
  666. package/dist/worker/rebase.js +58 -54
  667. package/dist/worker/remote-git-maintenance.d.ts +3 -0
  668. package/dist/worker/remote-git-maintenance.js +12 -0
  669. package/dist/worker/schema-gate.d.ts +49 -0
  670. package/dist/worker/schema-gate.js +226 -0
  671. package/dist/worker/self-checks.d.ts +107 -0
  672. package/dist/worker/self-checks.js +358 -0
  673. package/dist/worker/shared-repo-git.d.ts +96 -0
  674. package/dist/worker/shared-repo-git.js +459 -0
  675. package/dist/worker/sparse-cone.d.ts +20 -0
  676. package/dist/worker/sparse-cone.js +136 -0
  677. package/dist/worker/stale-mirror.d.ts +82 -0
  678. package/dist/worker/stale-mirror.js +130 -0
  679. package/dist/worker/task-runner.d.ts +5 -3
  680. package/dist/worker/task-runner.js +287 -186
  681. package/dist/worker/worker-context.d.ts +36 -37
  682. package/package.json +35 -7
  683. package/dist/editor/EditorPanes.d.ts +0 -10
  684. package/dist/editor/EditorPanes.js +0 -98
  685. package/dist/editor/components/EditorFooter.d.ts +0 -11
  686. package/dist/editor/components/EditorFooter.js +0 -9
  687. package/dist/editor/components/EditorSidebar.d.ts +0 -67
  688. package/dist/editor/components/EditorSidebar.js +0 -22
@@ -0,0 +1,10 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { Text, Tooltip, VisuallyHidden } from '@mantine/core';
4
+ import { formatRelativeTime } from '../relative-time.js';
5
+ /** A relative label; the absolute date is a tooltip, and visually hidden text for a screen reader. */
6
+ export const RelativeTime = ({ iso, prefix, size, c, fs }) => {
7
+ const absolute = new Date(iso);
8
+ const valid = !Number.isNaN(absolute.getTime());
9
+ return (_jsx(Tooltip, { label: valid ? absolute.toLocaleString() : '', disabled: !valid, children: _jsxs(Text, { component: "time", dateTime: iso, size: size, c: c, fs: fs, children: [prefix, formatRelativeTime(iso), valid && (_jsx(VisuallyHidden, { component: "span", children: `, ${absolute.toLocaleString()}` }))] }) }));
10
+ };
@@ -13,5 +13,13 @@ export interface RenameEntryModalProps {
13
13
  isSaving?: boolean;
14
14
  /** Error message to display */
15
15
  error?: string | null;
16
+ /** A rename whose outcome is unknown, shown in yellow in place of `error` */
17
+ warning?: string | null;
18
+ /** Slugs held by the other entries of the collection; the entry's own slug is never a clash. */
19
+ existingSlugs?: ReadonlySet<string>;
20
+ /** The URL path the entry will be served at for a slug; undefined when it has no page. */
21
+ previewPath?: (slug: string) => string | undefined;
22
+ /** Where focus goes once it closes; Mantine's default, the element focused at open, without it. */
23
+ returnFocusTo?: () => HTMLElement | null;
16
24
  }
17
- export declare function RenameEntryModal({ isOpen, entryLabel, currentSlug, onSave, onClose, isSaving, error, }: RenameEntryModalProps): import("react/jsx-runtime").JSX.Element;
25
+ export declare function RenameEntryModal({ isOpen, entryLabel, currentSlug, onSave, onClose, isSaving, error, warning, existingSlugs, previewPath, returnFocusTo, }: RenameEntryModalProps): import("react/jsx-runtime").JSX.Element;
@@ -1,15 +1,17 @@
1
1
  'use client';
2
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  /**
4
4
  * Allows changing the slug (middle segment) of an entry filename.
5
5
  * The content ID is preserved, so drafts and references remain intact.
6
6
  */
7
7
  import { useState } from 'react';
8
8
  import { Modal, Stack, TextInput, Group, Button, Alert, Text } from '@mantine/core';
9
- import { IconAlertCircle } from '@tabler/icons-react';
10
- export function RenameEntryModal({ isOpen, entryLabel, currentSlug, onSave, onClose, isSaving = false, error = null, }) {
9
+ import { IconAlertCircle, IconAlertTriangle } from '@tabler/icons-react';
10
+ import { validateEntrySlug } from '../slug.js';
11
+ import { focusReturnProps } from '../utils/focus-return.js';
12
+ export function RenameEntryModal({ isOpen, entryLabel, currentSlug, onSave, onClose, isSaving = false, error = null, warning = null, existingSlugs, previewPath, returnFocusTo, }) {
11
13
  const [newSlug, setNewSlug] = useState(currentSlug);
12
- const [validationError, setValidationError] = useState(null);
14
+ const [touched, setTouched] = useState(false);
13
15
  // Reset state when modal opens (adjust state during render instead of effect)
14
16
  const [prevIsOpen, setPrevIsOpen] = useState(isOpen);
15
17
  const [prevSlug, setPrevSlug] = useState(currentSlug);
@@ -18,40 +20,26 @@ export function RenameEntryModal({ isOpen, entryLabel, currentSlug, onSave, onCl
18
20
  setPrevSlug(currentSlug);
19
21
  if (isOpen) {
20
22
  setNewSlug(currentSlug);
21
- setValidationError(null);
23
+ setTouched(false);
22
24
  }
23
25
  }
24
- const validateSlug = (slug) => {
25
- if (!slug.trim()) {
26
- return 'Slug cannot be empty';
27
- }
28
- if (slug.includes('/')) {
29
- return 'Slug cannot contain slashes';
30
- }
31
- if (!/^[a-z0-9][-a-z0-9]*$/.test(slug)) {
32
- return 'Slug must start with letter/number and contain only lowercase letters, numbers, and hyphens';
33
- }
34
- if (slug.length > 64) {
35
- return 'Slug must be 64 characters or less';
36
- }
37
- return null;
38
- };
39
- const handleSlugChange = (value) => {
40
- setNewSlug(value);
41
- setValidationError(validateSlug(value));
42
- };
43
- const handleSave = async () => {
44
- const validation = validateSlug(newSlug);
45
- if (validation) {
46
- setValidationError(validation);
26
+ const validationError = validateEntrySlug(newSlug, { existingSlugs, currentSlug });
27
+ const handleSubmit = async (event) => {
28
+ event.preventDefault();
29
+ if (isSaving)
30
+ return;
31
+ setTouched(true);
32
+ if (validationError)
47
33
  return;
48
- }
49
34
  if (newSlug === currentSlug) {
50
35
  onClose();
51
36
  return;
52
37
  }
53
38
  await onSave(newSlug);
54
39
  };
55
- const canSave = !validationError && newSlug.trim() !== '' && newSlug !== currentSlug && !isSaving;
56
- return (_jsx(Modal, { opened: isOpen, onClose: onClose, title: _jsx(Text, { fw: 600, children: "Rename Entry" }), size: "md", closeOnClickOutside: !isSaving, closeOnEscape: !isSaving, children: _jsxs(Stack, { gap: "md", "data-testid": "rename-entry-modal", children: [error && (_jsx(Alert, { icon: _jsx(IconAlertCircle, { size: 16 }), color: "red", variant: "light", children: error })), _jsxs(Text, { size: "sm", c: "dimmed", children: ["Renaming \"", entryLabel, "\""] }), _jsx(TextInput, { label: "Slug", description: "URL-friendly identifier (lowercase, alphanumeric + hyphens)", value: newSlug, onChange: (e) => handleSlugChange(e.currentTarget.value), error: validationError, placeholder: "my-entry-slug", required: true, disabled: isSaving, "data-autofocus": true, "data-testid": "rename-slug-input" }), _jsxs(Text, { size: "xs", c: "dimmed", children: ["Current: ", _jsx("code", { children: currentSlug }), newSlug !== currentSlug && (_jsxs(_Fragment, { children: [' → ', "New: ", _jsx("code", { children: newSlug })] }))] }), _jsxs(Group, { justify: "flex-end", gap: "sm", children: [_jsx(Button, { variant: "subtle", onClick: onClose, disabled: isSaving, children: "Cancel" }), _jsx(Button, { onClick: handleSave, loading: isSaving, disabled: !canSave, "data-testid": "rename-entry-submit", children: "Rename" })] })] }) }));
40
+ const slugPath = previewPath?.(newSlug || 'your-slug');
41
+ return (_jsx(Modal, { opened: isOpen, onClose: onClose, title: _jsx(Text, { fw: 600, children: "Rename entry" }), size: "md", closeOnClickOutside: !isSaving, closeOnEscape: !isSaving, ...focusReturnProps(returnFocusTo), children: _jsx("form", { onSubmit: handleSubmit, noValidate: true, children: _jsxs(Stack, { gap: "md", "data-testid": "rename-entry-modal", children: [warning ? (_jsx(Alert, { icon: _jsx(IconAlertTriangle, { size: 16 }), color: "yellow", variant: "light", children: warning })) : (error && (_jsx(Alert, { icon: _jsx(IconAlertCircle, { size: 16 }), color: "red", variant: "light", children: error }))), _jsxs(Text, { size: "sm", c: "dimmed", children: ["Renaming \u201C", entryLabel, "\u201D"] }), _jsx(TextInput, { label: "Slug", description: slugPath ? `URL path: ${slugPath}` : 'Lowercase letters, numbers and hyphens', value: newSlug, onChange: (e) => {
42
+ setNewSlug(e.currentTarget.value);
43
+ setTouched(true);
44
+ }, error: touched ? validationError : null, placeholder: "my-entry-slug", required: true, disabled: isSaving, "data-autofocus": true, "data-testid": "rename-slug-input" }), _jsxs(Group, { justify: "flex-end", gap: "sm", children: [_jsx(Button, { size: "sm", variant: "subtle", onClick: onClose, disabled: isSaving, children: "Cancel" }), _jsx(Button, { size: "sm", type: "submit", loading: isSaving, disabled: newSlug === currentSlug, "data-testid": "rename-entry-submit", children: "Rename" })] })] }) }) }));
57
45
  }
@@ -0,0 +1,9 @@
1
+ export interface RetryToastMessageProps {
2
+ /** What failed, in plain words. */
3
+ message: string;
4
+ onRetry: () => void;
5
+ /** The button's label, when the way forward is not a plain retry. */
6
+ actionLabel?: string;
7
+ }
8
+ /** The body of an error toast whose failure the user can try again from the toast. */
9
+ export declare function RetryToastMessage({ message, onRetry, actionLabel, }: RetryToastMessageProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,6 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Button, Group, Text } from '@mantine/core';
3
+ /** The body of an error toast whose failure the user can try again from the toast. */
4
+ export function RetryToastMessage({ message, onRetry, actionLabel = 'Retry', }) {
5
+ return (_jsxs(Group, { justify: "space-between", align: "center", gap: "sm", wrap: "nowrap", children: [_jsx(Text, { size: "sm", miw: 0, children: message }), _jsx(Button, { size: "compact-sm", variant: "subtle", onClick: onRetry, children: actionLabel })] }));
6
+ }
@@ -0,0 +1,25 @@
1
+ import type { LogicalPath } from '../../paths/types.js';
2
+ import { type SaveAllResult } from '../hooks/save-all.js';
3
+ interface SaveAllResultMessageProps {
4
+ result: SaveAllResult;
5
+ onOpenEntry: (path: LogicalPath) => void;
6
+ /** Runs Save all again over whatever is still unsaved. */
7
+ onRetry: () => void;
8
+ }
9
+ /**
10
+ * Names every entry Save all could not save, each opening that entry, with its reason and a
11
+ * warning or error mark. "Try again" is offered only when a failure is one a retry can pass.
12
+ * @internal Exported for stories.
13
+ */
14
+ export declare function SaveAllResultMessage({ result, onOpenEntry, onRetry }: SaveAllResultMessageProps): import("react/jsx-runtime").JSX.Element;
15
+ type Actions = Omit<SaveAllResultMessageProps, 'result'>;
16
+ /**
17
+ * Reports a finished Save all in one toast. Partly failed, it stays until dismissed: yellow when
18
+ * nothing failed for good, else red, each entry marked by its own severity.
19
+ */
20
+ export declare function showSaveAllResult(result: SaveAllResult, actions: Actions): void;
21
+ /** Takes an entry out of the result on screen, and the toast down with its last entry. */
22
+ export declare function settleSaveAllEntry(contentId: string): void;
23
+ /** Takes a partly failed result down, as when the branch it belongs to is left. */
24
+ export declare function clearSaveAllResult(): void;
25
+ export {};
@@ -0,0 +1,86 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { Anchor, Box, Button, Group, List, Stack, Text, VisuallyHidden } from '@mantine/core';
4
+ import { notifications } from '@mantine/notifications';
5
+ import { IconAlertTriangle, IconCircleX } from '@tabler/icons-react';
6
+ import { SAVE_ALL_RESULT_TOAST_ID, } from '../hooks/save-all.js';
7
+ import { getNotificationDuration } from '../utils/env.js';
8
+ const entries = (count) => `${count} ${count === 1 ? 'entry' : 'entries'}`;
9
+ /** Nothing is lost yet: no answer, or a change made elsewhere. */
10
+ const isWarning = (failure) => failure.kind === 'unanswered' || failure.kind === 'conflict';
11
+ /**
12
+ * Names every entry Save all could not save, each opening that entry, with its reason and a
13
+ * warning or error mark. "Try again" is offered only when a failure is one a retry can pass.
14
+ * @internal Exported for stories.
15
+ */
16
+ export function SaveAllResultMessage({ result, onOpenEntry, onRetry }) {
17
+ const retryable = result.failed.some((failure) => failure.kind === 'error' || failure.retryable);
18
+ return (_jsxs(Stack, { gap: 6, "data-testid": "save-all-result", children: [result.saved.length > 0 && _jsx(Text, { size: "sm", children: `${entries(result.saved.length)} saved.` }), _jsx(List, { size: "sm", spacing: 6, listStyleType: "none", pl: 0, children: result.failed.map((failure) => (_jsx(List.Item, { "data-testid": "save-all-failure", children: _jsxs(Group, { gap: 6, wrap: "nowrap", align: "flex-start", children: [_jsx(Box, { component: "span", display: "inline-flex", mt: 2, "aria-hidden": "true", c: isWarning(failure) ? 'var(--mantine-color-text)' : 'var(--canopy-danger-text)', children: isWarning(failure) ? _jsx(IconAlertTriangle, { size: 16 }) : _jsx(IconCircleX, { size: 16 }) }), _jsxs("div", { children: [_jsx(VisuallyHidden, { component: "span", children: isWarning(failure) ? 'Warning: ' : 'Error: ' }), _jsx(Anchor, { component: "button", type: "button", size: "sm", fw: 600, onClick: () => onOpenEntry(failure.path), children: failure.label }), _jsx(Text, { size: "sm", span: true, children: `: ${failure.reason}` }), failure.recreate && (_jsx(Button, { display: "block", mt: 4, size: "compact-xs", variant: "default", onClick: failure.recreate, children: "Save as new entry" }))] })] }) }, failure.contentId))) }), _jsx(Text, { size: "sm", children: result.failed.length === 1
19
+ ? 'Its unsaved changes are kept.'
20
+ : 'Their unsaved changes are kept.' }), retryable && (_jsx(Button, { size: "compact-sm", variant: "default", w: "fit-content", onClick: onRetry, children: "Try again" }))] }));
21
+ }
22
+ // The result on screen, so an entry settled elsewhere (saved, refreshed, discarded) leaves it.
23
+ let shown;
24
+ const renderFailures = (result, actions, update) => {
25
+ const data = {
26
+ id: SAVE_ALL_RESULT_TOAST_ID,
27
+ // An unanswered save may have landed, so "not saved" would claim more than is known.
28
+ title: `${result.failed.length === result.total
29
+ ? entries(result.total)
30
+ : `${result.failed.length} of ${entries(result.total)}`} ${result.failed.some((failure) => failure.kind === 'unanswered') ? 'may not have saved' : 'not saved'}`,
31
+ message: _jsx(SaveAllResultMessage, { result: result, ...actions }),
32
+ color: result.failed.every(isWarning) ? 'yellow' : 'red',
33
+ autoClose: false,
34
+ withCloseButton: true,
35
+ onClose: () => {
36
+ shown = undefined;
37
+ },
38
+ };
39
+ if (update)
40
+ notifications.update(data);
41
+ else
42
+ notifications.show(data);
43
+ };
44
+ /**
45
+ * Reports a finished Save all in one toast. Partly failed, it stays until dismissed: yellow when
46
+ * nothing failed for good, else red, each entry marked by its own severity.
47
+ */
48
+ export function showSaveAllResult(result, actions) {
49
+ shown = undefined;
50
+ notifications.hide(SAVE_ALL_RESULT_TOAST_ID);
51
+ if (result.total === 0 || result.failed.length === 0) {
52
+ notifications.show({
53
+ id: SAVE_ALL_RESULT_TOAST_ID,
54
+ // An empty run found every change already on the server, or settled meanwhile.
55
+ message: result.total === 0 ? 'No changes to save' : `${entries(result.saved.length)} saved`,
56
+ color: result.total === 0 ? 'blue' : 'green',
57
+ autoClose: getNotificationDuration(4000),
58
+ withCloseButton: true,
59
+ });
60
+ return;
61
+ }
62
+ shown = { result, actions };
63
+ renderFailures(result, actions, false);
64
+ }
65
+ /** Takes an entry out of the result on screen, and the toast down with its last entry. */
66
+ export function settleSaveAllEntry(contentId) {
67
+ if (!shown)
68
+ return;
69
+ const failed = shown.result.failed.filter((failure) => failure.contentId !== contentId);
70
+ if (failed.length === shown.result.failed.length)
71
+ return;
72
+ if (failed.length === 0) {
73
+ clearSaveAllResult();
74
+ return;
75
+ }
76
+ // Settled, the entry is no longer part of the run the title counts.
77
+ shown = { ...shown, result: { ...shown.result, failed, total: shown.result.total - 1 } };
78
+ renderFailures(shown.result, shown.actions, true);
79
+ }
80
+ /** Takes a partly failed result down, as when the branch it belongs to is left. */
81
+ export function clearSaveAllResult() {
82
+ if (!shown)
83
+ return;
84
+ shown = undefined;
85
+ notifications.hide(SAVE_ALL_RESULT_TOAST_ID);
86
+ }
@@ -0,0 +1,30 @@
1
+ import type { SaveAllProgress } from '../hooks/save-all.js';
2
+ export interface SaveSplitButtonProps {
3
+ onSave: () => void;
4
+ /** Why Save can't act now; empty when it can. */
5
+ saveBlockedReason: string;
6
+ /** The editor is loading or saving: Save waits without a reason. */
7
+ busy: boolean;
8
+ onSaveAll: () => void;
9
+ /** Entries on the branch with unsaved changes. */
10
+ unsavedCount: number;
11
+ /** Why Save all can't act now, other than nothing being unsaved. */
12
+ saveAllBlockedReason?: string;
13
+ /** Set while Save all runs. */
14
+ saveAllProgress: SaveAllProgress | null;
15
+ /** A Save of the open entry is in flight. */
16
+ saving?: boolean;
17
+ /** Rises each time a single Save lands, whichever entry it saved. */
18
+ savedSignal?: number;
19
+ /** The last Save that landed saved the open entry. */
20
+ savedHere?: boolean;
21
+ /** The open entry has unsaved changes. */
22
+ entryDirty: boolean;
23
+ /** Writing drafts to localStorage failed, so they live only in this tab. */
24
+ draftStorageFailed: boolean;
25
+ }
26
+ /**
27
+ * The unsaved indicator, then Save with a caret offering "Save all". The indicator is icon-only
28
+ * below `lg`, its text then its Tooltip and accessible name.
29
+ */
30
+ export declare function SaveSplitButton({ onSave, saveBlockedReason, busy, onSaveAll, unsavedCount, saveAllBlockedReason, saveAllProgress, saving, savedSignal, savedHere, entryDirty, draftStorageFailed, }: SaveSplitButtonProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,108 @@
1
+ 'use client';
2
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useEffect, useRef, useState } from 'react';
4
+ import { Box, Button, Loader, Menu, Text, Tooltip, VisuallyHidden } from '@mantine/core';
5
+ import { useReducedMotion } from '@mantine/hooks';
6
+ import { IconAlertTriangle, IconCheck, IconChevronDown, IconCircleDot, IconLoader2, } from '@tabler/icons-react';
7
+ import { EDITOR_ACTIONS } from '../copy.js';
8
+ import { ReasonedMenuItem } from './ReasonedMenuItem.js';
9
+ import { useMenuItemFocusReturn } from '../utils/focus-return.js';
10
+ const STORAGE_FAILED_HINT = "Save before you leave or reload, or they'll be lost.";
11
+ const SAVED_SHOWN_MS = 2000;
12
+ // Below `sm` the status words are visually hidden, so Save keeps its own width beside an icon.
13
+ const LABEL_TEXT_CLASS = 'canopy-save-label-text';
14
+ const LABEL_STYLES = `
15
+ @media (max-width: 47.99em) {
16
+ .${LABEL_TEXT_CLASS} {
17
+ position: absolute; width: 1px; height: 1px; overflow: hidden;
18
+ clip: rect(0 0 0 0); white-space: nowrap;
19
+ }
20
+ }
21
+ `;
22
+ /**
23
+ * Every label in one grid cell, only `shown` visible, so the button keeps the widest one's width
24
+ * and the top bar never shifts.
25
+ */
26
+ function SaveLabels({ shown, spinner }) {
27
+ const labels = {
28
+ save: EDITOR_ACTIONS.save,
29
+ saving: (_jsxs(_Fragment, { children: [_jsx(Box, { component: "span", display: "inline-flex", w: 14, children: shown === 'saving' &&
30
+ (spinner ? (_jsx(Loader, { size: 14, color: "currentColor" })) : (_jsx(IconLoader2, { size: 14, "aria-hidden": "true" }))) }), _jsx("span", { className: LABEL_TEXT_CLASS, children: "Saving\u2026" })] })),
31
+ saved: (_jsxs(_Fragment, { children: [_jsx(IconCheck, { size: 14, "aria-hidden": "true" }), _jsx("span", { className: LABEL_TEXT_CLASS, children: "Saved" })] })),
32
+ };
33
+ return (_jsx(Box, { component: "span", pos: "relative", style: { display: 'inline-grid' }, children: Object.keys(labels).map((key) => (_jsx(Box, { component: "span", "aria-hidden": key === shown ? undefined : true, style: {
34
+ gridArea: '1 / 1',
35
+ display: 'inline-flex',
36
+ alignItems: 'center',
37
+ justifyContent: 'center',
38
+ gap: 6,
39
+ visibility: key === shown ? 'visible' : 'hidden',
40
+ }, children: labels[key] }, key))) }));
41
+ }
42
+ /**
43
+ * The unsaved indicator, then Save with a caret offering "Save all". The indicator is icon-only
44
+ * below `lg`, its text then its Tooltip and accessible name.
45
+ */
46
+ export function SaveSplitButton({ onSave, saveBlockedReason, busy, onSaveAll, unsavedCount, saveAllBlockedReason, saveAllProgress, saving = false, savedSignal, savedHere = false, entryDirty, draftStorageFailed, }) {
47
+ const caret = useRef(null);
48
+ const menuFocus = useMenuItemFocusReturn(caret);
49
+ const reducedMotion = useReducedMotion();
50
+ // Each landed Save shows "Saved" once, as it lands and only on its own entry: one that landed
51
+ // before mount or while another entry was open shows nothing later.
52
+ const [seenSignal, setSeenSignal] = useState(savedSignal ?? 0);
53
+ const [flashing, setFlashing] = useState();
54
+ // Leaving the saved entry ends its "Saved", so a quick return doesn't show or announce it again.
55
+ const [wasHere, setWasHere] = useState(savedHere);
56
+ if (wasHere !== savedHere) {
57
+ setWasHere(savedHere);
58
+ if (!savedHere)
59
+ setFlashing(undefined);
60
+ }
61
+ if (savedSignal !== undefined && savedSignal > seenSignal) {
62
+ setSeenSignal(savedSignal);
63
+ setFlashing(savedHere ? savedSignal : undefined);
64
+ }
65
+ useEffect(() => {
66
+ if (flashing === undefined)
67
+ return;
68
+ const timer = setTimeout(() => setFlashing(undefined), SAVED_SHOWN_MS);
69
+ return () => clearTimeout(timer);
70
+ }, [flashing]);
71
+ // Blocked, Save stays focusable (`aria-disabled`, not `disabled`), so its reason opens on focus.
72
+ const saveBlocked = !!saveBlockedReason || busy || !!saveAllProgress;
73
+ const label = saving
74
+ ? 'saving'
75
+ : flashing !== undefined &&
76
+ flashing === savedSignal &&
77
+ savedHere &&
78
+ !entryDirty &&
79
+ !saveAllProgress
80
+ ? 'saved'
81
+ : 'save';
82
+ const saveGreyed = saveBlocked && !saveAllProgress && label === 'save';
83
+ const indicatorText = draftStorageFailed
84
+ ? 'Unsaved · not kept on this device'
85
+ : 'Unsaved · kept on this device';
86
+ const indicatorColor = draftStorageFailed ? 'var(--canopy-danger-text)' : 'dimmed';
87
+ return (_jsxs(_Fragment, { children: [_jsx(Box, { role: "status", ta: "right", w: { base: 20, lg: 112 }, style: { flexShrink: 0 }, children: entryDirty && (_jsx(Tooltip, { label: draftStorageFailed ? `${indicatorText}. ${STORAGE_FAILED_HINT}` : indicatorText, multiline: true, w: 260, children: _jsxs(Text, { size: "xs", lh: 1.2, c: indicatorColor, "data-testid": "unsaved-indicator", children: [_jsx(Box, { component: "span", visibleFrom: "lg", children: indicatorText }), _jsx(Box, { component: "span", hiddenFrom: "lg", display: "inline-flex", "aria-hidden": "true", children: draftStorageFailed ? _jsx(IconAlertTriangle, { size: 18 }) : _jsx(IconCircleDot, { size: 18 }) }), _jsx(VisuallyHidden, { component: "span", hiddenFrom: "lg", children: indicatorText }), draftStorageFailed && (_jsxs(VisuallyHidden, { component: "span", children: [". ", STORAGE_FAILED_HINT] }))] }) })) }), _jsx(VisuallyHidden, { role: "status", children: saveAllProgress
88
+ ? `Saving ${saveAllProgress.total} ${saveAllProgress.total === 1 ? 'entry' : 'entries'}…`
89
+ : label === 'saved'
90
+ ? 'Saved'
91
+ : '' }), _jsx("style", { children: LABEL_STYLES }), _jsxs(Button.Group, { style: { flexShrink: 0 }, children: [_jsx(Tooltip, { label: saveBlockedReason,
92
+ // Not beside "Saving…" or "Saved", which already say what happened.
93
+ disabled: !saveBlockedReason || busy || label !== 'save', multiline: true, maw: 280, children: _jsx(Button, { "data-testid": "save-button", variant: "filled", size: "sm", leftSection: saveAllProgress && !reducedMotion ? (_jsx(Loader, { size: 14, color: "currentColor" })) : undefined, onClick: () => {
94
+ if (!saveBlocked)
95
+ onSave();
96
+ }, "data-disabled": saveGreyed ? true : undefined, "aria-disabled": saveBlocked ? true : undefined, children: saveAllProgress ? (`Saving ${Math.min(saveAllProgress.done + 1, saveAllProgress.total)} of ${saveAllProgress.total}…`) : (_jsx(SaveLabels, { shown: label, spinner: !reducedMotion })) }) }), _jsxs(Menu, { withinPortal: true, shadow: "sm", position: "bottom-end", ...menuFocus.menu, children: [_jsx(Tooltip, { label: "Save options", events: { hover: true, focus: true, touch: false }, children: _jsx(Menu.Target, { children: _jsx(Button, { ref: caret,
97
+ // Light beside a greyed Save, so a blocked split button shows no filled half.
98
+ variant: saveGreyed ? 'light' : 'filled', size: "sm", px: 8, "aria-label": "Save options", "data-testid": "save-options-button", style: { borderInlineStart: '1px solid var(--mantine-color-body)' }, children: _jsx(IconChevronDown, { size: 14, "aria-hidden": "true" }) }) }) }), _jsx(Menu.Dropdown, { ...menuFocus.dropdown, children: _jsx(ReasonedMenuItem, { "data-testid": "save-all-menu-item", onClick: onSaveAll, blockedReason: saveAllBlockedReason ??
99
+ (saveAllProgress
100
+ ? `${EDITOR_ACTIONS.saveAll} is already running.`
101
+ : unsavedCount === 0
102
+ ? 'No entry on this branch has unsaved changes.'
103
+ : busy
104
+ ? 'Wait for the current save or load to finish.'
105
+ : undefined), children: unsavedCount > 0
106
+ ? `${EDITOR_ACTIONS.saveAll} (${unsavedCount})`
107
+ : EDITOR_ACTIONS.saveAll }) })] })] })] }));
108
+ }
@@ -0,0 +1,39 @@
1
+ import React from 'react';
2
+ export interface StagedChangesDrawerProps {
3
+ opened: boolean;
4
+ /** Closes the drawer. Called directly when clean, only after a confirmed discard when dirty, and never while saving. */
5
+ onClose: () => void;
6
+ title: string;
7
+ description: string;
8
+ size: number;
9
+ isDirty: boolean;
10
+ isSaving: boolean;
11
+ /** Whether the viewer may save; without it the save bar never renders. */
12
+ canEdit: boolean;
13
+ saveLabel: string;
14
+ onSave: () => void;
15
+ onDiscard: () => void;
16
+ /** A modal of the panel's own is open: the drawer yields Escape to it (Mantine listens for Escape on window, capture phase). */
17
+ childModalOpen?: boolean;
18
+ /** Runs once the close transition has finished; see `useOpeningKey`. */
19
+ onExited?: () => void;
20
+ /** Names the element that takes focus after the drawer closes, for an opener that unmounted (a menu item). */
21
+ returnFocusTo?: () => HTMLElement | null;
22
+ children: React.ReactNode;
23
+ }
24
+ /**
25
+ * A key that changes each time the drawer finishes closing. A panel keyed with it, and passing
26
+ * `onExited` through, gets fresh state for each opening, as the drawer's own children do. A
27
+ * reopen during the close transition cancels it, so that opening keeps the previous state.
28
+ */
29
+ export declare function useOpeningKey(): {
30
+ key: number;
31
+ onExited: () => void;
32
+ };
33
+ /**
34
+ * The drawer shell for an admin panel that stages edits and saves them as one batch
35
+ * (groups, permissions). Staged edits exist only in the panel's state, so the shell keeps
36
+ * them from being lost unnoticed: a title badge and a sticky save bar while dirty, a
37
+ * discard confirm on close and on the save bar's Discard changes, and a `beforeunload` prompt.
38
+ */
39
+ export declare const StagedChangesDrawer: React.FC<StagedChangesDrawerProps>;
@@ -0,0 +1,92 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useCallback, useEffect, useReducer, useRef } from 'react';
4
+ import { Badge, Button, Drawer, Fieldset, Group, Text, Title } from '@mantine/core';
5
+ import { openConfirm } from '../utils/confirm-modal.js';
6
+ import { focusReturnProps, rememberOpener, returnTarget } from '../utils/focus-return.js';
7
+ /**
8
+ * A key that changes each time the drawer finishes closing. A panel keyed with it, and passing
9
+ * `onExited` through, gets fresh state for each opening, as the drawer's own children do. A
10
+ * reopen during the close transition cancels it, so that opening keeps the previous state.
11
+ */
12
+ export function useOpeningKey() {
13
+ const [key, onExited] = useReducer((n) => n + 1, 0);
14
+ return { key, onExited };
15
+ }
16
+ /** Asks the browser to confirm a reload or navigation, for exactly as long as `isDirty` holds. */
17
+ function useBeforeUnloadWhileDirty(isDirty) {
18
+ useEffect(() => {
19
+ if (!isDirty)
20
+ return;
21
+ const handler = (event) => {
22
+ event.preventDefault();
23
+ // Chrome before 119 shows the prompt only when returnValue is set.
24
+ event.returnValue = '';
25
+ };
26
+ window.addEventListener('beforeunload', handler);
27
+ return () => window.removeEventListener('beforeunload', handler);
28
+ }, [isDirty]);
29
+ }
30
+ /**
31
+ * The drawer shell for an admin panel that stages edits and saves them as one batch
32
+ * (groups, permissions). Staged edits exist only in the panel's state, so the shell keeps
33
+ * them from being lost unnoticed: a title badge and a sticky save bar while dirty, a
34
+ * discard confirm on close and on the save bar's Discard changes, and a `beforeunload` prompt.
35
+ */
36
+ export const StagedChangesDrawer = ({ opened, onClose, title, description, size, isDirty, isSaving, canEdit, saveLabel, onSave, onDiscard, childModalOpen = false, onExited, returnFocusTo, children, }) => {
37
+ // A ref, not state: @mantine/modals runs the confirm's onClose inside its reducer, during
38
+ // render, where setting another component's state draws a React warning. While the confirm is open the
39
+ // drawer still hears Escape, so requestClose must ignore it.
40
+ const confirmOpenRef = useRef(false);
41
+ const titleRef = useRef(null);
42
+ useBeforeUnloadWhileDirty(isDirty);
43
+ const confirmDiscard = useCallback((body, onConfirm, focusReturn = {}) => {
44
+ confirmOpenRef.current = true;
45
+ openConfirm({
46
+ ...focusReturn,
47
+ title: 'Discard unsaved changes?',
48
+ children: _jsx(Text, { size: "sm", children: body }),
49
+ labels: { confirm: 'Discard changes', cancel: 'Keep editing' },
50
+ confirmProps: { color: 'red' },
51
+ onConfirm,
52
+ // Mantine calls onClose for every exit, the confirm included.
53
+ onClose: () => {
54
+ confirmOpenRef.current = false;
55
+ },
56
+ });
57
+ }, []);
58
+ const requestClose = useCallback(() => {
59
+ // The save is already on its way to the server; discarding now could not stop it.
60
+ if (isSaving || confirmOpenRef.current)
61
+ return;
62
+ if (!isDirty) {
63
+ onClose();
64
+ return;
65
+ }
66
+ confirmDiscard('Closing now drops the changes you have not saved.', () => {
67
+ onDiscard();
68
+ onClose();
69
+ });
70
+ }, [isDirty, isSaving, onClose, onDiscard, confirmDiscard]);
71
+ const requestDiscard = useCallback(() => {
72
+ if (isSaving || confirmOpenRef.current)
73
+ return;
74
+ // Kept, the changes keep the save bar and its button; discarded, both go, and the title stands in.
75
+ const opener = rememberOpener();
76
+ confirmDiscard('Discarding drops the changes you have not saved.', onDiscard, focusReturnProps(() => returnTarget(opener, () => titleRef.current)));
77
+ }, [isSaving, onDiscard, confirmDiscard]);
78
+ const { onExitTransitionEnd: returnFocus, ...focusReturn } = focusReturnProps(returnFocusTo);
79
+ return (_jsxs(Drawer, { opened: opened, onClose: requestClose, closeOnEscape: !childModalOpen, ...focusReturn, onExitTransitionEnd: () => {
80
+ returnFocus?.();
81
+ onExited?.();
82
+ }, position: "right", title: _jsxs("div", { children: [_jsxs(Group, { gap: "xs", children: [_jsx(Title, { order: 4, ref: titleRef, tabIndex: -1, children: title }), isDirty && (_jsx(Badge, { color: "orange", variant: "light", children: "Unsaved changes" }))] }), _jsx(Text, { size: "xs", c: "dimmed", children: description })] }), padding: "md", size: size, overlayProps: { blur: 2 }, children: [_jsx(Fieldset, { variant: "unstyled", disabled: isSaving, "aria-busy": isSaving, children: children }), canEdit && isDirty && (
83
+ // The drawer's content box is the scroll container, so a sticky bar stays in view
84
+ // however long the panel's list grows.
85
+ _jsxs(Group, { justify: "space-between", py: "sm", gap: "sm", style: {
86
+ position: 'sticky',
87
+ bottom: 0,
88
+ zIndex: 1,
89
+ background: 'var(--mantine-color-body)',
90
+ borderTop: '1px solid var(--mantine-color-default-border)',
91
+ }, children: [_jsx(Text, { size: "sm", c: "dimmed", children: "Changes take effect only after you save." }), _jsxs(Group, { gap: "sm", children: [_jsx(Button, { size: "sm", variant: "subtle", color: "neutral", onClick: requestDiscard, disabled: isSaving, children: "Discard changes" }), _jsx(Button, { size: "sm", onClick: onSave, loading: isSaving, disabled: isSaving, children: saveLabel })] })] }))] }));
92
+ };
@@ -0,0 +1,6 @@
1
+ export interface UnavailableEntryNoticeProps {
2
+ /** Registry keys the entry's type names; empty when only the 503 flagged it. */
3
+ schemaRefs: readonly string[];
4
+ }
5
+ /** Stands in for the form of an entry whose type the running code cannot resolve. */
6
+ export declare function UnavailableEntryNotice({ schemaRefs }: UnavailableEntryNoticeProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,9 @@
1
+ 'use client';
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import { Alert } from '@mantine/core';
4
+ import { IconAlertCircle } from '@tabler/icons-react';
5
+ import { UnavailableTypeMessage } from './UnavailableTypeMessage.js';
6
+ /** Stands in for the form of an entry whose type the running code cannot resolve. */
7
+ export function UnavailableEntryNotice({ schemaRefs }) {
8
+ return (_jsx("div", { style: { padding: 'var(--mantine-spacing-md)' }, "data-testid": "unavailable-entry-notice", children: _jsx(Alert, { color: "yellow", icon: _jsx(IconAlertCircle, { size: 16 }), title: "This entry can't be edited yet", children: _jsx(UnavailableTypeMessage, { schemaRefs: schemaRefs }) }) }));
9
+ }
@@ -0,0 +1,6 @@
1
+ export interface UnavailableTypeMessageProps {
2
+ /** Registry keys the unavailable entry types name. */
3
+ schemaRefs: readonly string[];
4
+ }
5
+ /** The shared "this editor version doesn't know this content type" text, refs in a code style. */
6
+ export declare function UnavailableTypeMessage({ schemaRefs }: UnavailableTypeMessageProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,9 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
+ import { Code } from '@mantine/core';
4
+ import { unavailableTypeMessageParts } from '../unavailable-entry-type.js';
5
+ /** The shared "this editor version doesn't know this content type" text, refs in a code style. */
6
+ export function UnavailableTypeMessage({ schemaRefs }) {
7
+ const { before, after } = unavailableTypeMessageParts(schemaRefs);
8
+ return (_jsxs(_Fragment, { children: [before, schemaRefs.map((ref, i) => (_jsxs("span", { children: [i > 0 && ', ', _jsx(Code, { children: ref })] }, ref))), after] }));
9
+ }
@@ -0,0 +1,18 @@
1
+ export interface UndoToastMessageProps {
2
+ /** Names what was removed: `"Branch-aware" from Features`, `item 2 from Features`, `"SEO"`. */
3
+ label: string;
4
+ /** `clear` for an optional object's Clear. */
5
+ action?: 'remove' | 'clear';
6
+ /** A sentence after the message, such as where the item's comments went. */
7
+ note?: string;
8
+ onUndo: () => void;
9
+ /**
10
+ * Called when keyboard focus reaches the toast. Mantine pauses its timer on hover only, so the
11
+ * caller stops it here or the toast can close under the focused Undo.
12
+ */
13
+ onHold?: () => void;
14
+ /** Called when focus leaves the toast, so its timer can run again. */
15
+ onRelease?: () => void;
16
+ }
17
+ /** The body of the toast a form Remove or Clear raises in place of a confirmation. */
18
+ export declare function UndoToastMessage({ label, action, note, onUndo, onHold, onRelease, }: UndoToastMessageProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,10 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Button, Group, Text } from '@mantine/core';
3
+ /** The body of the toast a form Remove or Clear raises in place of a confirmation. */
4
+ export function UndoToastMessage({ label, action = 'remove', note, onUndo, onHold, onRelease, }) {
5
+ const [done, doing] = action === 'clear' ? ['Cleared', 'clearing'] : ['Removed', 'removing'];
6
+ return (_jsxs(Group, { justify: "space-between", align: "center", gap: "sm", wrap: "nowrap", onFocus: onHold, onBlur: (event) => {
7
+ if (!event.currentTarget.contains(event.relatedTarget))
8
+ onRelease?.();
9
+ }, children: [_jsx(Text, { size: "sm", miw: 0, children: note ? `${done} ${label}. ${note}` : `${done} ${label}` }), _jsx(Button, { size: "compact-sm", variant: "subtle", style: { flexShrink: 0 }, onClick: onUndo, "aria-label": `Undo ${doing} ${label}`, children: "Undo" })] }));
10
+ }
@@ -1,15 +1,11 @@
1
1
  import type { UserSearchResult } from '../../auth/types.js';
2
2
  import type { CanopyUserId } from '../../types.js';
3
3
  export interface UserBadgeProps {
4
- /** User ID to display */
5
4
  userId: CanopyUserId;
6
- /** Function to fetch user metadata by ID */
7
5
  getUserMetadata: (userId: string) => Promise<UserSearchResult | null>;
8
- /** Display mode */
9
6
  variant?: 'avatar-only' | 'avatar-name' | 'full';
10
7
  /** Show email tooltip on hover (default: true) */
11
8
  showEmailTooltip?: boolean;
12
- /** Optional removal button */
13
9
  onRemove?: (userId: CanopyUserId) => void;
14
10
  /** Color theme (for Badge wrapper) */
15
11
  color?: string;
@@ -19,9 +15,10 @@ export interface UserBadgeProps {
19
15
  badgeVariant?: 'filled' | 'light' | 'outline';
20
16
  /** Always show Badge wrapper (even without onRemove) */
21
17
  showBadge?: boolean;
22
- /** Loading state override */
23
18
  loading?: boolean;
24
19
  /** Cached user data to avoid fetching (e.g., from search results) */
25
20
  cachedUser?: UserSearchResult;
21
+ /** Admin-only surfaces: a person with no name to show has their id in the tooltip, remove label and hidden text. */
22
+ revealIdOnFallback?: boolean;
26
23
  }
27
24
  export declare const UserBadge: React.FC<UserBadgeProps>;