@pantheon-systems/puck-css 0.12.0 → 0.14.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 (559) hide show
  1. package/README.md +1 -1
  2. package/dist/agent/components/AgentActionButton.js +0 -13
  3. package/dist/agent/components/AgentActionButton.js.map +1 -1
  4. package/dist/agent/components/AgentActionModal.js +0 -13
  5. package/dist/agent/components/AgentActionModal.js.map +1 -1
  6. package/dist/agent/components/AgentStatusPanel.js +0 -10
  7. package/dist/agent/components/AgentStatusPanel.js.map +1 -1
  8. package/dist/agent/components/index.js +0 -5
  9. package/dist/agent/components/index.js.map +1 -1
  10. package/dist/agent/useAgentEdit.js +0 -12
  11. package/dist/agent/useAgentEdit.js.map +1 -1
  12. package/dist/agent/useAgentTrigger.js +0 -29
  13. package/dist/agent/useAgentTrigger.js.map +1 -1
  14. package/dist/auth/P1AuthProvider.d.ts +9 -2
  15. package/dist/auth/P1AuthProvider.d.ts.map +1 -1
  16. package/dist/auth/P1AuthProvider.js +21 -44
  17. package/dist/auth/P1AuthProvider.js.map +1 -1
  18. package/dist/auth/P1LoginPage.js +0 -18
  19. package/dist/auth/P1LoginPage.js.map +1 -1
  20. package/dist/auth/index.d.ts +2 -0
  21. package/dist/auth/index.d.ts.map +1 -1
  22. package/dist/auth/index.js +1 -9
  23. package/dist/auth/index.js.map +1 -1
  24. package/dist/auth/logout.d.ts +23 -0
  25. package/dist/auth/logout.d.ts.map +1 -0
  26. package/dist/auth/logout.js +14 -0
  27. package/dist/auth/logout.js.map +1 -0
  28. package/dist/auth/storage-keys.d.ts +5 -0
  29. package/dist/auth/storage-keys.d.ts.map +1 -0
  30. package/dist/auth/storage-keys.js +3 -0
  31. package/dist/auth/storage-keys.js.map +1 -0
  32. package/dist/collaboration/components/AgentActivityBanner.js +0 -15
  33. package/dist/collaboration/components/AgentActivityBanner.js.map +1 -1
  34. package/dist/collaboration/components/CollaboratorAvatars.js +0 -17
  35. package/dist/collaboration/components/CollaboratorAvatars.js.map +1 -1
  36. package/dist/collaboration/components/FocusRegionHighlight.js +0 -9
  37. package/dist/collaboration/components/FocusRegionHighlight.js.map +1 -1
  38. package/dist/collaboration/components/PresenceIndicator.js +0 -9
  39. package/dist/collaboration/components/PresenceIndicator.js.map +1 -1
  40. package/dist/collaboration/useBranchPresence.js +0 -36
  41. package/dist/collaboration/useBranchPresence.js.map +1 -1
  42. package/dist/collaboration/useFocusRegionReporting.js +0 -75
  43. package/dist/collaboration/useFocusRegionReporting.js.map +1 -1
  44. package/dist/collaboration/usePresence.js +0 -37
  45. package/dist/collaboration/usePresence.js.map +1 -1
  46. package/dist/collaboration/useSitePresence.js +0 -36
  47. package/dist/collaboration/useSitePresence.js.map +1 -1
  48. package/dist/collaboration/utils/avatarColor.js +0 -3
  49. package/dist/collaboration/utils/avatarColor.js.map +1 -1
  50. package/dist/collaboration/utils/focusHighlightConfig.js +0 -61
  51. package/dist/collaboration/utils/focusHighlightConfig.js.map +1 -1
  52. package/dist/collaboration/utils/focusRegionMap.js +1 -80
  53. package/dist/collaboration/utils/focusRegionMap.js.map +1 -1
  54. package/dist/collaboration/utils/initials.js +0 -14
  55. package/dist/collaboration/utils/initials.js.map +1 -1
  56. package/dist/collaboration/utils/presenceIdentity.js +0 -26
  57. package/dist/collaboration/utils/presenceIdentity.js.map +1 -1
  58. package/dist/core/FocusHighlightContext.js +0 -31
  59. package/dist/core/FocusHighlightContext.js.map +1 -1
  60. package/dist/core/NotificationContext.js +2 -67
  61. package/dist/core/NotificationContext.js.map +1 -1
  62. package/dist/core/P1PuckContext.js +0 -31
  63. package/dist/core/P1PuckContext.js.map +1 -1
  64. package/dist/core/PresenceContext.js +0 -18
  65. package/dist/core/PresenceContext.js.map +1 -1
  66. package/dist/core/config.js +0 -11
  67. package/dist/core/config.js.map +1 -1
  68. package/dist/core/featureConfig.js +0 -32
  69. package/dist/core/featureConfig.js.map +1 -1
  70. package/dist/core/index.d.ts +1 -1
  71. package/dist/core/index.d.ts.map +1 -1
  72. package/dist/core/plugin-types.d.ts +9 -0
  73. package/dist/core/plugin-types.d.ts.map +1 -1
  74. package/dist/core/types.d.ts +7 -1
  75. package/dist/core/types.d.ts.map +1 -1
  76. package/dist/core/types.js +0 -3
  77. package/dist/core/types.js.map +1 -1
  78. package/dist/core/utils/debounce.js +0 -18
  79. package/dist/core/utils/debounce.js.map +1 -1
  80. package/dist/core/utils/requestError.js +0 -5
  81. package/dist/core/utils/requestError.js.map +1 -1
  82. package/dist/core/utils/retry.js +0 -31
  83. package/dist/core/utils/retry.js.map +1 -1
  84. package/dist/core/utils/throttle.js +0 -24
  85. package/dist/core/utils/throttle.js.map +1 -1
  86. package/dist/data/cross-reference-resolve.js +0 -6
  87. package/dist/data/cross-reference-resolve.js.map +1 -1
  88. package/dist/data/cross-reference.js +0 -15
  89. package/dist/data/cross-reference.js.map +1 -1
  90. package/dist/data/css-queries/css-query-registry.d.ts +2 -2
  91. package/dist/data/css-queries/css-query-registry.js +1 -1
  92. package/dist/data/css-queries/css-query-registry.js.map +1 -1
  93. package/dist/data/dal/index.js +0 -21
  94. package/dist/data/dal/index.js.map +1 -1
  95. package/dist/data/dal/init.js +0 -40
  96. package/dist/data/dal/init.js.map +1 -1
  97. package/dist/data/dal/p1-store.d.ts.map +1 -1
  98. package/dist/data/dal/p1-store.js +11 -41
  99. package/dist/data/dal/p1-store.js.map +1 -1
  100. package/dist/data/data-list-block/builtin-components/cards.d.ts +1 -0
  101. package/dist/data/data-list-block/builtin-components/cards.d.ts.map +1 -1
  102. package/dist/data/data-list-block/builtin-components/cards.js +16 -16
  103. package/dist/data/data-list-block/builtin-components/cards.js.map +1 -1
  104. package/dist/data/data-list-block/builtin-components/listing.d.ts +1 -0
  105. package/dist/data/data-list-block/builtin-components/listing.d.ts.map +1 -1
  106. package/dist/data/data-list-block/builtin-components/listing.js +4 -3
  107. package/dist/data/data-list-block/builtin-components/listing.js.map +1 -1
  108. package/dist/data/data-list-block/builtin-components/rows.d.ts +1 -0
  109. package/dist/data/data-list-block/builtin-components/rows.d.ts.map +1 -1
  110. package/dist/data/data-list-block/builtin-components/rows.js +3 -3
  111. package/dist/data/data-list-block/builtin-components/rows.js.map +1 -1
  112. package/dist/data/data-list-block/create-data-list-block.d.ts +1 -0
  113. package/dist/data/data-list-block/create-data-list-block.d.ts.map +1 -1
  114. package/dist/data/data-list-block/create-data-list-block.js +7 -11
  115. package/dist/data/data-list-block/create-data-list-block.js.map +1 -1
  116. package/dist/data/data-list-block/data-list.css +314 -0
  117. package/dist/data/data-list-block/utils.js +0 -1
  118. package/dist/data/data-list-block/utils.js.map +1 -1
  119. package/dist/data/fields/pds-field-helpers.js +1 -4
  120. package/dist/data/fields/pds-field-helpers.js.map +1 -1
  121. package/dist/data/fields/visible-when.js +0 -5
  122. package/dist/data/fields/visible-when.js.map +1 -1
  123. package/dist/data/fields.d.ts.map +1 -1
  124. package/dist/data/fields.js +7 -14
  125. package/dist/data/fields.js.map +1 -1
  126. package/dist/data/get-page.js +0 -1
  127. package/dist/data/get-page.js.map +1 -1
  128. package/dist/data/page-editor-meta.js +0 -7
  129. package/dist/data/page-editor-meta.js.map +1 -1
  130. package/dist/data/page-store.js +0 -32
  131. package/dist/data/page-store.js.map +1 -1
  132. package/dist/data/page-structure.js +0 -3
  133. package/dist/data/page-structure.js.map +1 -1
  134. package/dist/data/paths.d.ts +6 -0
  135. package/dist/data/paths.d.ts.map +1 -1
  136. package/dist/data/paths.js +3 -16
  137. package/dist/data/paths.js.map +1 -1
  138. package/dist/data/query-provider.d.ts +14 -0
  139. package/dist/data/query-provider.d.ts.map +1 -1
  140. package/dist/data/query-provider.js +20 -9
  141. package/dist/data/query-provider.js.map +1 -1
  142. package/dist/data/remote-datasources/fetch-http-json.js +0 -5
  143. package/dist/data/remote-datasources/fetch-http-json.js.map +1 -1
  144. package/dist/data/remote-datasources/loader.js +0 -21
  145. package/dist/data/remote-datasources/loader.js.map +1 -1
  146. package/dist/data/remote-datasources/remote-datasource-registry.js +0 -4
  147. package/dist/data/remote-datasources/remote-datasource-registry.js.map +1 -1
  148. package/dist/data/resolve-data-templates.js +0 -24
  149. package/dist/data/resolve-data-templates.js.map +1 -1
  150. package/dist/data/route-templates.js +0 -29
  151. package/dist/data/route-templates.js.map +1 -1
  152. package/dist/data/semantic-ops.js +0 -11
  153. package/dist/data/semantic-ops.js.map +1 -1
  154. package/dist/data/styles.js +0 -2
  155. package/dist/data/styles.js.map +1 -1
  156. package/dist/data/template-autocomplete.js +0 -8
  157. package/dist/data/template-autocomplete.js.map +1 -1
  158. package/dist/data/utils.d.ts +16 -0
  159. package/dist/data/utils.d.ts.map +1 -1
  160. package/dist/data/utils.js +21 -1
  161. package/dist/data/utils.js.map +1 -1
  162. package/dist/editor/P1App.d.ts.map +1 -1
  163. package/dist/editor/P1App.js +6 -45
  164. package/dist/editor/P1App.js.map +1 -1
  165. package/dist/editor/P1PuckProvider.d.ts +1 -1
  166. package/dist/editor/P1PuckProvider.d.ts.map +1 -1
  167. package/dist/editor/P1PuckProvider.js +54 -561
  168. package/dist/editor/P1PuckProvider.js.map +1 -1
  169. package/dist/editor/aiPanelStore.js +0 -7
  170. package/dist/editor/aiPanelStore.js.map +1 -1
  171. package/dist/editor/components/BranchSelector.js +0 -21
  172. package/dist/editor/components/BranchSelector.js.map +1 -1
  173. package/dist/editor/components/CollapsibleSectionHeader.js +1 -4
  174. package/dist/editor/components/CollapsibleSectionHeader.js.map +1 -1
  175. package/dist/editor/components/DraggableRow.js +0 -1
  176. package/dist/editor/components/DraggableRow.js.map +1 -1
  177. package/dist/editor/components/EditorReloadOverlay.d.ts +16 -0
  178. package/dist/editor/components/EditorReloadOverlay.d.ts.map +1 -0
  179. package/dist/editor/components/EditorReloadOverlay.js +13 -0
  180. package/dist/editor/components/EditorReloadOverlay.js.map +1 -0
  181. package/dist/editor/components/NotificationContainer.js +0 -20
  182. package/dist/editor/components/NotificationContainer.js.map +1 -1
  183. package/dist/editor/components/OutlinePanel.js +1 -12
  184. package/dist/editor/components/OutlinePanel.js.map +1 -1
  185. package/dist/editor/components/P1InspectorFields.d.ts.map +1 -1
  186. package/dist/editor/components/P1InspectorFields.js +12 -14
  187. package/dist/editor/components/P1InspectorFields.js.map +1 -1
  188. package/dist/editor/components/P1TemplateFields.js +6 -12
  189. package/dist/editor/components/P1TemplateFields.js.map +1 -1
  190. package/dist/editor/components/PanelHeader.d.ts.map +1 -1
  191. package/dist/editor/components/PanelHeader.js +3 -2
  192. package/dist/editor/components/PanelHeader.js.map +1 -1
  193. package/dist/editor/components/PublishButton.js +0 -21
  194. package/dist/editor/components/PublishButton.js.map +1 -1
  195. package/dist/editor/components/PuckDataCapture.js +0 -14
  196. package/dist/editor/components/PuckDataCapture.js.map +1 -1
  197. package/dist/editor/components/PuckDataSynchronizer.js +0 -67
  198. package/dist/editor/components/PuckDataSynchronizer.js.map +1 -1
  199. package/dist/editor/components/PuckSelectionTracker.js +0 -62
  200. package/dist/editor/components/PuckSelectionTracker.js.map +1 -1
  201. package/dist/editor/components/SaveIndicator.js +0 -41
  202. package/dist/editor/components/SaveIndicator.js.map +1 -1
  203. package/dist/editor/components/Toast.js +1 -32
  204. package/dist/editor/components/Toast.js.map +1 -1
  205. package/dist/editor/components/VersionBannerOverride.d.ts +7 -1
  206. package/dist/editor/components/VersionBannerOverride.d.ts.map +1 -1
  207. package/dist/editor/components/VersionBannerOverride.js +17 -24
  208. package/dist/editor/components/VersionBannerOverride.js.map +1 -1
  209. package/dist/editor/components/collapsibleSectionContext.js +0 -10
  210. package/dist/editor/components/collapsibleSectionContext.js.map +1 -1
  211. package/dist/editor/components/componentIconName.js +0 -23
  212. package/dist/editor/components/componentIconName.js.map +1 -1
  213. package/dist/editor/components/connectable.js +0 -5
  214. package/dist/editor/components/connectable.js.map +1 -1
  215. package/dist/editor/components/merge-preview/MergePreviewRenderer.js +2 -74
  216. package/dist/editor/components/merge-preview/MergePreviewRenderer.js.map +1 -1
  217. package/dist/editor/components/merge-preview/ScaledContent.js +0 -6
  218. package/dist/editor/components/merge-preview/ScaledContent.js.map +1 -1
  219. package/dist/editor/components/merge-preview/index.js +0 -5
  220. package/dist/editor/components/merge-preview/index.js.map +1 -1
  221. package/dist/editor/components/outlineTree.js +0 -31
  222. package/dist/editor/components/outlineTree.js.map +1 -1
  223. package/dist/editor/components/useDragReorder.js +0 -1
  224. package/dist/editor/components/useDragReorder.js.map +1 -1
  225. package/dist/editor/composePlugins.d.ts +9 -3
  226. package/dist/editor/composePlugins.d.ts.map +1 -1
  227. package/dist/editor/composePlugins.js +3 -8
  228. package/dist/editor/composePlugins.js.map +1 -1
  229. package/dist/editor/defaultPlugins.d.ts +1 -1
  230. package/dist/editor/defaultPlugins.d.ts.map +1 -1
  231. package/dist/editor/defaultPlugins.js +3 -1
  232. package/dist/editor/defaultPlugins.js.map +1 -1
  233. package/dist/editor/freshFieldTypes.d.ts +12 -0
  234. package/dist/editor/freshFieldTypes.d.ts.map +1 -0
  235. package/dist/editor/freshFieldTypes.js +11 -0
  236. package/dist/editor/freshFieldTypes.js.map +1 -0
  237. package/dist/editor/logout-store.d.ts +13 -0
  238. package/dist/editor/logout-store.d.ts.map +1 -0
  239. package/dist/editor/logout-store.js +23 -0
  240. package/dist/editor/logout-store.js.map +1 -0
  241. package/dist/editor/plugin/P1Plugin.d.ts +16 -3
  242. package/dist/editor/plugin/P1Plugin.d.ts.map +1 -1
  243. package/dist/editor/plugin/P1Plugin.js +21 -171
  244. package/dist/editor/plugin/P1Plugin.js.map +1 -1
  245. package/dist/editor/plugin/context-sync-key.js +0 -19
  246. package/dist/editor/plugin/context-sync-key.js.map +1 -1
  247. package/dist/editor/plugin/createP1Overrides.js +1 -49
  248. package/dist/editor/plugin/createP1Overrides.js.map +1 -1
  249. package/dist/editor/plugin/document-sync-plugin.js +0 -50
  250. package/dist/editor/plugin/document-sync-plugin.js.map +1 -1
  251. package/dist/editor/plugin/fieldGuidance.js +0 -5
  252. package/dist/editor/plugin/fieldGuidance.js.map +1 -1
  253. package/dist/editor/plugin/index.js +0 -6
  254. package/dist/editor/plugin/index.js.map +1 -1
  255. package/dist/editor/plugin/selectHeaderCollaborators.js +0 -8
  256. package/dist/editor/plugin/selectHeaderCollaborators.js.map +1 -1
  257. package/dist/editor/presets.js +2 -2
  258. package/dist/editor/stablePluginDeps.d.ts +9 -0
  259. package/dist/editor/stablePluginDeps.d.ts.map +1 -0
  260. package/dist/editor/stablePluginDeps.js +15 -0
  261. package/dist/editor/stablePluginDeps.js.map +1 -0
  262. package/dist/editor/thumbnails/LiveThumbnail.js +1 -49
  263. package/dist/editor/thumbnails/LiveThumbnail.js.map +1 -1
  264. package/dist/editor/thumbnails/ThumbnailCard.js +0 -25
  265. package/dist/editor/thumbnails/ThumbnailCard.js.map +1 -1
  266. package/dist/editor/thumbnails/buildLiveThumbnailDrawer.js +0 -21
  267. package/dist/editor/thumbnails/buildLiveThumbnailDrawer.js.map +1 -1
  268. package/dist/editor/thumbnails/humanizeComponentName.js +0 -17
  269. package/dist/editor/thumbnails/humanizeComponentName.js.map +1 -1
  270. package/dist/editor/thumbnails/resolveLiveThumbnailDrawer.js +0 -11
  271. package/dist/editor/thumbnails/resolveLiveThumbnailDrawer.js.map +1 -1
  272. package/dist/editor/thumbnails/thumbnailCache.js +0 -38
  273. package/dist/editor/thumbnails/thumbnailCache.js.map +1 -1
  274. package/dist/editor/useAutoSave.js +0 -32
  275. package/dist/editor/useAutoSave.js.map +1 -1
  276. package/dist/editor/useBranches.js +0 -30
  277. package/dist/editor/useBranches.js.map +1 -1
  278. package/dist/editor/useComponentRegistry.d.ts +1 -1
  279. package/dist/editor/useComponentRegistry.js +0 -28
  280. package/dist/editor/useComponentRegistry.js.map +1 -1
  281. package/dist/editor/useDocuments.js +0 -51
  282. package/dist/editor/useDocuments.js.map +1 -1
  283. package/dist/editor/useMergePreview.js +8 -12
  284. package/dist/editor/useMergePreview.js.map +1 -1
  285. package/dist/editor/useP1Editor.d.ts +35 -7
  286. package/dist/editor/useP1Editor.d.ts.map +1 -1
  287. package/dist/editor/useP1Editor.js +146 -243
  288. package/dist/editor/useP1Editor.js.map +1 -1
  289. package/dist/editor/useP1Overrides.js +15 -50
  290. package/dist/editor/useP1Overrides.js.map +1 -1
  291. package/dist/editor/useP1Plugin.d.ts +10 -4
  292. package/dist/editor/useP1Plugin.d.ts.map +1 -1
  293. package/dist/editor/useP1Plugin.js +34 -56
  294. package/dist/editor/useP1Plugin.js.map +1 -1
  295. package/dist/editor/useRealtime.d.ts +15 -3
  296. package/dist/editor/useRealtime.d.ts.map +1 -1
  297. package/dist/editor/useRealtime.js +12 -91
  298. package/dist/editor/useRealtime.js.map +1 -1
  299. package/dist/editor/useResponsivePanels.js +2 -35
  300. package/dist/editor/useResponsivePanels.js.map +1 -1
  301. package/dist/editor/useSidebarScrollPreservation.js +0 -14
  302. package/dist/editor/useSidebarScrollPreservation.js.map +1 -1
  303. package/dist/editor/utils/buildThumbnailOverride.js +0 -9
  304. package/dist/editor/utils/buildThumbnailOverride.js.map +1 -1
  305. package/dist/editor/utils/componentRegistry.d.ts +6 -11
  306. package/dist/editor/utils/componentRegistry.d.ts.map +1 -1
  307. package/dist/editor/utils/componentRegistry.js +1 -71
  308. package/dist/editor/utils/componentRegistry.js.map +1 -1
  309. package/dist/editor/utils/createStablePluginArray.js +0 -27
  310. package/dist/editor/utils/createStablePluginArray.js.map +1 -1
  311. package/dist/editor/utils/path.js +0 -6
  312. package/dist/editor/utils/path.js.map +1 -1
  313. package/dist/editor/utils/puckYjsBinding.d.ts +1 -1
  314. package/dist/editor/utils/puckYjsBinding.js +0 -99
  315. package/dist/editor/utils/puckYjsBinding.js.map +1 -1
  316. package/dist/editor/utils/restoreDocumentVersion.js +0 -12
  317. package/dist/editor/utils/restoreDocumentVersion.js.map +1 -1
  318. package/dist/editor/utils/sanitizeRichtextDefaults.js +0 -26
  319. package/dist/editor/utils/sanitizeRichtextDefaults.js.map +1 -1
  320. package/dist/editor/utils/snapshotToPuckData.js +0 -8
  321. package/dist/editor/utils/snapshotToPuckData.js.map +1 -1
  322. package/dist/editor/utils/syncComponentRegistry.d.ts +2 -2
  323. package/dist/editor/utils/syncComponentRegistry.js +1 -76
  324. package/dist/editor/utils/syncComponentRegistry.js.map +1 -1
  325. package/dist/editor/utils/syncComponentRegistryWriteOnly.d.ts +7 -7
  326. package/dist/editor/utils/syncComponentRegistryWriteOnly.js +0 -22
  327. package/dist/editor/utils/syncComponentRegistryWriteOnly.js.map +1 -1
  328. package/dist/editor/utils/templatePath.js +0 -5
  329. package/dist/editor/utils/templatePath.js.map +1 -1
  330. package/dist/features/content-type-templates/editor/useTemplatePermissions.js +0 -3
  331. package/dist/features/content-type-templates/editor/useTemplatePermissions.js.map +1 -1
  332. package/dist/features/content-type-templates/hooks/useTemplateList.js +0 -30
  333. package/dist/features/content-type-templates/hooks/useTemplateList.js.map +1 -1
  334. package/dist/features/content-type-templates/index.js +0 -8
  335. package/dist/features/content-type-templates/index.js.map +1 -1
  336. package/dist/features/content-type-templates/permissions/createPuckPermissions.js +1 -43
  337. package/dist/features/content-type-templates/permissions/createPuckPermissions.js.map +1 -1
  338. package/dist/features/content-type-templates/permissions/role-permissions.d.ts +8 -0
  339. package/dist/features/content-type-templates/permissions/role-permissions.d.ts.map +1 -1
  340. package/dist/features/content-type-templates/permissions/role-permissions.js +3 -29
  341. package/dist/features/content-type-templates/permissions/role-permissions.js.map +1 -1
  342. package/dist/features/content-type-templates/permissions/useContentRole.js +0 -24
  343. package/dist/features/content-type-templates/permissions/useContentRole.js.map +1 -1
  344. package/dist/features/content-type-templates/permissions/useResolveContentRole.d.ts +5 -5
  345. package/dist/features/content-type-templates/permissions/useResolveContentRole.js +2 -22
  346. package/dist/features/content-type-templates/permissions/useResolveContentRole.js.map +1 -1
  347. package/dist/features/content-type-templates/stores/index.js +0 -5
  348. package/dist/features/content-type-templates/stores/index.js.map +1 -1
  349. package/dist/features/content-type-templates/stores/template-store-api.js +0 -14
  350. package/dist/features/content-type-templates/stores/template-store-api.js.map +1 -1
  351. package/dist/features/content-type-templates/stores/template-store.d.ts +1 -1
  352. package/dist/features/content-type-templates/stores/template-store.js +0 -12
  353. package/dist/features/content-type-templates/stores/template-store.js.map +1 -1
  354. package/dist/features/content-type-templates/types.js +0 -5
  355. package/dist/features/content-type-templates/types.js.map +1 -1
  356. package/dist/features/content-type-templates/ui/ActionBarPinButton.js +12 -21
  357. package/dist/features/content-type-templates/ui/ActionBarPinButton.js.map +1 -1
  358. package/dist/features/content-type-templates/ui/TemplateDetailsPanel.js +0 -23
  359. package/dist/features/content-type-templates/ui/TemplateDetailsPanel.js.map +1 -1
  360. package/dist/features/content-type-templates/ui/TemplateSelector.js +0 -19
  361. package/dist/features/content-type-templates/ui/TemplateSelector.js.map +1 -1
  362. package/dist/features/content-type-templates/validation/structural-validation.js +0 -40
  363. package/dist/features/content-type-templates/validation/structural-validation.js.map +1 -1
  364. package/dist/features/localization/authority.d.ts +26 -0
  365. package/dist/features/localization/authority.d.ts.map +1 -0
  366. package/dist/features/localization/authority.js +17 -0
  367. package/dist/features/localization/authority.js.map +1 -0
  368. package/dist/features/localization/index.d.ts +11 -0
  369. package/dist/features/localization/index.d.ts.map +1 -0
  370. package/dist/features/localization/index.js +7 -0
  371. package/dist/features/localization/index.js.map +1 -0
  372. package/dist/features/localization/prop-target.d.ts +26 -0
  373. package/dist/features/localization/prop-target.d.ts.map +1 -0
  374. package/dist/features/localization/prop-target.js +15 -0
  375. package/dist/features/localization/prop-target.js.map +1 -0
  376. package/dist/features/localization/puck-overrides.d.ts +12 -0
  377. package/dist/features/localization/puck-overrides.d.ts.map +1 -0
  378. package/dist/features/localization/puck-overrides.js +12 -0
  379. package/dist/features/localization/puck-overrides.js.map +1 -0
  380. package/dist/features/localization/relation.d.ts +14 -0
  381. package/dist/features/localization/relation.d.ts.map +1 -0
  382. package/dist/features/localization/relation.js +7 -0
  383. package/dist/features/localization/relation.js.map +1 -0
  384. package/dist/features/localization/translatable.d.ts +24 -0
  385. package/dist/features/localization/translatable.d.ts.map +1 -0
  386. package/dist/features/localization/translatable.js +30 -0
  387. package/dist/features/localization/translatable.js.map +1 -0
  388. package/dist/features/localization/types.d.ts +17 -0
  389. package/dist/features/localization/types.d.ts.map +1 -0
  390. package/dist/features/localization/types.js +2 -0
  391. package/dist/features/localization/types.js.map +1 -0
  392. package/dist/features/localization/ui/AuthorityFieldControl.d.ts +32 -0
  393. package/dist/features/localization/ui/AuthorityFieldControl.d.ts.map +1 -0
  394. package/dist/features/localization/ui/AuthorityFieldControl.js +36 -0
  395. package/dist/features/localization/ui/AuthorityFieldControl.js.map +1 -0
  396. package/dist/features/localization/ui/TranslatabilityToggle.d.ts +17 -0
  397. package/dist/features/localization/ui/TranslatabilityToggle.d.ts.map +1 -0
  398. package/dist/features/localization/ui/TranslatabilityToggle.js +16 -0
  399. package/dist/features/localization/ui/TranslatabilityToggle.js.map +1 -0
  400. package/dist/features/localization/useLocalizationData.d.ts +37 -0
  401. package/dist/features/localization/useLocalizationData.d.ts.map +1 -0
  402. package/dist/features/localization/useLocalizationData.js +88 -0
  403. package/dist/features/localization/useLocalizationData.js.map +1 -0
  404. package/dist/index.d.ts +6 -4
  405. package/dist/index.d.ts.map +1 -1
  406. package/dist/index.js +2 -43
  407. package/dist/index.js.map +1 -1
  408. package/dist/merge/components/conflict-notifications/ConflictNotificationToast.js +0 -19
  409. package/dist/merge/components/conflict-notifications/ConflictNotificationToast.js.map +1 -1
  410. package/dist/merge/components/conflict-notifications/index.js +0 -5
  411. package/dist/merge/components/conflict-notifications/index.js.map +1 -1
  412. package/dist/merge/components/conflict-notifications/useConflictNotifications.js +0 -48
  413. package/dist/merge/components/conflict-notifications/useConflictNotifications.js.map +1 -1
  414. package/dist/merge/components/conflict-resolution/ComponentConflictGroup.js +0 -36
  415. package/dist/merge/components/conflict-resolution/ComponentConflictGroup.js.map +1 -1
  416. package/dist/merge/components/conflict-resolution/PuckFieldResolutionPanel.js +0 -40
  417. package/dist/merge/components/conflict-resolution/PuckFieldResolutionPanel.js.map +1 -1
  418. package/dist/merge/components/conflict-resolution/RenderedResolutionPreview.js +0 -21
  419. package/dist/merge/components/conflict-resolution/RenderedResolutionPreview.js.map +1 -1
  420. package/dist/merge/components/conflict-resolution/index.js +0 -5
  421. package/dist/merge/components/conflict-resolution/index.js.map +1 -1
  422. package/dist/merge/components/merge-resolution/CherryPickVisualPanel.js +0 -44
  423. package/dist/merge/components/merge-resolution/CherryPickVisualPanel.js.map +1 -1
  424. package/dist/merge/components/merge-resolution/ComponentClickOverlay.js +0 -24
  425. package/dist/merge/components/merge-resolution/ComponentClickOverlay.js.map +1 -1
  426. package/dist/merge/components/merge-resolution/DocumentResolutionDetail.d.ts.map +1 -1
  427. package/dist/merge/components/merge-resolution/DocumentResolutionDetail.js +6 -31
  428. package/dist/merge/components/merge-resolution/DocumentResolutionDetail.js.map +1 -1
  429. package/dist/merge/components/merge-resolution/index.js +0 -3
  430. package/dist/merge/components/merge-resolution/index.js.map +1 -1
  431. package/dist/merge/useMergeResolution.d.ts +1 -1
  432. package/dist/merge/useMergeResolution.d.ts.map +1 -1
  433. package/dist/merge/useMergeResolution.js +6 -75
  434. package/dist/merge/useMergeResolution.js.map +1 -1
  435. package/dist/merge/utils/namespacePuckData.d.ts.map +1 -1
  436. package/dist/merge/utils/namespacePuckData.js +8 -8
  437. package/dist/merge/utils/namespacePuckData.js.map +1 -1
  438. package/dist/merge/utils/puckFieldClassifier.js +0 -183
  439. package/dist/merge/utils/puckFieldClassifier.js.map +1 -1
  440. package/dist/p1/editor/connect/connect-preview-config.js +0 -5
  441. package/dist/p1/editor/connect/connect-preview-config.js.map +1 -1
  442. package/dist/p1/editor/connect/field-connect-plugin.js +0 -6
  443. package/dist/p1/editor/connect/field-connect-plugin.js.map +1 -1
  444. package/dist/p1/editor/editor-preview-resolve.js +0 -13
  445. package/dist/p1/editor/editor-preview-resolve.js.map +1 -1
  446. package/dist/p1/editor/hooks/api-hooks.js +0 -7
  447. package/dist/p1/editor/hooks/api-hooks.js.map +1 -1
  448. package/dist/p1/editor/hooks/useLiveEditorContext.d.ts.map +1 -1
  449. package/dist/p1/editor/hooks/useLiveEditorContext.js +2 -13
  450. package/dist/p1/editor/hooks/useLiveEditorContext.js.map +1 -1
  451. package/dist/p1/editor/hooks/useLiveRemoteDatasources.js +0 -22
  452. package/dist/p1/editor/hooks/useLiveRemoteDatasources.js.map +1 -1
  453. package/dist/p1/editor/hooks/useP1Plugins.js +0 -5
  454. package/dist/p1/editor/hooks/useP1Plugins.js.map +1 -1
  455. package/dist/p1/editor/remote-datasources/remote-datasource-explorer-plugin.js +0 -7
  456. package/dist/p1/editor/remote-datasources/remote-datasource-explorer-plugin.js.map +1 -1
  457. package/dist/p1/editor/template-autocomplete-layer.js +0 -4
  458. package/dist/p1/editor/template-autocomplete-layer.js.map +1 -1
  459. package/dist/pds/components/AgentChip.js +0 -3
  460. package/dist/pds/components/AgentChip.js.map +1 -1
  461. package/dist/pds/components/CreatePageModal.js +1 -97
  462. package/dist/pds/components/CreatePageModal.js.map +1 -1
  463. package/dist/pds/components/LoadingMessage.d.ts.map +1 -1
  464. package/dist/pds/components/LoadingMessage.js +2 -1
  465. package/dist/pds/components/LoadingMessage.js.map +1 -1
  466. package/dist/pds/components/LoadingMessage.module.css +2 -21
  467. package/dist/pds/components/LoadingOverlay.d.ts +17 -0
  468. package/dist/pds/components/LoadingOverlay.d.ts.map +1 -0
  469. package/dist/pds/components/LoadingOverlay.js +7 -0
  470. package/dist/pds/components/LoadingOverlay.js.map +1 -0
  471. package/dist/pds/components/LoadingOverlay.module.css +28 -0
  472. package/dist/pds/components/P1EditorHeader.d.ts +9 -2
  473. package/dist/pds/components/P1EditorHeader.d.ts.map +1 -1
  474. package/dist/pds/components/P1EditorHeader.js +6 -26
  475. package/dist/pds/components/P1EditorHeader.js.map +1 -1
  476. package/dist/pds/components/P1EditorHeader.module.css +26 -0
  477. package/dist/pds/components/PageNavigator.js +0 -16
  478. package/dist/pds/components/PageNavigator.js.map +1 -1
  479. package/dist/pds/components/PageNotFound.d.ts +27 -0
  480. package/dist/pds/components/PageNotFound.d.ts.map +1 -0
  481. package/dist/pds/components/PageNotFound.js +24 -0
  482. package/dist/pds/components/PageNotFound.js.map +1 -0
  483. package/dist/pds/components/PageNotFound.module.css +51 -0
  484. package/dist/pds/components/PresenceStack.js +0 -9
  485. package/dist/pds/components/PresenceStack.js.map +1 -1
  486. package/dist/pds/components/PublishControl.js +0 -6
  487. package/dist/pds/components/PublishControl.js.map +1 -1
  488. package/dist/pds/components/WorkstreamSwitcher.js +1 -25
  489. package/dist/pds/components/WorkstreamSwitcher.js.map +1 -1
  490. package/dist/pds/index.d.ts +4 -0
  491. package/dist/pds/index.d.ts.map +1 -1
  492. package/dist/pds/index.js +2 -29
  493. package/dist/pds/index.js.map +1 -1
  494. package/dist/pds/theme/pds-core-content.js +0 -5
  495. package/dist/pds/theme/pds-core-content.js.map +1 -1
  496. package/dist/pds/utils/deriveDocState.js +0 -1
  497. package/dist/pds/utils/deriveDocState.js.map +1 -1
  498. package/dist/pds/utils/deriveLiveDocState.js +0 -13
  499. package/dist/pds/utils/deriveLiveDocState.js.map +1 -1
  500. package/dist/registry-sync.js +0 -4
  501. package/dist/registry-sync.js.map +1 -1
  502. package/dist/server.js +0 -7
  503. package/dist/server.js.map +1 -1
  504. package/dist/versioning/components/ReadOnlyFieldsGuard.js +0 -3
  505. package/dist/versioning/components/ReadOnlyFieldsGuard.js.map +1 -1
  506. package/dist/versioning/components/VersionBannerActions.js +0 -1
  507. package/dist/versioning/components/VersionBannerActions.js.map +1 -1
  508. package/dist/versioning/components/version-compare/BranchDiffHeader.js +0 -7
  509. package/dist/versioning/components/version-compare/BranchDiffHeader.js.map +1 -1
  510. package/dist/versioning/components/version-compare/BranchMergeCompare.js +0 -19
  511. package/dist/versioning/components/version-compare/BranchMergeCompare.js.map +1 -1
  512. package/dist/versioning/components/version-compare/ComponentNode.js +0 -8
  513. package/dist/versioning/components/version-compare/ComponentNode.js.map +1 -1
  514. package/dist/versioning/components/version-compare/ComponentTree.js +0 -10
  515. package/dist/versioning/components/version-compare/ComponentTree.js.map +1 -1
  516. package/dist/versioning/components/version-compare/DiffHeader.js +0 -3
  517. package/dist/versioning/components/version-compare/DiffHeader.js.map +1 -1
  518. package/dist/versioning/components/version-compare/DocumentDiffList.js +0 -14
  519. package/dist/versioning/components/version-compare/DocumentDiffList.js.map +1 -1
  520. package/dist/versioning/components/version-compare/PropDiffPanel.js +0 -7
  521. package/dist/versioning/components/version-compare/PropDiffPanel.js.map +1 -1
  522. package/dist/versioning/components/version-compare/PropDiffRow.js +0 -3
  523. package/dist/versioning/components/version-compare/PropDiffRow.js.map +1 -1
  524. package/dist/versioning/components/version-compare/PropValueDisplay.js +0 -22
  525. package/dist/versioning/components/version-compare/PropValueDisplay.js.map +1 -1
  526. package/dist/versioning/components/version-compare/VersionComparePage.js +0 -13
  527. package/dist/versioning/components/version-compare/VersionComparePage.js.map +1 -1
  528. package/dist/versioning/components/version-compare/VisualBranchCompare.js +0 -17
  529. package/dist/versioning/components/version-compare/VisualBranchCompare.js.map +1 -1
  530. package/dist/versioning/components/version-compare/VisualVersionCompare.js +0 -23
  531. package/dist/versioning/components/version-compare/VisualVersionCompare.js.map +1 -1
  532. package/dist/versioning/components/version-compare/index.js +0 -5
  533. package/dist/versioning/components/version-compare/index.js.map +1 -1
  534. package/dist/versioning/components/version-history/AgentCheckpointBadge.js +0 -14
  535. package/dist/versioning/components/version-history/AgentCheckpointBadge.js.map +1 -1
  536. package/dist/versioning/components/version-history/VersionItem.js +0 -7
  537. package/dist/versioning/components/version-history/VersionItem.js.map +1 -1
  538. package/dist/versioning/components/version-history/index.js +0 -5
  539. package/dist/versioning/components/version-history/index.js.map +1 -1
  540. package/dist/versioning/useCheckpoints.js +0 -28
  541. package/dist/versioning/useCheckpoints.js.map +1 -1
  542. package/dist/versioning/useVersions.js +0 -31
  543. package/dist/versioning/useVersions.js.map +1 -1
  544. package/dist/versioning/utils/branchDiff.js +0 -93
  545. package/dist/versioning/utils/branchDiff.js.map +1 -1
  546. package/dist/versioning/utils/diff.js +0 -79
  547. package/dist/versioning/utils/diff.js.map +1 -1
  548. package/dist/versioning/utils/formatVersionDate.js +0 -12
  549. package/dist/versioning/utils/formatVersionDate.js.map +1 -1
  550. package/dist/versioning/utils/highlightConfig.js +0 -42
  551. package/dist/versioning/utils/highlightConfig.js.map +1 -1
  552. package/dist/versioning/utils/versionKind.js +0 -10
  553. package/dist/versioning/utils/versionKind.js.map +1 -1
  554. package/package.json +7 -7
  555. package/src/data/data-list-block/data-list.css +314 -0
  556. package/src/pds/theme/PuckEditorTheme.css +5 -5
  557. package/src/styles.css +1 -1
  558. package/src/pds/theme/PuckEditorTheme.test.ts +0 -195
  559. package/src/pds/theme/pds-core-content.ts +0 -8
