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
@@ -1,11 +1,16 @@
1
- import { useEffect, useMemo, useRef, useState } from 'react';
1
+ import { createElement, useCallback, useEffect, useMemo, useRef, useState } from 'react';
2
2
  import useSWR, { useSWRConfig } from 'swr';
3
3
  import { notifications } from '@mantine/notifications';
4
4
  import { buildWritePayload, normalizeContentPayload } from '../editor-utils.js';
5
5
  import { isDataOnlyFormat } from '../../utils/format.js';
6
+ import { withTitleAtPath } from '../../utils/title-field.js';
6
7
  import { getErrorMessage } from '../../utils/error.js';
7
8
  import { useApiClient } from '../context/index.js';
9
+ import { SCHEMA_UNAVAILABLE_CODE } from '../unavailable-entry-type.js';
8
10
  import { entriesKey, fetchEntriesAndSchema } from './useEntriesData.js';
11
+ import { RetryToastMessage } from '../components/RetryToastMessage.js';
12
+ import { isGatewayFailure, isUnanswered, MayHaveLandedError, UnansweredError } from '../unanswered.js';
13
+ const ENTRIES_LOAD_FAILED_TOAST_ID = 'canopy-entries-load-failed';
9
14
  // Re-exported so existing imports of `listAllEntries` from this module keep
10
15
  // working -- the implementation lives in useEntriesData.ts alongside the
11
16
  // other fetch/key/type pieces useEntryManager shares with the SWR layer.
@@ -18,13 +23,27 @@ export { listAllEntries } from './useEntriesData.js';
18
23
  * errors (when present) so the form can surface them next to the fields.
19
24
  */
