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,9 +1,11 @@
1
1
  'use client';
2
- import { jsx as _jsx } from "react/jsx-runtime";
3
- import { useCallback, useEffect, useMemo, useState } from 'react';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
4
4
  import { useSWRConfig } from 'swr';
5
- import { Text } from '@mantine/core';
6
- import { modals } from '@mantine/modals';
5
+ import { Alert, Stack, Text } from '@mantine/core';
6
+ import { IconAlertTriangle } from '@tabler/icons-react';
7
+ import { openConfirm } from '../utils/confirm-modal.js';
8
+ import { EDITOR_ACTIONS } from '../copy.js';
7
9
  import { notifications } from '@mantine/notifications';
8
10
  import { useApiClient } from '../context/index.js';
9
11
  import { BRANCHES_KEY, fetchBranches, useBranchesData } from './useBranchesData.js';
@@ -12,6 +14,18 @@ import { BRANCHES_KEY, fetchBranches, useBranchesData } from './useBranchesData.
12
14
  // which breaks adopters' production `next build` of the editor bundle.
13
15
  // paths/branch-name.ts is the dependency-free home of sanitizeBranchName.
14
16
  import { sanitizeBranchName } from '../../paths/branch-name.js';
17
+ import { RetryToastMessage } from '../components/RetryToastMessage.js';
18
+ import { isNonApiResponse } from '../../api/client.js';
19
+ import { isGatewayFailure, isUnanswered, UnansweredError } from '../unanswered.js';
20
+ import { describeUnsaved } from './useBranchActions.js';
21
+ const BRANCHES_LOAD_FAILED_TOAST_ID = 'canopy-branches-load-failed';
22
+ const BRANCH_OUTCOME_UNKNOWN_TOAST_ID = 'canopy-branch-outcome-unknown';
23
+ /** The error a refused branch action throws; a gateway's 5xx says nothing of whether it ran. */
24
+ function actionError(result, fallback) {
25
+ return isGatewayFailure(result) ? new UnansweredError() : new Error(result.error || fallback);
26
+ }
27
+ /** @internal Exported for tests. */
28
+ export const branchActionOutcomeUnknown = (action) => `${action} may have gone through even though the server didn't answer. Check the branch's status before trying again.`;
15
29
  /**
16
30
  * Wire a confirm modal's exits so exactly ONE of {confirmed, dismissed} runs,
17
31
  * whichever happens first.
@@ -24,6 +38,9 @@ import { sanitizeBranchName } from '../../paths/branch-name.js';
24
38
  * handler would settle the caller's promise before the confirmed work had
25
39
  * even run. Claiming the first exit resolves both.
26
40
  */
41
+ const submittedMessage = (stampedCount) => stampedCount === 0
42
+ ? 'Branch submitted for review'
43
+ : `Branch submitted for review. Dates set on ${stampedCount} new ${stampedCount === 1 ? 'entry' : 'entries'}.`;
27
44
  const confirmModalHandlers = (onConfirm, onDismiss) => {
28
45
  let settled = false;
29
46
  const claim = () => {
@@ -49,40 +66,84 @@ const confirmModalHandlers = (onConfirm, onDismiss) => {
49
66
  },
50
67
  };
51
68
  };