@@ -1,14 +1,10 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
- /**
3
- * P1 Puck Provider
4
- *
5
- * React context provider for P1 Puck integration.
6
- */
7
2
  import { useState, useCallback, useMemo, useEffect, useRef } from 'react';
8
3
  import isEqual from 'lodash.isequal';
9
4
  import { P1PuckContext } from '../core/P1PuckContext.js';
10
5
  import { NotificationProvider, useNotifications } from '../core/NotificationContext.js';
11
6
  import { PresenceContext } from '../core/PresenceContext.js';
7
+ import { P1SdkQueryProvider } from '../data/query-provider.js';
12
8
  import { debounce } from '../core/utils/debounce.js';
13
9
  import { describeRequestFailure } from '../core/utils/requestError.js';
14
10
  import { withRetry } from '../core/utils/retry.js';
@@ -16,75 +12,23 @@ import { resolveFeatureConfig } from '../core/featureConfig.js';
16
12
  import { createPuckPermissions } from '../features/content-type-templates/permissions/createPuckPermissions.js';
17
13
  import { useTemplateList } from '../features/content-type-templates/hooks/useTemplateList.js';
18
14
  import { presenceIdentityKey } from '../collaboration/utils/presenceIdentity.js';
19
- import { DEFAULT_CSS_FEATURE_PLUGINS } from './defaultPlugins.js';
20
- import { resolveActivePlugins, composeProviders } from './composePlugins.js';
15
+ import { DocumentPathNotFoundError, isNotFoundStatus } from '../data/utils.js';
16
+ import { DEFAULT_CCR_FEATURE_PLUGINS } from './defaultPlugins.js';
17
+ import { resolveActivePlugins, composeProviders, collectPuckPlugins, } from './composePlugins.js';
18
+ import { useStablePluginDeps } from './stablePluginDeps.js';
21
19
  import { snapshotToPuckData } from './utils/snapshotToPuckData.js';