20
25
  export class SaveApiError extends Error {
21
- constructor(status, serverMessage, fieldErrors) {
26
+ constructor(status,
27
+ /** The server's (or saveEntry's own) explanation, if any; `message` falls back to the status. */
28
+ serverMessage, fieldErrors, code) {
22
29
  super(serverMessage || `Save failed: ${status}`);
23
30
  this.status = status;
31
+ this.serverMessage = serverMessage;
24
32
  this.fieldErrors = fieldErrors;
33
+ this.code = code;
25
34
  this.name = 'SaveApiError';
26
35
  }
27
36
  }
37
+ /**
38
+ * Thrown by a read for editing when the API answers `SCHEMA_UNAVAILABLE`: the entry's type
39
+ * names a schema the running code lacks, so the entry cannot be opened until that changes.
40
+ */
41
+ export class EntrySchemaUnavailableError extends Error {
42
+ constructor(serverMessage) {
43
+ super(serverMessage || 'Entry type unavailable');
44
+ this.name = 'EntrySchemaUnavailableError';
45
+ }
46
+ }
28
47
  // Module-level singletons for the derived-empty fallbacks. These must be
29
48
  // referentially stable: `collectionByPath` and `currentEntry` memoize on them,
30
49
  // and a fresh `[]` each render would invalidate both on every render while a
@@ -32,6 +51,11 @@ export class SaveApiError extends Error {
32
51
  const EMPTY_ENTRIES = [];
33
52
  const EMPTY_COLLECTIONS = [];
34
53
  const EMPTY_SCHEMAS = [];
54
+ /**
55
+ * The URL's `entry`: a path; `''` for no entry and the navigator open (the crash screen's escape
56
+ * from an entry that crashes the editor); null opens the first entry.
57
+ */
58
+ const readUrlEntry = () => typeof window === 'undefined' ? null : new URLSearchParams(window.location.search).get('entry');
35
59
  /**
36
60
  * Custom hook for managing editor entries (CRUD operations).
37
61
  */
@@ -74,7 +98,11 @@ export function useEntryManager(options) {
74
98
  };
75
99
  // Initialize with prop value or empty (URL sync happens in effect after mount)
76
100
  const [selectedPath, setSelectedPath] = useState(options.initialSelectedId ?? '');
77
- const [navigatorOpen, setNavigatorOpen] = useState(false);
101
+ const selectedPathRef = useRef(selectedPath);
102
+ selectedPathRef.current = selectedPath;
103
+ // Counts selections through the returned setter, not this hook's own fallbacks.
104
+ const selectionsByCallerRef = useRef(0);
105
+ const [navigatorOpen, setNavigatorOpen] = useState(() => readUrlEntry() === '');
78
106
  const isInitialMount = useRef(true);
79
107
  const hasSyncedFromUrl = useRef(false);
80
108
  // OCC version tokens — captured on load, sent on save. Keyed by
@@ -148,11 +176,22 @@ export function useEntryManager(options) {
148
176
  const [createModalError, setCreateModalError] = useState(null);
149
177
  const [createModalCreating, setCreateModalCreating] = useState(false);
150
178
  // Store the URL entry param on mount (before any effects change the URL)
151
- const initialUrlEntry = useRef(null);
152
- if (typeof window !== 'undefined' && initialUrlEntry.current === null) {
153
- const params = new URLSearchParams(window.location.search);
154
- initialUrlEntry.current = params.get('entry');
179
+ const initialUrlEntry = useRef(undefined);
180
+ if (initialUrlEntry.current === undefined) {
181
+ initialUrlEntry.current = readUrlEntry();
155
182
  }
183
+ // Set by the URL's empty `entry` or by `closeEntry`; honoured until an entry is selected or the
184
+ // branch changes, after which a lost selection opens the first again.
185
+ const noEntryRequested = useRef(initialUrlEntry.current === '');
186
+ const selectEntryPath = useCallback((path) => {
187
+ selectionsByCallerRef.current += 1;
188
+ setSelectedPath(path);
189
+ }, []);
190
+ // No entry open, as the URL's empty `entry` asks, where a lost selection opens the first.
191
+ const closeEntry = useCallback(() => {
192
+ noEntryRequested.current = true;
193
+ selectEntryPath('');
194
+ }, [selectEntryPath]);
156
195
  const collectionByPath = useMemo(() => {
157
196
  const map = new Map();
158
197
  const walk = (collections) => {
@@ -178,32 +217,46 @@ export function useEntryManager(options) {
178
217
  .filter((e) => e.collectionPath === createModalCollection.path && e.slug)
179
218
  .map((e) => e.slug));
180
219
  }, [entriesState, createModalCollection]);
181
- const loadEntry = async (entry) => {
220
+ // The read both entry paths share; returns the raw payload so `loadEntry` can take its version.
221
+ const readEntryPayload = async (entry, requestBranch) => {
182
222
  if (!entry.collectionPath) {
183
223
  throw new Error('Entry missing collectionPath');
184
224
  }
185
- // Pin the branch this request targets: if the user switches branches
186
- // while the read is in flight, the token must be recorded under the
187
- // branch that actually served it, not the current one.
188
- const requestBranch = options.branchName;
189
225
  // Build path from collectionPath and slug (if it's a collection entry)
190
226
  const path = entry.slug ? `${entry.collectionPath}/${entry.slug}` : entry.collectionPath;
191
227
  const result = await apiClient.content.read({
192
228
  branch: requestBranch,
193
229
  path,
194
230
  });
195
- if (!result.ok)
231
+ if (!result.ok) {
232
+ if (result.code === SCHEMA_UNAVAILABLE_CODE)
233
+ throw new EntrySchemaUnavailableError(result.error);
196
234
  throw new Error(`Load failed: ${result.status}${result.error ? ` — ${result.error}` : ''}`);
235
+ }
236
+ return result.data;
237
+ };
238
+ const loadEntry = async (entry) => {
239
+ // Pin the branch this request targets: if the user switches branches
240
+ // while the read is in flight, the token must be recorded under the
241
+ // branch that actually served it, not the current one.
242
+ const requestBranch = options.branchName;
243
+ const data = await readEntryPayload(entry, requestBranch);
197
244
  // Capture OCC version token for next save
198
- if (entry.contentId && typeof result.data?.version === 'number') {
199
- entryVersionsRef.current.set(versionKey(requestBranch, entry.contentId), result.data.version);
245
+ if (entry.contentId && typeof data?.version === 'number') {
246
+ entryVersionsRef.current.set(versionKey(requestBranch, entry.contentId), data.version);
200
247
  }
201
- return normalizeContentPayload(result.data);
248
+ return normalizeContentPayload(data);
202
249
  };
250
+ // The same read as `loadEntry`, minus the token capture. A value read this way can be
251
+ // compared against but never saved against: only `loadEntry` makes an entry saveable.
252
+ const readEntryValue = async (entry) => normalizeContentPayload(await readEntryPayload(entry, options.branchName));
203
253
  // Read side of the same branch-qualified token map `saveEntry` writes/reads.
204
254
  // Deliberately keyed off the CURRENT branch (not a pinned one): callers use
205
255
  // it to reason about the entry they are looking at right now.
206
256
  const getEntryVersion = (contentId) => entryVersionsRef.current.get(versionKey(options.branchName, contentId));
257
+ const adoptEntryVersion = (contentId, version) => {
258
+ entryVersionsRef.current.set(versionKey(options.branchName, contentId), version);
259
+ };
207
260
  const saveEntry = async (entry, value) => {
208
261
  if (!entry.collectionPath) {
209
262
  throw new Error('Entry missing collectionPath');
@@ -226,16 +279,17 @@ export function useEntryManager(options) {
226
279
  // no token means this entry was never successfully read on this branch and nothing could check the save
227
280
  // against other editors' work. Refused as a conflict; the server refuses it too.
228
281
  if (expectedVersion === undefined) {
229
- throw new SaveApiError(409, 'This entry has not been loaded from the server, so the save cannot be checked ' +
230
- "against other editors' changes. Reload it and try again.");
282
+ throw new SaveApiError(409, "This entry hasn't finished loading, so it can't be saved yet. Reload it and try again (your unsaved edits will be lost).");
231
283
  }
232
284
  const writeBody = {
233
285
  ...payload,
234
286
  expectedVersion,
235
287
  };
236
288
  const result = await apiClient.content.write(writeParams, writeBody);
289
+ if (isGatewayFailure(result))
290
+ throw new UnansweredError();
237
291
  if (!result.ok)
238
- throw new SaveApiError(result.status, result.error, result.fieldErrors);
292
+ throw new SaveApiError(result.status, result.error, result.fieldErrors, result.code);
239
293
  // Update stored version token from write response
240
294
  if (typeof result.data?.version === 'number') {
241
295
  entryVersionsRef.current.set(versionKey(requestBranch, entry.contentId), result.data.version);
@@ -265,6 +319,76 @@ export function useEntryManager(options) {
265
319
  }
266
320
  return normalizeContentPayload(result.data);
267
321
  };
322
+ // The branch's entries as the server lists them now, without replacing the list on screen.
323
+ const listEntriesUncommitted = async (branch) => (await fetchEntriesAndSchema(apiClient, branch, {
324
+ resolvePreviewSrc: options.resolvePreviewSrc,
325
+ })).entries;
326
+ // Commits a fresh list and opens what `pick` finds (returned either way), unless the user was
327
+ // off `fromPath` or chose another entry or branch since. Not judged after the refresh, which
328
+ // moves the selection off a vanished `fromPath` on its own.
329
+ const refreshAndOpen = async (branch, fromPath, pick) => {
330
+ const stillOnIt = selectedPathRef.current === fromPath;
331
+ const selections = selectionsByCallerRef.current;
332
+ const found = (await refreshEntries(branch)).find(pick);
333
+ if (!found || branch !== currentBranchRef.current)
334
+ return undefined;
335
+ if (stillOnIt && selections === selectionsByCallerRef.current)
336
+ setSelectedPath(found.path);
337
+ return found;
338
+ };
339
+ const followMovedEntry = async (entry) => {
340
+ const branch = options.branchName;
341
+ const movedHere = (e) => e.contentId === entry.contentId && e.path !== entry.path;
342
+ if (!(await listEntriesUncommitted(branch)).some(movedHere))
343
+ return undefined;
344
+ return refreshAndOpen(branch, entry.path, movedHere);
345
+ };
346
+ // After a create that landed, a failed refresh is "not listed yet", never a failed create.
347
+ const openCreated = (branch, fromPath, pick) => refreshAndOpen(branch, fromPath, pick).catch((err) => {
348
+ console.error(err);
349
+ return undefined;
350
+ });
351
+ const recreateEntry = async (entry, value) => {
352
+ if (!entry.collectionPath) {
353
+ throw new Error('Entry missing collectionPath');
354
+ }
355
+ const branch = options.branchName;
356
+ const path = entry.slug ? `${entry.collectionPath}/${entry.slug}` : entry.collectionPath;
357
+ const writeParams = { branch, path };
358
+ if (entry.entryType)
359
+ writeParams.entryType = entry.entryType;
360
+ // The new entry, not the gone one a lagging list may still show at that path.
361
+ const createdHere = (e) => e.path === entry.path && e.contentId !== entry.contentId;
362
+ let result;
363
+ try {
364
+ result = await apiClient.content.write(writeParams, {
365
+ ...buildWritePayload(entry, value),
366
+ expectedVersion: null,
367
+ });
368
+ if (isGatewayFailure(result))
369
+ throw new UnansweredError();
370
+ }
371
+ catch (err) {
372
+ // An unanswered create may have landed: if the list now has a new entry at the path, it did.
373
+ if (!isUnanswered(err))
374
+ throw err;
375
+ const listed = await listEntriesUncommitted(branch).catch(() => []);
376
+ if (!listed.some(createdHere))
377
+ throw err;
378
+ const landed = await openCreated(branch, entry.path, createdHere);
379
+ // No answer, so no version: read it, as a draft moved onto the entry needs it as its base.
380
+ if (landed)
381
+ await loadEntry(landed).catch(console.error);
382
+ return landed;
383
+ }
384
+ if (!result.ok)
385
+ throw new SaveApiError(result.status, result.error, result.fieldErrors, result.code);
386
+ const created = await openCreated(branch, entry.path, createdHere);
387
+ if (created && typeof result.data?.version === 'number') {
388
+ entryVersionsRef.current.set(versionKey(branch, created.contentId), result.data.version);
389
+ }
390
+ return created;
391
+ };
268
392
  // Explicit reload: always issues a fresh, independent fetch (never
269
393
  // deduped/coalesced against an in-flight automatic load) so callers that
270
394
  // just mutated content (save, create, rename, schema change) are
@@ -272,9 +396,9 @@ export function useEntryManager(options) {
272
396
  // refreshSeqRef doc comment above; also warms the SWR cache for `branch`
273
397
  // so a later automatic re-fetch of the same key (e.g. switching away and
274
398
  // back) can reuse it instead of refetching.
275
- const refreshEntries = async (branch = options.branchName) => {
399
+ const refreshContent = async (branch = options.branchName) => {
276
400
  if (!branch)
277
- return [];
401
+ return { entries: [], collections: [] };
278
402
  const seq = claimRefreshSeq(branch);
279
403
  const fetched = await fetchEntriesAndSchema(apiClient, branch, {
280
404
  resolvePreviewSrc: options.resolvePreviewSrc,
@@ -292,8 +416,9 @@ export function useEntryManager(options) {
292
416
  setView({ branch, ...fetched });
293
417
  }
294
418
  }
295
- return fetched.entries;
419
+ return fetched;
296
420
  };
421
+ const refreshEntries = async (branch = options.branchName) => (await refreshContent(branch)).entries;
297
422
  const handleCreateEntry = async (collectionPath, _) => {
298
423
  const col = collectionByPath.get(collectionPath);
299
424
  if (!col || col.type === 'entry') {
@@ -303,7 +428,7 @@ export function useEntryManager(options) {
303
428
  setCreateModalError(null);
304
429
  setCreateModalOpen(true);
305
430
  };
306
- const handleCreateModalSubmit = async (slug, entryTypeName) => {
431
+ const handleCreateModalSubmit = async (slug, entryTypeName, title) => {
307
432
  if (!createModalCollection)
308
433
  return;
309
434
  setCreateModalCreating(true);
@@ -317,9 +442,10 @@ export function useEntryManager(options) {
317
442
  // already exist yet". The client always sends create intent here,
318
443
  // never a blind update, so a same-slug create cannot silently
319
444
  // overwrite existing content.
445
+ const data = title && selectedType?.titleField ? withTitleAtPath(selectedType.titleField, title) : {};
320
446
  const payload = isDataOnlyFormat(format)
321
- ? { format: format, data: {}, expectedVersion: null }
322
- : { format, data: {}, body: '', expectedVersion: null };
447
+ ? { format: format, data, expectedVersion: null }
448
+ : { format, data, body: '', expectedVersion: null };
323
449
  // Use collection path (e.g., "content/posts") not name (e.g., "posts")
324
450
  const path = `${createModalCollection.path}/${slug}`;
325
451
  const result = await apiClient.content.write({
@@ -327,6 +453,8 @@ export function useEntryManager(options) {
327
453
  path,
328
454
  entryType: entryTypeName,
329
455
  }, payload);
456
+ if (isGatewayFailure(result))
457
+ throw new UnansweredError();
330
458
  if (!result.ok) {
331
459
  const errorMsg = 'error' in result ? result.error : `Create failed: ${result.status}`;
332
460
  throw new Error(errorMsg);
@@ -352,6 +480,9 @@ export function useEntryManager(options) {
352
480
  console.error(err);
353
481
  const errorMessage = err instanceof Error ? err.message : 'Create failed';
354
482
  setCreateModalError(errorMessage);
483
+ // An unanswered create may have landed; the content tree then shows it.
484
+ if (isUnanswered(err))
485
+ void refreshEntries().catch(console.error);
355
486
  }
356
487
  finally {
357
488
  setCreateModalCreating(false);
@@ -364,32 +495,57 @@ export function useEntryManager(options) {
364
495
  setCreateModalCreating(false);
365
496
  };
366
497
  const renameEntry = async (path, newSlug) => {
498
+ const renaming = entriesState.find((e) => e.path === path);
499
+ const contentId = renaming?.contentId;
500
+ const label = renaming?.label ?? path;
501
+ // As `refreshAndOpen`: the new path opens only if the user is still on the entry and branch.
502
+ const branch = options.branchName;
503
+ const stillOnIt = selectedPathRef.current === path;
504
+ const selections = selectionsByCallerRef.current;
505
+ // The rename landed whatever the refresh does. The new path is selected only once the list
506
+ // holds it: before that, the selection check would find it missing and open the first entry.
507
+ const landed = async (newPath) => {
508
+ const listed = await refreshEntries().catch((err) => {
509
+ console.error(err);
510
+ return null;
511
+ });
512
+ if (newPath &&
513
+ listed?.some((e) => e.path === newPath) &&
514
+ stillOnIt &&
515
+ selections === selectionsByCallerRef.current &&
516
+ branch === currentBranchRef.current) {
517
+ setSelectedPath(newPath);
518
+ }
519
+ notifications.show({ message: 'Entry renamed', color: 'green' });
520
+ };
367
521
  options.setBusy(true);
368
522
  try {
369
523
  const result = await apiClient.content.renameEntry({
370
524
  branch: options.branchName,
371
525
  path,
372
526
  }, { newSlug });
527
+ if (isGatewayFailure(result))
528
+ throw new UnansweredError();
373
529
  if (!result.ok) {
374
530
  const errorMsg = 'error' in result ? result.error : `Rename failed: ${result.status}`;
375
531
  throw new Error(errorMsg);
376
532
  }
377
- // Update the selected path if the renamed entry is currently selected
378
- if (selectedPath === path && result.data) {
379
- setSelectedPath(result.data.newPath);
380
- }
381
- // Refresh entries to get updated paths
382
- await refreshEntries();
383
- notifications.show({
384
- message: 'Entry renamed successfully',
385
- color: 'green',
386
- });
533
+ await landed(result.data?.newPath);
387
534
  }
388
535
  catch (err) {
389
536
  console.error(err);
390
- const errorMessage = err instanceof Error ? err.message : 'Rename failed';
391
- notifications.show({ message: errorMessage, color: 'red' });
392
- throw err;
537
+ if (!isUnanswered(err))
538
+ throw err;
539
+ // Re-listed by content ID, under the new slug it landed. Still at the old path proves
540
+ // nothing: past the client's deadline the server may still be working.
541
+ const listed = await listEntriesUncommitted(options.branchName).catch(() => null);
542
+ const now = contentId ? listed?.find((e) => e.contentId === contentId) : undefined;
543
+ if (now && now.path !== path && now.slug === newSlug) {
544
+ await landed(now.path);
545
+ return;
546
+ }
547
+ void refreshEntries().catch(console.error);
548
+ throw new MayHaveLandedError(`Renaming "${label}" may have gone through even though the server didn't answer. Check the "Content" panel before trying again.`);
393
549
  }
394
550
  finally {
395
551
  options.setBusy(false);
@@ -407,6 +563,7 @@ export function useEntryManager(options) {
407
563
  isInitialMount.current = false;
408
564
  }
409
565
  else {
566
+ noEntryRequested.current = false;
410
567
  setSelectedPath('');
411
568
  // Bound version-map growth across switches. Correctness no longer
412
569
  // depends on this clear: tokens are keyed by `${branch}:${contentId}`
@@ -500,21 +657,37 @@ export function useEntryManager(options) {
500
657
  const notifiedErrorBranchRef = useRef(null);
501
658
  useEffect(() => {
502
659
  if (!entriesError || !options.branchName) {
660
+ if (notifiedErrorBranchRef.current !== null)
661
+ notifications.hide(ENTRIES_LOAD_FAILED_TOAST_ID);
503
662
  notifiedErrorBranchRef.current = null;
504
663
  return;
505
664
  }
506
665
  if (notifiedErrorBranchRef.current === options.branchName)
507
666
  return;
508
- notifiedErrorBranchRef.current = options.branchName;
667
+ const branch = options.branchName;
668
+ notifiedErrorBranchRef.current = branch;
509
669
  console.error(entriesError);
670
+ // A shown toast with this id ignores `show`, so another branch's would keep its Retry.
671
+ notifications.hide(ENTRIES_LOAD_FAILED_TOAST_ID);
510
672
  notifications.show({
673
+ id: ENTRIES_LOAD_FAILED_TOAST_ID,
511
674
  title: 'Could not load content',
512
- message: getErrorMessage(entriesError),
675
+ message: createElement(RetryToastMessage, {
676
+ message: getErrorMessage(entriesError),
677
+ onRetry: () => {
678
+ notifications.hide(ENTRIES_LOAD_FAILED_TOAST_ID);
679
+ notifiedErrorBranchRef.current = null;
680
+ void globalMutate(entriesKey(branch));
681
+ },
682
+ }),
513
683
  color: 'red',
684
+ autoClose: false,
514
685
  });
515
686
  }, [entriesError, options.branchName]);
516
687
  // Validate selected entry when entries change
517
688
  useEffect(() => {
689
+ if (selectedPath)
690
+ noEntryRequested.current = false;
518
691
  // Skip validation if entries haven't loaded yet
519
692
  if (entriesState.length === 0)
520
693
  return;
@@ -530,6 +703,8 @@ export function useEntryManager(options) {
530
703
  // If the selected entry exists, keep it
531
704
  if (entriesState.find((e) => e.path === selectedPath))
532
705
  return;
706
+ if (selectedPath === '' && noEntryRequested.current)
707
+ return;
533
708
  // Fall back to first entry
534
709
  setSelectedPath(entriesState[0]?.path ?? '');
535
710
  }, [entriesState, selectedPath]);
@@ -544,6 +719,10 @@ export function useEntryManager(options) {
544
719
  if (selectedPath) {
545
720
  url.searchParams.set('entry', selectedPath);
546
721
  }
722
+ else if (noEntryRequested.current) {
723
+ // Kept, so a reload still opens no entry.
724
+ url.searchParams.set('entry', '');
725
+ }
547
726
  else {
548
727
  url.searchParams.delete('entry');
549
728
  }
@@ -551,21 +730,28 @@ export function useEntryManager(options) {
551
730
  }, [selectedPath]);
552
731
  return {
553
732
  selectedPath,
554
- setSelectedPath,
733
+ setSelectedPath: selectEntryPath,
734
+ closeEntry,
555
735
  entries: entriesState,
556
736
  setEntries,
557
737
  collections: collectionsState,
558
738
  currentEntry,
559
739
  availableSchemas,
560
740
  entriesInitializing,
741
+ entriesLoadFailed: Boolean(options.branchName) && Boolean(entriesError),
561
742
  navigatorOpen,
562
743
  setNavigatorOpen,
563
744
  refreshEntries,
745
+ refreshContent,
564
746
  handleCreateEntry,
565
747
  renameEntry,
566
748
  loadEntry,
749
+ readEntryValue,
567
750
  saveEntry,
568
751
  getEntryVersion,
752
+ adoptEntryVersion,
753
+ followMovedEntry,
754
+ recreateEntry,
569
755
  collectionByPath: collectionByPath,
570
756
  createModalOpen,
571
757
  createModalCollection,
@@ -1,6 +1,8 @@
1
- import { useCallback, useEffect, useRef, useState } from 'react';
1
+ import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
2
2
  import { notifications } from '@mantine/notifications';
3
3
  import { useApiClient } from '../context/index.js';
4
+ import { SETTINGS_CHANGED_CODE, SettingsChangedError } from '../settings-conflict.js';
5
+ import { createUserMetadataBatcher } from './user-metadata-batcher.js';
4
6
  /**
5
7
  * Custom hook for managing internal groups (CRUD operations).
6
8
  */
@@ -39,10 +41,13 @@ export function useGroupManager(options) {
39
41
  expectedContentVersion: versionRef.current,
40
42
  });
41
43
  if (!result.ok) {
42
- throw new Error(result.error || 'Failed to save groups');
44
+ const message = result.error || 'Failed to save groups';
45
+ throw result.code === SETTINGS_CHANGED_CODE
46
+ ? new SettingsChangedError(message)
47
+ : new Error(message);
43
48
  }
44
49
  notifications.show({
45
- title: 'Groups Saved',
50
+ title: 'Groups saved',
46
51
  message: 'Internal groups have been updated',
47
52
  color: 'green',
48
53
  });
@@ -50,7 +55,10 @@ export function useGroupManager(options) {
50
55
  }
51
56
  catch (err) {
52
57
  const message = err instanceof Error ? err.message : 'Failed to save groups';
53
- notifications.show({ message, color: 'red' });
58
+ notifications.show({
59
+ message,
60
+ color: err instanceof SettingsChangedError ? 'yellow' : 'red',
61
+ });
54
62
  throw err;
55
63
  }
56
64
  }, []);
@@ -70,18 +78,7 @@ export function useGroupManager(options) {
70
78
  return [];
71
79
  }
72
80
  }, []);
73
- const handleGetUserMetadata = useCallback(async (userId) => {
74
- try {
75
- const result = await apiClient.permissions.getUserMetadata({ userId });
76
- if (!result.ok)
77
- return null;
78
- return result.data?.user ?? null;
79
- }
80
- catch (err) {
81
- console.error('Get user metadata failed:', err);
82
- return null;
83
- }
84
- }, []);
81
+ const handleGetUserMetadata = useMemo(() => createUserMetadataBatcher(apiClient), [apiClient]);
85
82
  const handleSearchExternalGroups = useCallback(async (query) => {
86
83
  try {
87
84
  const result = await apiClient.groups.searchExternal({ q: query });
@@ -1,6 +1,7 @@
1
- import { useEffect, useRef, useState } from 'react';
1
+ import { useCallback, useEffect, useRef, useState } from 'react';
2
2
  import { notifications } from '@mantine/notifications';
3
3
  import { useApiClient } from '../context/index.js';
4
+ import { SETTINGS_CHANGED_CODE, SettingsChangedError } from '../settings-conflict.js';
4
5
  /**
5
6
  * Custom hook for managing path permissions (CRUD operations).
6
7
  */
@@ -42,10 +43,13 @@ export function usePermissionManager(options) {
42
43
  expectedContentVersion: versionRef.current,
43
44
  });
44
45
  if (!result.ok) {
45
- throw new Error(result.error || 'Failed to save permissions');
46
+ const message = result.error || 'Failed to save permissions';
47
+ throw result.code === SETTINGS_CHANGED_CODE
48
+ ? new SettingsChangedError(message)
49
+ : new Error(message);
46
50
  }
47
51
  notifications.show({
48
- title: 'Permissions Saved',
52
+ title: 'Permissions saved',
49
53
  message: 'Permissions have been updated',
50
54
  color: 'green',
51
55
  });
@@ -53,7 +57,10 @@ export function usePermissionManager(options) {
53
57
  }
54
58
  catch (err) {
55
59
  const message = err instanceof Error ? err.message : 'Failed to save permissions';
56
- notifications.show({ message, color: 'red' });
60
+ notifications.show({
61
+ message,
62
+ color: err instanceof SettingsChangedError ? 'yellow' : 'red',
63
+ });
57
64
  throw err;
58
65
  }
59
66
  };
@@ -64,13 +71,14 @@ export function usePermissionManager(options) {
64
71
  // an unreadable groups.json rendered as a silently empty picker with no
65
72
  // warning, which is the same failure class listGroupsHandler's deliberate
66
73
  // 500 exists to prevent.
67
- const handleListGroups = async () => {
74
+ // Stable identity: useGroupsAndUsers reloads groups whenever this function changes.
75
+ const handleListGroups = useCallback(async () => {
68
76
  const result = await apiClient.permissions.listGroups();
69
77
  if (!result.ok) {
70
78
  throw new Error(result.error || 'Failed to load groups');
71
79
  }
72
80
  return result.data?.groups ?? [];
73
- };
81
+ }, [apiClient]);
74
82
  useEffect(() => {
75
83
  if (options.isOpen) {
76
84
  loadPermissions();
@@ -0,0 +1,21 @@
1
+ import type { FieldConfig } from '../../config/index.js';
2
+ import type { PreviewMarks } from '../PreviewFrame.js';
3
+ import { type InexactMark } from '../preview-marks.js';
4
+ /**
5
+ * What the framed page reports of its marks while highlighting is on, kept for the `src` that
6
+ * said so: whether it marks nothing, and which marks name no field of the entry. Each such mark
7
+ * is a console warning once per page and path, since the toggle's note only counts them. A report
8
+ * is checked against the draft as it stood when the report arrived, not the draft since, and the
9
+ * page reports after each draft (`usePreviewHighlight`), so the two stay in step.
10
+ */
11
+ export declare function usePreviewMarks({ src, highlightEnabled, fields, data, }: {
12
+ src: string | undefined;
13
+ highlightEnabled: boolean;
14
+ fields: readonly FieldConfig[];
15
+ data: unknown;
16
+ }): {
17
+ onMarks: (reported: PreviewMarks) => void;
18
+ clearMarks: () => void;
19
+ marksNothing: boolean;
20
+ inexactMarks: InexactMark[];
21
+ };
@@ -0,0 +1,33 @@
1
+ import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
2
+ import { findInexactMarks } from '../preview-marks.js';
3
+ const NO_MARKS = [];
4
+ /**
5
+ * What the framed page reports of its marks while highlighting is on, kept for the `src` that
6
+ * said so: whether it marks nothing, and which marks name no field of the entry. Each such mark
7
+ * is a console warning once per page and path, since the toggle's note only counts them. A report
8
+ * is checked against the draft as it stood when the report arrived, not the draft since, and the
9
+ * page reports after each draft (`usePreviewHighlight`), so the two stay in step.
10
+ */
11
+ export function usePreviewMarks({ src, highlightEnabled, fields, data, }) {
12
+ const [marks, setMarks] = useState(null);
13
+ const current = highlightEnabled && marks !== null && marks.src === src ? marks : null;
14
+ const latestData = useRef(data);
15
+ useEffect(() => {
16
+ latestData.current = data;
17
+ });
18
+ const inexactMarks = useMemo(() => (current?.paths ? findInexactMarks(fields, current.data, current.paths) : NO_MARKS), [current?.paths, current?.data, fields]);
19
+ const warned = useRef(new Set());
20
+ useEffect(() => {
21
+ for (const { path, nearest } of inexactMarks) {
22
+ const key = JSON.stringify([src, path]);
23
+ if (warned.current.has(key))
24
+ continue;
25
+ warned.current.add(key);
26
+ console.warn(`[canopycms] The preview marks "${path}", which names no field of this entry` +
27
+ (nearest ? `; its nearest field is "${nearest}".` : '.'));
28
+ }
29
+ }, [inexactMarks, src]);
30
+ const onMarks = useCallback((reported) => setMarks({ ...reported, src: src ?? '', data: latestData.current }), [src]);
31
+ const clearMarks = useCallback(() => setMarks(null), []);
32
+ return { onMarks, clearMarks, marksNothing: current?.count === 0, inexactMarks };
33
+ }