canopycms 0.0.67-int.90 → 0.0.67

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 (539) hide show
  1. package/README.md +30 -3
  2. package/dist/ai/generate.js +1 -27
  3. package/dist/ai/handler.d.ts +0 -4
  4. package/dist/ai/handler.js +0 -11
  5. package/dist/ai/index.d.ts +1 -8
  6. package/dist/ai/index.js +1 -8
  7. package/dist/ai/json-to-markdown.js +15 -58
  8. package/dist/ai/resolve-branch.d.ts +2 -7
  9. package/dist/ai/resolve-branch.js +2 -8
  10. package/dist/ai/strip-mdx.d.ts +5 -13
  11. package/dist/ai/strip-mdx.js +5 -17
  12. package/dist/ai/to-plain-text.d.ts +10 -28
  13. package/dist/ai/to-plain-text.js +10 -28
  14. package/dist/ai/transform-components.d.ts +2 -4
  15. package/dist/ai/transform-components.js +8 -19
  16. package/dist/ai/types.d.ts +5 -10
  17. package/dist/api/admin-branch-health.d.ts +4 -12
  18. package/dist/api/admin-branch-health.js +26 -66
  19. package/dist/api/admin.d.ts +4 -12
  20. package/dist/api/admin.js +10 -26
  21. package/dist/api/assets.d.ts +0 -25
  22. package/dist/api/assets.js +50 -91
  23. package/dist/api/branch-merge.d.ts +1 -1
  24. package/dist/api/branch-merge.js +0 -2
  25. package/dist/api/branch-review.d.ts +0 -8
  26. package/dist/api/branch-review.js +0 -13
  27. package/dist/api/branch-status.d.ts +0 -3
  28. package/dist/api/branch-status.js +8 -33
  29. package/dist/api/branch-withdraw.d.ts +0 -1
  30. package/dist/api/branch-withdraw.js +6 -9
  31. package/dist/api/branch.d.ts +7 -12
  32. package/dist/api/branch.js +7 -44
  33. package/dist/api/client.d.ts +71 -224
  34. package/dist/api/client.js +82 -233
  35. package/dist/api/comments.d.ts +2 -4
  36. package/dist/api/comments.js +0 -17
  37. package/dist/api/content.d.ts +0 -6
  38. package/dist/api/content.js +66 -142
  39. package/dist/api/entries-constants.d.ts +5 -9
  40. package/dist/api/entries-constants.js +5 -9
  41. package/dist/api/entries.d.ts +8 -13
  42. package/dist/api/entries.js +20 -51
  43. package/dist/api/github-sync.d.ts +2 -4
  44. package/dist/api/github-sync.js +4 -8
  45. package/dist/api/groups.d.ts +3 -0
  46. package/dist/api/groups.js +8 -21
  47. package/dist/api/guards.d.ts +4 -16
  48. package/dist/api/guards.js +3 -22
  49. package/dist/api/index.d.ts +4 -4
  50. package/dist/api/permissions.d.ts +2 -0
  51. package/dist/api/permissions.js +18 -40
  52. package/dist/api/reference-options.d.ts +0 -1
  53. package/dist/api/reference-options.js +3 -11
  54. package/dist/api/request-body-hash.d.ts +12 -23
  55. package/dist/api/request-body-hash.js +12 -23
  56. package/dist/api/resolve-references.d.ts +0 -1
  57. package/dist/api/resolve-references.js +13 -28
  58. package/dist/api/route-builder.d.ts +3 -7
  59. package/dist/api/route-builder.js +1 -1
  60. package/dist/api/routes.d.ts +12 -0
  61. package/dist/api/routes.js +47 -0
  62. package/dist/api/schema.d.ts +23 -33
  63. package/dist/api/schema.js +20 -74
  64. package/dist/api/settings-helpers.js +4 -5
  65. package/dist/api/types.d.ts +0 -5
  66. package/dist/api/user.js +0 -5
  67. package/dist/api/validators.d.ts +14 -52
  68. package/dist/api/validators.js +14 -52
  69. package/dist/assets/asset-prefixes.d.ts +4 -8
  70. package/dist/assets/asset-prefixes.js +4 -8
  71. package/dist/assets/asset-src.d.ts +5 -22
  72. package/dist/assets/asset-src.js +5 -21
  73. package/dist/assets/factory.d.ts +1 -2
  74. package/dist/assets/factory.js +1 -2
  75. package/dist/assets/finalize.d.ts +3 -1
  76. package/dist/assets/finalize.js +3 -1
  77. package/dist/assets/index.d.ts +3 -3
  78. package/dist/assets/index.js +1 -1
  79. package/dist/assets/keys.d.ts +5 -3
  80. package/dist/assets/keys.js +6 -3
  81. package/dist/assets/pipeline.d.ts +3 -2
  82. package/dist/assets/pipeline.js +22 -56
  83. package/dist/assets/sharp-loader.d.ts +2 -10
  84. package/dist/assets/sharp-loader.js +2 -10
  85. package/dist/assets/store-local.d.ts +1 -2
  86. package/dist/assets/store-local.js +1 -2
  87. package/dist/assets/store-s3.d.ts +3 -4
  88. package/dist/assets/svg-sanitizer.d.ts +6 -14
  89. package/dist/assets/svg-sanitizer.js +6 -14
  90. package/dist/assets/transform-directives.d.ts +3 -4
  91. package/dist/assets/transform-directives.js +1 -1
  92. package/dist/assets/transform.d.ts +19 -30
  93. package/dist/assets/transform.js +18 -31
  94. package/dist/auth/cache.d.ts +2 -3
  95. package/dist/auth/cache.js +2 -3
  96. package/dist/auth/caching-auth-plugin.d.ts +11 -22
  97. package/dist/auth/caching-auth-plugin.js +12 -14
  98. package/dist/auth/context-helpers.d.ts +6 -20
  99. package/dist/auth/context-helpers.js +5 -17
  100. package/dist/auth/file-based-auth-cache.d.ts +10 -21
  101. package/dist/auth/file-based-auth-cache.js +23 -51
  102. package/dist/auth/plugin.d.ts +29 -61
  103. package/dist/auth/plugin.js +9 -13
  104. package/dist/auth/types.d.ts +11 -20
  105. package/dist/authorization/branch.d.ts +30 -49
  106. package/dist/authorization/branch.js +16 -42
  107. package/dist/authorization/content.d.ts +11 -40
  108. package/dist/authorization/content.js +13 -45
  109. package/dist/authorization/groups/index.d.ts +0 -3
  110. package/dist/authorization/groups/index.js +0 -3
  111. package/dist/authorization/groups/loader.d.ts +14 -28
  112. package/dist/authorization/groups/loader.js +14 -38
  113. package/dist/authorization/groups/schema.d.ts +0 -6
  114. package/dist/authorization/groups/schema.js +4 -7
  115. package/dist/authorization/helpers.d.ts +9 -28
  116. package/dist/authorization/helpers.js +9 -28
  117. package/dist/authorization/index.d.ts +3 -25
  118. package/dist/authorization/index.js +3 -35
  119. package/dist/authorization/path.d.ts +6 -13
  120. package/dist/authorization/path.js +8 -25
  121. package/dist/authorization/permissions/index.d.ts +0 -3
  122. package/dist/authorization/permissions/index.js +0 -3
  123. package/dist/authorization/permissions/loader.d.ts +14 -27
  124. package/dist/authorization/permissions/loader.js +14 -32
  125. package/dist/authorization/permissions/schema.d.ts +1 -6
  126. package/dist/authorization/permissions/schema.js +6 -13
  127. package/dist/authorization/protected-branch.d.ts +36 -65
  128. package/dist/authorization/protected-branch.js +29 -41
  129. package/dist/authorization/settings-file-store.d.ts +38 -81
  130. package/dist/authorization/settings-file-store.js +38 -79
  131. package/dist/authorization/types.d.ts +4 -26
  132. package/dist/authorization/types.js +0 -6
  133. package/dist/authorization/validation.d.ts +1 -23
  134. package/dist/authorization/validation.js +1 -24
  135. package/dist/branch-health.d.ts +39 -67
  136. package/dist/branch-health.js +27 -43
  137. package/dist/branch-metadata-file.d.ts +16 -32
  138. package/dist/branch-metadata-file.js +13 -26
  139. package/dist/branch-metadata.d.ts +49 -80
  140. package/dist/branch-metadata.js +57 -87
  141. package/dist/branch-registry.d.ts +26 -64
  142. package/dist/branch-registry.js +44 -93
  143. package/dist/branch-schema-cache.d.ts +34 -124
  144. package/dist/branch-schema-cache.js +49 -155
  145. package/dist/branch-workspace.d.ts +4 -4
  146. package/dist/branch-workspace.js +15 -19
  147. package/dist/build/generate-ai-content.d.ts +1 -0
  148. package/dist/build/generate-ai-content.js +12 -25
  149. package/dist/build-canopy.d.ts +17 -27
  150. package/dist/build-canopy.js +16 -23
  151. package/dist/build-mode.d.ts +25 -27
  152. package/dist/build-mode.js +25 -29
  153. package/dist/cli/cli.d.ts +15 -26
  154. package/dist/cli/cli.js +601 -978
  155. package/dist/cli/generate-ai-content.js +351 -628
  156. package/dist/cli/github-app-manifest.d.ts +84 -0
  157. package/dist/cli/github-app-manifest.js +161 -0
  158. package/dist/cli/init-github-app.d.ts +110 -444
  159. package/dist/cli/init-github-app.js +236 -773
  160. package/dist/cli/init.js +39 -80
  161. package/dist/cli/migrate.d.ts +13 -10
  162. package/dist/cli/migrate.js +22 -30
  163. package/dist/cli/project-detect.d.ts +17 -31
  164. package/dist/cli/project-detect.js +31 -42
  165. package/dist/cli/project-root.d.ts +4 -11
  166. package/dist/cli/project-root.js +4 -11
  167. package/dist/cli/prompt.d.ts +10 -0
  168. package/dist/cli/prompt.js +67 -0
  169. package/dist/cli/sync.d.ts +0 -8
  170. package/dist/cli/sync.js +8 -28
  171. package/dist/cli/template-files/middleware.ts.template +15 -29
  172. package/dist/cli/templates.d.ts +2 -12
  173. package/dist/cli/templates.js +4 -4
  174. package/dist/client.d.ts +1 -1
  175. package/dist/comment-store.d.ts +32 -53
  176. package/dist/comment-store.js +32 -52
  177. package/dist/config/flatten.d.ts +0 -3
  178. package/dist/config/flatten.js +2 -12
  179. package/dist/config/helpers.js +2 -2
  180. package/dist/config/index.d.ts +2 -2
  181. package/dist/config/index.js +1 -4
  182. package/dist/config/schemas/collection.d.ts +0 -8
  183. package/dist/config/schemas/collection.js +0 -10
  184. package/dist/config/schemas/config.d.ts +6 -78
  185. package/dist/config/schemas/config.js +38 -54
  186. package/dist/config/schemas/field.d.ts +2 -242
  187. package/dist/config/schemas/field.js +9 -20
  188. package/dist/config/schemas/media.js +1 -1
  189. package/dist/config/schemas/permissions.js +0 -2
  190. package/dist/config/schemas/url.d.ts +5 -6
  191. package/dist/config/schemas/url.js +5 -6
  192. package/dist/config/types.d.ts +45 -33
  193. package/dist/config/types.js +0 -1
  194. package/dist/config.d.ts +1 -8
  195. package/dist/config.js +1 -9
  196. package/dist/content-id-index.d.ts +76 -188
  197. package/dist/content-id-index.js +79 -230
  198. package/dist/content-index-generation.d.ts +27 -48
  199. package/dist/content-index-generation.js +35 -84
  200. package/dist/content-index-registry.d.ts +15 -24
  201. package/dist/content-index-registry.js +5 -9
  202. package/dist/content-listing.d.ts +64 -107
  203. package/dist/content-listing.js +68 -118
  204. package/dist/content-reader.d.ts +20 -29
  205. package/dist/content-reader.js +10 -18
  206. package/dist/content-store.d.ts +210 -336
  207. package/dist/content-store.js +413 -657
  208. package/dist/content-tree.d.ts +44 -86
  209. package/dist/content-tree.js +26 -54
  210. package/dist/context.d.ts +67 -94
  211. package/dist/context.js +53 -72
  212. package/dist/dev-content-watcher.d.ts +18 -18
  213. package/dist/dev-content-watcher.js +41 -44
  214. package/dist/editor/BranchManager.d.ts +3 -6
  215. package/dist/editor/BranchManager.js +24 -44
  216. package/dist/editor/CanopyEditor.js +5 -2
  217. package/dist/editor/CommentsPanel.js +0 -2
  218. package/dist/editor/Editor.js +19 -76
  219. package/dist/editor/EditorAuthGate.d.ts +27 -0
  220. package/dist/editor/EditorAuthGate.js +140 -0
  221. package/dist/editor/EntryNavigator.js +0 -20
  222. package/dist/editor/FormRenderer.d.ts +1 -2
  223. package/dist/editor/FormRenderer.js +8 -23
  224. package/dist/editor/admin/SystemHealthPanel.js +20 -37
  225. package/dist/editor/admin/useSystemHealth.d.ts +2 -3
  226. package/dist/editor/admin/useSystemHealth.js +10 -12
  227. package/dist/editor/client-reference-resolver.d.ts +2 -5
  228. package/dist/editor/client-reference-resolver.js +2 -19
  229. package/dist/editor/comments/BranchComments.js +0 -4
  230. package/dist/editor/comments/EntryComments.js +0 -4
  231. package/dist/editor/comments/FieldWrapper.js +0 -4
  232. package/dist/editor/comments/InlineCommentThread.js +0 -2
  233. package/dist/editor/comments/ThreadCarousel.d.ts +0 -58
  234. package/dist/editor/comments/ThreadCarousel.js +0 -12
  235. package/dist/editor/components/EditorFooter.d.ts +0 -8
  236. package/dist/editor/components/EditorFooter.js +0 -5
  237. package/dist/editor/components/EditorHeader.d.ts +0 -31
  238. package/dist/editor/components/EditorHeader.js +21 -71
  239. package/dist/editor/components/EditorSidebar.d.ts +0 -19
  240. package/dist/editor/components/EditorSidebar.js +0 -16
  241. package/dist/editor/components/EntryCreateModal.d.ts +4 -4
  242. package/dist/editor/components/EntryCreateModal.js +7 -22
  243. package/dist/editor/components/RenameEntryModal.js +0 -4
  244. package/dist/editor/components/UserBadge.js +0 -10
  245. package/dist/editor/components/index.js +0 -2
  246. package/dist/editor/context/ApiClientContext.d.ts +8 -7
  247. package/dist/editor/context/ApiClientContext.js +39 -11
  248. package/dist/editor/context/AssetContext.d.ts +7 -17
  249. package/dist/editor/context/AssetContext.js +7 -17
  250. package/dist/editor/context/EditorIdentityContext.d.ts +13 -0
  251. package/dist/editor/context/EditorIdentityContext.js +7 -0
  252. package/dist/editor/context/EditorStateContext.d.ts +10 -41
  253. package/dist/editor/context/EditorStateContext.js +7 -23
  254. package/dist/editor/context/SWRProvider.d.ts +3 -6
  255. package/dist/editor/context/index.d.ts +1 -20
  256. package/dist/editor/context/index.js +1 -20
  257. package/dist/editor/editor-utils.d.ts +8 -47
  258. package/dist/editor/editor-utils.js +19 -72
  259. package/dist/editor/fields/BlockField.d.ts +2 -2
  260. package/dist/editor/fields/BlockField.js +1 -2
  261. package/dist/editor/fields/CodeField.d.ts +0 -1
  262. package/dist/editor/fields/CodeField.js +0 -1
  263. package/dist/editor/fields/DateTimeField.d.ts +15 -22
  264. package/dist/editor/fields/DateTimeField.js +17 -26
  265. package/dist/editor/fields/ImageField.d.ts +2 -2
  266. package/dist/editor/fields/ImageField.js +8 -14
  267. package/dist/editor/fields/InlineGroupField.d.ts +0 -1
  268. package/dist/editor/fields/InlineGroupField.js +0 -1
  269. package/dist/editor/fields/MarkdownField.d.ts +0 -1
  270. package/dist/editor/fields/MarkdownField.js +6 -11
  271. package/dist/editor/fields/MdxImageDialog.d.ts +1 -1
  272. package/dist/editor/fields/MdxImageDialog.js +0 -1
  273. package/dist/editor/fields/NumberField.d.ts +9 -19
  274. package/dist/editor/fields/NumberField.js +11 -22
  275. package/dist/editor/fields/NumberListField.d.ts +7 -10
  276. package/dist/editor/fields/NumberListField.js +7 -10
  277. package/dist/editor/fields/ObjectField.d.ts +5 -8
  278. package/dist/editor/fields/ObjectField.js +0 -1
  279. package/dist/editor/fields/ReferenceField.d.ts +1 -1
  280. package/dist/editor/fields/ReferenceField.js +11 -16
  281. package/dist/editor/fields/SelectField.d.ts +2 -2
  282. package/dist/editor/fields/SelectField.js +0 -1
  283. package/dist/editor/fields/StringListField.d.ts +0 -4
  284. package/dist/editor/fields/StringListField.js +4 -9
  285. package/dist/editor/fields/TextField.d.ts +0 -1
  286. package/dist/editor/fields/TextField.js +0 -1
  287. package/dist/editor/fields/ToggleField.d.ts +0 -1
  288. package/dist/editor/fields/ToggleField.js +0 -1
  289. package/dist/editor/fields/entry-link/EntryLinkContext.js +2 -4
  290. package/dist/editor/fields/entry-link/InsertEntryLink.d.ts +0 -3
  291. package/dist/editor/fields/entry-link/InsertEntryLink.js +0 -4
  292. package/dist/editor/group-manager/ExternalGroupsTab.d.ts +0 -3
  293. package/dist/editor/group-manager/ExternalGroupsTab.js +0 -1
  294. package/dist/editor/group-manager/GroupCard.d.ts +0 -3
  295. package/dist/editor/group-manager/GroupForm.d.ts +0 -3
  296. package/dist/editor/group-manager/InternalGroupsTab.d.ts +0 -3
  297. package/dist/editor/group-manager/InternalGroupsTab.js +0 -2
  298. package/dist/editor/group-manager/MemberList.d.ts +0 -3
  299. package/dist/editor/group-manager/hooks/useExternalGroupSearch.d.ts +0 -3
  300. package/dist/editor/group-manager/hooks/useExternalGroupSearch.js +0 -3
  301. package/dist/editor/group-manager/hooks/useGroupState.d.ts +0 -3
  302. package/dist/editor/group-manager/hooks/useGroupState.js +0 -5
  303. package/dist/editor/group-manager/hooks/useUserSearch.d.ts +0 -3
  304. package/dist/editor/group-manager/hooks/useUserSearch.js +0 -3
  305. package/dist/editor/group-manager/index.d.ts +0 -6
  306. package/dist/editor/group-manager/index.js +0 -10
  307. package/dist/editor/group-manager/types.d.ts +2 -5
  308. package/dist/editor/group-manager/types.js +0 -3
  309. package/dist/editor/hooks/index.d.ts +6 -11
  310. package/dist/editor/hooks/index.js +6 -12
  311. package/dist/editor/hooks/useBranchActions.d.ts +0 -14
  312. package/dist/editor/hooks/useBranchActions.js +0 -22
  313. package/dist/editor/hooks/useBranchManager.d.ts +2 -48
  314. package/dist/editor/hooks/useBranchManager.js +4 -64
  315. package/dist/editor/hooks/useBranchesData.d.ts +7 -6
  316. package/dist/editor/hooks/useBranchesData.js +7 -6
  317. package/dist/editor/hooks/useCommentSystem.d.ts +0 -49
  318. package/dist/editor/hooks/useCommentSystem.js +1 -36
  319. package/dist/editor/hooks/useDraftManager.d.ts +2 -35
  320. package/dist/editor/hooks/useDraftManager.js +23 -64
  321. package/dist/editor/hooks/useEditorLayout.d.ts +0 -20
  322. package/dist/editor/hooks/useEditorLayout.js +0 -23
  323. package/dist/editor/hooks/useEntriesData.d.ts +1 -0
  324. package/dist/editor/hooks/useEntriesData.js +2 -3
  325. package/dist/editor/hooks/useEntryLinkResolution.d.ts +2 -2
  326. package/dist/editor/hooks/useEntryLinkResolution.js +2 -2
  327. package/dist/editor/hooks/useEntryManager.d.ts +2 -27
  328. package/dist/editor/hooks/useEntryManager.js +29 -78
  329. package/dist/editor/hooks/useGroupManager.d.ts +0 -21
  330. package/dist/editor/hooks/useGroupManager.js +0 -21
  331. package/dist/editor/hooks/usePermissionManager.d.ts +0 -20
  332. package/dist/editor/hooks/usePermissionManager.js +0 -20
  333. package/dist/editor/hooks/useReferenceResolution.d.ts +2 -33
  334. package/dist/editor/hooks/useReferenceResolution.js +1 -48
  335. package/dist/editor/hooks/useSchemaManager.d.ts +8 -27
  336. package/dist/editor/hooks/useSchemaManager.js +0 -19
  337. package/dist/editor/hooks/useUserContext.d.ts +2 -9
  338. package/dist/editor/hooks/useUserContext.js +10 -10
  339. package/dist/editor/hooks/useUserMetadata.d.ts +0 -2
  340. package/dist/editor/hooks/useUserMetadata.js +0 -3
  341. package/dist/editor/media/AssetCard.d.ts +0 -1
  342. package/dist/editor/media/AssetCard.js +1 -2
  343. package/dist/editor/media/CropStep.d.ts +0 -1
  344. package/dist/editor/media/CropStep.js +0 -1
  345. package/dist/editor/media/MediaLibrary.d.ts +1 -2
  346. package/dist/editor/media/MediaLibrary.js +1 -2
  347. package/dist/editor/media/MediaLibraryBody.d.ts +0 -1
  348. package/dist/editor/media/MediaLibraryBody.js +0 -1
  349. package/dist/editor/media/crop-math.d.ts +1 -1
  350. package/dist/editor/media/crop-math.js +1 -1
  351. package/dist/editor/permission-manager/GroupSelector.d.ts +0 -3
  352. package/dist/editor/permission-manager/PermissionEditor.d.ts +0 -4
  353. package/dist/editor/permission-manager/PermissionEditor.js +0 -1
  354. package/dist/editor/permission-manager/PermissionLevelBadge.d.ts +0 -7
  355. package/dist/editor/permission-manager/PermissionLevelBadge.js +0 -4
  356. package/dist/editor/permission-manager/PermissionTree.d.ts +0 -4
  357. package/dist/editor/permission-manager/UserSelector.d.ts +0 -3
  358. package/dist/editor/permission-manager/constants.d.ts +0 -3
  359. package/dist/editor/permission-manager/hooks/useGroupsAndUsers.d.ts +0 -3
  360. package/dist/editor/permission-manager/hooks/useGroupsAndUsers.js +0 -10
  361. package/dist/editor/permission-manager/hooks/usePermissionTree.d.ts +0 -14
  362. package/dist/editor/permission-manager/hooks/usePermissionTree.js +0 -11
  363. package/dist/editor/permission-manager/index.d.ts +0 -7
  364. package/dist/editor/permission-manager/index.js +0 -16
  365. package/dist/editor/permission-manager/types.d.ts +2 -12
  366. package/dist/editor/permission-manager/types.js +0 -3
  367. package/dist/editor/permission-manager/utils.d.ts +0 -28
  368. package/dist/editor/permission-manager/utils.js +9 -23
  369. package/dist/editor/preview-bridge.d.ts +5 -1
  370. package/dist/editor/preview-bridge.js +4 -4
  371. package/dist/editor/schema-editor/CollectionEditor.d.ts +0 -5
  372. package/dist/editor/schema-editor/CollectionEditor.js +9 -31
  373. package/dist/editor/schema-editor/EntryTypeEditor.d.ts +0 -6
  374. package/dist/editor/schema-editor/EntryTypeEditor.js +3 -24
  375. package/dist/editor/schema-editor/index.d.ts +0 -7
  376. package/dist/editor/schema-editor/index.js +0 -7
  377. package/dist/editor/utils/env.d.ts +3 -18
  378. package/dist/editor/utils/env.js +6 -25
  379. package/dist/entry-link-resolver.d.ts +13 -35
  380. package/dist/entry-link-resolver.js +20 -53
  381. package/dist/entry-schema-registry.d.ts +20 -60
  382. package/dist/entry-schema-registry.js +20 -69
  383. package/dist/entry-schema.d.ts +118 -223
  384. package/dist/entry-schema.js +46 -91
  385. package/dist/git-manager.d.ts +101 -200
  386. package/dist/git-manager.js +211 -384
  387. package/dist/github-service.d.ts +34 -75
  388. package/dist/github-service.js +37 -91
  389. package/dist/http/handler.d.ts +6 -37
  390. package/dist/http/handler.js +65 -120
  391. package/dist/http/router.d.ts +15 -32
  392. package/dist/http/router.js +34 -99
  393. package/dist/http/types.d.ts +15 -40
  394. package/dist/http/types.js +3 -7
  395. package/dist/id.d.ts +4 -10
  396. package/dist/id.js +4 -10
  397. package/dist/index.js +6 -9
  398. package/dist/operating-mode/client-safe-strategy.d.ts +7 -14
  399. package/dist/operating-mode/client-safe-strategy.js +8 -28
  400. package/dist/operating-mode/client-unsafe-strategy.d.ts +6 -16
  401. package/dist/operating-mode/client-unsafe-strategy.js +9 -34
  402. package/dist/operating-mode/client.d.ts +3 -10
  403. package/dist/operating-mode/client.js +3 -8
  404. package/dist/operating-mode/deployment-name-fixtures.d.ts +14 -18
  405. package/dist/operating-mode/deployment-name-fixtures.js +14 -18
  406. package/dist/operating-mode/deployment-name.d.ts +19 -29
  407. package/dist/operating-mode/deployment-name.js +29 -47
  408. package/dist/operating-mode/index.d.ts +4 -25
  409. package/dist/operating-mode/index.js +9 -31
  410. package/dist/operating-mode/mode-env.d.ts +32 -54
  411. package/dist/operating-mode/mode-env.js +36 -58
  412. package/dist/operating-mode/types.d.ts +24 -72
  413. package/dist/operating-mode/types.js +1 -7
  414. package/dist/paths/branch-name.d.ts +30 -40
  415. package/dist/paths/branch-name.js +30 -40
  416. package/dist/paths/branch.d.ts +4 -18
  417. package/dist/paths/branch.js +6 -22
  418. package/dist/paths/index.d.ts +2 -13
  419. package/dist/paths/index.js +3 -23
  420. package/dist/paths/normalize-server.d.ts +3 -15
  421. package/dist/paths/normalize-server.js +3 -15
  422. package/dist/paths/normalize.d.ts +13 -33
  423. package/dist/paths/normalize.js +13 -33
  424. package/dist/paths/resolve.d.ts +4 -17
  425. package/dist/paths/resolve.js +4 -22
  426. package/dist/paths/types.d.ts +12 -34
  427. package/dist/paths/types.js +5 -7
  428. package/dist/paths/validation.d.ts +22 -119
  429. package/dist/paths/validation.js +30 -148
  430. package/dist/reference-resolver.d.ts +0 -13
  431. package/dist/reference-resolver.js +0 -18
  432. package/dist/resolve-canopy-user.d.ts +16 -18
  433. package/dist/resolve-canopy-user.js +17 -26
  434. package/dist/resource-generation.d.ts +16 -25
  435. package/dist/resource-generation.js +34 -97
  436. package/dist/schema/meta-loader.d.ts +2 -14
  437. package/dist/schema/meta-loader.js +15 -64
  438. package/dist/schema/resolver.d.ts +1 -4
  439. package/dist/schema/resolver.js +1 -6
  440. package/dist/schema/schema-store-types.d.ts +0 -12
  441. package/dist/schema/schema-store.d.ts +61 -138
  442. package/dist/schema/schema-store.js +85 -282
  443. package/dist/schema/types.d.ts +0 -4
  444. package/dist/server.d.ts +62 -93
  445. package/dist/server.js +63 -94
  446. package/dist/services.d.ts +23 -47
  447. package/dist/services.js +55 -98
  448. package/dist/settings-workspace.d.ts +17 -30
  449. package/dist/settings-workspace.js +51 -81
  450. package/dist/static/index.d.ts +26 -52
  451. package/dist/static/index.js +33 -67
  452. package/dist/{worker/task-queue.d.ts → task-queue/cms-task-queue.d.ts} +7 -9
  453. package/dist/task-queue/cms-task-queue.js +13 -0
  454. package/dist/task-queue/task-queue-config.d.ts +3 -0
  455. package/dist/{worker → task-queue}/task-queue-config.js +1 -6
  456. package/dist/task-queue/task-queue.d.ts +16 -22
  457. package/dist/task-queue/task-queue.js +20 -44
  458. package/dist/task-queue/types.d.ts +0 -1
  459. package/dist/task-queue/worker-status.d.ts +35 -0
  460. package/dist/task-queue/worker-status.js +40 -0
  461. package/dist/types.d.ts +40 -51
  462. package/dist/url-collision.d.ts +26 -40
  463. package/dist/url-collision.js +47 -63
  464. package/dist/url-exclusivity-fixtures.d.ts +15 -20
  465. package/dist/url-exclusivity-fixtures.js +14 -19
  466. package/dist/url-path-resolver.d.ts +8 -16
  467. package/dist/url-path-resolver.js +19 -29
  468. package/dist/user.d.ts +8 -21
  469. package/dist/user.js +10 -19
  470. package/dist/utils/async-mutex.js +4 -6
  471. package/dist/utils/atomic-write.d.ts +2 -11
  472. package/dist/utils/atomic-write.js +2 -11
  473. package/dist/utils/body-field.d.ts +8 -15
  474. package/dist/utils/body-field.js +8 -15
  475. package/dist/utils/content-serialize.d.ts +18 -24
  476. package/dist/utils/content-serialize.js +109 -160
  477. package/dist/utils/content-write-lock.d.ts +51 -106
  478. package/dist/utils/content-write-lock.js +66 -111
  479. package/dist/utils/debug.d.ts +7 -23
  480. package/dist/utils/debug.js +7 -23
  481. package/dist/utils/entry-url.d.ts +11 -23
  482. package/dist/utils/entry-url.js +11 -26
  483. package/dist/utils/error.d.ts +25 -90
  484. package/dist/utils/error.js +60 -147
  485. package/dist/utils/flatten-group-fields.d.ts +5 -7
  486. package/dist/utils/flatten-group-fields.js +5 -7
  487. package/dist/utils/format.d.ts +1 -5
  488. package/dist/utils/format.js +1 -5
  489. package/dist/utils/fs.d.ts +3 -4
  490. package/dist/utils/fs.js +3 -4
  491. package/dist/utils/git.d.ts +52 -83
  492. package/dist/utils/git.js +79 -125
  493. package/dist/utils/logger.d.ts +35 -50
  494. package/dist/utils/logger.js +35 -52
  495. package/dist/utils/provisioning-lock.d.ts +23 -35
  496. package/dist/utils/provisioning-lock.js +49 -69
  497. package/dist/utils/sanitize-href.d.ts +5 -10
  498. package/dist/utils/sanitize-href.js +8 -18
  499. package/dist/utils/title-field.d.ts +17 -20
  500. package/dist/utils/title-field.js +18 -27
  501. package/dist/utils/typed-filename.d.ts +20 -48
  502. package/dist/utils/typed-filename.js +25 -61
  503. package/dist/utils/url-prefix.d.ts +3 -3
  504. package/dist/utils/url-prefix.js +3 -3
  505. package/dist/validation/block-structural-keys.d.ts +16 -31
  506. package/dist/validation/block-structural-keys.js +19 -29
  507. package/dist/validation/deletion-checker.d.ts +0 -34
  508. package/dist/validation/deletion-checker.js +1 -43
  509. package/dist/validation/entry-link-validator.d.ts +6 -13
  510. package/dist/validation/entry-link-validator.js +4 -13
  511. package/dist/validation/entry-type-reference-validator.d.ts +13 -18
  512. package/dist/validation/entry-type-reference-validator.js +13 -18
  513. package/dist/validation/entry-validator.d.ts +13 -25
  514. package/dist/validation/entry-validator.js +15 -32
  515. package/dist/validation/field-traversal.d.ts +3 -30
  516. package/dist/validation/field-traversal.js +1 -32
  517. package/dist/validation/reference-validator.d.ts +5 -24
  518. package/dist/validation/reference-validator.js +5 -30
  519. package/dist/worker/cms-worker.d.ts +172 -232
  520. package/dist/worker/cms-worker.js +241 -333
  521. package/dist/worker/git-sync.d.ts +32 -133
  522. package/dist/worker/git-sync.js +129 -191
  523. package/dist/worker/github-auth.d.ts +13 -19
  524. package/dist/worker/github-auth.js +13 -19
  525. package/dist/worker/history-rewrite.d.ts +53 -66
  526. package/dist/worker/history-rewrite.js +54 -60
  527. package/dist/worker/log.d.ts +24 -41
  528. package/dist/worker/log.js +25 -42
  529. package/dist/worker/rebase.d.ts +20 -110
  530. package/dist/worker/rebase.js +279 -354
  531. package/dist/worker/task-runner.d.ts +32 -66
  532. package/dist/worker/task-runner.js +126 -162
  533. package/dist/worker/worker-context.d.ts +41 -81
  534. package/package.json +4 -4
  535. package/dist/cli/template-files/middleware-clerk.ts.template +0 -37
  536. package/dist/worker/task-queue-config.d.ts +0 -8
  537. package/dist/worker/task-queue.js +0 -19
  538. package/dist/worker/worker-status.d.ts +0 -47
  539. package/dist/worker/worker-status.js +0 -52