22
20
  import { useDocuments } from './useDocuments.js';
23
21
  import { useRealtime } from './useRealtime.js';
24
- /**
25
- * Identity to record alongside a loaded payload.
26
- *
27
- * `branch` must be the branch the load was requested for, not the branch on the
28
- * returned version: for an inherited document the backend serves main's
29
- * published version, and recording main would make every consumer treat the
30
- * payload as belonging to another workstream.
31
- *
32
- * Returns a fresh object per call so consumers re-run even when two documents
33
- * share payload identity — snapshotToPuckData yields one shared constant for
34
- * every blank snapshot.
35
- */
36
22
  function originFor(doc, branch, versionId, historical) {
37
23
  return doc
38
24
  ? { branchId: branch, documentId: doc.id, documentPath: doc.path, versionId, historical }
39
25
  : null;
40
26
  }
41
- /**
42
- * Provider component for P1 Puck integration.
43
- *
44
- * Wraps your Puck editor to provide P1 functionality including:
45
- * - Auto-save with debouncing
46
- * - Document loading
47
- * - Checkpoint (publish) creation
48
- * - Branch switching
49
- * - Toast notifications for errors and success
50
- *
51
- * @example
52
- * ```tsx
53
- * import { P1PuckProvider, P1PuckEditor } from '@pantheon-systems/puck-css';
54
- * import { P1Client } from '@pantheon-systems/css-client';
55
- *
56
- * const client = new P1Client({
57
- * baseUrl: 'http://localhost:8787',
58
- * apiKey: 'your-api-key',
59
- * });
60
- *
61
- * function App() {
62
- * return (
63
- * <P1PuckProvider
64
- * client={client}
65
- * siteId="site-123"
66
- * branchId="branch-456"
67
- * userId="user-789"
68
- * >
69
- * <P1PuckEditor config={puckConfig} documentPath="/home" />
70
- * </P1PuckProvider>
71
- * );
72
- * }
73
- * ```
74
- */
75
27
  export function P1PuckProvider(props) {
76
28
  return (_jsx(NotificationProvider, { children: _jsx(P1PuckProviderInner, { ...props }) }));
77
29
  }
78
- /** Query param naming the workstream to open, set by the dashboard's "Open in visual editor". */
79
30
  const BRANCH_QUERY_PARAM = 'branch';
80
- /**
81
- * Rejected here rather than downstream: the value reaches API paths and the
82
- * realtime URL unescaped, so anything that could change a URL's shape ("/",
83
- * ".", "?", "#") must never get that far. refreshBranches still decides whether
84
- * a well-formed id actually exists.
85
- */
86
31
  const BRANCH_ID_PATTERN = /^[A-Za-z0-9_-]{1,64}$/;
87
- /** Kept pure — the param is consumed in an effect, not while rendering. */
88
32
  function readDeepLinkBranchId() {
89
33
  if (typeof window === 'undefined')
90
34
  return '';
@@ -97,10 +41,6 @@ function readDeepLinkBranchId() {
97
41
  return '';
98
42
  }
99
43
  }
100
- /**
101
- * Left in place the param would re-apply on the next remount (this provider
102
- * remounts on route-param changes), silently undoing a manual workstream switch.
103
- */
104
44
  function stripDeepLinkBranchParam() {
105
45
  if (typeof window === 'undefined')
106
46
  return;
@@ -112,37 +52,30 @@ function stripDeepLinkBranchParam() {
112
52
  window.history.replaceState(window.history.state, '', `${url.pathname}${url.search}${url.hash}`);
113
53
  }
114
54
  catch {
115
- // history/URL unavailable — the param is harmless if it stays
116
55
  }
117
56
  }
