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,14 +1,20 @@
1
1
  'use client';
2
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { useCallback, useEffect, useRef, useState } from 'react';
4
- import { formatCanopyPath } from './canopy-path.js';
2
+ /**
3
+ * The preview bridge's protocol and its host-page side: the hooks a framed page runs. Served to
4
+ * hosts through `canopycms/preview`, so everything it reaches stays free of CSS and editor UI
5
+ * (`pnpm lint:bundle`); the editor's side is `PreviewFrame.tsx`.
6
+ */
7
+ import { useCallback, useEffect, useLayoutEffect, useState } from 'react';
8
+ import { createFieldProps } from './field-props.js';
9
+ import { setPreviewAssetBase } from './preview-asset-base.js';
5
10
  import { isSamePreviewPath } from './preview-path.js';
6
- export const __CANOPY_PREVIEW_CLIENT__ = true;
11
+ import { readAssetBase } from './raw-asset-base.js';
7
12
  export const CANOPY_PREVIEW_MESSAGE = 'canopycms:draft:update';
8
13
  export const CANOPY_PREVIEW_FOCUS = 'canopycms:preview:focus';
9
14
  export const CANOPY_PREVIEW_HIGHLIGHT = 'canopycms:preview:highlight';
10
15
  export const CANOPY_PREVIEW_READY = 'canopycms:preview:ready';
11
16
  export const CANOPY_PREVIEW_ERROR = 'canopycms:preview:error';
17
+ export const CANOPY_PREVIEW_MARKS = 'canopycms:preview:marks';
12
18
  /**
13
19
  * Resolve a (possibly relative) URL to an origin for postMessage targeting.
14
20
  * Falls back to the page's own origin, so same-origin editor/preview setups
@@ -32,7 +38,7 @@ export const resolveMessageOrigin = (url) => {
32
38
  * a sandboxed embed shares that string, so it identifies nothing, and postMessage
33
39
  * throws on a literal 'null' targetOrigin. '' covers the SSR fallback.
34
40
  */