@@ -27,7 +27,6 @@ const normalizeOptions = (options) => {
27
27
  };
28
28
  const fieldKey = (path) => formatCanopyPath(path);
29
29
  export const FormRenderer = ({ fields, value, onChange, customRenderers, branch = 'main', comments = [], currentEntryPath, currentUserId, canResolve = false, focusedFieldPath, highlightThreadId, onAddComment, onResolveThread, onResolvedValueChange, onLoadingStateChange, conflictNotice = false, fieldErrors, }) => {
30
- // Use the extracted reference resolution hook for live preview
31
30
  useReferenceResolution({
32
31
  value,
33
32
  fields,
@@ -47,7 +46,6 @@ export const FormRenderer = ({ fields, value, onChange, customRenderers, branch
47
46
  const renderFieldControl = (field, currentValue, update, path) => {
48
47
  const fieldId = `field-${fieldKey(path).replace(/[^a-zA-Z0-9_-]/g, '-')}`;
49
48
  const canopyPath = normalizeCanopyPath(path);
50
- // Filter comments for this specific field
51
49
  const fieldThreads = currentEntryPath && onAddComment
52
50
  ? comments.filter((thread) => thread.type === 'field' &&
53
51
  thread.entryPath === currentEntryPath &&
@@ -62,14 +60,12 @@ export const FormRenderer = ({ fields, value, onChange, customRenderers, branch
62
60
  path,
63
61
  id: fieldId,
64
62
  }) }, fieldKey(path)));
65
- // Wrap custom fields with FieldWrapper if comments enabled
66
63
  if (currentEntryPath && currentUserId && onAddComment && onResolveThread) {
67
64
  return (_jsx(FieldWrapper, { canopyPath: canopyPath, entryPath: currentEntryPath, threads: fieldThreads, autoFocus: focusedFieldPath === canopyPath, currentUserId: currentUserId, canResolve: canResolve, onAddComment: onAddComment, onResolveThread: onResolveThread, highlightThreadId: highlightThreadId, children: renderedField }, fieldKey(path)));
68
65
  }
69
66
  return renderedField;
70
67
  }
71
68
  const label = field.label ?? field.name;
72
- // Helper to wrap field with FieldWrapper if comments enabled
73
69
  const wrapWithComments = (renderedField) => {
74
70
  if (currentEntryPath && currentUserId && onAddComment && onResolveThread) {
75
71
  return (_jsx(FieldWrapper, { canopyPath: canopyPath, entryPath: currentEntryPath, threads: fieldThreads, autoFocus: focusedFieldPath === canopyPath, currentUserId: currentUserId, canResolve: canResolve, onAddComment: onAddComment, onResolveThread: onResolveThread, highlightThreadId: highlightThreadId, children: renderedField }));
@@ -145,24 +141,14 @@ export const FormRenderer = ({ fields, value, onChange, customRenderers, branch
145
141
  update(nextItems);
146
142
  }, renderField: renderField, path: [...path, idx], dataCanopyField: normalizeCanopyPath([...path, idx]) })] }) }, fieldKey([...path, idx])))), items.length === 0 && (_jsx(Text, { size: "xs", c: "dimmed", children: "No items yet. Add one to get started." }))] })] }) }, fieldKey(path)));