118
- /**
119
- * Inner provider component that has access to notification context.
120
- */
121
- function P1PuckProviderInner({ client, siteId, branchId: initialBranchId, userId, autoSaveDelay = 3000, maxRetries = 3, showErrorNotifications = true, enableRealtime = true, wsBaseUrl, realtimeApiKey, realtimeSyncInterval: _realtimeSyncInterval = 250, realtimeTokenRefresher,
122
- // Phase 9: Presence props
123
- presenceEnabled = true, presencePollingInterval = 5000, userName: _userName, userAvatar: _userAvatar, userNameResolver,
124
- // Phase 9: Agent mode props
125
- agentModeEnabled = false, agentId, agentTrigger: _agentTrigger,
126
- // Phase 9: Callbacks
127
- onPresenceChange, onAgentConflict: _onAgentConflict,
128
- // Plugin system props (B.4)
129
- featurePlugins, featureConfig,
130
- // Content Type Templates (PROPOSAL-010)
131
- // Consumers should resolve the role via useResolveContentRole and pass it here.
132
- userRole = 'editor', children, }) {
133
- // Access notification context
57
+ function P1PuckProviderInner({ client, siteId, branchId: initialBranchId, userId, autoSaveDelay = 3000, maxRetries = 3, showErrorNotifications = true, enableRealtime = true, wsBaseUrl, realtimeApiKey, realtimeSyncInterval: _realtimeSyncInterval = 250, realtimeTokenRefresher, presenceEnabled = true, presencePollingInterval = 5000, userName: _userName, userAvatar: _userAvatar, userNameResolver, agentModeEnabled = false, agentId, agentTrigger: _agentTrigger, onPresenceChange, onAgentConflict: _onAgentConflict, featurePlugins, featureConfig, userRole = 'editor', children, }) {
134
58
  const notificationContext = useNotifications();
135
- // Persist selected branch in sessionStorage so it survives provider remounts
136
- // (e.g. when P1App is rendered per-page instead of in a shared layout).
137
- const branchStorageKey = `css-branch-${siteId}`;
59
+ const branchStorageKey = `ccr-branch-${siteId}`;
60
+ const legacyBranchStorageKey = `css-branch-${siteId}`;
138
61
  const getPersistedBranchId = useCallback(() => {
139
62
  try {
140
- return (typeof window !== 'undefined' && sessionStorage.getItem(branchStorageKey)) || '';
63
+ if (typeof window === 'undefined')
64
+ return '';
65
+ const persisted = sessionStorage.getItem(branchStorageKey);
66
+ if (persisted !== null)
67
+ return persisted;
68
+ const legacy = sessionStorage.getItem(legacyBranchStorageKey);
69
+ if (legacy !== null) {
70
+ sessionStorage.setItem(branchStorageKey, legacy);
71
+ sessionStorage.removeItem(legacyBranchStorageKey);
72
+ }
73
+ return legacy || '';
141
74
  }
142
75
  catch {
143
76
  return '';
144
77
  }
145
- }, [branchStorageKey]);
78
+ }, [branchStorageKey, legacyBranchStorageKey]);
146
79
  const persistBranchId = useCallback((id) => {
147
80
  try {
148
81
  if (typeof window !== 'undefined') {
@@ -150,33 +83,21 @@ userRole = 'editor', children, }) {
150
83
  }
151
84
  }
152
85
  catch {
153
- // sessionStorage may be unavailable (SSR, privacy mode)
154
86
  }
155
87
  }, [branchStorageKey]);
156
- // Site name — fetched once on mount from the P1 API
157
88
  const [siteName, setSiteName] = useState(null);
158
89
  useEffect(() => {
159
90
  client.sites?.get(siteId)
160
91
  ?.then((site) => setSiteName(site.name))
161
92
  .catch(() => { });
162
93
  }, [client, siteId]);
163
- // Outranks the prop (a build-time default) and sessionStorage (a previous
164
- // visit): this names the workstream the user just asked for. refreshBranches
165
- // validates it, so a stale or foreign id still falls back to main.
166
94
  const [deepLinkBranchId] = useState(readDeepLinkBranchId);
167
- // Branch state - start with the deep link, initialBranchId, persisted branch,
168
- // or empty (will be set to main)
169
95
  const [branchId, setBranchId] = useState(() => {
170
96
  if (deepLinkBranchId)
171
97
  return deepLinkBranchId;
172
98
  if (initialBranchId)
173
99
  return initialBranchId;
174
- try {
175
- return (typeof window !== 'undefined' && sessionStorage.getItem(branchStorageKey)) || '';
176
- }
177
- catch {
178
- return '';
179
- }
100
+ return getPersistedBranchId();
180
101
  });
181
102
  const [branches, setBranches] = useState([]);
182
103
  const [branchResolutionError, setBranchResolutionError] = useState(null);
@@ -184,115 +105,48 @@ userRole = 'editor', children, }) {
184
105
  const [branchesLoading, setBranchesLoading] = useState(() => {
185
106
  if (deepLinkBranchId || initialBranchId)
186
107
  return false;
187
- try {
188
- return !(typeof window !== 'undefined' && sessionStorage.getItem(branchStorageKey));
189
- }
190
- catch {
191
- return true;
192
- }
108
+ return !getPersistedBranchId();
193
109
  });
194
- // Persist before stripping: once the param is gone, a remount landing before
195
- // the branch list resolves would otherwise fall back to a stale persisted
196
- // branch. refreshBranches replaces this with a validated value.
197
110
  useEffect(() => {
198
111
  if (!deepLinkBranchId)
199
112
  return;
200
113
  persistBranchId(deepLinkBranchId);
201
114
  stripDeepLinkBranchParam();
202
115
  }, [deepLinkBranchId, persistBranchId]);
203
- // Document state
204
116
  const [currentDocument, setCurrentDocument] = useState(null);
205
117
  const [currentData, setCurrentData] = useState(null);
206
118
  const [currentDataOrigin, setCurrentDataOrigin] = useState(null);
207
- // True while loadDocument is in flight. Consumers use it to distinguish
208
- // "no document while switching" (keep showing the old canvas) from
209
- // "genuinely no document" (show the empty state).
210
119
  const [documentLoading, setDocumentLoading] = useState(false);
211
- // Template state (PROPOSAL-010)
212
120
  const [currentTemplate, setCurrentTemplate] = useState(null);
213
- // Save state
214
121
  const [saveStatus, setSaveStatus] = useState('idle');
215
122
  const [lastSaved, setLastSaved] = useState(null);
216
123
  const [saveError, setSaveError] = useState(null);
217
- // Pending data for debounced save - use ref to avoid recreating debounce
218
124
  const pendingDataRef = useRef(null);
219
125
  const currentDocumentRef = useRef(null);
220
126
  const initializedRef = useRef(false);
221
- // Track the latest Puck action metadata for inclusion in sync payloads.
222
- // Buffered actions captured by handleAction (Puck's onAction callback)
223
- // Array accumulates all structural actions during an edit session.
224
- // Forwarded to backend on save for version history, then cleared.
225
127
  const pendingActionsRef = useRef([]);
226
- // Tracks the document path that the current data in state belongs to.
227
- // Set alongside every setCurrentData call to record the data's origin.
228
- // Checked before every write (realtime or REST) to prevent cross-document
229
- // corruption during rapid document switching.
230
128
  const currentDataDocumentPathRef = useRef(null);
231
129
  const currentDataOriginRef = useRef(null);
232
- // Commit data and the identity it was loaded under in a single render pass.
233
- //
234
- // Path alone cannot distinguish two branches' copies of the same page, which
235
- // is what let one workstream's data be published under another's sync key.
236
- // Because both land in one commit, a consumer reading them together can never
237
- // see a new identity beside old data.
238
130
  const commitCurrentData = useCallback((data, origin) => {
239
131
  currentDataDocumentPathRef.current = origin?.documentPath ?? null;
240
132
  currentDataOriginRef.current = origin;
241
133
  setCurrentDataOrigin(origin);
242
134
  setCurrentData(data);
243
135
  }, []);
244
- // Snapshot of the data loadDocument just loaded. The first onChange after a
245
- // load echoes this back; saveData drops that echo by structural compare
246
- // (not a one-shot flag, which would swallow a lone genuine first edit like a
247
- // pin toggle). Structural, not string: Puck re-normalizes key order.
248
136
  const suppressNextSaveRef = useRef(null);
249
- // Monotonically increasing counter for stale loadDocument response detection.
250
- // Each loadDocument call increments this and captures the current value.
251
- // After each async operation, the captured value is compared to the current
252
- // value — if they differ, a newer loadDocument call has started and the
253
- // current one should bail out.
254
137
  const loadRequestIdRef = useRef(0);
255
- // Track realtime connection state for use in performSave (avoids stale closure)
256
138
  const realtimeConnectedRef = useRef(false);
257
- // Auto-save pause state
258
139
  const [autoSavePaused, setAutoSavePaused] = useState(false);
259
- // Version viewing state
260
140
  const [viewingVersion, setViewingVersion] = useState(null);
261
- // True while returnToLatest is fetching the latest version from the server,
262
- // so the banner can show progress during the round trip.
263
141
  const [isReturningToLatest, setIsReturningToLatest] = useState(false);
264
- // Last known "latest" snapshot — a write-only internal fallback for
265
- // returnToLatest when no live Yjs snapshot is available and the server
266
- // re-fetch fails. Backed by a ref (not state): nothing renders from it, so
267
- // making it state would add a setState on every autosave and churn the
268
- // context memo / returnToLatest identity for no consumer benefit.
269
142
  const latestVersionDataRef = useRef(null);
270
- // Remote sync key - changes when remote updates arrive to trigger Puck sync
271
143
  const [remoteSyncKey, setRemoteSyncKey] = useState(null);
272
- // Counter to track pending remote updates that will trigger onChange callbacks.
273
- // When we receive a remote update and sync it to Puck via setData, Puck fires onChange.
274
- // We need to skip the realtime send for that onChange to prevent bounce-back loops.
275
- // Using a counter is more reliable than a timeout because it's synchronized with
276
- // the actual number of remote updates received.
277
144
  const pendingRemoteUpdatesRef = useRef(0);
278
- // Flag to track if we're currently applying a remote sync.
279
- // This is set before setCurrentData and cleared after a short delay to ensure
280
- // all related onChange events are skipped (there may be multiple: one from data
281
- // prop change and one from setData dispatch).
282
- // Pending remote data ref - stores latest data during debounce period
283
145
  const pendingRemoteDataRef = useRef(null);
284
- // Debounce timer for remote sync - batches rapid updates to reduce flickering
285
146
  const remoteSyncTimerRef = useRef(null);
286
- // Delay for debouncing remote sync updates (ms)
287
147
  const REMOTE_SYNC_DEBOUNCE_DELAY = 50;
288
- // Ref to track viewingVersion for use in callbacks (avoids stale closure)
289
148
  const viewingVersionRef = useRef(null);
290
- // Tracks the latest data seen by saveData when NOT viewing historical.
291
- // Used by returnToLatest to restore unsaved edits that were in Puck's
292
- // memory when the user navigated to a historical version.
293
149
  const latestLocalDataRef = useRef(null);
294
- // Helper to cancel any pending remote sync - used when loading documents/versions
295
- // This prevents race conditions where a pending remote update overrides loaded data
296
150
  const cancelPendingRemoteSync = useCallback(() => {
297
151
  if (remoteSyncTimerRef.current) {
298
152
  clearTimeout(remoteSyncTimerRef.current);
@@ -300,10 +154,8 @@ userRole = 'editor', children, }) {
300
154
  }
301
155
  pendingRemoteDataRef.current = null;
302
156
  }, []);
303
- // WebSocket presence state - used when realtime is connected for instant presence updates
304
157
  const [wsPresenceActors, setWsPresenceActors] = useState([]);
305
158
  const [wsPresenceActive, setWsPresenceActive] = useState(false);
306
- // Helper to enrich actors with display names from the resolver
307
159
  const enrichActorsWithNames = useCallback((actors) => {
308
160
  if (!userNameResolver)
309
161
  return actors;
@@ -312,7 +164,8 @@ userRole = 'editor', children, }) {
312
164
  return resolvedName ? { ...actor, name: resolvedName } : actor;
313
165
  });
314
166
  }, [userNameResolver]);
315
- // Real-time collaboration hook
167
+ const [baselineResetKey, setBaselineResetKey] = useState(0);
168
+ const loadDocForResetRef = useRef(null);
316
169
  const realtime = useRealtime({
317
170
  baseUrl: wsBaseUrl ?? '',
318
171
  apiKey: realtimeApiKey,
@@ -324,47 +177,48 @@ userRole = 'editor', children, }) {
324
177
  actorType: 'user',
325
178
  enabled: enableRealtime && !!wsBaseUrl,
326
179
  initialData: currentData,
180
+ resetKey: baselineResetKey,
327
181
  onServerReload: () => {
328
182
  notificationContext.addInfo('Document is being refreshed by the server. Reconnecting...');
329
183
  },
330
- // WebSocket presence callbacks - receive instant presence updates
184
+ onBaselineReset: () => {
185
+ notificationContext.addInfo('Your local changes could not be synced after a branch merge. Reconnecting with fresh content...');
186
+ const docPath = currentDocumentRef.current?.path;
187
+ if (!docPath)
188
+ return;
189
+ void loadDocForResetRef.current?.(docPath)
190
+ .then(() => {
191
+ setBaselineResetKey((k) => k + 1);
192
+ })
193
+ .catch(() => {
194
+ setBaselineResetKey((k) => k + 1);
195
+ notificationContext.addError('Could not reload content after a branch merge. Reconnecting — you may need to refresh if the editor stays blank.');
196
+ });
197
+ },
331
198
  onPresenceUpdate: (actors) => {
332
- // Mark WebSocket presence as active (first update received)
333
199
  setWsPresenceActive(true);
334
- // Filter out self, enrich with names, and update state
335
200
  const filtered = actors.filter((a) => a.actorId !== userId);
336
201
  const enriched = enrichActorsWithNames(filtered);
337
202
  setWsPresenceActors(enriched);
338
203
  },
339
204
  onFocusRegionBroadcast: (actorId, focusRegions) => {
340
- // Update focus regions for the specific actor
341
205
  setWsPresenceActors((prev) => prev.map((a) => (a.actorId === actorId ? { ...a, focusRegions } : a)));
342
206
  },
343
207
  onRemoteUpdate: (data) => {
344
208
  if (viewingVersionRef.current !== null) {
345
209
  return;
346
210
  }
347
- // Reject empty data that would overwrite real editor content.
348
- // During initial Yjs sync, the remote doc may be empty, producing
349
- // { content: [], root: { props: {} } }. Applying this would clear
350
- // the editor and trigger a save loop.
351
211
  const rootProps = data.root.props;
352
212
  if (data.content.length === 0 &&
353
213
  (!rootProps || Object.keys(rootProps).length === 0) &&
354
214
  !data.zones) {
355
215
  return;
356
216
  }
357
- // Store the latest data - will be used when debounce fires
358
217
  pendingRemoteDataRef.current = data;
359
- // Clear any pending debounce timer
360
218
  if (remoteSyncTimerRef.current) {
361
219
  clearTimeout(remoteSyncTimerRef.current);
362
220
  }
363
- // Debounce the sync to batch rapid successive updates
364
- // This prevents plugin recreation and flickering on every update
365
221
  remoteSyncTimerRef.current = setTimeout(() => {
366
- // Double-check we're still not viewing a historical version
367
- // (user might have switched while debounce was pending)
368
222
  if (viewingVersionRef.current !== null) {
369
223
  pendingRemoteDataRef.current = null;
370
224
  remoteSyncTimerRef.current = null;
@@ -372,22 +226,10 @@ userRole = 'editor', children, }) {
372
226
  }
373
227
  const dataToSync = pendingRemoteDataRef.current;
374
228
  if (dataToSync) {
375
- // Increment counter to skip the onChange echo(es) that will fire
376
- // when Puck processes the setCurrentData call below.
377
- // The content-based guards in puckDataToYMap and RealtimeClient
378
- // prevent actual echo loops at the Y.Doc/WebSocket layer.
379
229
  pendingRemoteUpdatesRef.current += 1;
380
- // Update current data when remote changes arrive. Same document and
381
- // branch as whatever is loaded, but no longer a stored version — this
382
- // syncs through remoteSyncKey below, not the document-sync plugin.
383
230
  commitCurrentData(dataToSync, originFor(currentDocumentRef.current, currentDataOriginRef.current?.branchId ?? null, null, currentDataOriginRef.current?.historical ?? false));
384
- // Update sync key to trigger Puck re-sync
385
231
  setRemoteSyncKey(`remote-${Date.now()}`);
386
232
  pendingRemoteDataRef.current = null;
387
- // Safety net: reset counter after React has processed the state updates.
388
- // If the remote data is identical to current editor state, Puck won't
389
- // fire onChange and the counter would never be decremented naturally.
390
- // Leaving it > 0 would cause subsequent local edits to be dropped.
391
233
  setTimeout(() => {
392
234
  pendingRemoteUpdatesRef.current = 0;
393
235
  }, 100);
@@ -396,10 +238,8 @@ userRole = 'editor', children, }) {
396
238
  }, REMOTE_SYNC_DEBOUNCE_DELAY);
397
239
  },