35
- const isOpaqueOrigin = (origin) => origin === 'null' || origin === '';
41
+ export const isOpaqueOrigin = (origin) => origin === 'null' || origin === '';
36
42
  /**
37
43
  * True only for messages from the embedding editor window with the expected origin.
38
44
  *
@@ -50,14 +56,8 @@ export const isTrustedEditorMessage = (event, editorOrigin) => {
50
56
  return false;
51
57
  return event.origin === expected && event.source === window.parent;
52
58
  };
53
- export const sendDraftUpdate = (iframe, message, targetOrigin) => {
54
- if (!iframe?.contentWindow)
55
- return;
56
- const target = targetOrigin ?? resolveMessageOrigin(iframe.src);
57
- if (isOpaqueOrigin(target))
58
- return;
59
- iframe.contentWindow.postMessage(message, target);
60
- };
59
+ /** The most distinct mark paths one report carries, and the longest path it carries. */
60
+ export const MARK_REPORT_LIMITS = { paths: 500, pathLength: 512 };
61
61
  const resolvePreviewPath = (explicit) => {
62
62
  if (explicit)
63
63
  return explicit;
@@ -76,9 +76,7 @@ export const useCanopyPreview = (opts) => {
76
76
  const { data, isLoading } = usePreviewData(resolvedPath, opts.initialData, bridgeOpts);
77
77
  const highlightEnabled = usePreviewHighlight(bridgeOpts);
78
78
  usePreviewFocusEmitter(resolvedPath, bridgeOpts);
79
- const fieldProps = (canopyPath) => ({
80
- 'data-canopy-path': Array.isArray(canopyPath) ? formatCanopyPath(canopyPath) : canopyPath,
81
- });
79
+ const fieldProps = createFieldProps();
82
80
  /**
83
81
  * Report that the current draft fails to compile/render (the editor surfaces it
84
82
  * next to the preview). Call with null once the draft renders cleanly again.
@@ -106,6 +104,7 @@ export const useCanopyPreview = (opts) => {
106
104
  */
107
105
  export const usePreviewData = (path, initialData, opts) => {
108
106
  const [data, setData] = useState(initialData);
107
+ // Every key of the loading state is optional, so an empty object is a valid one.
109
108
  const [isLoading, setIsLoading] = useState({});
110
109
  const editorOrigin = opts?.editorOrigin;
111
110
  useEffect(() => {
@@ -121,6 +120,8 @@ export const usePreviewData = (path, initialData, opts) => {
121
120
  return;
122
121
  if (typeof msg.path !== 'string' || !isSamePreviewPath(msg.path, path))
123
122
  return;
123
+ // Before setData, so the render the draft triggers already reads it.
124
+ setPreviewAssetBase(readAssetBase(msg.assetBase));
124
125
  setData(msg.data);
125
126
  if (msg.isLoading !== undefined) {
126
127
  setIsLoading(msg.isLoading);
@@ -128,8 +129,8 @@ export const usePreviewData = (path, initialData, opts) => {
128
129
  };
129
130
  window.addEventListener('message', handler);
130
131
  // Notify parent that this preview page is ready to receive draft updates.
131
- // This is needed because onLoad in the parent fires before React effects run,
132
- // so the first postMessage from the parent arrives before this listener is set up.
132
+ // This is needed because onLoad in the parent can fire before React effects run,
133
+ // so the first postMessage from the parent may arrive before this listener is set up.
133
134
  const target = resolveMessageOrigin(editorOrigin);
134
135
  if (!isOpaqueOrigin(target)) {
135
136
  window.parent.postMessage({ type: CANOPY_PREVIEW_READY, path }, target);
@@ -138,6 +139,26 @@ export const usePreviewData = (path, initialData, opts) => {
138
139
  }, [path, editorOrigin]);
139
140
  return { data, isLoading };
140
141
  };
142
+ /** React 18 warns about a layout effect in a server render, where no effect runs. */
143
+ const useBrowserLayoutEffect = (effect, deps) => (typeof window === 'undefined' ? useEffect : useLayoutEffect)(effect, deps);
144
+ /**
145
+ * Holds a `createPreviewPage` view back until hydration commits, then stores `assetBase` and opens
146
+ * before paint: the server HTML has no public `/assets/t/` URL and nothing to mismatch. A draft's
147
+ * prefix still wins, since drafts follow the ready message `usePreviewData` posts from a passive
148
+ * effect. With no `assetBase` (a public page) it is open from the start.
149
+ * @internal `withCanopyPreview` is its only caller.
150
+ */
151
+ export const usePreviewAssetBaseGate = (assetBase) => {
152
+ const held = assetBase !== undefined;
153
+ const [open, setOpen] = useState(!held);
154
+ useBrowserLayoutEffect(() => {
155
+ if (open)
156
+ return;
157
+ setPreviewAssetBase(readAssetBase(assetBase));
158
+ setOpen(true);
159
+ }, [open, assetBase]);
160
+ return open;
161
+ };
141
162
  /**
142
163
  * Hook for preview pages to listen for highlight mode and toggle an outline on clickable elements.
143
164
  */
@@ -175,6 +196,70 @@ export const usePreviewHighlight = (opts) => {
175
196
  window.addEventListener('message', handler);
176
197
  return () => window.removeEventListener('message', handler);
177
198
  }, [editorOrigin]);
199
+ // Reported after the render that turned highlighting on, whenever the marks change (content
200
+ // rendered after hydration), and after every trusted draft even when they do not: the editor checks a
201
+ // report against its draft as it stood when the report arrived, so a block whose template
202
+ // changed under unchanged marks needs a fresh one.
203
+ useEffect(() => {
204
+ if (!enabled || window.parent === window)
205
+ return;
206
+ const target = resolveMessageOrigin(editorOrigin);
207
+ if (isOpaqueOrigin(target))
208
+ return;
209
+ let reported;
210
+ const report = () => {
211
+ const marks = document.querySelectorAll('[data-canopy-path]');
212
+ const paths = new Set();
213
+ for (const mark of marks) {
214
+ if (paths.size === MARK_REPORT_LIMITS.paths)
215
+ break;
216
+ const path = mark.getAttribute('data-canopy-path') ?? '';
217
+ if (path.length <= MARK_REPORT_LIMITS.pathLength)
218
+ paths.add(path);
219
+ }
220
+ const msg = {
221
+ type: CANOPY_PREVIEW_MARKS,
222
+ count: marks.length,
223
+ paths: [...paths],
224
+ };
225
+ const key = JSON.stringify([msg.count, msg.paths]);
226
+ if (key === reported)
227
+ return;
228
+ reported = key;
229
+ window.parent.postMessage(msg, target);
230
+ };
231
+ report();
232
+ // A trailing throttle, so steady DOM churn cannot hold the report back, on a timer rather
233
+ // than requestAnimationFrame, which a hidden frame never runs. It also gives a draft time to
234
+ // render before the report it triggers.
235
+ let timer;
236
+ const schedule = () => {
237
+ timer ??= setTimeout(() => {
238
+ timer = undefined;
239
+ report();
240
+ }, 100);
241
+ };
242
+ const observer = new MutationObserver(schedule);
243
+ observer.observe(document.body, {
244
+ subtree: true,
245
+ childList: true,
246
+ attributeFilter: ['data-canopy-path'],
247
+ });
248
+ const onDraft = (event) => {
249
+ if (!isTrustedEditorMessage(event, editorOrigin))
250
+ return;
251
+ if (event.data?.type !== CANOPY_PREVIEW_MESSAGE)
252
+ return;
253
+ reported = undefined;
254
+ schedule();
255
+ };
256
+ window.addEventListener('message', onDraft);
257
+ return () => {
258
+ observer.disconnect();
259
+ window.removeEventListener('message', onDraft);
260
+ clearTimeout(timer);
261
+ };
262
+ }, [enabled, editorOrigin]);
178
263
  return enabled;
179
264
  };
180
265
  /**
@@ -207,129 +292,3 @@ export const usePreviewFocusEmitter = (entryPath, opts) => {
207
292
  return () => document.removeEventListener('click', handleClick);
208
293
  }, [entryPath, editorOrigin]);
209
294
  };
210
- /**
211
- * Lightweight iframe wrapper to keep the preview in sync with form state.
212
- * It posts the latest draft data to the iframe after load and when data changes.
213
- */
214
- export const PreviewFrame = ({ src, path, data, isLoading, className, style, highlightEnabled, onPreviewError, }) => {
215
- const iframeRef = useRef(null);
216
- // Pin the preview origin from the src prop: outbound messages target it (never '*'),
217
- // and inbound messages must come from it. An iframe that navigates cross-origin
218
- // silently stops participating in the bridge.
219
- const previewOrigin = resolveMessageOrigin(src);
220
- // Show progress bar while waiting for the preview's ready handshake.
221
- const [syncPending, setSyncPending] = useState(data !== undefined);
222
- // Reset when navigating to a different entry (src change = new iframe page load).
223
- const [prevSrc, setPrevSrc] = useState(src);
224
- if (src !== prevSrc) {
225
- setPrevSrc(src);
226
- setSyncPending(data !== undefined);
227
- }
228
- // Inject the progress bar keyframe animation once per page.
229
- useEffect(() => {
230
- const styleId = 'canopycms-preview-sync-style';
231
- if (!document.getElementById(styleId)) {
232
- const el = document.createElement('style');
233
- el.id = styleId;
234
- el.textContent = `@keyframes canopy-preview-sync { 0% { transform: translateX(-100%); } 100% { transform: translateX(250%); } }`;
235
- document.head.appendChild(el);
236
- }
237
- }, []);
238
- const post = () => {
239
- if (data === undefined)
240
- return;
241
- sendDraftUpdate(iframeRef.current, {
242
- type: CANOPY_PREVIEW_MESSAGE,
243
- path,
244
- data,
245
- isLoading,
246
- }, previewOrigin);
247
- };
248
- const postHighlight = () => {
249
- if (!iframeRef.current?.contentWindow)
250
- return;
251
- if (isOpaqueOrigin(previewOrigin))
252
- return;
253
- const msg = {
254
- type: CANOPY_PREVIEW_HIGHLIGHT,
255
- enabled: Boolean(highlightEnabled),
256
- };
257
- iframeRef.current.contentWindow.postMessage(msg, previewOrigin);
258
- };
259
- // Keep refs pointing at the latest closures so the message handler below never goes stale.
260
- const postRef = useRef(post);
261
- const postHighlightRef = useRef(postHighlight);
262
- const onPreviewErrorRef = useRef(onPreviewError);
263
- useEffect(() => {
264
- postRef.current = post;
265
- postHighlightRef.current = postHighlight;
266
- onPreviewErrorRef.current = onPreviewError;
267
- });
268
- useEffect(() => {
269
- post();
270
- // eslint-disable-next-line react-hooks/exhaustive-deps -- post is tracked via ref
271
- }, [data, isLoading]);
272
- useEffect(() => {
273
- postHighlight();
274
- // eslint-disable-next-line react-hooks/exhaustive-deps -- postHighlight is tracked via ref
275
- }, [highlightEnabled]);
276
- // When the preview page's React effects have run and its message listener is ready,
277
- // it sends CANOPY_PREVIEW_READY. We respond with the current data so the preview
278
- // receives the draft even if it wasn't ready when onLoad fired.
279
- useEffect(() => {
280
- const handleMessage = (event) => {
281
- if (!iframeRef.current || event.source !== iframeRef.current.contentWindow)
282
- return;
283
- // Source check alone is insufficient if the iframe navigated cross-origin.
284
- if (isOpaqueOrigin(previewOrigin) || event.origin !== previewOrigin)
285
- return;
286
- const type = event.data?.type;
287
- if (type === CANOPY_PREVIEW_READY) {
288
- postRef.current();
289
- postHighlightRef.current();
290
- setSyncPending(false);
291
- }
292
- else if (type === CANOPY_PREVIEW_ERROR) {
293
- const msg = event.data;
294
- // Shape-check the payload: an adopter passing an Error object (instead of
295
- // err.message) must not crash the editor when rendered as a React child.
296
- if (msg.message != null && typeof msg.message !== 'string')
297
- return;
298
- const fieldPath = typeof msg.fieldPath === 'string' ? msg.fieldPath : undefined;
299
- onPreviewErrorRef.current?.(msg.message == null
300
- ? null
301
- : { message: msg.message, ...(fieldPath ? { fieldPath } : {}) });
302
- }
303
- };
304
- window.addEventListener('message', handleMessage);
305
- return () => window.removeEventListener('message', handleMessage);
306
- }, [previewOrigin]);
307
- return (_jsxs("div", { className: className, style: { position: 'relative', overflow: 'hidden', ...style }, children: [syncPending && (_jsx("div", { style: {
308
- position: 'absolute',
309
- top: 0,
310
- left: 0,
311
- right: 0,
312
- height: 3,
313
- zIndex: 1,
314
- overflow: 'hidden',
315
- }, children: _jsx("div", { style: {
316
- position: 'absolute',
317
- top: 0,
318
- left: 0,
319
- height: '100%',
320
- width: '40%',
321
- background: 'var(--mantine-color-blue-filled, #228be6)',
322
- borderRadius: '0 2px 2px 0',
323
- animation: 'canopy-preview-sync 1.5s ease-in-out infinite',
324
- } }) })), _jsx("iframe", { ref: iframeRef, src: src, style: {
325
- display: 'block',
326
- position: 'absolute',
327
- inset: 0,
328
- width: '100%',
329
- height: '100%',
330
- border: 'none',
331
- }, onLoad: () => {
332
- post();
333
- postHighlight();
334
- } })] }));
335
- };
@@ -0,0 +1,15 @@
1
+ import type { FieldConfig } from '../config/index.js';
2
+ /** A preview mark whose path names no field of the entry, and the closest path that does. */
3
+ export interface InexactMark {
4
+ /** The mark's path, normalized. */
5
+ path: string;
6
+ /** Its longest prefix that names a field, absent when not even the first segment does. */
7
+ nearest?: string;
8
+ }
9
+ /**
10
+ * The marks naming no field: a key the schema lacks, a segment below a reference, image or
11
+ * scalar, or another template's field. Walks the schema, not the form's DOM, which leaves out
12
+ * collapsed fields; the draft only picks a block item's template, and an item it lacks takes
13
+ * every path, since marks can trail a draft by one report.
14
+ */
15
+ export declare const findInexactMarks: (fields: readonly FieldConfig[], data: unknown, paths: readonly string[]) => InexactMark[];
@@ -0,0 +1,74 @@
1
+ import { flattenGroupFields } from '../utils/flatten-group-fields.js';
2
+ import { resolveBlockItem } from '../validation/field-traversal.js';
3
+ import { formatCanopyPath, normalizeCanopyPath, parseCanopyPath } from './canopy-path.js';
4
+ const isRecord = (value) => typeof value === 'object' && value !== null && !Array.isArray(value);
5
+ const itemAt = (list, index) => Array.isArray(list) ? list[index] : undefined;
6
+ const enterField = (field, value) => {
7
+ if (field.type === 'block')
8
+ return { kind: 'blocks', field: field, data: value };
9
+ if ('list' in field && field.list)
10
+ return { kind: 'items', field, data: value };
11
+ if (field.type === 'object') {
12
+ return { kind: 'record', fields: field.fields, data: value };
13
+ }
14
+ return { kind: 'leaf' };
15
+ };
16
+ const step = (position, segment) => {
17
+ switch (position.kind) {
18
+ case 'unknown':
19
+ return position;
20
+ case 'leaf':
21
+ return undefined;
22
+ case 'record': {
23
+ if (typeof segment !== 'string')
24
+ return undefined;
25
+ const field = flattenGroupFields(position.fields).find((f) => f.name === segment);
26
+ if (!field)
27
+ return undefined;
28
+ return enterField(field, isRecord(position.data) ? position.data[segment] : undefined);
29
+ }
30
+ case 'items': {
31
+ if (typeof segment !== 'number')
32
+ return undefined;
33
+ if (position.field.type !== 'object')
34
+ return { kind: 'leaf' };
35
+ const fields = position.field.fields;
36
+ return { kind: 'record', fields, data: itemAt(position.data, segment) };
37
+ }
38
+ case 'blocks': {
39
+ if (typeof segment !== 'number')
40
+ return undefined;
41
+ const item = itemAt(position.data, segment);
42
+ const resolved = isRecord(item) ? resolveBlockItem(position.field, item) : undefined;
43
+ return resolved ? { kind: 'record', ...resolved } : { kind: 'unknown' };
44
+ }
45
+ }
46
+ };
47
+ /**
48
+ * The marks naming no field: a key the schema lacks, a segment below a reference, image or
49
+ * scalar, or another template's field. Walks the schema, not the form's DOM, which leaves out
50
+ * collapsed fields; the draft only picks a block item's template, and an item it lacks takes
51
+ * every path, since marks can trail a draft by one report.
52
+ */
53
+ export const findInexactMarks = (fields, data, paths) => {
54
+ const inexact = new Map();
55
+ for (const raw of paths) {
56
+ const path = normalizeCanopyPath(raw);
57
+ const segments = parseCanopyPath(path);
58
+ let position = { kind: 'record', fields, data };
59
+ let matched = 0;
60
+ for (const segment of segments) {
61
+ position = step(position, segment);
62
+ if (!position)
63
+ break;
64
+ matched++;
65
+ }
66
+ if (position && segments.length > 0)
67
+ continue;
68
+ inexact.set(path, {
69
+ path,
70
+ ...(matched > 0 ? { nearest: formatCanopyPath(segments.slice(0, matched)) } : {}),
71
+ });
72
+ }
73
+ return [...inexact.values()];
74
+ };
@@ -8,3 +8,15 @@
8
8
  export declare const normalizePreviewPath: (url: string) => string;
9
9
  /** Whether two preview URLs name the same page; see `normalizePreviewPath`. */
10
10
  export declare const isSamePreviewPath: (a: string, b: string) => boolean;
11
+ /** Where `buildPreviewSrc` mounts the preview routes, and the `basePath` the site's pages keep. */
12
+ export interface PreviewMount {
13
+ basePath?: string;
14
+ previewPrefix?: string;
15
+ }
16
+ /**
17
+ * The previewed page's path on the site, as the preview toolbar shows and copies it: without the
18
+ * `previewPrefix` mount, the `branch` query the top bar already names, or a fragment. A relative
19
+ * URL stays relative. An absolute one keeps its origin, since it may be another site, unless an
20
+ * absolute `previewPrefix` mount is what was taken out.
21
+ */
22
+ export declare const displayPreviewPath: (src: string, mount?: PreviewMount) => string;
@@ -1,4 +1,4 @@
1
- import { stripTrailingSlashes } from '../utils/url-prefix.js';
1
+ import { isAbsoluteUrl, joinUrlPrefix, stripTrailingSlashes } from '../utils/url-prefix.js';
2
2
  /**
3
3
  * Reduces a preview URL to what identifies the page, its path and query, with no origin,
4
4
  * fragment or trailing slash. The editor names a page by the `src` it built and the framed page
@@ -18,3 +18,47 @@ export const normalizePreviewPath = (url) => {
18
18
  };
19
19
  /** Whether two preview URLs name the same page; see `normalizePreviewPath`. */
20
20
  export const isSamePreviewPath = (a, b) => normalizePreviewPath(a) === normalizePreviewPath(b);
21
+ /** `url` with the `previewPrefix` mount taken out, so `/base/preview/posts/x` reads `/base/posts/x`. */
22
+ const stripPreviewPrefix = (url, { basePath, previewPrefix }) => {
23
+ if (!previewPrefix || !stripTrailingSlashes(previewPrefix))
24
+ return url;
25
+ const mount = stripTrailingSlashes(joinUrlPrefix(basePath, joinUrlPrefix(previewPrefix, '/')));
26
+ if (!url.startsWith(mount))
27
+ return url;
28
+ const rest = url.slice(mount.length);
29
+ if (rest !== '' && !/^[/?#]/.test(rest))
30
+ return url;
31
+ // An absolute prefix skips `basePath`, so the page under it has none either.
32
+ const base = isAbsoluteUrl(mount) ? '' : stripTrailingSlashes(joinUrlPrefix(basePath, '/'));
33
+ return rest.startsWith('/') ? `${base}${rest}` : `${base || '/'}${rest}`;
34
+ };
35
+ /**
36
+ * The previewed page's path on the site, as the preview toolbar shows and copies it: without the
37
+ * `previewPrefix` mount, the `branch` query the top bar already names, or a fragment. A relative
38
+ * URL stays relative. An absolute one keeps its origin, since it may be another site, unless an
39
+ * absolute `previewPrefix` mount is what was taken out.
40
+ */
41
+ export const displayPreviewPath = (src, mount = {}) => {
42
+ const url = stripPreviewPrefix(src, mount);
43
+ let parsed;
44
+ try {
45
+ parsed = new URL(url, 'http://preview.invalid');
46
+ }
47
+ catch {
48
+ return url;
49
+ }
50
+ // Filtered as text: a URLSearchParams round trip would form-encode the query it keeps (`flag`
51
+ // becomes `flag=`, `%20` becomes `+`).
52
+ const query = parsed.search
53
+ .slice(1)
54
+ .split('&')
55
+ .filter((part) => part !== '' && part.split('=', 1)[0] !== 'branch')
56
+ .join('&');
57
+ const path = `${parsed.pathname}${query ? `?${query}` : ''}`;
58
+ if (!isAbsoluteUrl(url))
59
+ return path;
60
+ // `origin` is 'null' for a scheme the URL spec does not treat as special.
61
+ return url.startsWith('//')
62
+ ? `//${parsed.host}${path}`
63
+ : `${parsed.protocol}//${parsed.host}${path}`;
64
+ };
@@ -0,0 +1,27 @@
1
+ import type { PreviewWidth } from './hooks/useEditorLayout.js';
2
+ /** The CSS width each fixed preview width renders the site at. */
3
+ export declare const PREVIEW_DEVICE_WIDTHS: {
4
+ readonly desktop: 1280;
5
+ readonly tablet: 768;
6
+ readonly mobile: 390;
7
+ };
8
+ /** Where and how large the preview iframe renders inside a pane of a given size. */
9
+ export interface PreviewViewport {
10
+ /** The iframe's CSS width: the width the site's breakpoints see. */
11
+ frameWidth: number;
12
+ /** The iframe's CSS height, before scaling; scaled, it fills the pane's height. */
13
+ frameHeight: number;
14
+ /** The factor the iframe is scaled by, at most 1. */
15
+ scale: number;
16
+ /** The left offset that centres the scaled iframe in the pane. */
17
+ offsetX: number;
18
+ }
19
+ /**
20
+ * Fits the preview into a pane. "fit" renders at the pane's width. A fixed width renders at
21
+ * exactly that CSS width and, when the pane is narrower, scales down so the whole width shows,
22
+ * stretching the CSS height by the same factor so the scaled frame still fills the pane's height.
23
+ * An unmeasured pane (zero or non-finite) renders unscaled.
24
+ */
25
+ export declare function computePreviewViewport(previewWidth: PreviewWidth, paneWidth: number, paneHeight: number): PreviewViewport;
26
+ /** The readout beside the width selector: the CSS width, plus the scale when it rounds below 100%. */
27
+ export declare function formatPreviewWidthReadout({ frameWidth, scale }: PreviewViewport): string;
@@ -0,0 +1,34 @@
1
+ /** The CSS width each fixed preview width renders the site at. */
2
+ export const PREVIEW_DEVICE_WIDTHS = {
3
+ desktop: 1280,
4
+ tablet: 768,
5
+ mobile: 390,
6
+ };
7
+ const positiveOr = (value, fallback) => Number.isFinite(value) && value > 0 ? value : fallback;
8
+ /**
9
+ * Fits the preview into a pane. "fit" renders at the pane's width. A fixed width renders at
10
+ * exactly that CSS width and, when the pane is narrower, scales down so the whole width shows,
11
+ * stretching the CSS height by the same factor so the scaled frame still fills the pane's height.
12
+ * An unmeasured pane (zero or non-finite) renders unscaled.
13
+ */
14
+ export function computePreviewViewport(previewWidth, paneWidth, paneHeight) {
15
+ const width = positiveOr(paneWidth, 0);
16
+ const height = positiveOr(paneHeight, 0);
17
+ if (previewWidth === 'fit') {
18
+ return { frameWidth: width, frameHeight: height, scale: 1, offsetX: 0 };
19
+ }
20
+ const frameWidth = PREVIEW_DEVICE_WIDTHS[previewWidth];
21
+ const scale = width > 0 ? Math.min(1, width / frameWidth) : 1;
22
+ return {
23
+ frameWidth,
24
+ frameHeight: height / scale,
25
+ scale,
26
+ offsetX: width > 0 ? Math.max(0, (width - frameWidth * scale) / 2) : 0,
27
+ };
28
+ }
29
+ /** The readout beside the width selector: the CSS width, plus the scale when it rounds below 100%. */
30
+ export function formatPreviewWidthReadout({ frameWidth, scale }) {
31
+ const px = `${Math.round(frameWidth)} px`;
32
+ const percent = Math.round(scale * 100);
33
+ return percent < 100 ? `${px} at ${percent}%` : px;
34
+ }
@@ -0,0 +1,5 @@
1
+ /** The signed-in asset prefix, React-free so `createPreviewPage` can build it on a server. */
2
+ /** The raw route under `basePath`: unlike `/assets`, it computes derivatives no build made. */
3
+ export declare function authenticatedAssetBase(basePath?: string): string;
4
+ /** `value` if it is a same-origin path: nothing else may steer a preview's `<img>`s. */
5
+ export declare function readAssetBase(value: unknown): string | undefined;
@@ -0,0 +1,14 @@
1
+ /** The signed-in asset prefix, React-free so `createPreviewPage` can build it on a server. */
2
+ import { joinUrlPrefix, toSameOriginPath } from '../utils/url-prefix.js';
3
+ /** The raw asset route (`api/assets.ts`'s `assetRawRoute`) under the default API base. */
4
+ const RAW_ASSET_ROUTE = '/api/canopycms/assets/raw';
5
+ /** The raw route under `basePath`: unlike `/assets`, it computes derivatives no build made. */
6
+ export function authenticatedAssetBase(basePath) {
7
+ return joinUrlPrefix(basePath, RAW_ASSET_ROUTE);
8
+ }
9
+ /** `value` if it is a same-origin path: nothing else may steer a preview's `<img>`s. */
10
+ export function readAssetBase(value) {
11
+ return typeof value === 'string' && value.startsWith('/') && toSameOriginPath(value) === value
12
+ ? value
13
+ : undefined;
14
+ }
@@ -1,7 +1,7 @@
1
1
  /**
2
- * Formats an ISO timestamp as a short relative-time label ("just now",
3
- * "5m ago", "3h ago", "2d ago"), falling back to a locale date string once
4
- * the timestamp is more than a week old. Invalid input is returned
5
- * unchanged so callers never render "Invalid Date" to the user.
2
+ * Formats an ISO timestamp as a short relative-time label ("just now", "5m ago", "3h ago",
3
+ * "2d ago", "2w ago", "3mo ago", "1y ago"). Weeks run to 4, months to 11. A timestamp in the
4
+ * future reads "just now", and invalid input is returned unchanged so callers never render
5
+ * "Invalid Date" to the user.
6
6
  */
7
7
  export declare function formatRelativeTime(iso: string): string;
@@ -1,8 +1,11 @@
1
+ const MINUTE_MS = 60 * 1000;
2
+ const HOUR_MS = 60 * MINUTE_MS;
3
+ const DAY_MS = 24 * HOUR_MS;
1
4
  /**
2
- * Formats an ISO timestamp as a short relative-time label ("just now",
3
- * "5m ago", "3h ago", "2d ago"), falling back to a locale date string once
4
- * the timestamp is more than a week old. Invalid input is returned
5
- * unchanged so callers never render "Invalid Date" to the user.
5
+ * Formats an ISO timestamp as a short relative-time label ("just now", "5m ago", "3h ago",
6
+ * "2d ago", "2w ago", "3mo ago", "1y ago"). Weeks run to 4, months to 11. A timestamp in the
7
+ * future reads "just now", and invalid input is returned unchanged so callers never render
8
+ * "Invalid Date" to the user.
6
9
  */
7
10
  export function formatRelativeTime(iso) {
8
11
  const date = new Date(iso);
@@ -10,16 +13,21 @@ export function formatRelativeTime(iso) {
10
13
  return iso;
11
14
  }
12
15
  const diffMs = Date.now() - date.getTime();
13
- const minutes = Math.floor(diffMs / (1000 * 60));
16
+ const minutes = Math.floor(diffMs / MINUTE_MS);
14
17
  if (minutes < 1)
15
18
  return 'just now';
16
19
  if (minutes < 60)
17
20
  return `${minutes}m ago`;
18
- const hours = Math.floor(diffMs / (1000 * 60 * 60));
21
+ const hours = Math.floor(diffMs / HOUR_MS);
19
22
  if (hours < 24)
20
23
  return `${hours}h ago`;
21
- const days = Math.floor(diffMs / (1000 * 60 * 60 * 24));
24
+ const days = Math.floor(diffMs / DAY_MS);
22
25
  if (days < 7)
23
26
  return `${days}d ago`;
24
- return date.toLocaleDateString();
27
+ if (days < 35)
28
+ return `${Math.floor(days / 7)}w ago`;
29
+ const years = Math.floor(days / 365);
30
+ if (years >= 1)
31
+ return `${years}y ago`;
32
+ return `${Math.min(11, Math.max(1, Math.floor(days / 30)))}mo ago`;
25
33
  }
@@ -39,10 +39,27 @@ export interface CollectionEditorProps {
39
39
  onAddEntryType?: (collectionPath: LogicalPath, entryType: CreateEntryTypeInput) => Promise<SchemaOpResult>;
40
40
  /** Called when an entry type is updated (edit mode only); resolves to the save result */
41
41
  onUpdateEntryType?: (collectionPath: LogicalPath, entryTypeName: string, updates: Partial<CreateEntryTypeInput>) => Promise<SchemaOpResult>;
42
- /** Called when an entry type is removed (edit mode only) */
43
- onRemoveEntryType?: (collectionPath: LogicalPath, entryTypeName: string) => Promise<SchemaOpResult> | void;
42
+ /** Called when an entry type is removed (edit mode only); resolves to the save result */
43
+ onRemoveEntryType?: (collectionPath: LogicalPath, entryTypeName: string) => Promise<SchemaOpResult>;
44
+ /**
45
+ * Re-reads the entry types from the branch (edit mode only), after an action the server never
46
+ * answered; resolves to null when the read fails.
47
+ */
48
+ onReloadEntryTypes?: (collectionPath: LogicalPath) => Promise<ExistingEntryType[] | null>;
44
49
  onClose: () => void;
45
50
  isSaving?: boolean;
46
51
  error?: string | null;
52
+ /**
53
+ * What was entered for a create or details save that got no answer and that the opener's fresh
54
+ * read did not show landed, filled in when the dialog opens (docs/ux-guidelines.md: a create closed after no answer
55
+ * prefills its dialog's next open).
56
+ */
57
+ restored?: RestoredCollectionInput | null;
58
+ /** Drops `restored`, once the user discards it. */
59
+ onDiscardRestored?: () => void;
47
60
  }
48
- export declare function CollectionEditor({ isOpen, editingCollection, parentPath, availableSchemas, onSave, onAddEntryType, onUpdateEntryType, onRemoveEntryType, onClose, isSaving, error, }: CollectionEditorProps): import("react/jsx-runtime").JSX.Element;
61
+ /** A create's input, or a details save's changed fields. */
62
+ export type RestoredCollectionInput = Partial<CollectionFormData> & {
63
+ slug?: string;
64
+ };
65
+ export declare function CollectionEditor({ isOpen, editingCollection, parentPath, availableSchemas, onSave, onAddEntryType, onUpdateEntryType, onRemoveEntryType, onReloadEntryTypes, onClose, isSaving, error, restored, onDiscardRestored, }: CollectionEditorProps): import("react/jsx-runtime").JSX.Element;