147
143
  }
148
- // Non-list object: unlike the list branch above (where each item has
149
- // its own Remove button), this field had no way to return to
150
- // `undefined` once given a value. That mattered because a required
151
- // child field inside it can be filled and then cleared back to `''`,
152
- // which `isEmptyForRequired` treats as empty (entry-validator.ts) --
153
- // but the object as a whole only reads as "absent" when it is
154
- // `undefined`/`null`, not when its children are individually empty.
155
- // Without a way to unset the object, that state was permanently
156
- // invalid. The Clear button (wired through ObjectField's onRemove)
157
- // fixes that by resetting to `undefined`, never `{}` (which would
158
- // still carry a required-but-empty child into validation).
159
- //
160
- // Shown only when the field is optional (a required object should
161
- // not be clearable back to a missing state) and only once it
162
- // actually has a value (nothing to clear otherwise). Labeled
163
- // "Clear" rather than "Remove" so it doesn't read as deleting the
164
- // field from the schema, the way "Remove" correctly does for a list
165
- // item above.
144
+ // Unlike the list branch above (each item has its own Remove button), a
145
+ // non-list object has no other way back to `undefined`: a required child
146
+ // filled and cleared to `''` reads as empty via `isEmptyForRequired`
147
+ // (entry-validator.ts), but the object itself only reads "absent" when
148
+ // `undefined`/`null`. The Clear button (ObjectField's `onRemove`) resets to
149
+ // `undefined`, never `{}` -- shown only when the field is optional and
150
+ // already has a value, and labeled "Clear" rather than "Remove" so it
151
+ // doesn't read as deleting the field from the schema.
166
152
  const hasValue = currentValue !== undefined && currentValue !== null;