398
240
  });
399
- // Keep a stable ref to realtime for use in callbacks without dependency churn
400
241
  const realtimeRef = useRef(realtime);
401
242
  realtimeRef.current = realtime;
402
- // Cleanup remote sync timer on unmount
403
243
  useEffect(() => {
404
244
  return () => {
405
245
  if (remoteSyncTimerRef.current) {
@@ -407,33 +247,16 @@ userRole = 'editor', children, }) {
407
247
  }
408
248
  };
409
249
  }, []);
410
- // PuckDataCapture catch-up: detects data that Puck's onChange missed.
411
- // Puck's onChange can miss the very last keystroke in a typing burst because
412
- // createOnChange dispatches asynchronously (via resolveComponentData) and
413
- // the Zustand subscriber may not fire for the final state update in time.
414
- // PuckDataCapture subscribes to Puck's Zustand store independently via
415
- // createUsePuck() and writes the true current data to this ref.
416
- // The onDataChange callback debounces and compares with what saveData last
417
- // sent — if they differ, it pushes the corrected data through realtime.
418
250
  const realtimeDataCaptureRef = useRef(null);
419
251
  const lastSentDataRef = useRef(null);
420
252
  const captureTimerRef = useRef(null);
421
- // Track the last data sent via saveData so we can detect missed updates.
422
- // Updated in saveData's realtime branch (the happy path).
423
253
  const trackSentData = useCallback((data) => {
424
254
  lastSentDataRef.current = JSON.stringify(data);
425
255
  }, []);
426
- // Callback for PuckDataCapture.onDataChange — fires when Puck's store
427
- // updates (may fire for changes that onChange misses).
428
256
  const handleRealtimeDataCapture = useCallback((data) => {
429
257
  if (!enableRealtime)
430
258
  return;
431
- // Store the latest data in the ref for comparison when the timer fires.
432
- // The ref always holds the most recent Zustand store snapshot.
433
259
  realtimeDataCaptureRef.current = data;
434
- // Debounce: wait 800ms after the last store update to allow onChange
435
- // to process normally. When the timer fires, compare the ref's current
436
- // data (not the closure's stale copy) against what was last sent.
437
260
  if (captureTimerRef.current)
438
261
  clearTimeout(captureTimerRef.current);
439
262
  captureTimerRef.current = setTimeout(() => {
@@ -445,10 +268,8 @@ userRole = 'editor', children, }) {
445
268
  if (!currentData)
446
269
  return;
447
270
  const dataJson = JSON.stringify(currentData);
448
- // If saveData already sent this exact data, no catch-up needed
449
271
  if (dataJson === lastSentDataRef.current)
450
272
  return;
451
- // Data origin guard
452
273
  const currentPath = currentDocumentRef.current?.path ?? null;
453
274
  const dataOriginPath = currentDataDocumentPathRef.current;
454
275
  if (dataOriginPath !== currentPath)
@@ -457,31 +278,24 @@ userRole = 'editor', children, }) {
457
278
  lastSentDataRef.current = dataJson;
458
279
  }, 800);
459
280
  }, [enableRealtime]);
460
- // Cleanup capture timer on unmount
461
281
  useEffect(() => {
462
282
  return () => {
463
283
  if (captureTimerRef.current)
464
284
  clearTimeout(captureTimerRef.current);
465
285
  };
466
286
  }, []);
467
- // Keep refs in sync with state
468
287
  useEffect(() => {
469
288
  currentDocumentRef.current = currentDocument;
470
289
  }, [currentDocument]);
471
290
  useEffect(() => {
472
291
  viewingVersionRef.current = viewingVersion;
473
292
  }, [viewingVersion]);
474
- // Keep realtime connection ref in sync (used by performSave to avoid stale closure)
475
293
  useEffect(() => {
476
294
  realtimeConnectedRef.current = realtime.connected;
477
295
  }, [realtime.connected]);
478
- // Create client with user principal
479
296
  const userClient = useMemo(() => client.withPrincipal({ id: userId, type: 'user' }), [client, userId]);
480
- // Document list for current branch
481
297
  const { documents: branchDocuments, loading: documentsLoading, error: documentsError, create: createDocumentRaw, remove: removeDocumentRaw, refresh: refreshDocuments, } = useDocuments({ client: userClient, siteId, branchId });
482
- // Template list for current branch
483
298
  const { templates: branchTemplates, loading: templatesLoading, error: templatesError, refresh: refreshTemplates, } = useTemplateList(userClient, siteId, branchId);
484
- // Stable document create/delete callbacks
485
299
  const branchIdRef = useRef(branchId);
486
300
  branchIdRef.current = branchId;
487
301
  const createDocumentRawRef = useRef(createDocumentRaw);
@@ -491,16 +305,10 @@ userRole = 'editor', children, }) {
491
305
  throw new Error('Cannot create document: no branch selected');
492
306
  }
493
307
  if (template) {
494
- // The list endpoint carries no layout content; fetch the full template.
495
308
  const fullTemplate = await userClient.templates.get(siteId, branchIdRef.current, template.id);
496
- // A template without a content array has no stored layout snapshot;
497
- // creating a page from it would silently produce a blank page. An empty
498
- // array is a legitimate empty layout and passes.
499
309
  if (!fullTemplate.content) {
500
310
  throw new Error(`Template "${template.label || template.name}" has no layout yet. Open it in the editor and add components before creating pages from it.`);
501
311
  }
502
- // The backend builds the initial version from the template, preserving
503
- // each component's durable slot id; no client snapshot is sent.
504
312
  await createDocumentRawRef.current(path, undefined, {
505
313
  templateId: fullTemplate.id,
506
314
  templateVersion: fullTemplate.version,
@@ -511,14 +319,8 @@ userRole = 'editor', children, }) {
511
319
  await createDocumentRawRef.current(path, undefined, title ? { title } : undefined);
512
320
  }
513
321
  }, [userClient, siteId]);
514
- // Stable update-template callback (template metadata: label/description/URL
515
- // pattern). Used by the editor's template-mode right sidebar to persist details
516
- // to the Template record, then refresh the list so the changes propagate.
517
322
  const refreshTemplatesRef = useRef(refreshTemplates);
518
323
  refreshTemplatesRef.current = refreshTemplates;
519
- // Create a new template (empty layout, built in the template editor
520
- // afterwards). Returns the created Template so callers can navigate to its
521
- // editor. Used by the Create Page modal's "New template" flow.
522
324
  const stableCreateTemplate = useCallback(async (params) => {
523
325
  if (!branchIdRef.current) {
524
326
  throw new Error('Cannot create template: no branch selected');
@@ -532,7 +334,6 @@ userRole = 'editor', children, }) {
532
334
  throw new Error('Cannot update template: no branch selected');
533
335
  }
534
336
  const updated = await userClient.templates.update(siteId, branchIdRef.current, templateId, params);
535
- // Keep the in-editor template in sync if it's the one being edited.
536
337
  setCurrentTemplate((prev) => (prev && prev.id === updated.id ? updated : prev));
537
338
  await refreshTemplatesRef.current();
538
339
  }, [userClient, siteId]);
@@ -544,7 +345,6 @@ userRole = 'editor', children, }) {
544
345
  }
545
346
  await removeDocumentRawRef.current(documentId);
546
347
  }, []);
547
- // Load branches
548
348
  const refreshBranches = useCallback(async () => {
549
349
  try {
550
350
  setBranchesLoading(true);
@@ -552,33 +352,25 @@ userRole = 'editor', children, }) {
552
352
  const branchList = await userClient.branches.list(siteId);
553
353
  setBranches(branchList);
554
354
  let effectiveBranchId = branchIdRef.current;
555
- // If no branchId set, try persisted branch, then default to main
556
355
  if (!effectiveBranchId) {
557
356
  const persisted = getPersistedBranchId();
558
357
  if (persisted && branchList.some((b) => b.id === persisted)) {
559
358
  effectiveBranchId = persisted;
560
359
  }
561
360
  }
562
- // Validate that the branch exists in the list; fall back to main if not
563
361
  if (effectiveBranchId && !branchList.some((b) => b.id === effectiveBranchId)) {
564
362
  const mainBranch = branchList.find((b) => b.isMain);
565
363
  effectiveBranchId = mainBranch?.id ?? effectiveBranchId;
566
364
  }
567
- // Default to main if still empty
568
365
  if (!effectiveBranchId) {
569
366
  const mainBranch = branchList.find((b) => b.isMain);
570
367
  if (mainBranch) {
571
368
  effectiveBranchId = mainBranch.id;
572
369
  }
573
370
  }
574
- // A list that resolves without a usable branch strands the editor exactly
575
- // as a refused list does — most often a siteId that doesn't exist, which
576
- // this endpoint answers with an empty list rather than a 404.
577
371
  if (!effectiveBranchId) {
578
372
  setBranchResolutionError(new Error(`GET /api/sites/${siteId}/branches returned no usable branch. Check that the site id is correct and that the site has a main branch.`));
579
373
  }
580
- // Persisting an empty id would poison the next visit and build requests with a
581
- // blank branch segment; keep whatever is already selected instead.
582
374
  if (effectiveBranchId) {
583
375
  persistBranchId(effectiveBranchId);
584
376
  setBranchId(effectiveBranchId);
@@ -586,14 +378,8 @@ userRole = 'editor', children, }) {
586
378
  setCurrentBranch(branchList.find((b) => b.id === effectiveBranchId) ?? null);
587
379
  }
588
380
  catch (error) {
589
- // The editor cannot open a document without a branch, so this has to
590
- // reach the UI: swallowed, it leaves the editor on "Loading document"
591
- // forever with no indication of what was refused.
592
381
  setBranchResolutionError(describeRequestFailure(`GET /api/sites/${siteId}/branches`, error));
593
382
  console.error('Failed to load branches:', error);
594
- // Without a branch nothing else can load: useDocuments' fetch is branch-guarded, so
595
- // documentsLoading stays true forever and the editor sits blank. Say so rather than
596
- // leaving the user with an unexplained empty screen.
597
383
  if (showErrorNotifications && notificationContext?.addError) {
598
384
  const message = error instanceof Error ? error.message : String(error);
599
385
  notificationContext.addError(`Failed to load branches: ${message}`, () => void refreshBranchesRef.current?.());
@@ -603,8 +389,6 @@ userRole = 'editor', children, }) {
603
389
  setBranchesLoading(false);
604
390
  }
605
391
  }, [userClient, siteId, getPersistedBranchId, persistBranchId, showErrorNotifications, notificationContext]);
606
- // Lets the failure notification offer a retry without refreshBranches depending on
607
- // itself, the same ref idiom the template callbacks above use.
608
392
  const refreshBranchesRef = useRef(refreshBranches);
609
393
  refreshBranchesRef.current = refreshBranches;
610
394
  const createBranch = useCallback(async (name) => {
@@ -612,36 +396,28 @@ userRole = 'editor', children, }) {
612
396
  await refreshBranches();
613
397
  return branch;
614
398
  }, [userClient, siteId, refreshBranches]);
615
- // Initial branch load - only once
616
399
  useEffect(() => {
617
400
  if (!initializedRef.current) {
618
401
  initializedRef.current = true;
619
402
  void refreshBranches();
620
403
  }
621
404
  }, [refreshBranches]);
622
- // Perform save operation - uses refs to avoid dependency issues
623
405
  const performSave = useCallback(async (options) => {
624
406
  const dataToSave = pendingDataRef.current;
625
407
  const doc = currentDocumentRef.current;
626
408
  if (!dataToSave || !doc) {
627
409
  return;
628
410
  }
629
- // Data origin guard: reject writes if data doesn't belong to current document
630
411
  if (currentDataDocumentPathRef.current !== doc.path) {
631
412
  pendingDataRef.current = null;
632
413
  return;
633
414
  }
634
- // When realtime is connected, the DO handles persistence via WebSocket sync.
635
- // Skip REST API save to avoid creating duplicate versions without CRDT state.
636
415
  if (enableRealtime && realtimeConnectedRef.current) {
637
- // The data is already being sent via realtime.applyLocalChange in saveData.
638
- // Mark as saved since the DO will persist it.
639
416
  pendingDataRef.current = null;
640
417
  setSaveStatus('saved');
641
418
  setLastSaved(new Date());
642
419
  return;
643
420
  }
644
- // Fallback: REST API save when realtime is disabled or disconnected
645
421
  setSaveStatus('saving');
646
422
  setSaveError(null);
647
423
  try {
@@ -660,47 +436,33 @@ userRole = 'editor', children, }) {
660
436
  : userClient.versions.create(siteId, params));
661
437
  pendingActionsRef.current = [];
662
438
  }, { maxAttempts: maxRetries });
663
- // Note: We intentionally do NOT call setCurrentData(dataToSave) here.
664
- // The data is already in Puck's internal state (it came from Puck's onChange).
665
- // Updating currentData would trigger a re-render cascade that recreates the
666
- // p1Plugin, causing Puck to potentially re-render and flicker.
667
- // currentData should only be updated when loading a document or switching versions.
668
439
  pendingDataRef.current = null;
669
440
  setSaveStatus('saved');
670
441
  setLastSaved(new Date());
671
- // Keep the "latest" cache in sync with what was just persisted (REST
672
- // success = confirmed persistence) so returning from a historical preview
673
- // restores the correct content even if the on-return re-fetch fails.
674
442
  latestVersionDataRef.current = dataToSave;
675
443
  }
676
444
  catch (error) {
677
445
  setSaveStatus('error');
678
446
  const saveErr = error instanceof Error ? error : new Error(String(error));
679
447
  setSaveError(saveErr);
680
- // Show error notification with retry action
681
448
  if (showErrorNotifications) {
682
449
  notificationContext.addError(`Failed to save changes: ${saveErr.message}`, () => void performSave());
683
450
  }
684
451
  }
685
452
  }, [userClient, siteId, branchId, maxRetries, showErrorNotifications, notificationContext, enableRealtime]);
686
- // Debounced save
687
453
  const debouncedSave = useMemo(() => debounce(() => {
688
454
  void performSave();
689
455
  }, autoSaveDelay), [performSave, autoSaveDelay]);
690
- // Cleanup debounce on unmount
691
456
  useEffect(() => {
692
457
  return () => {
693
458
  debouncedSave.cancel();
694
459
  };
695
460
  }, [debouncedSave]);
696
- // Best-effort flush of a pending debounced save when the tab is closed,
697
- // navigated away from, or hidden.
698
461
  useEffect(() => {
699
462
  if (typeof window === 'undefined' || typeof document === 'undefined')
700
463
  return;
701
464
  const flushPendingSave = () => {
702
465
  if (pendingDataRef.current) {
703
- // keepalive lets the POST finish after the page is discarded.
704
466
  debouncedSave.cancel();
705
467
  void performSave({ keepalive: true });
706
468
  }
@@ -719,19 +481,14 @@ userRole = 'editor', children, }) {
719
481
  document.removeEventListener('visibilitychange', handleVisibilityChange);
720
482
  };
721
483
  }, [debouncedSave, performSave]);
722
- // Pause auto-save
723
484
  const pauseAutoSave = useCallback(() => {
724
485
  debouncedSave.pause();
725
486
  setAutoSavePaused(true);
726
487
  }, [debouncedSave]);
727
- // Resume auto-save
728
488
  const resumeAutoSave = useCallback(() => {
729
489
  debouncedSave.resume();
730
490
  setAutoSavePaused(false);
731
491
  }, [debouncedSave]);
732
- // Capture Puck action metadata from the onAction callback.
733
- // This captures the action type and relevant metadata fields so they
734
- // can be included in the sync payload for backend version storage.
735
492
  const handleAction = useCallback((action) => {
736
493
  const puckAction = {
737
494
  type: action.type || 'unknown',
@@ -756,28 +513,17 @@ userRole = 'editor', children, }) {
756
513
  pendingActionsRef.current = buffer.slice(-500);
757
514
  }
758
515
  }, []);