52
- const showSubmitConfirmation = (branchName, onConfirm, onDismiss) => {
53
- modals.openConfirmModal({
54
- title: 'Submit Branch for Review',
55
- children: (_jsx(Text, { size: "sm", style: { whiteSpace: 'pre-line' }, children: `Are you sure you want to submit "${branchName}" for review?\n\nThis will:\n• Create a pull request for review\n• Change the branch status to "submitted"\n• Notify reviewers of pending changes` })),
56
- labels: { confirm: 'Submit Branch', cancel: 'Cancel' },
57
- confirmProps: { color: 'brand' },
69
+ /**
70
+ * With `unsaved` edits on the branch, the dialog says they are left out: a submit sends only what
71
+ * is saved, and the branch is read-only in review, so they wait as drafts until it is editable.
72
+ */
73
+ const showSubmitConfirmation = (branchName, unsaved, onConfirm, onDismiss) => {
74
+ const leftOut = unsaved && unsaved.count > 0 ? unsaved : undefined;
75
+ openConfirm({
76
+ title: EDITOR_ACTIONS.submitForReview,
77
+ children: (_jsxs(Stack, { gap: "sm", children: [leftOut && (_jsxs(Alert, { color: "yellow", icon: _jsx(IconAlertTriangle, { size: 16 }), title: `${leftOut.count} ${leftOut.count === 1 ? "entry has unsaved changes that won't" : "entries have unsaved changes that won't"} be submitted`, "data-testid": "submit-unsaved-warning", children: [describeUnsaved(leftOut), " To include them, cancel and save first. If you submit now, those changes stay unsaved on this branch while it is in review."] })), _jsx(Text, { size: "sm", style: { whiteSpace: 'pre-line' }, children: `Submit "${branchName}" for review?\n\nThis will:\n• Create a pull request for review\n• Change the branch status to "In review"\n• Notify reviewers of pending changes` })] })),
78
+ labels: {
79
+ confirm: leftOut ? 'Submit anyway' : EDITOR_ACTIONS.submitForReview,
80
+ cancel: 'Cancel',
81
+ },
82
+ confirmProps: { color: 'brand', 'data-testid': 'confirm-submit-branch' },
58
83
  ...confirmModalHandlers(onConfirm, onDismiss),
59
84
  });
60
85
  };
61
- const showWithdrawConfirmation = (branchName, onConfirm, onDismiss) => {
62
- modals.openConfirmModal({
63
- title: 'Withdraw Branch from Review',
64
- children: (_jsx(Text, { size: "sm", style: { whiteSpace: 'pre-line' }, children: `Are you sure you want to withdraw "${branchName}" from review?\n\nThis will:\n• Convert the pull request to a draft\n• Change the branch status back to "editing"\n• Remove from review queue` })),
65
- labels: { confirm: 'Withdraw Branch', cancel: 'Cancel' },
66
- confirmProps: { color: 'orange' },
86
+ /**
87
+ * The withdraw dialog's pull-request bullet, matching what api/branch-withdraw.ts does: an open
88
+ * PR becomes a draft; a closed one is left alone and its number dropped from the branch, so a
89
+ * resubmit opens a new one. A merged PR archives its branch, which withdraw refuses, so that
90
+ * bullet is only seen on a listing that lags.
91
+ * @internal Exported for tests.
92
+ */
93
+ export function withdrawPullRequestBullet(pr) {
94
+ if (!pr.pullRequestNumber)
95
+ return undefined;
96
+ const label = `pull request #${pr.pullRequestNumber}`;
97
+ if (pr.pullRequestState === 'closed') {
98
+ return `Leave the closed ${label} as it is; submitting again opens a new one`;
99
+ }
100
+ if (pr.pullRequestState === 'merged')
101
+ return `Leave the merged ${label} as it is`;
102
+ return `Convert ${label} to a draft`;
103
+ }
104
+ const showWithdrawConfirmation = (branchName, pr, onConfirm, onDismiss) => {
105
+ const bullets = [
106
+ withdrawPullRequestBullet(pr),
107
+ 'Change the branch status back to "editing"',
108
+ 'Remove from review queue',
109
+ ].filter((b) => b !== undefined);
110
+ openConfirm({
111
+ title: EDITOR_ACTIONS.withdrawFromReview,
112
+ children: (_jsx(Text, { size: "sm", style: { whiteSpace: 'pre-line' }, children: `Withdraw "${branchName}" from review?\n\nThis will:\n${bullets.map((b) => `• ${b}`).join('\n')}` })),
113
+ labels: { confirm: EDITOR_ACTIONS.withdrawFromReview, cancel: 'Cancel' },
114
+ confirmProps: { color: 'orange', 'data-testid': 'confirm-withdraw-branch' },
115
+ ...confirmModalHandlers(onConfirm, onDismiss),
116
+ });
117
+ };
118
+ const showRequestChangesConfirmation = (branchName, onConfirm, onDismiss) => {
119
+ openConfirm({
120
+ title: EDITOR_ACTIONS.requestChanges,
121
+ children: (_jsx(Text, { size: "sm", style: { whiteSpace: 'pre-line' }, children: `Send "${branchName}" back to its editor?\n\nThis will:\n• Change the branch status back to "editing"\n• Let its editor change it and submit again` })),
122
+ labels: { confirm: EDITOR_ACTIONS.requestChanges, cancel: 'Cancel' },
123
+ confirmProps: { color: 'red', 'data-testid': 'confirm-request-changes' },
67
124
  ...confirmModalHandlers(onConfirm, onDismiss),
68
125
  });
69
126
  };
70
127
  /**
71
- * Unlike submit/withdraw (both reversible), delete is irreversible -- it
72
- * unlinks branch.json, removes the clone, and removes the branch head from
73
- * the local git mirror. Before this, `handleDelete` below called
74
- * `apiClient.branches.delete` immediately on click with no confirmation
75
- * anywhere in the chain (BranchManager.tsx's delete button calls `onDelete`
76
- * directly), while submit and withdraw -- both reversible -- each went
77
- * through this same `modals.openConfirmModal` pattern. This closes that gap;
78
- * `color: 'red'` marks it as the destructive one, matching the button's own
79
- * color in BranchManager.tsx.
128
+ * Delete is the one irreversible branch action: it unlinks branch.json and removes the clone,
129
+ * the local mirror's head and, for a branch with a PR, the GitHub branch. BranchManager.tsx's
130
+ * delete menu item calls `onDelete` directly, so this dialog is the only confirmation; `color:
131
+ * 'red'` matches that item.
80
132
  */
81
- const showDeleteConfirmation = (branchName, onConfirm, onDismiss) => {
82
- modals.openConfirmModal({
83
- title: 'Delete Branch',
84
- children: (_jsx(Text, { size: "sm", style: { whiteSpace: 'pre-line' }, children: `Are you sure you want to delete "${branchName}"?\n\nThis will:\n• Permanently remove the branch and its clone\n• Discard any unsaved or unmerged changes\n\nThis cannot be undone.` })),
85
- labels: { confirm: 'Delete Branch', cancel: 'Cancel' },
133
+ const showDeleteConfirmation = (branchName, pr, onConfirm, onDismiss) => {
134
+ // The server deletes the GitHub branch only for a branch with a PR (api/github-sync.ts's
135
+ // syncDeleteRemoteBranch); GitHub closes a PR whose branch is deleted.
136
+ const bullets = [
137
+ 'Permanently remove the branch and its clone',
138
+ pr.pullRequestNumber
139
+ ? `Delete its branch on GitHub too, which closes pull request #${pr.pullRequestNumber} if it is still open`
140
+ : undefined,
141
+ 'Discard any unsaved or unmerged changes',
142
+ ].filter((b) => b !== undefined);
143
+ openConfirm({
144
+ title: 'Delete branch',
145
+ children: (_jsx(Text, { size: "sm", style: { whiteSpace: 'pre-line' }, children: `Delete "${branchName}"?\n\nThis will:\n${bullets.map((b) => `• ${b}`).join('\n')}\n\nThis cannot be undone.` })),
146
+ labels: { confirm: 'Delete branch', cancel: 'Cancel' },
86
147
  // data-testid is load-bearing, not decoration: the e2e helper
87
148
  // (apps/test-app/e2e/fixtures/branch-page.ts's deleteBranch) already
88
149
  // anticipated a delete confirmation and clicks
@@ -137,7 +198,7 @@ function reconcilePendingBranches(pending, listed, receivedAt) {
137
198
  */
138
199
  export function useBranchManager(options) {
139
200
  const apiClient = useApiClient();
140
- const { mutate: globalMutate } = useSWRConfig();
201
+ const { cache, mutate: globalMutate } = useSWRConfig();
141
202
  const [branchName, setBranchName] = useState(options.initialBranch);
142
203
  const { data: branchesData, error: branchesError, isValidating: branchesIsValidating, } = useBranchesData(apiClient);
143
204
  const [pendingBranches, setPendingBranches] = useState([]);
@@ -148,15 +209,25 @@ export function useBranchManager(options) {
148
209
  { branch, addedAt: Date.now() },
149
210
  ]);
150
211
  }, []);
151
- // After a server-side change to a pending branch, its overlaid copy is stale,
152
- // and a lagging listing would show it (a submitted branch as still writable).
153
- // Forgetting it leaves the listings alone to decide, failing closed if they lag.
154
212
  const forgetCreatedBranch = (name) => {
155
213
  setPendingBranches((prev) => {
156
214
  const kept = prev.filter((p) => p.branch.name !== name);
157
215
  return kept.length === prev.length ? prev : kept;
158
216
  });
159
217
  };
218
+ // After a workflow action, a pending branch's overlaid copy is stale. The
219
+ // server's returned copy replaces it, so a listing that still lacks the
220
+ // branch shows its new status instead of hiding it; without one, forgetting
221
+ // it fails closed rather than showing the pre-action state.
222
+ const updateCreatedBranch = (name, branch) => {
223
+ if (!branch) {
224
+ forgetCreatedBranch(name);
225
+ return;
226
+ }
227
+ setPendingBranches((prev) => prev.some((p) => p.branch.name === name)
228
+ ? prev.map((p) => (p.branch.name === name ? { ...p, branch } : p))
229
+ : prev);
230
+ };
160
231
  // Each received listing is a new `branchesData` (see `BranchesData.receivedAt`);
161
232
  // the grace window is measured to its arrival.
162
233
  useEffect(() => {
@@ -170,25 +241,33 @@ export function useBranchManager(options) {
170
241
  setBranchName(branchesData.defaultBranch);
171
242
  }
172
243
  }, [branchesData, branchName]);
244
+ // One toast, replaced on each failure (a shown id ignores `show`); sticky because the editor
245
+ // cannot function without the branch list.
246
+ const showBranchesLoadFailed = (message) => {
247
+ notifications.hide(BRANCHES_LOAD_FAILED_TOAST_ID);
248
+ notifications.show({
249
+ id: BRANCHES_LOAD_FAILED_TOAST_ID,
250
+ title: 'Could not load branches',
251
+ message: (_jsx(RetryToastMessage, { message: message, onRetry: () => {
252
+ notifications.hide(BRANCHES_LOAD_FAILED_TOAST_ID);
253
+ void reloadBranches();
254
+ } })),
255
+ color: 'red',
256
+ autoClose: false,
257
+ });
258
+ };
173
259
  // Surfaces/clears the sticky error toast the same way loadBranches() does.
174
260
  useEffect(() => {
175
261
  if (branchesError) {
176
262
  console.error(branchesError);
177
- const message = branchesError instanceof Error ? branchesError.message : 'Failed to load branches';
178
- // Fixed id: retries update the existing toast instead of stacking; sticky
179
- // because the editor cannot function without the branch list.
180
- notifications.show({
181
- id: 'canopy-branches-load-failed',
182
- message,
183
- color: 'red',
184
- autoClose: false,
185
- });
263
+ showBranchesLoadFailed(branchesError instanceof Error ? branchesError.message : 'Failed to load branches');
186
264
  }
187
265
  else if (branchesData) {
188
266
  // A previous failure may have left the sticky error toast up; clear it
189
267
  // now that loading succeeded (provisioning failures are often transient).
190
- notifications.hide('canopy-branches-load-failed');
268
+ notifications.hide(BRANCHES_LOAD_FAILED_TOAST_ID);
191
269
  }
270
+ // eslint-disable-next-line react-hooks/exhaustive-deps -- showBranchesLoadFailed is new every render and reads nothing that changes
192
271
  }, [branchesData, branchesError]);
193
272
  // Mirrors the automatic load's in-flight state onto the shared busy flag,
194
273
  // matching loadBranches()'s own setBusy bracket below for explicit reloads.
@@ -214,6 +293,9 @@ export function useBranchManager(options) {
214
293
  access: {
215
294
  users: b.access.allowedUsers,
216
295
  groups: b.access.allowedGroups,
296
+ managerOrAdminAllowed: b.access.managerOrAdminAllowed,
297
+ withheldUsers: b.withheldAccess?.users,
298
+ withheldGroups: b.withheldAccess?.groups,
217
299
  },
218
300
  pullRequestUrl: b.pullRequestUrl,
219
301
  pullRequestNumber: b.pullRequestNumber,
@@ -253,136 +335,250 @@ export function useBranchManager(options) {
253
335
  // bound hook (and anything else reading BRANCHES_KEY) picks it up.
254
336
  // Side effects (default-branch adoption, error/success notifications) are
255
337
  // driven by the effects above, which react to that same cache write.
256
- const loadBranches = async () => {
338
+ // Resolves to the fresh listing, or undefined when the fetch failed.
339
+ const reloadBranches = async () => {
257
340
  options.setBusy(true);
258
341
  try {
259
- const fresh = await fetchBranches(apiClient);
342
+ const fresh = await fetchBranches(apiClient, { cache, mutate: globalMutate });
260
343
  await globalMutate(BRANCHES_KEY, fresh, { revalidate: false });
344
+ return fresh;
261
345
  }
262
346
  catch (err) {
263
347
  console.error(err);
264
- const message = err instanceof Error ? err.message : 'Failed to load branches';
265
- notifications.show({
266
- id: 'canopy-branches-load-failed',
267
- message,
268
- color: 'red',
269
- autoClose: false,
270
- });
348
+ showBranchesLoadFailed(err instanceof Error ? err.message : 'Failed to load branches');
349
+ return undefined;
271
350
  }
272
351
  finally {
273
352
  options.setBusy(false);
274
353
  }
275
354
  };
276
- const handleSubmit = async (branchNameToSubmit) => {
277
- return new Promise((resolve, reject) => {
278
- showSubmitConfirmation(branchNameToSubmit, async () => {
355
+ const loadBranches = async () => {
356
+ await reloadBranches();
357
+ };
358
+ // The confirm dialogs' handlers run long after the render that opened them, so they read
359
+ // these at confirm time rather than from that render's closure.
360
+ const latest = useRef({ branchName, branches, branchesData });
361
+ latest.current = { branchName, branches, branchesData };
362
+ // A legacy deep link can carry the raw form of a listed name; the actions key, look up and
363
+ // send the listed name, resolved the way `currentBranch` resolves it.
364
+ const listedName = (name) => latest.current.branches.some((b) => b.name === name) ? name : sanitizeBranchName(name);
365
+ const pullRequestOf = (name) => {
366
+ const b = latest.current.branches.find((x) => x.name === name);
367
+ return { pullRequestNumber: b?.pullRequestNumber, pullRequestState: b?.pullRequestState };
368
+ };
369
+ // Keys of the actions whose confirm is open or whose work is in flight.
370
+ const actionsInFlight = useRef(new Set());
371
+ /**
372
+ * Runs `run` unless the same action on the same branch already has its confirm open or its
373
+ * work in flight; then this request opens nothing and resolves at once. Mantine closes only
374
+ * the confirm that was confirmed, so a second one opened by a repeated click would stay up
375
+ * after the action succeeded.
376
+ */
377
+ const singleFlight = async (action, branch, run) => {
378
+ const key = JSON.stringify([action, branch]);
379
+ if (actionsInFlight.current.has(key))
380
+ return;
381
+ actionsInFlight.current.add(key);
382
+ try {
383
+ await run();
384
+ }
385
+ finally {
386
+ actionsInFlight.current.delete(key);
387
+ }
388
+ };
389
+ // A workflow action that got no answer may have gone through, so the list reloads to show
390
+ // where the branch stands rather than inviting a blind retry.
391
+ const showOutcomeUnknown = (action) => {
392
+ notifications.hide(BRANCH_OUTCOME_UNKNOWN_TOAST_ID);
393
+ notifications.show({
394
+ id: BRANCH_OUTCOME_UNKNOWN_TOAST_ID,
395
+ message: branchActionOutcomeUnknown(action),
396
+ color: 'yellow',
397
+ autoClose: false,
398
+ withCloseButton: true,
399
+ });
400
+ };
401
+ const reportActionFailure = async (err, fallback, action) => {
402
+ if (isUnanswered(err)) {
403
+ showOutcomeUnknown(action);
404
+ await loadBranches();
405
+ return;
406
+ }
407
+ notifications.show({ message: err instanceof Error ? err.message : fallback, color: 'red' });
408
+ };
409
+ const handleSubmit = (requested) => {
410
+ const branchNameToSubmit = listedName(requested);
411
+ return singleFlight('submit', branchNameToSubmit, async () => {
412
+ // Drafts belong to the open branch, so only its submit can leave edits behind. Without
413
+ // that check the dialog opens in this same tick.
414
+ const checking = branchNameToSubmit === listedName(latest.current.branchName)
415
+ ? options.getUnsaved?.().catch((err) => {
416
+ console.error(err);
417
+ return undefined;
418
+ })
419
+ : undefined;
420
+ // Checking can wait on draft verification, so the busy state shows it is working meanwhile.
421
+ if (checking)
279
422
  options.setBusy(true);
280
- try {
281
- const result = await apiClient.workflow.submit({
282
- branch: branchNameToSubmit,
283
- });
284
- if (!result.ok) {
285
- throw new Error(result.error || 'Failed to submit branch');
423
+ const unsaved = checking && (await checking.finally(() => options.setBusy(false)));
424
+ return new Promise((resolve, reject) => {
425
+ showSubmitConfirmation(branchNameToSubmit, unsaved, async () => {
426
+ // A listing that arrived while the dialog was open may show the branch already
427
+ // submitted; submitting it again would only fail.
428
+ const listed = latest.current.branches.find((b) => b.name === branchNameToSubmit);
429
+ if (listed?.status === 'submitted') {
430
+ notifications.show({
431
+ message: 'Branch is already submitted for review',
432
+ color: 'blue',
433
+ });
434
+ resolve();
435
+ return;
286
436
  }
287
- notifications.show({
288
- message: 'Branch submitted for review',
289
- color: 'green',
290
- });
291
- forgetCreatedBranch(branchNameToSubmit);
292
- await loadBranches();
293
- resolve();
294
- }
295
- catch (err) {
296
- const message = err instanceof Error ? err.message : 'Failed to submit branch';
297
- notifications.show({ message, color: 'red' });
298
- reject(err);
299
- }
300
- finally {
301
- options.setBusy(false);
302
- }
303
- },
304
- // Dismissal is not an error. Rejecting here meant clicking Cancel
305
- // logged a console error at the only call site (Editor.tsx catches
306
- // these into console.error), which is both noise and a trip hazard
307
- // for the CI=1 stray-console gate. Settled once, by onCancel OR
308
- // onClose, so an Escape/overlay dismissal can't leave this pending.
309
- () => resolve());
437
+ options.setBusy(true);
438
+ try {
439
+ const result = await apiClient.workflow.submit({
440
+ branch: branchNameToSubmit,
441
+ });
442
+ if (!result.ok)
443
+ throw actionError(result, 'Failed to submit branch');
444
+ const stamped = result.data?.stamped;
445
+ if (stamped && branchNameToSubmit === listedName(latest.current.branchName)) {
446
+ options.onStamped?.(stamped);
447
+ }
448
+ notifications.show({
449
+ message: submittedMessage(stamped?.length ?? 0),
450
+ color: 'green',
451
+ });
452
+ updateCreatedBranch(branchNameToSubmit, result.data?.branch);
453
+ await loadBranches();
454
+ resolve();
455
+ }
456
+ catch (err) {
457
+ await reportActionFailure(err, 'Failed to submit branch', EDITOR_ACTIONS.submitForReview);
458
+ reject(err);
459
+ }
460
+ finally {
461
+ options.setBusy(false);
462
+ }
463
+ },
464
+ // Dismissal is not an error: the call sites log rejections to the console. Settled
465
+ // once, by onCancel OR onClose, so an Escape/overlay dismissal can't leave this pending.
466
+ () => resolve());
467
+ });
310
468
  });
311
469
  };
312
- const handleWithdraw = async (branchNameToWithdraw) => {
313
- return new Promise((resolve, reject) => {
314
- showWithdrawConfirmation(branchNameToWithdraw, async () => {
470
+ const handleWithdraw = (requested) => {
471
+ const branchNameToWithdraw = listedName(requested);
472
+ return singleFlight('withdraw', branchNameToWithdraw, () => new Promise((resolve, reject) => {
473
+ showWithdrawConfirmation(branchNameToWithdraw, pullRequestOf(branchNameToWithdraw), async () => {
315
474
  options.setBusy(true);
316
475
  try {
317
476
  const result = await apiClient.workflow.withdraw({
318
477
  branch: branchNameToWithdraw,
319
478
  });
320
- if (!result.ok) {
321
- throw new Error(result.error || 'Failed to withdraw branch');
322
- }
479
+ if (!result.ok)
480
+ throw actionError(result, 'Failed to withdraw branch');
323
481
  notifications.show({ message: 'Branch withdrawn', color: 'blue' });
324
- forgetCreatedBranch(branchNameToWithdraw);
482
+ updateCreatedBranch(branchNameToWithdraw, result.data?.branch);
325
483
  await loadBranches();
326
484
  resolve();
327
485
  }
328
486
  catch (err) {
329
- const message = err instanceof Error ? err.message : 'Failed to withdraw branch';
330
- notifications.show({ message, color: 'red' });
487
+ await reportActionFailure(err, 'Failed to withdraw branch', EDITOR_ACTIONS.withdrawFromReview);
331
488
  reject(err);
332
489
  }
333
490
  finally {
334
491
  options.setBusy(false);
335
492
  }
336
493
  }, () => resolve());
337
- });
494
+ }));
338
495
  };
339
- const handleRequestChanges = async (branchNameForChanges) => {
340
- options.setBusy(true);
341
- try {
342
- const result = await apiClient.workflow.requestChanges({ branch: branchNameForChanges });
343
- if (!result.ok) {
344
- throw new Error(result.error || 'Failed to request changes');
496
+ const handleRequestChanges = (branchNameForChanges) => singleFlight('request-changes', branchNameForChanges, () => new Promise((resolve) => {
497
+ showRequestChangesConfirmation(branchNameForChanges, async () => {
498
+ options.setBusy(true);
499
+ try {
500
+ const result = await apiClient.workflow.requestChanges({
501
+ branch: branchNameForChanges,
502
+ });
503
+ if (!result.ok)
504
+ throw actionError(result, 'Failed to request changes');
505
+ notifications.show({ message: 'Changes requested', color: 'blue' });
506
+ updateCreatedBranch(branchNameForChanges, result.data?.branch);
507
+ await loadBranches();
345
508
  }
346
- notifications.show({ message: 'Changes requested', color: 'orange' });
347
- forgetCreatedBranch(branchNameForChanges);
348
- await loadBranches();
349
- }
350
- catch (err) {
351
- const message = err instanceof Error ? err.message : 'Failed to request changes';
352
- notifications.show({ message, color: 'red' });
353
- }
354
- finally {
355
- options.setBusy(false);
509
+ catch (err) {
510
+ await reportActionFailure(err, 'Failed to request changes', EDITOR_ACTIONS.requestChanges);
511
+ }
512
+ finally {
513
+ options.setBusy(false);
514
+ resolve();
515
+ }
516
+ }, () => resolve());
517
+ }));
518
+ /** After a delete: drop it from the created-branch overlay, and leave it if it is open. */
519
+ const leaveDeletedBranch = (deleted, fresh) => {
520
+ forgetCreatedBranch(deleted);
521
+ // Matched the way `currentBranch` resolves a name, since the deleted branch is no longer
522
+ // listed to resolve against.
523
+ const open = latest.current.branchName;
524
+ if (open === deleted || sanitizeBranchName(open) === deleted) {
525
+ // Nothing is left to keep, so no unsaved-changes prompt. With no default known yet, the
526
+ // empty name lets the default-branch adoption effect pick it.
527
+ setBranchName(fresh?.defaultBranch ?? latest.current.branchesData?.defaultBranch ?? '');
356
528
  }
357
529
  };
358
- const handleDelete = async (branchNameToDelete) => {
359
- return new Promise((resolve) => {
360
- showDeleteConfirmation(branchNameToDelete, async () => {
530
+ const handleDelete = (requested) => {
531
+ const branchNameToDelete = listedName(requested);
532
+ return singleFlight('delete', branchNameToDelete, () => new Promise((resolve) => {
533
+ showDeleteConfirmation(branchNameToDelete, pullRequestOf(branchNameToDelete), async () => {
361
534
  options.setBusy(true);
362
535
  try {
363
536
  const result = await apiClient.branches.delete({
364
537
  branch: branchNameToDelete,
365
538
  });
366
- if (!result.ok) {
367
- throw new Error(result.error || 'Failed to delete branch');
539
+ // The API's own 404: the branch is already gone, by an earlier unanswered delete or
540
+ // another user's.
541
+ if (!result.ok && result.status === 404 && !isNonApiResponse(result)) {
542
+ notifications.show({ message: 'Branch deleted', color: 'green' });
543
+ leaveDeletedBranch(branchNameToDelete, await reloadBranches());
544
+ return;
368
545
  }
369
- notifications.show({ message: 'Branch deleted', color: 'green' });
370
- forgetCreatedBranch(branchNameToDelete);
371
- await loadBranches();
546
+ if (!result.ok)
547
+ throw actionError(result, 'Failed to delete branch');
548
+ const cleanupWarning = result.data?.cleanupWarning;
549
+ notifications.show(cleanupWarning
550
+ ? {
551
+ message: `Branch deleted, with a warning: ${cleanupWarning}`,
552
+ color: 'yellow',
553
+ autoClose: false,
554
+ }
555
+ : { message: 'Branch deleted', color: 'green' });
556
+ leaveDeletedBranch(branchNameToDelete, await reloadBranches());
372
557
  }
373
558
  catch (err) {
374
- const message = err instanceof Error ? err.message : 'Failed to delete branch';
375
- notifications.show({ message, color: 'red' });
559
+ if (!isUnanswered(err)) {
560
+ await reportActionFailure(err, 'Failed to delete branch', 'Delete branch');
561
+ return;
562
+ }
563
+ // Settled by the list: unlisted, and not created too recently to be listed, means deleted.
564
+ const fresh = await reloadBranches();
565
+ const recent = pendingBranches.filter((p) => Date.now() - p.addedAt <= CREATED_BRANCH_GRACE_MS);
566
+ const listed = mergePendingBranches(fresh?.branches ?? [], recent).some((b) => sanitizeBranchName(b.name) === sanitizeBranchName(branchNameToDelete));
567
+ if (fresh && !listed) {
568
+ notifications.show({ message: 'Branch deleted', color: 'green' });
569
+ leaveDeletedBranch(branchNameToDelete, fresh);
570
+ }
571
+ else {
572
+ showOutcomeUnknown('Delete branch');
573
+ }
376
574
  }
377
575
  finally {
378
576
  options.setBusy(false);
379
- // Resolve regardless of API success/failure, matching the
380
- // pre-confirmation behavior (errors were shown as a toast, not
381
- // rethrown). Cancellation resolves too -- see below.
577
+ // Failures surface as the toast above, never as a rejection; cancelling resolves too.
382
578
  resolve();
383
579
  }
384
580
  }, () => resolve());
385
- });
581
+ }));
386
582
  };
387
583
  const handleReloadBranchData = async () => {
388
584
  await loadBranches();
@@ -11,7 +11,7 @@
11
11
  * })` -- see the comment on `fetchBranches` for why it doesn't just call
12
12
  * `mutate()` to revalidate.
13
13
  */
14
- import { type SWRResponse } from 'swr';
14
+ import { type Cache, type ScopedMutator, type SWRResponse } from 'swr';
15
15
  import type { ApiClient } from '../context/index.js';
16
16
  import type { BranchListItem } from '../../api/branch.js';
17
17
  /** Cache key for the branches list. */
@@ -27,10 +27,14 @@ export interface BranchesData {
27
27
  receivedAt: number;
28
28
  }
29
29
  /**
30
- * Fetch the branches list. Exported for direct testing and for the
31
- * imperative reload path.
30
+ * Fetch the branches list, writing the owners it names into the user-metadata cache before the
31
+ * listing lands, so owner badges render named. Exported for direct testing and for the imperative
32
+ * reload path.
32
33
  */
33
- export declare function fetchBranches(apiClient: Pick<ApiClient, 'branches'>): Promise<BranchesData>;
34
+ export declare function fetchBranches(apiClient: Pick<ApiClient, 'branches'>, swr: {
35
+ cache: Cache;
36
+ mutate: ScopedMutator;
37
+ }): Promise<BranchesData>;
34
38
  /**
35
39
  * How often to re-poll while at least one branch is mid-flight, in ms.
36
40
  * Only ever active in the transient window below, so this is not a