167
153
  const canClear = objectField.required !== true && hasValue;
168
154
  return wrapWithComments(_jsx(ObjectField, { label: label, fields: objectField.fields, value: currentValue, onChange: (next) => update(next), onRemove: canClear ? () => update(undefined) : undefined, renderField: renderField, path: path, dataCanopyField: normalizeCanopyPath(path) }, fieldKey(path)));
@@ -184,4 +170,3 @@ export const FormRenderer = ({ fields, value, onChange, customRenderers, branch
184
170
  return (_jsx("div", { children: renderField(field, val, (next) => onChange({ ...value, [field.name]: next }), path) }, fieldKey(path)));
185
171
  })] }));
186
172
  };
187
- export default FormRenderer;
@@ -1,14 +1,13 @@
1
1
  'use client';
2
2
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
3
  /**
4
- * SystemHealthPanel - Admin-only modal surfacing the observability
5
- * endpoints from PR-A1..A4: task-queue/worker liveness (Overview), task
6
- * recovery (Tasks), and branch-directory recovery (Branches).
4
+ * SystemHealthPanel - Admin-only modal surfacing observability endpoints:
5
+ * task-queue/worker liveness (Overview), task recovery (Tasks), and
6
+ * branch-directory recovery (Branches).
7
7
  *
8
- * Visibility is entirely the caller's responsibility -- Editor.tsx only
9
- * renders/opens this for admins (see isAdmin(userContext?.groups) there).
10
- * This component does not re-check that itself, same as GroupManager and
11
- * PermissionManager rely on their callers for that gate.
8
+ * Visibility is the caller's responsibility: Editor.tsx renders/opens this
9
+ * only for admins (see isAdmin(userContext?.groups)); this component does not
10
+ * re-check that itself, same as GroupManager and PermissionManager.
12
11
  */