759
- // Public save function (triggers debounce)
760
- // Also resumes auto-save if paused, per user requirement
761
- // Sends changes via WebSocket when realtime is enabled (but not for remote updates)
762
516
  const saveData = useCallback((data) => {
763
517
  if (suppressNextSaveRef.current !== null) {
764
518
  const loadedSnapshot = suppressNextSaveRef.current;
765
519
  suppressNextSaveRef.current = null;
766
- // Drop only the echo; a structurally different first onChange is a real edit.
767
520
  if (isEqual(data, loadedSnapshot)) {
768
521
  return;
769
522
  }
770
523
  }
771
- // Only genuine user edits reach here — load/restore echoes arm
772
- // suppressNextSaveRef before they fire and are returned early above.
773
524
  if (!viewingVersionRef.current) {
774
525
  latestLocalDataRef.current = data;
775
526
  }
776
- // When realtime is enabled, detect whether this onChange came from a remote
777
- // sync (Yjs update from another client) vs. a local user edit.
778
- // Remote updates should NOT trigger a REST save — the DO handles persistence.
779
- // Important: we must NOT set pendingDataRef for remote syncs, otherwise
780
- // getHasUnsavedChanges() will incorrectly report unsaved changes.
781
527
  if (enableRealtime && realtime.connected) {
782
528
  if (pendingRemoteUpdatesRef.current > 0) {
783
529
  pendingRemoteUpdatesRef.current -= 1;
@@ -788,26 +534,16 @@ userRole = 'editor', children, }) {
788
534
  }
789
535
  else {
790
536
  const currentPath = currentDocumentRef.current?.path ?? null;
791
- // Data origin guard: verify the data in state belongs to the
792
- // current document. During rapid document switching, a stale
793
- // loadDocument call can resolve and set data from document Y
794
- // into state after currentDocument has been updated to X.
795
537
  const dataOriginPath = currentDataDocumentPathRef.current;
796
538
  if (dataOriginPath !== currentPath) {
797
539
  console.warn('[P1PuckProvider] saveData SKIPPED: data origin mismatch.', 'dataOrigin:', dataOriginPath, 'currentDoc:', currentPath);
798
540
  return;
799
541
  }
800
- // Connection identity guard: verify the realtime connection is
801
- // bound to the same document we're currently editing.
802
542
  const connectedPath = realtime.connectedDocumentPath;
803
543
  if (currentPath !== connectedPath) {
804
544
  console.warn('[P1PuckProvider] saveData SKIPPED: connection identity mismatch.', 'currentDoc:', currentPath, 'connectedDoc:', connectedPath);
805
545
  return;
806
546
  }
807
- // Local user edit — send via WebSocket (DO handles persistence)
808
- // Echo prevention is handled at lower layers:
809
- // - puckDataToYMap no-ops when Y.Doc already has identical data
810
- // - RealtimeClient.lastSentSnapshot drops sends matching last sent/received
811
547
  const actions = pendingActionsRef.current;
812
548
  if (actions.length > 0) {
813
549
  realtime.applyLocalChange(data, actions);
@@ -817,21 +553,11 @@ userRole = 'editor', children, }) {
817
553
  }
818
554
  trackSentData(data);
819
555
  pendingActionsRef.current = [];
820
- // Data sent via WebSocket — DO handles persistence.
821
- // Update save status directly (skip debouncedSave/performSave chain).
822
556
  setSaveStatus('saved');
823
557
  setLastSaved(new Date());
824
- // NOTE: intentionally do NOT update latestVersionDataRef here. This
825
- // is the optimistic local send, before the DO confirms persistence —
826
- // caching it would assert content that may never be saved. Realtime
827
- // returns use the live Yjs snapshot (or the server re-fetch), not
828
- // this cache (PCC-3421).
829
558
  return;
830
559
  }
831
560
  }
832
- // Mirror the realtime guard: don't save when previewing a historical version.
833
- // Without this, the PuckDataSynchronizer echo from loadVersion resumes the
834
- // debounce and writes historical data to the server.
835
561
  if (viewingVersionRef.current !== null) {
836
562
  return;
837
563
  }
@@ -842,15 +568,12 @@ userRole = 'editor', children, }) {
842
568
  }
843
569
  debouncedSave();
844
570
  }, [debouncedSave, enableRealtime, realtime.connected, trackSentData]);
845
- // Force immediate save
846
571
  const saveNow = useCallback(async () => {
847
572
  debouncedSave.cancel();
848
- // When realtime is connected, ensure the latest data is sent via WebSocket
849
573
  if (enableRealtime && realtimeConnectedRef.current && pendingDataRef.current) {
850
574
  const currentPath = currentDocumentRef.current?.path ?? null;
851
575
  const dataOriginPath = currentDataDocumentPathRef.current;
852
576
  const connectedPath = realtime.connectedDocumentPath;
853
- // Data origin + connection identity guards
854
577
  if (dataOriginPath === currentPath && currentPath === connectedPath) {
855
578
  realtime.applyLocalChange(pendingDataRef.current);
856
579
  pendingDataRef.current = null;
@@ -858,13 +581,9 @@ userRole = 'editor', children, }) {
858
581
  setLastSaved(new Date());
859
582
  return;
860
583
  }
861
- // Fall through to REST save if mismatch
862
584
  }
863
585
  await performSave();
864
586
  }, [debouncedSave, performSave, enableRealtime]);
865
- // Persist current in-memory edits as a REST version before a destructive operation.
866
- // Realtime: confirms WebSocket delivery then snapshots latestLocalDataRef as a version.
867
- // Non-realtime: delegates to performSave which creates a version from pendingDataRef.
868
587
  const persistCurrentEdits = useCallback(async () => {
869
588
  if (!enableRealtime || !realtimeConnectedRef.current) {
870
589
  await performSave();
@@ -890,55 +609,38 @@ userRole = 'editor', children, }) {
890
609
  }
891
610
  }
892
611
  }, [enableRealtime, realtime, performSave, userClient, siteId, branchId]);
893
- // Load document by path
894
612
  const loadDocument = useCallback(async (path) => {
895
- // Flush any pending debounced save while the outgoing document's
896
- // identity is still current, then drop the pending buffer so a late
897
- // debounce can never write one document's data into another.
898
613
  if (pendingDataRef.current) {
899
614
  debouncedSave.cancel();
900
615
  await performSave();
901
- // performSave clears the buffer only on success; if it survived, the
902
- // flush failed — abort the switch instead of discarding the edits.
903
616
  if (pendingDataRef.current) {
904
617
  return;
905
618
  }
906
619
  }
907
620
  debouncedSave.cancel();
908
621
  pendingDataRef.current = null;
909
- // Invalidate cached local edit so returnToLatest() doesn't treat it as unsaved.
910
622
  latestLocalDataRef.current = null;
911
- // Cancel any pending remote sync to prevent race conditions
912
- // where a stale remote update overrides the document we're loading
913
623
  cancelPendingRemoteSync();
914
- // Increment load request counter and capture for staleness detection.
915
- // If a newer loadDocument call starts before our awaits resolve, the
916
- // counter will have been incremented again and our captured value
917
- // will no longer match — signaling that our response is stale.
918
624
  loadRequestIdRef.current += 1;
919
625
  const thisRequestId = loadRequestIdRef.current;
920
626
  setDocumentLoading(true);
921
- // Pre-clear the current document so nothing can save against the old
922
- // one while the fetch is in flight. The canvas keeps showing the
923
- // previous document — documentLoading suppresses the empty state.
924
627
  commitCurrentData(null, null);
925
628
  try {
926
- const doc = await userClient.documents.getByPath(siteId, path.startsWith('/') ? path.slice(1) : path, branchId);
927
- // Staleness check: a newer loadDocument call has started
629
+ const doc = await userClient.documents
630
+ .getByPath(siteId, path.startsWith('/') ? path.slice(1) : path, branchId)
631
+ .catch((err) => {
632
+ throw isNotFoundStatus(err) ? new DocumentPathNotFoundError(path) : err;
633
+ });
928
634
  if (thisRequestId !== loadRequestIdRef.current) {
929
635
  console.debug('[P1PuckProvider] Stale loadDocument — skipping (after getByPath)');
930
636
  return;
931
637
  }
932
- // Use ID not path — paths may differ by normalization, causing a false disconnect.
933
- // Same-doc keeps Yjs alive; cross-doc nulls out so useRealtime seeds a fresh Y.Doc.
934
638
  const isSameDocument = doc.id === currentDocumentRef.current?.id;
935
- // Null data before identity update so useRealtime sees initialData=null.
936
639
  commitCurrentData(null, null);
937
640
  if (!isSameDocument) {
938
641
  setCurrentDocument(null);
939
642
  }
940
643
  setCurrentDocument(doc);
941
- // Fetch template if document is bound to one (PROPOSAL-010)
942
644
  if (doc.templateId) {
943
645
  try {
944
646
  if (!userClient.templates) {
@@ -955,81 +657,47 @@ userRole = 'editor', children, }) {
955
657
  else {
956
658
  setCurrentTemplate(null);
957
659
  }
958
- // Get latest version
959
660
  const version = await userClient.versions.getLatest(siteId, branchId, doc.id);
960
661
  const puckData = snapshotToPuckData(version.snapshot);
961
- // Staleness check: a newer loadDocument call has started
962
662
  if (thisRequestId !== loadRequestIdRef.current) {
963
663
  console.debug('[P1PuckProvider] Stale loadDocument — skipping (after getLatest)');
964
664
  return;
965
665
  }
966
- // Mark REST data as non-local to prevent the Puck onChange handler
967
- // from echoing it back through the Y.Doc via applyLocalChange.
968
- // This is critical after the Hibernatable WS migration because
969
- // initializeIfNeeded() latency can invert the ordering of WebSocket
970
- // initial state vs REST response, causing the remote-update guard
971
- // (pendingRemoteUpdatesRef) to be bypassed.
972
666
  if (enableRealtime) {
973
667
  pendingRemoteUpdatesRef.current += 1;
974
- // Safety net: reset after React has processed the state updates.
975
- // If Puck's data is identical to current editor state, onChange
976
- // won't fire and the counter would never be decremented naturally.
977
- // Leaving it > 0 would cause subsequent local edits to be dropped.
978
668
  setTimeout(() => {
979
669
  pendingRemoteUpdatesRef.current = 0;
980
670
  }, 100);
981
671
  }
982
- // IMPORTANT: Update the ref BEFORE state updates
983
- // Setting to null allows remote updates after document loads
984
672
  viewingVersionRef.current = null;
985
- // Cancel any pending PuckDataCapture catch-up timer and reset its
986
- // ref before arming the data-origin guard below. Without this, the
987
- // timer can fire in the window between currentDataDocumentPathRef
988
- // being set to doc.path and the new document's first remote update
989
- // arriving — passing the origin check while still carrying the
990
- // previous document's Puck state.
991
673
  if (captureTimerRef.current) {
992
674
  clearTimeout(captureTimerRef.current);
993
675
  captureTimerRef.current = null;
994
676
  }
995
677
  realtimeDataCaptureRef.current = null;
996
678
  suppressNextSaveRef.current = puckData;
997
- // branchId is the branch this load requested, deliberately not
998
- // version.branchId: for an inherited document the backend serves main's
999
- // published version, so the version names main while the payload is what
1000
- // the requested branch should display. Recording main here would make
1001
- // every consumer reject the payload as foreign.
1002
679
  commitCurrentData(puckData, originFor(doc, branchId, version.id, false));
1003
680
  latestVersionDataRef.current = puckData;
1004
681
  setViewingVersion(null);
1005
- // Clear remoteSyncKey so document sync takes priority
1006
682
  setRemoteSyncKey(null);
1007
683
  pendingDataRef.current = null;
1008
684
  setSaveStatus('idle');
1009
685
  setSaveError(null);
1010
686
  }
1011
687
  catch (error) {
1012
- // Use warn, not error: callers handle this and console.error triggers
1013
- // the Next.js dev overlay unnecessarily.
1014
688
  console.warn('[P1PuckProvider] loadDocument failed:', error);
1015
- // Unload the current document so VersionBannerOverride shows the empty
1016
- // state instead of the previous document's content.
1017
689
  commitCurrentData(null, null);
1018
690
  setCurrentDocument(null);
1019
691
  throw error;
1020
692
  }
1021
693
  finally {
1022
- // Guarded so a stale call's completion can't clear the flag while a
1023
- // newer load is still in flight.
1024
694
  if (thisRequestId === loadRequestIdRef.current) {
1025
695
  setDocumentLoading(false);
1026
696
  }
1027
697
  }
1028
698
  }, [userClient, siteId, branchId, cancelPendingRemoteSync, enableRealtime, debouncedSave, performSave, commitCurrentData]);
1029
- // Load a specific version into the editor
699
+ loadDocForResetRef.current = loadDocument;
1030
700
  const loadVersion = useCallback(async (version) => {
1031
- // Cancel any pending remote sync to prevent race conditions
1032
- // where a stale remote update overrides the version we're loading
1033
701
  cancelPendingRemoteSync();
1034
702
  try {
1035
703
  const doc = currentDocumentRef.current;
@@ -1037,30 +705,19 @@ userRole = 'editor', children, }) {
1037
705
  throw new Error('No document loaded');
1038
706
  }
1039
707
  let versionToUse = version;
1040
- // If the version doesn't have snapshot data, try fetching it from the API
1041
708
  if (!version.snapshot || Object.keys(version.snapshot).length === 0) {
1042
709
  try {
1043
710
  versionToUse = await userClient.versions.get(siteId, branchId, doc.id, version.id);
1044
711
  }
1045
712
  catch (fetchError) {
1046
- // If fetching fails, log a warning and use the version as-is
1047
713
  console.warn('Could not fetch full version data, using version from list:', fetchError);
1048
714
  }
1049
715
  }
1050
- // Parses a JSON-string snapshot and falls back to blank Puck data for
1051
- // an empty/invalid snapshot (e.g. version 1's initial blank state).
1052
716
  const puckData = snapshotToPuckData(versionToUse.snapshot);
1053
- // IMPORTANT: Update the ref BEFORE state updates to block any incoming remote updates
1054
- // The effect that syncs viewingVersionRef runs AFTER render, which is too late
1055
- // Remote updates check this ref, so we need it set synchronously
1056
717
  viewingVersionRef.current = versionToUse;
1057
- // Historical: the document-sync plugin must not push this into the
1058
- // canvas that owns the live undo stack — the version bridge handles it.
1059
718
  commitCurrentData(puckData, originFor(doc, branchId, versionToUse.id, true));
1060
719
  setViewingVersion(versionToUse);
1061
- // Clear remoteSyncKey so version sync takes priority
1062
720
  setRemoteSyncKey(null);
1063
- // Pause auto-save when viewing historical version
1064
721
  debouncedSave.pause();
1065
722
  setAutoSavePaused(true);
1066
723
  pendingDataRef.current = null;
@@ -1071,13 +728,8 @@ userRole = 'editor', children, }) {
1071
728
  throw error;
1072
729
  }
1073
730
  }, [userClient, siteId, branchId, debouncedSave, cancelPendingRemoteSync, commitCurrentData]);
1074
- // Return to the latest version
1075
731
  const returnToLatest = useCallback(async () => {
1076
- // Cancel any pending remote sync to prevent race conditions
1077
- // The latest data will be loaded fresh, then remote sync can resume
1078
732
  cancelPendingRemoteSync();
1079
- // Get current state from Yjs if realtime is connected
1080
- // This ensures we get any changes made by other users while viewing history
1081
733
  let dataToRestore = null;
1082
734
  if (enableRealtime && realtime.connected) {
1083
735
  const currentYjsData = realtime.getSnapshot();
@@ -1085,20 +737,12 @@ userRole = 'editor', children, }) {
1085
737
  dataToRestore = currentYjsData;
1086
738
  }
1087
739
  }