13
12
  import { Alert, Badge, Button, Code, Group, Loader, Modal, Paper, SegmentedControl, SimpleGrid, Spoiler, Stack, Table, Tabs, Text, ThemeIcon, Tooltip, } from '@mantine/core';
14
13
  import { IconAlertCircle, IconAlertTriangle } from '@tabler/icons-react';
@@ -37,20 +36,16 @@ function formatAgeMs(ms) {
37
36
  return `${totalDays}d ${totalHours % 24}h`;
38
37
  }
39
38
  /**
40
- * Whether the Purge button should be disabled for a corrupt-metadata
41
- * or orphan row, and the tooltip explaining why.
42
- * - Base branch: never purgeable -- the server already 400s
43
- * ('The base branch directory can never be purged', see
44
- * purgeBranchDirHandler in api/admin-branch-health.ts); this is UX
45
- * honesty, not a new rail.
46
- * - Fresh provisioning lock: provisioning may genuinely be in progress
47
- * (mirrors the server's [H1] freshness rail) -- applies to BOTH kinds,
48
- * not just orphans.
39
+ * Whether the Purge button should be disabled for this row, and why (tooltip).
40
+ * - Base branch: never purgeable — the server already 400s this (UX
41
+ * consistency, not a new rail); see purgeBranchDirHandler in
42
+ * api/admin-branch-health.ts.
43
+ * - Fresh provisioning lock ([H1] freshness rail): provisioning may
44
+ * genuinely be in progress, for either row kind.
49
45
  * - Orphan-only youth rail: a directory younger than
50
- * ORPHAN_YOUTH_THRESHOLD_MS may still be a clone in progress that hasn't
51
- * written branch.json yet. Corrupt-metadata dirs are exempt from this
52
- * server-side (a parseable-then-corrupted file isn't a mid-clone
53
- * signature), so they're exempt here too.
46
+ * ORPHAN_YOUTH_THRESHOLD_MS may still be mid-clone, before branch.json is
47
+ * written. Corrupt-metadata dirs are exempt server-side too (a
48
+ * parseable-then-corrupted file isn't a mid-clone signature).
54
49
  */
55
50
  function purgeGateFor(entry) {
56
51
  if (entry.isBaseBranch) {
@@ -99,10 +94,8 @@ const TASK_STATUS_VALUES = TASK_STATUS_OPTIONS.map((o) => o.value);
99
94
  function isAdminTaskStatus(value) {
100
95
  return TASK_STATUS_VALUES.includes(value);
101
96
  }
102
- // ============================================================================
103
97
  // Confirm-modal copy (races/limitations these actions accept -- see
104
98
  // api/admin.ts and api/admin-branch-health.ts's handler docstrings)
105
- // ============================================================================
106
99
  const RETRY_CONFIRM_TEXT = 'Retrying may duplicate work if the task also runs another way; task actions are safe to run twice.';
107
100
  function deleteConfirmText(status) {
108
101
  if (status === 'pending') {
@@ -117,9 +110,6 @@ export function SystemHealthPanel({ opened, onClose }) {
117
110
  const health = useSystemHealth({ isOpen: opened });
118
111
  return (_jsx(Modal, { opened: opened, onClose: onClose, title: "System health", size: "xl", children: _jsxs(Tabs, { defaultValue: "overview", children: [_jsxs(Tabs.List, { children: [_jsx(Tabs.Tab, { value: "overview", children: "Overview" }), _jsx(Tabs.Tab, { value: "tasks", children: "Tasks" }), _jsx(Tabs.Tab, { value: "branches", children: "Branches" })] }), _jsx(Tabs.Panel, { value: "overview", pt: "md", children: _jsx(OverviewTab, { health: health }) }), _jsx(Tabs.Panel, { value: "tasks", pt: "md", children: _jsx(TasksTab, { health: health }) }), _jsx(Tabs.Panel, { value: "branches", pt: "md", children: _jsx(BranchesTab, { health: health }) })] }) }));
119
112
  }
120
- // ============================================================================
121
- // Overview tab
122
- // ============================================================================
123
113
  function OverviewTab({ health }) {
124
114
  const { status, statusLoading, isRecentFatalError, error, refresh } = health;
125
115
  if (!status && statusLoading) {
@@ -135,9 +125,6 @@ function OverviewTab({ health }) {
135
125
  ? ` · ${lastGitSync.skippedLocked.length} skipped (content write in progress)`
136
126
  : ''] }), lastGitSync.failed.length > 0 && (_jsx(Spoiler, { maxHeight: 0, showLabel: `${lastGitSync.failed.length} failed — show details`, hideLabel: "Hide", children: _jsx(Stack, { gap: 4, mt: 4, children: lastGitSync.failed.map((f) => (_jsxs(Text, { size: "xs", c: "red", children: [f.branch, ": ", f.error] }, f.branch))) }) }))] })), _jsx(SimpleGrid, { cols: 5, spacing: "xs", children: ['pending', 'processing', 'completed', 'failed', 'corrupt'].map((key) => (_jsxs(Paper, { withBorder: true, p: "xs", radius: "md", ta: "center", children: [_jsx(Text, { size: "xs", c: "dimmed", tt: "capitalize", children: key }), _jsx(Text, { size: "lg", fw: 700, c: (key === 'failed' || key === 'corrupt') && status.queue[key] > 0 ? 'red' : undefined, children: status.queue[key] })] }, key))) }), status.queue.oldestPendingAgeMs !== undefined && (_jsxs(Text, { size: "xs", c: "dimmed", children: ["Oldest pending task: ", formatAgeMs(status.queue.oldestPendingAgeMs), " old"] })), _jsxs(Group, { justify: "space-between", align: "center", children: [_jsxs(Text, { size: "xs", c: "dimmed", children: ["Generated at ", status.generatedAt] }), _jsx(Button, { size: "xs", variant: "light", onClick: () => refresh(), loading: statusLoading, children: "Refresh" })] }), _jsx(Text, { size: "xs", c: "dimmed", children: "Data may lag up to ~60s (shared-filesystem caching)" })] }));
137
127
  }
138
- // ============================================================================
139
- // Tasks tab
140
- // ============================================================================
141
128
  function TasksTab({ health }) {
142
129
  const { taskStatus, setTaskStatus, tasks, tasksLoading } = health;
143
130
  const isCorrupt = taskStatus === 'corrupt';
@@ -170,9 +157,6 @@ function TasksTab({ health }) {
170
157
  ? corruptRows.map((file) => (_jsxs(Table.Tr, { children: [_jsx(Table.Td, { children: _jsx("span", { title: file.fileName, children: truncate(file.fileName, 30) }) }), _jsxs(Table.Td, { children: [file.size, " bytes"] }), _jsx(Table.Td, { children: file.mtime }), _jsx(Table.Td, { children: _jsx(Code, { block: true, style: { maxWidth: 320, whiteSpace: 'pre-wrap' }, children: file.rawSnippet }) }), _jsx(Table.Td, { children: _jsx(Button, { size: "xs", variant: "light", color: "red", "data-testid": `delete-task-${file.fileName}`, onClick: () => handleDeleteClick('corrupt', file.fileName), children: "Delete" }) })] }, file.fileName)))
171
158
  : taskRows.map((task) => (_jsxs(Table.Tr, { children: [_jsx(Table.Td, { children: _jsx("span", { title: task.id, children: truncate(task.id, 12) }) }), _jsx(Table.Td, { children: task.action }), _jsx(Table.Td, { children: task.createdAt }), _jsx(Table.Td, { children: task.retryCount ?? 0 }), _jsx(Table.Td, { children: task.error ? (_jsx(Tooltip, { label: task.error, multiline: true, maw: 400, children: _jsx(Text, { size: "xs", style: { cursor: 'help' }, children: truncate(task.error, 50) }) })) : (_jsx(Text, { size: "xs", c: "dimmed", children: "\u2014" })) }), _jsx(Table.Td, { children: _jsxs(Group, { gap: "xs", children: [taskStatus === 'failed' && (_jsx(Button, { size: "xs", variant: "light", "data-testid": `retry-task-${task.id}`, onClick: () => handleRetryClick(task), children: "Retry" })), canDelete && (_jsx(Button, { size: "xs", variant: "light", color: "red", "data-testid": `delete-task-${task.id}`, onClick: () => handleDeleteClick(taskStatus, `${task.id}.json`), children: "Delete" }))] }) })] }, task.id))) })] }) }))] }));
172
159
  }