1088
- // 1. Edits typed inside the debounce window — cleared from pendingDataRef by loadVersion but still live here.
1089
740
  const hadLocalEdits = !!latestLocalDataRef.current;
1090
741
  dataToRestore ??= latestLocalDataRef.current;
1091
- latestLocalDataRef.current = null; // consume — prevents stale ref short-circuiting server fetch on next history visit
1092
- // Re-fetch the true latest from the server when no live Yjs snapshot is
1093
- // available. The cached latestVersionDataRef is only a last-resort fallback
1094
- // (e.g. no current document); it is NOT used on fetch failure — silently
1095
- // editing on a possibly-stale snapshot and resuming autosave is the exact
1096
- // data-loss path from PCC-3421. On failure we notify and abort instead,
1097
- // keeping the user in read-only preview so nothing is overwritten.
742
+ latestLocalDataRef.current = null;
1098
743
  if (!dataToRestore) {
1099
744
  const doc = currentDocumentRef.current;
1100
745
  if (doc) {
1101
- // 2. Authoritative server state — the only source that reflects saves from other tabs, users, or agents.
1102
746
  setIsReturningToLatest(true);
1103
747
  try {
1104
748
  const version = await userClient.versions.getLatest(siteId, branchId, doc.id);
@@ -1116,16 +760,10 @@ userRole = 'editor', children, }) {
1116
760
  setIsReturningToLatest(false);
1117
761
  }
1118
762
  }
1119
- // 3. No document context — can't fetch; use last value written by performSave.
1120
763
  dataToRestore ??= latestVersionDataRef.current;
1121
764
  }
1122
765
  if (dataToRestore) {
1123
- // IMPORTANT: Update the ref BEFORE state updates
1124
- // Setting to null allows remote updates to resume
1125
766
  viewingVersionRef.current = null;
1126
- // Suppress the PuckDataSynchronizer echo: setCurrentData triggers a setData
1127
- // dispatch and Puck fires onChange → saveData. Arm the ref so saveData drops
1128
- // that echo; we control the save explicitly below.
1129
767
  suppressNextSaveRef.current = dataToRestore;
1130
768
  if (enableRealtime) {
1131
769
  pendingRemoteUpdatesRef.current += 1;
@@ -1136,14 +774,9 @@ userRole = 'editor', children, }) {
1136
774
  commitCurrentData(dataToRestore, originFor(currentDocumentRef.current, branchId, null, false));
1137
775
  latestVersionDataRef.current = dataToRestore;
1138
776
  setViewingVersion(null);
1139
- // Clear remoteSyncKey so latest version sync takes priority
1140
777
  setRemoteSyncKey(null);
1141
- // Resume auto-save
1142
778
  debouncedSave.resume();
1143
779
  setAutoSavePaused(false);
1144
- // Only queue a save when restoring from local in-memory edits — those were
1145
- // never persisted. Data from the server fetch is already the latest saved
1146
- // version; queuing a save would create a spurious identical version.
1147
780
  if (hadLocalEdits) {
1148
781
  pendingDataRef.current = dataToRestore;
1149
782
  debouncedSave();
@@ -1151,12 +784,7 @@ userRole = 'editor', children, }) {
1151
784
  setSaveStatus('idle');
1152
785
  }
1153
786
  }, [debouncedSave, cancelPendingRemoteSync, enableRealtime, realtime, userClient, siteId, branchId, showErrorNotifications, notificationContext, commitCurrentData]);
1154
- // Computed property for whether viewing historical version
1155
787
  const isViewingHistoricalVersion = viewingVersion !== null;
1156
- // =========================================================================
1157
- // Content Type Templates: Permission Resolution (PROPOSAL-010)
1158
- // =========================================================================
1159
- // Resolve template: either from document binding or registry path
1160
788
  const resolvedTemplate = useMemo(() => {
1161
789
  if (currentTemplate)
1162
790
  return currentTemplate;
@@ -1168,17 +796,12 @@ userRole = 'editor', children, }) {
1168
796
  return null;
1169
797
  return branchTemplates.find((t) => t.name === match[1]) ?? null;
1170
798
  }, [currentTemplate, currentDocument?.path, branchTemplates]);
1171
- // Create Puck permissions resolver based on current template and user role
1172
799
  const resolvePermissions = useMemo(() => createPuckPermissions(resolvedTemplate, userRole, isViewingHistoricalVersion), [resolvedTemplate, userRole, isViewingHistoricalVersion]);
1173
- // Show notification when template list fails to load
1174
800
  useEffect(() => {
1175
801
  if (templatesError && showErrorNotifications && notificationContext?.addError) {
1176
802
  notificationContext.addError(`Failed to load templates: ${templatesError.message}`, () => void refreshTemplates());
1177
803
  }
1178
804
  }, [templatesError]);
1179
- // =========================================================================
1180
- // Content Type Templates: Re-fetch template on tab focus (CUJ-15a)
1181
- // =========================================================================
1182
805
  useEffect(() => {
1183
806
  if (typeof document === 'undefined')
1184
807
  return;
@@ -1197,37 +820,26 @@ userRole = 'editor', children, }) {
1197
820
  });
1198
821
  }
1199
822
  catch {
1200
- // Silently ignore — stale template is better than no template
1201
823
  }
1202
824
  };
1203
825
  document.addEventListener('visibilitychange', handler);
1204
826
  return () => document.removeEventListener('visibilitychange', handler);
1205
827
  }, [siteId, userClient]);
1206
- // =========================================================================
1207
- // Phase 9: Presence & Agent Mode State
1208
- // =========================================================================
1209
- // Presence state (when presenceEnabled)
1210
828
  const [presenceActors, setPresenceActors] = useState([]);
1211
829
  const presenceInitializedRef = useRef(false);
1212
- // Track previous actors for onPresenceChange callback comparison
1213
830
  const prevActorsRef = useRef([]);
1214
- // Conflict notifications state
1215
831
  const [conflicts, setConflicts] = useState([]);
1216
- // Agent edit session state (when agentModeEnabled && agentId)
1217
832
  const [agentSession, setAgentSession] = useState(null);
1218
833
  const [agentEditLoading, setAgentEditLoading] = useState(false);
1219
834
  const [agentEditError, setAgentEditError] = useState(null);
1220
- // Fetch presence data
1221
835
  const fetchPresence = useCallback(async () => {
1222
836
  if (!presenceEnabled || !branchId)
1223
837
  return;
1224
838
  try {
1225
839
  const branchPresence = await userClient.presence.getBranchPresence(siteId, branchId);
1226
- // Filter out self and enrich with names
1227
840
  const filteredActors = branchPresence.actors.filter((actor) => actor.actorId !== userId);
1228
841
  const enrichedActors = enrichActorsWithNames(filteredActors);
1229
842
  setPresenceActors(enrichedActors);
1230
- // Call onPresenceChange if actors changed
1231
843
  if (onPresenceChange) {
1232
844
  const actorsChanged = JSON.stringify(enrichedActors.map((a) => a.id).sort()) !==
1233
845
  JSON.stringify(prevActorsRef.current.map((a) => a.id).sort());
@@ -1241,27 +853,19 @@ userRole = 'editor', children, }) {
1241
853
  console.error('Failed to fetch presence:', error);
1242
854
  }
1243
855
  }, [presenceEnabled, branchId, siteId, userId, userClient, onPresenceChange, enrichActorsWithNames]);
1244
- // Keep fetchPresence in a ref to avoid restarting the interval when callback changes
1245
856
  const fetchPresenceRef = useRef(fetchPresence);
1246
857
  useEffect(() => {
1247
858
  fetchPresenceRef.current = fetchPresence;
1248
859
  }, [fetchPresence]);
1249
- // Initial presence fetch and polling
1250
- // HTTP polling is skipped when WebSocket presence is active and connected
1251
860
  useEffect(() => {
1252
861
  if (!presenceEnabled)
1253
862
  return;
1254
- // Skip HTTP polling if WebSocket presence is handling updates
1255
863
  const shouldSkipPolling = wsPresenceActive && realtime.connected;
1256
- // Initial fetch (only if WS isn't active yet)
1257
864
  if (!presenceInitializedRef.current && !shouldSkipPolling) {
1258
865
  presenceInitializedRef.current = true;
1259
866
  void fetchPresenceRef.current();
1260
867
  }
1261
- // Set up polling - use ref to avoid restarting interval when callback changes
1262
- // Skip polling when WebSocket is handling presence
1263
868
  const intervalId = setInterval(() => {
1264
- // Check again at each interval - WS state may have changed
1265
869
  if (!wsPresenceActive || !realtime.connected) {
1266
870
  void fetchPresenceRef.current();
1267
871
  }
@@ -1270,29 +874,21 @@ userRole = 'editor', children, }) {
1270
874
  clearInterval(intervalId);
1271
875
  };
1272
876
  }, [presenceEnabled, presencePollingInterval, realtime.connected]);
1273
- // Reset presence when disabled
1274
877
  useEffect(() => {
1275
878
  if (!presenceEnabled) {
1276
879
  setPresenceActors([]);
1277
880
  presenceInitializedRef.current = false;
1278
881
  }
1279
882
  }, [presenceEnabled]);
1280
- // Reset WebSocket presence state when disconnected so the UI falls back
1281
- // to HTTP-polled presence data. Without this, wsPresenceActive stays
1282
- // true after disconnect and the UI shows stale WebSocket presence
1283
- // (e.g. an agent that already completed its edit session still appears).
1284
883
  useEffect(() => {
1285
884
  if (!realtime.connected) {
1286
885
  setWsPresenceActive(false);
1287
886
  setWsPresenceActors([]);
1288
887
  }
1289
888
  }, [realtime.connected]);
1290
- // Compute derived presence values
1291
- // Prefer WebSocket presence when connected for instant updates
1292
889
  const presenceState = useMemo(() => {
1293
890
  if (!presenceEnabled)
1294
891
  return null;
1295
- // Use WebSocket presence when active and connected, otherwise fall back to HTTP polling data
1296
892
  const effectiveActors = wsPresenceActive && realtime.connected ? wsPresenceActors : presenceActors;
1297
893
  const humans = effectiveActors.filter((actor) => actor.role === 'human');
1298
894
  const agents = effectiveActors.filter((actor) => actor.role === 'agent');
@@ -1307,16 +903,10 @@ userRole = 'editor', children, }) {
1307
903
  refresh: fetchPresence,
1308
904
  };
1309
905
  }, [presenceEnabled, presenceActors, wsPresenceActors, wsPresenceActive, realtime.connected, fetchPresence]);
1310
- // Keep presence in a ref so it can be read via getter without triggering
1311
- // context recreation. Presence changes frequently (focus region broadcasts)
1312
- // but shouldn't cause PuckDataSynchronizer or plugin re-renders.
1313
906
  const presenceStateRef = useRef(presenceState);
1314
907
  presenceStateRef.current = presenceState;
1315
908
  const humanPresenceKey = presenceIdentityKey(presenceState?.humans);
1316
909
  const agentPresenceKey = presenceIdentityKey(presenceState?.agents);
1317
- // =========================================================================
1318
- // Phase 9: Agent Edit Capabilities (when this client IS an agent)
1319
- // =========================================================================
1320
910
  const agentEditCapabilities = useMemo(() => {
1321
911
  if (!agentModeEnabled || !agentId)
1322
912
  return null;
@@ -1423,11 +1013,7 @@ userRole = 'editor', children, }) {
1423
1013
  agentEditLoading,
1424
1014
  agentEditError,
1425
1015
  ]);
1426
- // =========================================================================
1427
- // Phase 9: Agent Trigger (for human users to trigger agents)
1428
- // =========================================================================
1429
1016
  const triggerAgentFn = useMemo(() => {
1430
- // Only available when agentModeEnabled but no agentId (human user)
1431
1017
  if (!agentModeEnabled || agentId)
1432
1018
  return null;
1433
1019
  const documentPath = currentDocument?.path ?? null;
@@ -1436,7 +1022,6 @@ userRole = 'editor', children, }) {
1436
1022
  return { success: false, error: 'No document loaded' };
1437
1023
  }
1438
1024
  try {
1439
- // Check permission
1440
1025
  const permission = await userClient.agentEdit.canEdit(siteId, branchId, documentPath, {
1441
1026
  agentId: action.agentId,
1442
1027
  trigger: 'human_requested',
@@ -1447,7 +1032,6 @@ userRole = 'editor', children, }) {
1447
1032
  if (!permission.allowed) {
1448
1033
  return { success: false, error: permission.reason };
1449
1034
  }
1450
- // Start edit session
1451
1035
  const session = await userClient.agentEdit.startEdit(siteId, branchId, documentPath, {
1452
1036
  agentId: action.agentId,
1453
1037
  trigger: 'human_requested',
@@ -1465,13 +1049,9 @@ userRole = 'editor', children, }) {
1465
1049
  }
1466
1050
  };
1467
1051
  }, [agentModeEnabled, agentId, currentDocument, siteId, branchId, userId, userClient]);
1468
- // =========================================================================
1469
- // Phase 9: Conflict Notifications
1470
- // =========================================================================
1471
1052
  const dismissConflict = useCallback((id) => {
1472
1053
  setConflicts((prev) => prev.filter((c) => c.id !== id));
1473
1054
  }, []);
1474
- // Stop an agent's edit session (human-initiated)
1475
1055
  const handleStopAgent = useCallback(async (agent) => {
1476
1056
  const documentPath = currentDocumentRef.current?.path;
1477
1057
  if (!documentPath) {
@@ -1481,7 +1061,6 @@ userRole = 'editor', children, }) {
1481
1061
  try {
1482
1062
  await userClient.agentEdit.stopAgent(siteId, branchId, documentPath, agent.actorId);
1483
1063
  notificationContext.addSuccess(`Agent "${agent.name}" has been stopped`);
1484
- // Refresh presence to reflect the agent's removal
1485
1064
  if (presenceEnabled) {
1486
1065
  void fetchPresenceRef.current();
1487
1066
  }
@@ -1491,12 +1070,9 @@ userRole = 'editor', children, }) {
1491
1070
  notificationContext.addError(`Failed to stop agent: ${message}`);
1492
1071
  }
1493
1072
  }, [userClient, siteId, branchId, notificationContext, presenceEnabled]);
1494
- // Create checkpoint
1495
1073
  const createCheckpoint = useCallback(async (name) => {
1496
- // Save any pending changes first
1497
1074
  if (pendingDataRef.current) {
1498
1075
  if (enableRealtime && realtimeConnectedRef.current) {
1499
- // Data origin guard: only send if data belongs to current document
1500
1076
  const currentPath = currentDocumentRef.current?.path ?? null;
1501
1077
  if (currentDataDocumentPathRef.current === currentPath) {
1502
1078
  realtime.applyLocalChange(pendingDataRef.current);
@@ -1508,8 +1084,6 @@ userRole = 'editor', children, }) {
1508
1084
  await performSave();
1509
1085
  }
1510
1086
  }
1511
- // When realtime is connected, wait for the DO to acknowledge receipt of
1512
- // all preceding WebSocket messages before creating the checkpoint.
1513
1087
  if (enableRealtime && realtimeConnectedRef.current) {
1514
1088
  try {
1515
1089
  await realtime.waitForDelivery();
@@ -1525,13 +1099,11 @@ userRole = 'editor', children, }) {
1525
1099
  });
1526
1100
  return checkpoint;
1527
1101
  }, [userClient, siteId, branchId, debouncedSave, performSave, enableRealtime, realtime]);
1528
- // Publish current document
1529
1102
  const publishDocument = useCallback(async () => {
1530
1103
  const doc = currentDocumentRef.current;
1531
1104
  if (!doc) {
1532
1105
  throw new Error('No document loaded to publish');
1533
1106
  }
1534
- // Send any pending changes before publish
1535
1107
  if (pendingDataRef.current) {
1536
1108
  if (enableRealtime && realtimeConnectedRef.current) {
1537
1109
  const currentPath = currentDocumentRef.current?.path ?? null;
@@ -1545,10 +1117,6 @@ userRole = 'editor', children, }) {
1545
1117
  await performSave();
1546
1118
  }
1547
1119
  }
1548
- // When realtime is connected, use WebSocket-driven publish.
1549
- // The DO handles the entire flow: flush CRDT to Postgres, then
1550
- // create the checkpoint. TCP ordering guarantees all preceding
1551
- // binary CRDT updates are processed before the publish_request.
1552
1120
  if (enableRealtime && realtimeConnectedRef.current) {
1553
1121
  const publishResult = await realtime.requestPublish();
1554
1122
  if (!publishResult.success) {
@@ -1559,61 +1127,29 @@ userRole = 'editor', children, }) {
1559
1127
  }
1560
1128
  return publishResult.checkpoint;
1561
1129
  }
1562
- // Fallback: HTTP publish when realtime is not connected
1563
1130
  const result = await userClient.documents.publish(siteId, branchId, doc.id);
1564
1131
  return result.checkpoint;
1565
1132
  }, [userClient, siteId, branchId, debouncedSave, performSave, enableRealtime, realtime]);
1566
- // Tracks the currently in-flight switchBranch flush (everything after the
1567
- // synchronous branch-selection commit), so a rapid re-selection can await
1568
- // the prior call instead of racing it. See switchBranch below.
1569
1133
  const switchBranchInFlightRef = useRef(null);
1570
- // Switch branch
1571
1134
  const switchBranch = useCallback(async (newBranchId) => {
1572
- // Commit the new workstream selection FIRST, synchronously, before the
1573
- // async save-flush below. The UI (WorkstreamSwitcher) fires this inside
1574
- // startTransition without awaiting it, so its "Switching..." busy state
1575
- // clears as soon as this function's synchronous portion returns — well
1576
- // before a save-flush of the outgoing document would resolve. If the
1577
- // user navigates to a different document in that window, this provider
1578
- // can remount (the editor route has no layout.tsx to shield it across
1579
- // param changes), and its branchId initializer re-reads sessionStorage.
1580
- // Persisting here — before the flush, not after — guarantees that read
1581
- // always sees the newly selected branch instead of a stale one,
1582
- // preventing the workstream selector from snapping back to Live/main.
1583
1135
  setBranchId(newBranchId);
1584
1136
  persistBranchId(newBranchId);
1585
1137
  const branch = branches.find((b) => b.id === newBranchId);
1586
1138
  setCurrentBranch(branch ?? null);
1587
- // Serialize the flush portion below against any switchBranch call
1588
- // still in flight. Because the branch commit above is synchronous but
1589
- // the flush is not, a rapid second call (the switcher fires onSwitch
1590
- // fire-and-forget, so its busy state clears well before the flush
1591
- // resolves) can otherwise start its own flush while pendingDataRef
1592
- // still holds the first call's outgoing edit — grabbing a performSave
1593
- // closure already pointed at the intermediate branch and saving that
1594
- // same edit a second time, to the wrong branch. Awaiting the prior
1595
- // in-flight flush first guarantees only one flush ever reads/clears
1596
- // pendingDataRef at a time, and that each flush's performSave targets
1597
- // the branch that was current when that flush actually started.
1598
1139
  const previousSwitch = switchBranchInFlightRef.current;
1599
1140
  const thisSwitch = (async () => {
1600
1141
  if (previousSwitch) {
1601
- // A prior switch's flush failure shouldn't block this one.
1602
1142
  await previousSwitch.catch(() => undefined);
1603
1143
  }
1604
- // Cancel any pending remote sync - we're switching to a different branch
1605
1144
  cancelPendingRemoteSync();
1606
- // Save any pending changes on the outgoing document/branch first
1607
1145
  if (pendingDataRef.current) {
1608
1146
  debouncedSave.cancel();
1609
1147
  await performSave();
1610
1148
  }
1611
- // IMPORTANT: Update the ref BEFORE state updates
1612
1149
  viewingVersionRef.current = null;
1613
1150
  setCurrentDocument(null);
1614
1151
  commitCurrentData(null, null);
1615
1152
  setViewingVersion(null);
1616
- // Clear remoteSyncKey so new branch document sync takes priority
1617
1153
  setRemoteSyncKey(null);
1618
1154
  pendingDataRef.current = null;
1619
1155
  setSaveStatus('idle');
@@ -1629,7 +1165,6 @@ userRole = 'editor', children, }) {
1629
1165
  }
1630
1166
  }
1631
1167
  }, [branches, debouncedSave, performSave, cancelPendingRemoteSync, persistBranchId, commitCurrentData]);
1632
- // Presence context value (for hooks like useFocusRegionReporting)
1633
1168
  const presenceContextValue = useMemo(() => {
1634
1169
  if (!presenceEnabled)
1635
1170
  return null;
@@ -1649,8 +1184,6 @@ userRole = 'editor', children, }) {
1649
1184
  ?.filter(a => a.state === 'editing')
1650
1185
  ?.flatMap(a => a.focusRegions ?? []) ?? [],
1651
1186
  isAgentEditing: presenceState?.hasActiveAgents ?? false,
1652
- // Agent edit functions - placeholder implementations
1653
- // These are handled by agentEditCapabilities in the main context
1654
1187
  canEdit: async () => ({ allowed: true }),
1655
1188
  startEdit: async () => ({ sessionId: '' }),
1656
1189
  completeEdit: async () => ({ success: true }),
@@ -1667,13 +1200,6 @@ userRole = 'editor', children, }) {
1667
1200
  realtime.connected,
1668
1201
  presenceState,
1669
1202
  ]);
1670
- // =========================================================================
1671
- // Stable Callback Wrappers
1672
- // =========================================================================
1673
- // Wrap volatile callbacks in refs + stable wrappers so consumers don't need
1674
- // to stabilize them manually. The ref is updated inline during render (safe
1675
- // because it's a ref write, not a state write) and the stable wrapper
1676
- // delegates to ref.current, ensuring the latest implementation is always used.
1677
1203
  const saveDataRef = useRef(saveData);
1678
1204
  saveDataRef.current = saveData;
1679
1205
  const stableSaveData = useCallback((data) => saveDataRef.current(data), []);
@@ -1698,9 +1224,8 @@ userRole = 'editor', children, }) {
1698
1224
  const switchBranchRef = useRef(switchBranch);
1699
1225
  switchBranchRef.current = switchBranch;
1700
1226
  const stableSwitchBranch = useCallback((newBranchId) => switchBranchRef.current(newBranchId), []);
1701
- // Get pending actions for forwarding to backend
1702
1227
  const getPendingActions = useCallback(() => {
1703
- return [...pendingActionsRef.current]; // Return copy to prevent external mutation
1228
+ return [...pendingActionsRef.current];
1704
1229
  }, []);
1705
1230
  const loadDocumentRef = useRef(loadDocument);
1706
1231
  loadDocumentRef.current = loadDocument;
@@ -1714,10 +1239,6 @@ userRole = 'editor', children, }) {
1714
1239
  const handleStopAgentRef = useRef(handleStopAgent);
1715
1240
  handleStopAgentRef.current = handleStopAgent;
1716
1241
  const stableStopAgent = useCallback((agent) => handleStopAgentRef.current(agent), []);
1717
- // =========================================================================
1718
- // Stable Getters (Items 2 & 3)
1719
- // =========================================================================
1720
- // Refs updated inline during render, exposed as stable getter callbacks.
1721
1242
  const saveStatusGetterRef = useRef(saveStatus);
1722
1243
  saveStatusGetterRef.current = saveStatus;
1723
1244
  const getSaveStatus = useCallback(() => saveStatusGetterRef.current, []);
@@ -1728,7 +1249,6 @@ userRole = 'editor', children, }) {
1728
1249
  saveErrorGetterRef.current = saveError;
1729
1250
  const getSaveError = useCallback(() => saveErrorGetterRef.current, []);
1730
1251
  const getHasUnsavedChanges = useCallback(() => pendingDataRef.current !== null, []);
1731
- // Data sync getters (Item 3)
1732
1252
  const currentDataGetterRef = useRef(currentData);
1733
1253
  currentDataGetterRef.current = currentData;
1734
1254
  const remoteSyncKeyGetterRef = useRef(remoteSyncKey);
@@ -1750,9 +1270,6 @@ userRole = 'editor', children, }) {
1750
1270
  return `doc-${doc.id}-latest`;
1751
1271
  return undefined;
1752
1272
  }, []);
1753
- // =========================================================================
1754
- // Plugin Composition (B.4)
1755
- // =========================================================================
1756
1273
  const resolvedFeatureConfig = useMemo(() => {
1757
1274
  if (featureConfig)
1758
1275
  return resolveFeatureConfig(featureConfig);
@@ -1763,27 +1280,24 @@ userRole = 'editor', children, }) {
1763
1280
  });
1764
1281
  }, [featureConfig, enableRealtime, presenceEnabled, agentModeEnabled]);
1765
1282
  const activePlugins = useMemo(() => {
1766
- const plugins = featurePlugins ?? DEFAULT_CSS_FEATURE_PLUGINS;
1283
+ const plugins = featurePlugins ?? DEFAULT_CCR_FEATURE_PLUGINS;
1767
1284
  return resolveActivePlugins(plugins, resolvedFeatureConfig);
1768
1285
  }, [featurePlugins, resolvedFeatureConfig]);
1769
- const pluginDeps = useMemo(() => ({
1286
+ const pluginDeps = useStablePluginDeps({
1770
1287
  client: userClient,
1771
1288
  siteId,
1772
1289
  branchId,
1773
1290
  userId,
1774
1291
  config: resolvedFeatureConfig,
1775
- }), [userClient, siteId, branchId, userId, resolvedFeatureConfig]);
1292
+ });
1776
1293
  const ComposedPluginProviders = useMemo(() => composeProviders(activePlugins, resolvedFeatureConfig, pluginDeps), [activePlugins, resolvedFeatureConfig, pluginDeps]);
1777
- // =========================================================================
1778
- // safeData (Item 4) — never null
1779
- // =========================================================================
1294
+ const featurePuckPlugins = useMemo(() => collectPuckPlugins(activePlugins, pluginDeps), [activePlugins, pluginDeps]);
1780
1295
  const EMPTY_PUCK_DATA = useMemo(() => ({ content: [], root: { props: {} } }), []);
1781
1296
  const lastGoodDataRef = useRef(EMPTY_PUCK_DATA);
1782
1297
  if (currentDocument && currentData) {
1783
1298
  lastGoodDataRef.current = currentData;
1784
1299
  }
1785
1300
  const safeData = lastGoodDataRef.current;
1786
- // Context value
1787
1301
  const contextValue = useMemo(() => ({
1788
1302
  client: userClient,
1789
1303
  notifications: notificationContext,
@@ -1805,16 +1319,13 @@ userRole = 'editor', children, }) {
1805
1319
  createCheckpoint: stableCreateCheckpoint,
1806
1320
  publishDocument: stablePublishDocument,
1807
1321
  switchBranch: stableSwitchBranch,
1808
- // Stable getters (Items 2, 3)
1809
1322
  getSaveStatus,
1810
1323
  getLastSaved,
1811
1324
  getSaveError,
1812
1325
  getHasUnsavedChanges,
1813
1326
  getSyncData,
1814
1327
  getDataSyncKey,
1815
- // safeData (Item 4)
1816
1328
  safeData,
1817
- // Documents (Item 8)
1818
1329
  documents: branchDocuments,
1819
1330
  documentsLoading,
1820
1331
  refreshDocuments,
@@ -1833,9 +1344,6 @@ userRole = 'editor', children, }) {
1833
1344
  pauseAutoSave: stablePauseAutoSave,
1834
1345
  resumeAutoSave: stableResumeAutoSave,
1835
1346
  viewingVersion,
1836
- // Exposed for type/back-compat only — nothing renders from it. Read from
1837
- // the ref; the memo already recomputes each save (via lastSaved), so this
1838
- // stays adequately fresh without being a reactive dependency.
1839
1347
  latestVersionData: latestVersionDataRef.current,
1840
1348
  isViewingHistoricalVersion,
1841
1349
  isReturningToLatest,
@@ -1844,16 +1352,9 @@ userRole = 'editor', children, }) {
1844
1352
  realtimeEnabled: enableRealtime,
1845
1353
  realtimeConnected: realtime.connected,
1846
1354
  remoteSyncKey,
1847
- // WebSocket presence - send focus regions via WebSocket when connected
1848
1355
  sendFocusRegions: realtime.sendFocusRegions,
1849
- // Puck action metadata capture - pass as onAction to <Puck>
1850
1356
  handleAction,
1851
- // Get pending actions for backend forwarding (PROPOSAL-010)
1852
1357
  getPendingActions,
1853
- // Phase 9: Presence & Agent values
1854
- // Use getter to avoid context recreation on every focus-region update.
1855
- // Expose humanPresenceCount/hasActiveHumans/hasActiveAgents directly so every
1856
- // join/leave triggers context re-renders and causes consumers to re-invoke the getter.
1857
1358
  get presence() { return presenceStateRef.current; },
1858
1359
  hasActiveHumans: presenceState?.hasActiveHumans ?? false,
1859
1360
  humanPresenceCount: presenceState?.humans.length ?? 0,
@@ -1863,12 +1364,10 @@ userRole = 'editor', children, }) {
1863
1364
  stopAgent: stableStopAgent,
1864
1365
  conflicts,
1865
1366
  dismissConflict,
1866
- // Feature configuration (Phase B.5)
1867
1367
  featureConfig: resolvedFeatureConfig,
1868
- // Internal: realtime data capture for catch-up (sends missed keystrokes)
1368
+ featurePuckPlugins,
1869
1369
  _realtimeDataCaptureRef: enableRealtime ? realtimeDataCaptureRef : null,
1870
1370
  _onRealtimeDataCapture: enableRealtime ? handleRealtimeDataCapture : null,
1871
- // Content Type Templates (PROPOSAL-010)
1872
1371
  userRole,
1873
1372
  templates: branchTemplates,
1874
1373
  templatesLoading,
@@ -1930,11 +1429,6 @@ userRole = 'editor', children, }) {
1930
1429
  handleAction,
1931
1430
  getPendingActions,
1932
1431
  handleRealtimeDataCapture,
1933
- // Phase 9 dependencies (full presenceState excluded — accessed via getter/ref,
1934
- // but humanPresenceCount/hasActiveHumans/hasActiveAgents are direct values so every
1935
- // join/leave triggers re-renders regardless of how many actors are present).
1936
- // The roster keys cover the changes a count cannot see: a same-frame join/leave of
1937
- // two humans would keep the count the same but change the roster.
1938
1432
  humanPresenceKey,
1939
1433
  agentPresenceKey,
1940
1434
  presenceState?.humans.length,
@@ -1947,7 +1441,7 @@ userRole = 'editor', children, }) {
1947
1441
  dismissConflict,
1948
1442
  enableRealtime,
1949
1443
  resolvedFeatureConfig,
1950
- // Content Type Templates
1444
+ featurePuckPlugins,
1951
1445
  userRole,
1952
1446
  branchTemplates,
1953
1447
  templatesLoading,
@@ -1956,8 +1450,7 @@ userRole = 'editor', children, }) {
1956
1450
  currentTemplate,
1957
1451
  resolvePermissions,
1958
1452
  ]);
1959
- // Wrap with PresenceContext.Provider when presence is enabled
1960
1453
  const wrappedChildren = presenceContextValue ? (_jsx(PresenceContext.Provider, { value: presenceContextValue, children: children })) : (children);
1961
- return (_jsx(P1PuckContext.Provider, { value: contextValue, children: _jsx(ComposedPluginProviders, { children: wrappedChildren }) }));
1454
+ return (_jsx(P1SdkQueryProvider, { children: _jsx(P1PuckContext.Provider, { value: contextValue, children: _jsx(ComposedPluginProviders, { children: wrappedChildren }) }) }));
1962
1455
  }
1963
1456
  //# sourceMappingURL=P1PuckProvider.js.map