173
- // ============================================================================
174
- // Branches tab
175
- // ============================================================================
176
160
  function BranchesTab({ health }) {
177
161
  const { branchHealth, branchHealthLoading } = health;
178
162
  const entries = branchHealth?.entries ?? [];
@@ -214,12 +198,11 @@ function BranchesTab({ health }) {
214
198
  function BranchHealthRow({ entry, onMarkMerged, onRepair, onPurge, }) {
215
199
  if (entry.kind === 'healthy' && entry.branch) {
216
200
  const b = entry.branch;
217
- // Mirror the rebase loop's skip logic (worker/rebase.ts):
218
- // the worker rebases every branch except 'submitted'/'approved' (under an
219
- // active PR) and 'archived' (already merged). Stated as an exclusion list
220
- // rather than `status === 'editing'` so that a status added later shows its
221
- // rebase failures by default instead of silently hiding them -- the bug
222
- // this replaced.
201
+ // Mirrors the rebase loop's skip logic (worker/rebase.ts): the worker
202
+ // rebases every branch except 'submitted'/'approved' (under an active PR)
203
+ // and 'archived' (already merged). An exclusion list, not `status ===
204
+ // 'editing'`, so a status added later still shows its rebase failures by
205
+ // default instead of silently hiding them.
223
206
  const showRebaseFailure = !['submitted', 'approved', 'archived'].includes(b.status) && !!b.rebaseFailure;
224
207
  const canMarkMerged = (b.status === 'submitted' || b.status === 'approved') && !!b.pullRequestNumber;
225
208
  return (_jsxs(Table.Tr, { children: [_jsx(Table.Td, { children: _jsxs(Group, { gap: 4, wrap: "nowrap", children: [_jsx(Text, { size: "sm", children: entry.dirName }), entry.isBaseBranch && (_jsx(Badge, { size: "xs", color: "gray", variant: "outline", children: "base" }))] }) }), _jsx(Table.Td, { children: _jsx(Badge, { color: branchStatusColorMap[b.status] ?? 'neutral', variant: "light", children: b.status }) }), _jsx(Table.Td, { children: b.pullRequestNumber ? (_jsxs(Text, { size: "xs", c: "blue", component: "a", href: b.pullRequestUrl, target: "_blank", rel: "noopener noreferrer", style: { textDecoration: 'underline' }, children: ["PR #", b.pullRequestNumber, b.pullRequestState ? ` (${b.pullRequestState})` : ''] })) : (_jsx(Text, { size: "xs", c: "dimmed", children: "\u2014" })) }), _jsx(Table.Td, { children: b.syncStatus === 'sync-failed' ? (_jsx(Tooltip, { label: b.syncFailureReason
@@ -14,9 +14,8 @@ export interface UseSystemHealthReturn {
14
14
  statusLoading: boolean;
15
15
  /**
16
16
  * True when workerStatus.lastFatalError exists and is < 30 min old, as of
17
- * the last fetch. Computed at fetch time (not in the component's render)
18
- * so the panel never calls Date.now() during render -- see the
19
- * react-hooks/purity rule this sidesteps.
17
+ * the last fetch. Computed at fetch time, not in the component's render, so
18
+ * the panel never calls Date.now() during render (react-hooks/purity).
20
19
  */
21
20
  isRecentFatalError: boolean;
22
21
  tasks: AdminTasksData | null;
@@ -1,12 +1,11 @@
1
1
  'use client';
2
2
  /**
3
- * useSystemHealth - Data + actions for the admin System Health panel (PR-U1).
3
+ * useSystemHealth - Data + actions for the admin System Health panel.
4
4
  *
5
- * Mirrors useGroupManager's shape: loads on open, exposes typed action
6
- * helpers that each notify then refresh(). Unlike useGroupManager, this also
7
- * polls every 30s while the panel stays open (cleared on close/unmount) --
8
- * queue depth and worker liveness go stale quickly, and the panel has no
9
- * other way to catch a worker coming back up or a task finishing.
5
+ * Mirrors useGroupManager's shape (load on open, typed actions that notify
6
+ * then refresh()), but also polls every 30s while open (cleared on
7
+ * close/unmount) since queue depth and worker liveness go stale quickly and
8
+ * the panel has no other way to catch a worker recovering or a task finishing.
10
9
  */
11
10
  import { useCallback, useEffect, useRef, useState } from 'react';
12
11
  import { notifications } from '@mantine/notifications';
@@ -14,9 +13,9 @@ import { useApiClient } from '../context/index.js';
14
13
  const POLL_INTERVAL_MS = 30_000;
15
14
  /**
16
15
  * Crash-loop rider window: a worker that fatally exits and restarts
17
- * repeatedly can flap between 'absent' and 'alive' liveness and never sit
18
- * still long enough to read 'stale' -- surfacing a recent lastFatalError
19
- * regardless of liveness state is the only way the panel catches that.
16
+ * repeatedly can flap between 'absent' and 'alive' and never sit still long
17
+ * enough to read 'stale' — surfacing a recent lastFatalError regardless of
18
+ * liveness state is the only way the panel catches that.
20
19
  */
21
20
  const CRASH_LOOP_WINDOW_MS = 30 * 60_000;
22
21
  export function useSystemHealth(options) {
@@ -31,9 +30,8 @@ export function useSystemHealth(options) {
31
30
  const [branchHealthLoading, setBranchHealthLoading] = useState(false);
32
31
  const [error, setError] = useState(null);
33
32
  // refresh() has no status argument, so it needs the CURRENT taskStatus
34
- // without taking a dependency on it (that would tear down/rebuild the
35
- // polling interval below every time the Tasks tab's segmented control
36
- // changes). A ref sidesteps the stale-closure problem cleanly.
33
+ // without depending on it (that would tear down/rebuild the poll interval
34
+ // below on every Tasks-tab status change) — a ref sidesteps the stale closure.
37
35
  const taskStatusRef = useRef(taskStatus);
38
36
  taskStatusRef.current = taskStatus;
39
37
  const fetchStatus = useCallback(async () => {
@@ -11,17 +11,14 @@ import type { ApiClient } from './context/index.js';
11
11
  * can't be sourced here via `useApiClient()`/`useOptionalApiClient()` directly; instead every
12
12
  * entry point takes an optional `apiClient`, which the caller (a hook, which CAN call
13
13
  * `useOptionalApiClient()` during render) resolves and passes down. Falls back to a
14
- * default-configured `createApiClient()` when no client is supplied, so direct callers/tests
15
- * that predate this DI path keep working unchanged.
14
+ * default-configured `createApiClient()` when no client is supplied.
16
15
  */
17
16
  /**
18
- * Find which fields changed between two form values.
19
17
  * Only returns top-level field configs for fields that changed.
18
+ * @internal Exported for tests.
20
19
  */
21
20
  export declare function findChangedFields(prevValue: FormValue, currentValue: FormValue, schema: EntrySchema): FieldConfig[];
22
21
  /**
23
- * Resolve changed references incrementally.
24
- * Only resolves reference fields that have changed.
25
22
  * Uses cache to avoid duplicate API calls.
26
23
  */
27
24
  export declare function resolveChangedReferences(prevValue: FormValue, currentValue: FormValue, schema: EntrySchema, branch: string, cache: Map<string, unknown>, apiClient?: ApiClient): Promise<Partial<FormValue>>;
@@ -10,19 +10,17 @@ import { flattenGroupFields } from '../utils/flatten-group-fields.js';
10
10
  * can't be sourced here via `useApiClient()`/`useOptionalApiClient()` directly; instead every
11
11
  * entry point takes an optional `apiClient`, which the caller (a hook, which CAN call
12
12
  * `useOptionalApiClient()` during render) resolves and passes down. Falls back to a
13
- * default-configured `createApiClient()` when no client is supplied, so direct callers/tests
14
- * that predate this DI path keep working unchanged.
13
+ * default-configured `createApiClient()` when no client is supplied.
15
14
  */
16
15
  /**
17
- * Find which fields changed between two form values.
18
16
  * Only returns top-level field configs for fields that changed.
17
+ * @internal Exported for tests.
19
18
  */
20
19
  export function findChangedFields(prevValue, currentValue, schema) {
21
20
  const changed = [];
22
21
  for (const field of flattenGroupFields(schema)) {
23
22
  const prevFieldValue = prevValue[field.name];
24
23
  const currentFieldValue = currentValue[field.name];
25
- // Deep equality check for objects and arrays
26
24
  if (JSON.stringify(prevFieldValue) !== JSON.stringify(currentFieldValue)) {
27
25
  changed.push(field);
28
26
  }
@@ -30,8 +28,6 @@ export function findChangedFields(prevValue, currentValue, schema) {
30
28
  return changed;
31
29
  }
32
30
  /**
33
- * Resolve changed references incrementally.
34
- * Only resolves reference fields that have changed.
35
31
  * Uses cache to avoid duplicate API calls.
36
32
  */
37
33
  export async function resolveChangedReferences(prevValue, currentValue, schema, branch, cache, apiClient) {
@@ -41,14 +37,11 @@ export async function resolveChangedReferences(prevValue, currentValue, schema,
41
37
  if (field.type === 'reference') {
42
38
  const refField = field;
43
39
  const fieldValue = currentValue[field.name];
44
- // Resolve this field's value
45
40
  if (refField.list && Array.isArray(fieldValue)) {
46
- // List of references
47
41
  const resolved = await Promise.all(fieldValue.map((id) => resolveReferenceId(id, branch, cache, apiClient)));
48
42
  updates[field.name] = resolved;
49
43
  }
50
44
  else if (fieldValue) {
51
- // Single reference
52
45
  const resolved = await resolveReferenceId(fieldValue, branch, cache, apiClient);
53
46
  updates[field.name] = resolved;
54
47
  }
@@ -56,13 +49,7 @@ export async function resolveChangedReferences(prevValue, currentValue, schema,
56
49
  }
57
50
  return updates;
58
51
  }
59
- /**
60
- * Resolve a single reference ID to full object.
61
- * Checks cache first, then makes API call if needed.
62
- * Returns original ID if resolution fails.
63
- */
64
52
  async function resolveReferenceId(id, branch, cache, apiClient) {
65
- // Only resolve string IDs
66
53
  if (typeof id !== 'string') {
67
54
  return id;
68
55
  }
@@ -71,7 +58,6 @@ async function resolveReferenceId(id, branch, cache, apiClient) {
71
58
  return id;
72
59
  }
73
60
  const cacheKey = `${branch}:${id}`;
74
- // Check cache first
75
61
  if (cache.has(cacheKey)) {
76
62
  return cache.get(cacheKey);
77
63
  }
@@ -82,17 +68,14 @@ async function resolveReferenceId(id, branch, cache, apiClient) {
82
68
  const client = apiClient ?? createApiClient();
83
69
  const result = await client.content.resolveReferences({ branch }, { ids: [id] });
84
70
  if (result.ok && result.data && result.data.resolved[id]) {
85
- // Cache and return resolved object
86
71
  const resolved = result.data.resolved[id];
87
72
  cache.set(cacheKey, resolved);
88
73
  return resolved;
89
74
  }
90
- // Resolution failed - return original ID string
91
75
  return id;
92
76
  }
93
77
  catch (error) {
94
78
  console.error(`Failed to resolve reference ID ${id}:`, error);
95
- // Return original ID on error
96
79
  return id;
97
80
  }
98
81
  }
@@ -9,15 +9,11 @@ import { ThreadCarousel } from './ThreadCarousel.js';
9
9
  */
10
10
  export const BranchComments = ({ comments, currentUserId, canResolve, onAddComment, onResolveThread, autoFocus, highlightThreadId, onGetUserMetadata, }) => {
11
11
  const [showCarousel, setShowCarousel] = useState(false);
12
- // Filter for branch-level threads
13
12
  const branchThreads = useMemo(() => comments.filter((t) => t.type === 'branch'), [comments]);
14
- // Show carousel if threads exist or if auto-focused
15
13
  const shouldShowCarousel = branchThreads.length > 0 || showCarousel || autoFocus;
16
- // Wrapper to add branch context to comment handler
17
14
  const handleAddComment = async (text, threadId) => {
18
15
  await onAddComment(text, 'branch', undefined, undefined, threadId);
19
16
  };
20
- // Show "New branch comment" button when no threads
21
17
  if (!shouldShowCarousel) {
22
18
  return (_jsx(Box, { children: _jsx(Button, { size: "xs", variant: "subtle", color: "gray", onClick: () => setShowCarousel(true), style: { fontSize: '0.75rem', height: '1.5rem' }, children: "New branch comment" }) }));
23
19
  }
@@ -9,15 +9,11 @@ import { ThreadCarousel } from './ThreadCarousel.js';
9
9
  */
10
10
  export const EntryComments = ({ comments, entryPath, currentUserId, canResolve, onAddComment, onResolveThread, autoFocus, highlightThreadId, }) => {
11
11
  const [showCarousel, setShowCarousel] = useState(false);
12
- // Filter for entry-level threads
13
12
  const entryThreads = useMemo(() => comments.filter((t) => t.type === 'entry' && t.entryPath === entryPath), [comments, entryPath]);
14
- // Show carousel if threads exist or if auto-focused
15
13
  const shouldShowCarousel = entryThreads.length > 0 || showCarousel || autoFocus;
16
- // Wrapper to add entry context to comment handler
17
14
  const handleAddComment = async (text, threadId) => {
18
15
  await onAddComment(text, 'entry', entryPath, undefined, threadId);
19
16
  };
20
- // Show "New file comment" button when no threads
21
17
  if (!shouldShowCarousel) {
22
18
  return (_jsx(Box, { children: _jsx(Button, { size: "xs", variant: "subtle", color: "gray", onClick: () => setShowCarousel(true), style: { fontSize: '0.75rem', height: '1.5rem' }, children: "New file comment" }) }));
23
19
  }
@@ -10,9 +10,7 @@ import { ThreadCarousel } from './ThreadCarousel.js';
10
10
  export const FieldWrapper = ({ children, canopyPath, entryPath, threads, autoFocus, currentUserId, canResolve, onAddComment, onResolveThread, highlightThreadId, }) => {
11
11
  const wrapperRef = useRef(null);
12
12
  const [showCarousel, setShowCarousel] = useState(false);
13
- // Show carousel if threads exist or user explicitly requested it
14
13
  const shouldShowCarousel = threads.length > 0 || showCarousel;
15
- // Scroll to field when focused from preview
16
14
  useEffect(() => {
17
15
  if (autoFocus) {
18
16
  wrapperRef.current?.scrollIntoView({
@@ -21,12 +19,10 @@ export const FieldWrapper = ({ children, canopyPath, entryPath, threads, autoFoc
21
19
  });
22
20
  }
23
21
  }, [autoFocus]);
24
- // Wrapper to add field context to comment handler
25
22
  const handleAddComment = async (text, threadId) => {
26
23
  await onAddComment(text, 'field', entryPath, canopyPath, threadId);
27
24
  };
28
25
  return (_jsxs(Box, { ref: wrapperRef, "data-canopy-field": canopyPath, pos: "relative", style: { width: '100%' }, children: [_jsx(Box, { style: {
29
- // Add subtle highlight when auto-focused
30
26
  outline: autoFocus ? '2px solid var(--mantine-color-blue-5)' : undefined,
31
27
  outlineOffset: autoFocus ? 2 : undefined,
32
28
  borderRadius: 4,
@@ -45,10 +45,8 @@ export const InlineCommentThread = ({ thread, onAddReply, onResolve, currentUser
45
45
  const canUserResolve = () => {
46
46
  if (!canResolve)
47
47
  return false;
48
- // Thread author or user with resolver permission
49
48
  return thread.authorId === currentUserId || canResolve;
50
49
  };
51
- // Always show full thread view with per-thread scrolling
52
50
  return (_jsx(Paper, { withBorder: true, p: "md", bg: "white", "data-testid": "inline-comment-thread", style: {
53
51
  width: '100%',
54
52
  overflowY: 'auto',
@@ -2,64 +2,11 @@ import React from 'react';
2
2
  import type { CommentThread } from '../../comment-store.js';
3
3
  import type { UserSearchResult } from '../../auth/types.js';
4
4
  /**
5
- * ThreadCarousel - Horizontal comment thread navigation component
6
- *
7
5
  * ## Purpose
8
6
  * Provides a horizontal carousel UI for navigating between multiple comment threads
9
7
  * on fields, entries, or branches. Supports both keyboard navigation (arrow buttons)
10
8
  * and displays a visual "peekaboo" preview of adjacent threads.
11
9
  *
12
- * ## Key Behaviors
13
- *
14
- * ### Layout & Sizing
15
- * - **Active thread width**: `calc(100% - 72px)` - fills container minus peekaboo space
16
- * - **Single thread width**: `calc(100% - 72px)` - same as active (for consistency)
17
- * - **Inactive thread width**: `400px` - fixed width when not in view
18
- * - **Peekaboo size**: `60px` - sliver of next thread shown on right edge
19
- * - **Gap between threads**: `12px`
20
- *
21
- * ### Peekaboo Preview
22
- * - Shows 60px of the next thread on the right edge (when not viewing last thread)
23
- * - Gradient fade overlay applied to peekaboo area for visual polish
24
- * - **Last thread behavior**: Invisible 60px spacer ensures last thread stays left-aligned
25
- * without showing previous threads on the left
26
- * - Peekaboo helps users discover there are more threads to navigate
27
- *
28
- * ### Thread Sorting
29
- * - **Unresolved threads first** (primary sort)
30
- * - **Newest first within same resolved state** (secondary sort by createdAt)
31
- * - This ensures urgent unresolved feedback appears first
32
- *
33
- * ### Navigation
34
- * - **Arrow buttons**: `← 2/5 →` counter with disabled states at boundaries
35
- * - **Smooth scrolling**: CSS `scroll-behavior: smooth` with programmatic scroll
36
- * - **Auto-scroll**: When `autoFocus` is true, automatically scrolls to first unresolved thread
37
- * - **Mouse scrolling disabled**: `overflowX: 'hidden'` - only button navigation allowed
38
- * - **Scroll calculation**: Accounts for active thread width + gaps when navigating
39
- *
40
- * ### Always-Visible Design
41
- * - Component renders even with 0 threads (shows "No comments yet" message)
42
- * - "New" button always accessible in header
43
- * - Header shows thread count when threads exist: "Comments (3)"
44
- * - Navigation arrows only appear when multiple threads exist
45
- *
46
- * ### Vertical Resizing
47
- * - **Default height**: 400px
48
- * - **Resize range**: 200px (min) to 600px (max)
49
- * - **Resize handle**: Bottom edge with visual feedback on hover/drag
50
- * - User can drag to adjust carousel height for their workflow
51
- *
52
- * ### New Thread Creation
53
- * - "New" button in header toggles inline thread creation box
54
- * - `autoOpenNewThread` prop auto-opens box (used when clicking "New comment" button)
55
- * - New thread box appears above carousel, with textarea and Create/Cancel buttons
56
- *
57
- * ## Usage Context
58
- * Used by:
59
- * - **FieldWrapper**: Inline comments beneath form fields
60
- * - **EntryComments**: Comments at top of entry form
61
- * - **BranchComments**: Comments at top of BranchManager
62
- *
63
10
  * ## Design Decisions
64
11
  *
65
12
  * ### Why calc(100% - 72px)?
@@ -67,11 +14,6 @@ import type { UserSearchResult } from '../../auth/types.js';
67
14
  * - Active thread fills remaining space for maximum readability
68
15
  * - Consistent sizing between single thread and active thread in multi-thread scenarios
69
16
  *
70
- * ### Why invisible spacer at end?
71
- * - Without spacer: last thread would align to right edge, showing previous threads on left
72
- * - With spacer: last thread stays left-aligned with blank space on right (matching peekaboo size)
73
- * - Creates consistent left-alignment across all thread positions
74
- *
75
17
  * ### Why disable mouse scrolling?
76
18
  * - Prevents accidental scroll-wheel navigation that could be jarring
77
19
  * - Forces deliberate button-based navigation for better UX
@@ -16,28 +16,23 @@ export const ThreadCarousel = ({ threads, label = 'Comments', contextType: _, cu
16
16
  const scrollContainerRef = useRef(null);
17
17
  const resizeStartY = useRef(0);
18
18
  const resizeStartHeight = useRef(0);
19
- // Sort: unresolved first, then resolved
20
19
  const sortedThreads = useMemo(() => [...threads].sort((a, b) => {
21
20
  if (a.resolved === b.resolved) {
22
- // Same resolved state: sort by createdAt (newest first)
23
21
  return new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime();
24
22
  }
25
23
  return a.resolved ? 1 : -1;
26
24
  }), [threads]);
27
- // Auto-scroll to first unresolved thread when autoFocus is true
28
25
  useEffect(() => {
29
26
  if (autoFocus && sortedThreads.length > 0) {
30
27
  const firstUnresolved = sortedThreads.find((t) => !t.resolved);
31
28
  if (firstUnresolved) {
32
29
  const index = sortedThreads.findIndex((t) => t.id === firstUnresolved.id);
33
30
  setCurrentIndex(index);
34
- // Scroll to thread
35
31
  scrollToIndex(index);
36
32
  }
37
33
  }
38
34
  // eslint-disable-next-line react-hooks/exhaustive-deps -- scrollToIndex is stable
39
35
  }, [autoFocus, sortedThreads]);
40
- // Scroll to and highlight specific thread when highlightThreadId changes
41
36
  useEffect(() => {
42
37
  if (highlightThreadId && sortedThreads.length > 0) {
43
38
  const threadIndex = sortedThreads.findIndex((t) => t.id === highlightThreadId);
@@ -45,7 +40,6 @@ export const ThreadCarousel = ({ threads, label = 'Comments', contextType: _, cu
45
40
  setCurrentIndex(threadIndex);
46
41
  scrollToIndex(threadIndex);
47
42
  setHighlightedThreadId(highlightThreadId);
48
- // Clear highlight after 2 seconds
49
43
  const timer = window.setTimeout(() => {
50
44
  setHighlightedThreadId(undefined);
51
45
  }, 2000);
@@ -54,13 +48,11 @@ export const ThreadCarousel = ({ threads, label = 'Comments', contextType: _, cu
54
48
  }
55
49
  // eslint-disable-next-line react-hooks/exhaustive-deps -- scrollToIndex is stable
56
50
  }, [highlightThreadId, sortedThreads]);
57
- // Auto-open new thread box when autoOpenNewThread is true
58
51
  useEffect(() => {
59
52
  if (autoOpenNewThread) {
60
53
  setShowNewThreadBox(true);
61
54
  }
62
55
  }, [autoOpenNewThread]);
63
- // Handle resize dragging
64
56
  useEffect(() => {
65
57
  const handleMouseMove = (e) => {
66
58
  if (!isResizing)
@@ -90,8 +82,6 @@ export const ThreadCarousel = ({ threads, label = 'Comments', contextType: _, cu
90
82
  const scrollToIndex = (index) => {
91
83
  if (scrollContainerRef.current) {
92
84
  const container = scrollContainerRef.current;
93
- // Calculate scroll position based on container width
94
- // Active thread takes most space, others are 400px + gap
95
85
  let scrollLeft = 0;
96
86
  for (let i = 0; i < index; i++) {
97
87
  if (sortedThreads.length > 1) {
@@ -175,12 +165,10 @@ export const ThreadCarousel = ({ threads, label = 'Comments', contextType: _, cu
175
165
  return (_jsx("div", { style: {
176
166
  scrollSnapAlign: 'start',
177
167
  flexShrink: 0,
178
- // Active thread or single thread stretches to fill space minus peekaboo
179
168
  width: isActive || sortedThreads.length === 1
180
169
  ? 'calc(100% - 72px)' // Active or single: leave room for peekaboo/spacing
181
170
  : 400, // Non-active: fixed width
182
171
  maxWidth: isActive || sortedThreads.length === 1 ? 'calc(100% - 72px)' : 400,
183
- // Add highlight animation
184
172
  outline: isHighlighted ? '3px solid var(--mantine-color-blue-5)' : undefined,
185
173
  outlineOffset: isHighlighted ? 2 : undefined,
186
174
  borderRadius: isHighlighted ? 8 : undefined,
@@ -1,6 +1,3 @@
1
- /**
2
- * Props for the EditorFooter component.
3
- */
4
1
  export interface EditorFooterProps {
5
2
  /**
6
3
  * Optional custom footer content.
@@ -10,10 +7,5 @@ export interface EditorFooterProps {
10
7
  /**
11
8
  * Footer component for the Editor.
12
9
  * Displays static links (Terms, Privacy) and copyright notice.
13
- *
14
- * @example
15
- * ```tsx
16
- * <EditorFooter />
17
- * ```
18
10
  */
19
11
  export declare function EditorFooter({ children }: EditorFooterProps): import("react/jsx-runtime").JSX.Element;
@@ -3,11 +3,6 @@ import { Group, Paper, Text } from '@mantine/core';
3
3
  /**
4
4
  * Footer component for the Editor.
5
5
  * Displays static links (Terms, Privacy) and copyright notice.
6
- *
7
- * @example
8
- * ```tsx
9
- * <EditorFooter />
10
- * ```
11
6
  */
12
7
  export function EditorFooter({ children }) {
13
8
  return (_jsx(Paper, { withBorder: true, radius: 0, shadow: "sm", px: "md", py: "xs", style: { position: 'fixed', left: 0, right: 0, bottom: 0, zIndex: 40 }, children: children ?? (_jsxs(Group, { gap: "md", justify: "center", children: [_jsx(Text, { size: "xs", c: "dimmed", children: "Terms" }), _jsx(Text, { size: "xs", c: "dimmed", children: "Privacy" }), _jsx(Text, { size: "xs", c: "dimmed", children: "\u00A9 CanopyCMS" })] })) }));