@wordpress/editor 14.41.1-next.v.202603102151.0 → 14.42.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 (301) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/build/components/collaborators-overlay/avatar-iframe-styles.cjs +12 -4
  3. package/build/components/collaborators-overlay/avatar-iframe-styles.cjs.map +2 -2
  4. package/build/components/collaborators-overlay/compute-selection.cjs +181 -0
  5. package/build/components/collaborators-overlay/compute-selection.cjs.map +7 -0
  6. package/build/components/collaborators-overlay/cursor-dom-utils.cjs +243 -0
  7. package/build/components/collaborators-overlay/cursor-dom-utils.cjs.map +7 -0
  8. package/build/components/collaborators-overlay/overlay-iframe-styles.cjs +6 -0
  9. package/build/components/collaborators-overlay/overlay-iframe-styles.cjs.map +2 -2
  10. package/build/components/collaborators-overlay/overlay.cjs +61 -37
  11. package/build/components/collaborators-overlay/overlay.cjs.map +2 -2
  12. package/build/components/collaborators-overlay/timing-utils.cjs +46 -0
  13. package/build/components/collaborators-overlay/timing-utils.cjs.map +7 -0
  14. package/build/components/collaborators-overlay/use-block-highlighting.cjs +5 -6
  15. package/build/components/collaborators-overlay/use-block-highlighting.cjs.map +2 -2
  16. package/build/components/collaborators-overlay/use-render-cursors.cjs +50 -140
  17. package/build/components/collaborators-overlay/use-render-cursors.cjs.map +3 -3
  18. package/build/components/collaborators-presence/index.cjs +38 -12
  19. package/build/components/collaborators-presence/index.cjs.map +2 -2
  20. package/build/components/collaborators-presence/list.cjs +27 -24
  21. package/build/components/collaborators-presence/list.cjs.map +2 -2
  22. package/build/components/collaborators-presence/use-collaborator-notifications.cjs +79 -107
  23. package/build/components/collaborators-presence/use-collaborator-notifications.cjs.map +3 -3
  24. package/build/components/editor-interface/index.cjs +9 -6
  25. package/build/components/editor-interface/index.cjs.map +2 -2
  26. package/build/components/inserter-sidebar/index.cjs +2 -1
  27. package/build/components/inserter-sidebar/index.cjs.map +2 -2
  28. package/build/components/page-attributes/parent.cjs +1 -1
  29. package/build/components/page-attributes/parent.cjs.map +2 -2
  30. package/build/components/post-locked-modal/index.cjs +16 -3
  31. package/build/components/post-locked-modal/index.cjs.map +2 -2
  32. package/build/components/post-revisions-preview/block-diff.cjs +39 -11
  33. package/build/components/post-revisions-preview/block-diff.cjs.map +2 -2
  34. package/build/components/post-revisions-preview/diff-markers.cjs +2 -2
  35. package/build/components/post-revisions-preview/diff-markers.cjs.map +2 -2
  36. package/build/components/post-revisions-preview/revisions-canvas.cjs +12 -75
  37. package/build/components/post-revisions-preview/revisions-canvas.cjs.map +3 -3
  38. package/build/components/post-revisions-preview/revisions-slider.cjs +5 -1
  39. package/build/components/post-revisions-preview/revisions-slider.cjs.map +2 -2
  40. package/build/components/post-template/block-theme.cjs +7 -4
  41. package/build/components/post-template/block-theme.cjs.map +2 -2
  42. package/build/components/post-template/hooks.cjs +39 -2
  43. package/build/components/post-template/hooks.cjs.map +2 -2
  44. package/build/components/post-template/panel.cjs +5 -42
  45. package/build/components/post-template/panel.cjs.map +3 -3
  46. package/build/components/preferences-modal/index.cjs +24 -0
  47. package/build/components/preferences-modal/index.cjs.map +2 -2
  48. package/build/components/provider/disable-non-page-content-blocks.cjs +31 -28
  49. package/build/components/provider/disable-non-page-content-blocks.cjs.map +3 -3
  50. package/build/components/provider/index.cjs +17 -5
  51. package/build/components/provider/index.cjs.map +2 -2
  52. package/build/components/provider/use-block-editor-settings.cjs +19 -5
  53. package/build/components/provider/use-block-editor-settings.cjs.map +3 -3
  54. package/build/components/provider/{use-post-content-blocks.cjs → use-post-content-block-types.cjs} +8 -19
  55. package/build/components/provider/use-post-content-block-types.cjs.map +7 -0
  56. package/build/components/provider/use-revision-blocks.cjs +106 -0
  57. package/build/components/provider/use-revision-blocks.cjs.map +7 -0
  58. package/build/components/revision-block-diff/index.cjs +84 -0
  59. package/build/components/revision-block-diff/index.cjs.map +7 -0
  60. package/build/components/sidebar/dataform-post-summary.cjs +36 -6
  61. package/build/components/sidebar/dataform-post-summary.cjs.map +2 -2
  62. package/build/components/sidebar/header.cjs +1 -1
  63. package/build/components/sidebar/header.cjs.map +2 -2
  64. package/build/components/sidebar/index.cjs +5 -1
  65. package/build/components/sidebar/index.cjs.map +3 -3
  66. package/build/components/{sync-connection-modal → sync-connection-error-modal}/index.cjs +90 -78
  67. package/build/components/sync-connection-error-modal/index.cjs.map +7 -0
  68. package/build/components/{sync-connection-modal → sync-connection-error-modal}/use-retry-countdown.cjs +14 -27
  69. package/build/components/sync-connection-error-modal/use-retry-countdown.cjs.map +7 -0
  70. package/build/components/template-content-panel/index.cjs +35 -31
  71. package/build/components/template-content-panel/index.cjs.map +3 -3
  72. package/build/components/visual-editor/index.cjs +2 -2
  73. package/build/components/visual-editor/index.cjs.map +2 -2
  74. package/build/store/actions.cjs +1 -3
  75. package/build/store/actions.cjs.map +2 -2
  76. package/build/store/private-actions.cjs +11 -2
  77. package/build/store/private-actions.cjs.map +2 -2
  78. package/build/store/private-selectors.cjs +52 -13
  79. package/build/store/private-selectors.cjs.map +2 -2
  80. package/build/store/reducer.cjs +12 -0
  81. package/build/store/reducer.cjs.map +2 -2
  82. package/build/utils/media-finalize/index.cjs +43 -0
  83. package/build/utils/media-finalize/index.cjs.map +7 -0
  84. package/build/utils/sync-error-messages.cjs +29 -16
  85. package/build/utils/sync-error-messages.cjs.map +3 -3
  86. package/build-module/components/collaborators-overlay/avatar-iframe-styles.mjs +12 -4
  87. package/build-module/components/collaborators-overlay/avatar-iframe-styles.mjs.map +2 -2
  88. package/build-module/components/collaborators-overlay/compute-selection.mjs +162 -0
  89. package/build-module/components/collaborators-overlay/compute-selection.mjs.map +7 -0
  90. package/build-module/components/collaborators-overlay/cursor-dom-utils.mjs +213 -0
  91. package/build-module/components/collaborators-overlay/cursor-dom-utils.mjs.map +7 -0
  92. package/build-module/components/collaborators-overlay/overlay-iframe-styles.mjs +6 -0
  93. package/build-module/components/collaborators-overlay/overlay-iframe-styles.mjs.map +2 -2
  94. package/build-module/components/collaborators-overlay/overlay.mjs +61 -37
  95. package/build-module/components/collaborators-overlay/overlay.mjs.map +2 -2
  96. package/build-module/components/collaborators-overlay/timing-utils.mjs +21 -0
  97. package/build-module/components/collaborators-overlay/timing-utils.mjs.map +7 -0
  98. package/build-module/components/collaborators-overlay/use-block-highlighting.mjs +5 -6
  99. package/build-module/components/collaborators-overlay/use-block-highlighting.mjs.map +2 -2
  100. package/build-module/components/collaborators-overlay/use-render-cursors.mjs +50 -140
  101. package/build-module/components/collaborators-overlay/use-render-cursors.mjs.map +2 -2
  102. package/build-module/components/collaborators-presence/index.mjs +39 -13
  103. package/build-module/components/collaborators-presence/index.mjs.map +2 -2
  104. package/build-module/components/collaborators-presence/list.mjs +27 -24
  105. package/build-module/components/collaborators-presence/list.mjs.map +2 -2
  106. package/build-module/components/collaborators-presence/use-collaborator-notifications.mjs +80 -108
  107. package/build-module/components/collaborators-presence/use-collaborator-notifications.mjs.map +2 -2
  108. package/build-module/components/editor-interface/index.mjs +10 -7
  109. package/build-module/components/editor-interface/index.mjs.map +2 -2
  110. package/build-module/components/inserter-sidebar/index.mjs +2 -1
  111. package/build-module/components/inserter-sidebar/index.mjs.map +2 -2
  112. package/build-module/components/page-attributes/parent.mjs +1 -1
  113. package/build-module/components/page-attributes/parent.mjs.map +2 -2
  114. package/build-module/components/post-locked-modal/index.mjs +16 -3
  115. package/build-module/components/post-locked-modal/index.mjs.map +2 -2
  116. package/build-module/components/post-revisions-preview/block-diff.mjs +39 -11
  117. package/build-module/components/post-revisions-preview/block-diff.mjs.map +2 -2
  118. package/build-module/components/post-revisions-preview/diff-markers.mjs +2 -2
  119. package/build-module/components/post-revisions-preview/diff-markers.mjs.map +2 -2
  120. package/build-module/components/post-revisions-preview/revisions-canvas.mjs +14 -80
  121. package/build-module/components/post-revisions-preview/revisions-canvas.mjs.map +2 -2
  122. package/build-module/components/post-revisions-preview/revisions-slider.mjs +5 -1
  123. package/build-module/components/post-revisions-preview/revisions-slider.mjs.map +2 -2
  124. package/build-module/components/post-template/block-theme.mjs +7 -4
  125. package/build-module/components/post-template/block-theme.mjs.map +2 -2
  126. package/build-module/components/post-template/hooks.mjs +37 -1
  127. package/build-module/components/post-template/hooks.mjs.map +2 -2
  128. package/build-module/components/post-template/panel.mjs +5 -42
  129. package/build-module/components/post-template/panel.mjs.map +2 -2
  130. package/build-module/components/preferences-modal/index.mjs +24 -0
  131. package/build-module/components/preferences-modal/index.mjs.map +2 -2
  132. package/build-module/components/provider/disable-non-page-content-blocks.mjs +31 -28
  133. package/build-module/components/provider/disable-non-page-content-blocks.mjs.map +2 -2
  134. package/build-module/components/provider/index.mjs +17 -5
  135. package/build-module/components/provider/index.mjs.map +2 -2
  136. package/build-module/components/provider/use-block-editor-settings.mjs +19 -5
  137. package/build-module/components/provider/use-block-editor-settings.mjs.map +2 -2
  138. package/build-module/components/provider/use-post-content-block-types.mjs +23 -0
  139. package/build-module/components/provider/use-post-content-block-types.mjs.map +7 -0
  140. package/build-module/components/provider/use-revision-blocks.mjs +81 -0
  141. package/build-module/components/provider/use-revision-blocks.mjs.map +7 -0
  142. package/build-module/components/revision-block-diff/index.mjs +53 -0
  143. package/build-module/components/revision-block-diff/index.mjs.map +7 -0
  144. package/build-module/components/sidebar/dataform-post-summary.mjs +36 -6
  145. package/build-module/components/sidebar/dataform-post-summary.mjs.map +2 -2
  146. package/build-module/components/sidebar/header.mjs +1 -1
  147. package/build-module/components/sidebar/header.mjs.map +2 -2
  148. package/build-module/components/sidebar/index.mjs +5 -1
  149. package/build-module/components/sidebar/index.mjs.map +2 -2
  150. package/build-module/components/sync-connection-error-modal/index.mjs +177 -0
  151. package/build-module/components/sync-connection-error-modal/index.mjs.map +7 -0
  152. package/build-module/components/sync-connection-error-modal/use-retry-countdown.mjs +36 -0
  153. package/build-module/components/sync-connection-error-modal/use-retry-countdown.mjs.map +7 -0
  154. package/build-module/components/template-content-panel/index.mjs +25 -31
  155. package/build-module/components/template-content-panel/index.mjs.map +2 -2
  156. package/build-module/components/visual-editor/index.mjs +2 -2
  157. package/build-module/components/visual-editor/index.mjs.map +2 -2
  158. package/build-module/store/actions.mjs +1 -3
  159. package/build-module/store/actions.mjs.map +2 -2
  160. package/build-module/store/private-actions.mjs +10 -2
  161. package/build-module/store/private-actions.mjs.map +2 -2
  162. package/build-module/store/private-selectors.mjs +50 -12
  163. package/build-module/store/private-selectors.mjs.map +2 -2
  164. package/build-module/store/reducer.mjs +11 -0
  165. package/build-module/store/reducer.mjs.map +2 -2
  166. package/build-module/utils/media-finalize/index.mjs +12 -0
  167. package/build-module/utils/media-finalize/index.mjs.map +7 -0
  168. package/build-module/utils/sync-error-messages.mjs +24 -16
  169. package/build-module/utils/sync-error-messages.mjs.map +3 -3
  170. package/build-style/style-rtl.css +95 -16
  171. package/build-style/style.css +95 -16
  172. package/build-types/components/collaborators-overlay/avatar-iframe-styles.d.ts +1 -1
  173. package/build-types/components/collaborators-overlay/avatar-iframe-styles.d.ts.map +1 -1
  174. package/build-types/components/collaborators-overlay/compute-selection.d.ts +24 -0
  175. package/build-types/components/collaborators-overlay/compute-selection.d.ts.map +1 -0
  176. package/build-types/components/collaborators-overlay/cursor-dom-utils.d.ts +72 -0
  177. package/build-types/components/collaborators-overlay/cursor-dom-utils.d.ts.map +1 -0
  178. package/build-types/components/collaborators-overlay/overlay-iframe-styles.d.ts +1 -1
  179. package/build-types/components/collaborators-overlay/overlay-iframe-styles.d.ts.map +1 -1
  180. package/build-types/components/collaborators-overlay/overlay.d.ts.map +1 -1
  181. package/build-types/components/collaborators-overlay/timing-utils.d.ts +11 -0
  182. package/build-types/components/collaborators-overlay/timing-utils.d.ts.map +1 -0
  183. package/build-types/components/collaborators-overlay/use-block-highlighting.d.ts.map +1 -1
  184. package/build-types/components/collaborators-overlay/use-render-cursors.d.ts +4 -0
  185. package/build-types/components/collaborators-overlay/use-render-cursors.d.ts.map +1 -1
  186. package/build-types/components/collaborators-presence/index.d.ts.map +1 -1
  187. package/build-types/components/collaborators-presence/list.d.ts +2 -1
  188. package/build-types/components/collaborators-presence/list.d.ts.map +1 -1
  189. package/build-types/components/collaborators-presence/use-collaborator-notifications.d.ts.map +1 -1
  190. package/build-types/components/editor-interface/index.d.ts.map +1 -1
  191. package/build-types/components/inserter-sidebar/index.d.ts.map +1 -1
  192. package/build-types/components/post-locked-modal/index.d.ts +2 -2
  193. package/build-types/components/post-locked-modal/index.d.ts.map +1 -1
  194. package/build-types/components/post-revisions-preview/block-diff.d.ts.map +1 -1
  195. package/build-types/components/post-revisions-preview/revisions-canvas.d.ts +2 -5
  196. package/build-types/components/post-revisions-preview/revisions-canvas.d.ts.map +1 -1
  197. package/build-types/components/post-revisions-preview/revisions-slider.d.ts.map +1 -1
  198. package/build-types/components/post-template/block-theme.d.ts +1 -3
  199. package/build-types/components/post-template/block-theme.d.ts.map +1 -1
  200. package/build-types/components/post-template/hooks.d.ts +1 -0
  201. package/build-types/components/post-template/hooks.d.ts.map +1 -1
  202. package/build-types/components/post-template/panel.d.ts.map +1 -1
  203. package/build-types/components/provider/disable-non-page-content-blocks.d.ts.map +1 -1
  204. package/build-types/components/provider/index.d.ts.map +1 -1
  205. package/build-types/components/provider/use-block-editor-settings.d.ts.map +1 -1
  206. package/build-types/components/provider/use-post-content-block-types.d.ts +9 -0
  207. package/build-types/components/provider/use-post-content-block-types.d.ts.map +1 -0
  208. package/build-types/components/provider/use-revision-blocks.d.ts +10 -0
  209. package/build-types/components/provider/use-revision-blocks.d.ts.map +1 -0
  210. package/build-types/components/revision-block-diff/index.d.ts +6 -0
  211. package/build-types/components/revision-block-diff/index.d.ts.map +1 -0
  212. package/build-types/components/sidebar/dataform-post-summary.d.ts.map +1 -1
  213. package/build-types/components/sidebar/index.d.ts.map +1 -1
  214. package/build-types/components/sync-connection-error-modal/index.d.ts +22 -0
  215. package/build-types/components/sync-connection-error-modal/index.d.ts.map +1 -0
  216. package/build-types/components/sync-connection-error-modal/use-retry-countdown.d.ts +11 -0
  217. package/build-types/components/sync-connection-error-modal/use-retry-countdown.d.ts.map +1 -0
  218. package/build-types/components/template-content-panel/index.d.ts.map +1 -1
  219. package/build-types/store/actions.d.ts.map +1 -1
  220. package/build-types/store/private-actions.d.ts +7 -0
  221. package/build-types/store/private-actions.d.ts.map +1 -1
  222. package/build-types/store/private-selectors.d.ts +7 -0
  223. package/build-types/store/private-selectors.d.ts.map +1 -1
  224. package/build-types/store/reducer.d.ts +14 -3
  225. package/build-types/store/reducer.d.ts.map +1 -1
  226. package/build-types/utils/media-finalize/index.d.ts +2 -0
  227. package/build-types/utils/media-finalize/index.d.ts.map +1 -0
  228. package/build-types/utils/sync-error-messages.d.ts +17 -3
  229. package/build-types/utils/sync-error-messages.d.ts.map +1 -1
  230. package/package.json +44 -44
  231. package/src/components/collaborators-overlay/avatar-iframe-styles.ts +12 -4
  232. package/src/components/collaborators-overlay/compute-selection.ts +307 -0
  233. package/src/components/collaborators-overlay/cursor-dom-utils.ts +382 -0
  234. package/src/components/collaborators-overlay/overlay-iframe-styles.ts +6 -0
  235. package/src/components/collaborators-overlay/overlay.tsx +59 -27
  236. package/src/components/collaborators-overlay/timing-utils.ts +30 -0
  237. package/src/components/collaborators-overlay/use-block-highlighting.ts +11 -10
  238. package/src/components/collaborators-overlay/use-render-cursors.ts +70 -242
  239. package/src/components/collaborators-presence/avatar/styles.scss +20 -4
  240. package/src/components/collaborators-presence/index.tsx +30 -5
  241. package/src/components/collaborators-presence/list.tsx +38 -24
  242. package/src/components/collaborators-presence/test/use-collaborator-notifications.ts +188 -246
  243. package/src/components/collaborators-presence/use-collaborator-notifications.ts +109 -166
  244. package/src/components/document-bar/style.scss +1 -1
  245. package/src/components/editor-interface/index.js +8 -6
  246. package/src/components/inserter-sidebar/index.js +4 -1
  247. package/src/components/page-attributes/parent.js +1 -1
  248. package/src/components/post-locked-modal/index.js +21 -3
  249. package/src/components/post-revisions-preview/block-diff.js +59 -20
  250. package/src/components/post-revisions-preview/diff-markers.js +2 -2
  251. package/src/components/post-revisions-preview/revisions-canvas.js +20 -98
  252. package/src/components/post-revisions-preview/revisions-slider.js +6 -1
  253. package/src/components/post-revisions-preview/test/block-diff.js +69 -31
  254. package/src/components/post-template/block-theme.js +4 -1
  255. package/src/components/post-template/hooks.js +42 -0
  256. package/src/components/post-template/panel.js +5 -59
  257. package/src/components/preferences-modal/index.js +18 -0
  258. package/src/components/provider/disable-non-page-content-blocks.js +42 -40
  259. package/src/components/provider/index.js +20 -2
  260. package/src/components/provider/use-block-editor-settings.js +21 -8
  261. package/src/components/provider/use-post-content-block-types.js +30 -0
  262. package/src/components/provider/use-revision-blocks.js +105 -0
  263. package/src/components/revision-block-diff/index.js +74 -0
  264. package/src/components/revision-block-diff/style.scss +13 -0
  265. package/src/components/sidebar/dataform-post-summary.js +61 -16
  266. package/src/components/sidebar/header.js +1 -1
  267. package/src/components/sidebar/index.js +2 -0
  268. package/src/components/sync-connection-error-modal/index.tsx +265 -0
  269. package/src/components/sync-connection-error-modal/style.scss +14 -0
  270. package/src/components/sync-connection-error-modal/use-retry-countdown.ts +57 -0
  271. package/src/components/template-content-panel/index.js +30 -38
  272. package/src/components/visual-editor/index.js +2 -2
  273. package/src/store/actions.js +1 -4
  274. package/src/store/private-actions.js +21 -2
  275. package/src/store/private-selectors.js +75 -10
  276. package/src/store/reducer.js +19 -0
  277. package/src/style.scss +2 -1
  278. package/src/utils/media-finalize/index.js +11 -0
  279. package/src/utils/media-finalize/test/index.js +34 -0
  280. package/src/utils/sync-error-messages.ts +72 -0
  281. package/src/utils/test/sync-error-messages.js +9 -32
  282. package/build/components/provider/use-post-content-blocks.cjs.map +0 -7
  283. package/build/components/sync-connection-modal/index.cjs.map +0 -7
  284. package/build/components/sync-connection-modal/use-retry-countdown.cjs.map +0 -7
  285. package/build-module/components/provider/use-post-content-blocks.mjs +0 -34
  286. package/build-module/components/provider/use-post-content-blocks.mjs.map +0 -7
  287. package/build-module/components/sync-connection-modal/index.mjs +0 -167
  288. package/build-module/components/sync-connection-modal/index.mjs.map +0 -7
  289. package/build-module/components/sync-connection-modal/use-retry-countdown.mjs +0 -49
  290. package/build-module/components/sync-connection-modal/use-retry-countdown.mjs.map +0 -7
  291. package/build-types/components/provider/use-post-content-blocks.d.ts +0 -2
  292. package/build-types/components/provider/use-post-content-blocks.d.ts.map +0 -1
  293. package/build-types/components/sync-connection-modal/index.d.ts +0 -8
  294. package/build-types/components/sync-connection-modal/index.d.ts.map +0 -1
  295. package/build-types/components/sync-connection-modal/use-retry-countdown.d.ts +0 -9
  296. package/build-types/components/sync-connection-modal/use-retry-countdown.d.ts.map +0 -1
  297. package/src/components/provider/use-post-content-blocks.js +0 -42
  298. package/src/components/sync-connection-modal/index.js +0 -200
  299. package/src/components/sync-connection-modal/style.scss +0 -9
  300. package/src/components/sync-connection-modal/use-retry-countdown.js +0 -70
  301. package/src/utils/sync-error-messages.js +0 -58
@@ -1,15 +1,16 @@
1
1
  /**
2
2
  * WordPress dependencies
3
3
  */
4
- import { usePrevious } from '@wordpress/compose';
5
4
  import { useDispatch, useSelect } from '@wordpress/data';
6
- import { useEffect } from '@wordpress/element';
5
+ import { useCallback } from '@wordpress/element';
7
6
  import { __, sprintf } from '@wordpress/i18n';
8
7
  import { store as noticesStore } from '@wordpress/notices';
9
8
  import {
10
9
  privateApis,
11
10
  type PostEditorAwarenessState,
11
+ type PostSaveEvent,
12
12
  } from '@wordpress/core-data';
13
+ import { store as preferencesStore } from '@wordpress/preferences';
13
14
 
14
15
  /**
15
16
  * Internal dependencies
@@ -17,7 +18,8 @@ import {
17
18
  import { unlock } from '../../lock-unlock';
18
19
  import { store as editorStore } from '../../store';
19
20
 
20
- const { useActiveCollaborators, useLastPostSave } = unlock( privateApis );
21
+ const { useOnCollaboratorJoin, useOnCollaboratorLeave, useOnPostSave } =
22
+ unlock( privateApis );
21
23
 
22
24
  /**
23
25
  * Notice IDs for each notification type. Using stable IDs prevents duplicate
@@ -29,12 +31,6 @@ const NOTIFICATION_TYPE = {
29
31
  COLLAB_USER_EXITED: 'collab-user-exited',
30
32
  } as const;
31
33
 
32
- const NOTIFICATIONS_CONFIG = {
33
- userEntered: true,
34
- userExited: true,
35
- postUpdated: true,
36
- };
37
-
38
34
  const PUBLISHED_STATUSES = [ 'publish', 'private', 'future' ];
39
35
 
40
36
  /**
@@ -72,72 +68,41 @@ export function useCollaboratorNotifications(
72
68
  postId: number | null,
73
69
  postType: string | null
74
70
  ): void {
75
- const activeCollaborators = useActiveCollaborators(
76
- postId,
77
- postType
78
- ) as PostEditorAwarenessState[];
79
-
80
- const lastPostSave = useLastPostSave( postId, postType );
81
-
82
- const { postStatus, isCollaborationEnabled } = useSelect( ( select ) => {
83
- const editorSel = select( editorStore );
84
- return {
85
- postStatus: editorSel.getCurrentPostAttribute( 'status' ) as
86
- | string
87
- | undefined,
88
- isCollaborationEnabled:
89
- editorSel.isCollaborationEnabledForCurrentPost(),
90
- };
91
- }, [] );
71
+ const { postStatus, isCollaborationEnabled, showNotifications } = useSelect(
72
+ ( select ) => {
73
+ const editorSel = select( editorStore );
74
+ return {
75
+ postStatus: editorSel.getCurrentPostAttribute( 'status' ) as
76
+ | string
77
+ | undefined,
78
+ isCollaborationEnabled:
79
+ editorSel.isCollaborationEnabledForCurrentPost(),
80
+ showNotifications:
81
+ select( preferencesStore ).get(
82
+ 'core',
83
+ 'showCollaborationNotifications'
84
+ ) ?? true,
85
+ };
86
+ },
87
+ []
88
+ );
92
89
 
93
90
  const { createNotice } = useDispatch( noticesStore );
94
91
 
95
- const prevCollaborators = usePrevious( activeCollaborators );
96
- const prevPostSave = usePrevious( lastPostSave );
97
-
98
- /*
99
- * Detect collaborator joins and leaves.
100
- */
101
- useEffect( () => {
102
- if ( ! isCollaborationEnabled ) {
103
- return;
104
- }
105
-
106
- /*
107
- * On first render usePrevious returns undefined. On subsequent renders
108
- * the list may still be empty while the store hydrates. In both cases,
109
- * skip to avoid spurious "X joined" toasts for users already present.
110
- */
111
- if ( ! prevCollaborators || prevCollaborators.length === 0 ) {
112
- return;
113
- }
114
-
115
- function notify( noticeId: string, message: string ) {
116
- void createNotice( 'info', message, {
117
- id: noticeId,
118
- type: 'snackbar',
119
- isDismissible: false,
120
- } );
121
- }
122
-
123
- const prevMap = new Map< number, PostEditorAwarenessState >(
124
- prevCollaborators.map( ( c ) => [ c.clientId, c ] )
125
- );
126
- const newMap = new Map< number, PostEditorAwarenessState >(
127
- activeCollaborators.map( ( c ) => [ c.clientId, c ] )
128
- );
129
-
130
- /*
131
- * Detect joins: new clientIds that weren't in the previous state.
132
- */
133
- if ( NOTIFICATIONS_CONFIG.userEntered ) {
134
- const me = activeCollaborators.find( ( c ) => c.isMe );
135
-
136
- for ( const [ clientId, collaborator ] of newMap ) {
137
- if ( prevMap.has( clientId ) || collaborator.isMe ) {
138
- continue;
139
- }
140
-
92
+ // Pass null when collaboration is disabled or notifications are
93
+ // turned off to prevent the hooks from subscribing to awareness state.
94
+ const shouldSubscribe = isCollaborationEnabled && showNotifications;
95
+ const effectivePostId = shouldSubscribe ? postId : null;
96
+ const effectivePostType = shouldSubscribe ? postType : null;
97
+
98
+ useOnCollaboratorJoin(
99
+ effectivePostId,
100
+ effectivePostType,
101
+ useCallback(
102
+ (
103
+ collaborator: PostEditorAwarenessState,
104
+ me?: PostEditorAwarenessState
105
+ ) => {
141
106
  /*
142
107
  * Skip collaborators who were present before the current user
143
108
  * joined. Their enteredAt is earlier than ours, meaning we're
@@ -148,111 +113,89 @@ export function useCollaboratorNotifications(
148
113
  collaborator.collaboratorInfo.enteredAt <
149
114
  me.collaboratorInfo.enteredAt
150
115
  ) {
151
- continue;
116
+ return;
152
117
  }
153
118
 
154
- notify(
155
- `${ NOTIFICATION_TYPE.COLLAB_USER_ENTERED }-${ collaborator.collaboratorInfo.id }`,
119
+ void createNotice(
120
+ 'info',
156
121
  sprintf(
157
122
  /* translators: %s: collaborator display name */
158
123
  __( '%s has joined the post.' ),
159
124
  collaborator.collaboratorInfo.name
160
- )
125
+ ),
126
+ {
127
+ id: `${ NOTIFICATION_TYPE.COLLAB_USER_ENTERED }-${ collaborator.collaboratorInfo.id }`,
128
+ type: 'snackbar',
129
+ isDismissible: false,
130
+ }
161
131
  );
162
- }
163
- }
164
-
165
- /*
166
- * Detect leaves by iterating the previous collaborator list. A leave
167
- * notification fires when a previously-connected collaborator either:
168
- * - transitions to isConnected=false (greyed-out in the UI), or
169
- * - disappears from the list entirely while still connected.
170
- * Already-disconnected collaborators that are later removed from the
171
- * list (after the 5 s delay) are silently ignored.
172
- */
173
- if ( NOTIFICATIONS_CONFIG.userExited ) {
174
- for ( const [ clientId, prevCollab ] of prevMap ) {
175
- if ( prevCollab.isMe || ! prevCollab.isConnected ) {
176
- continue;
177
- }
178
-
179
- const newCollab = newMap.get( clientId );
180
- if ( newCollab?.isConnected ) {
181
- continue;
182
- }
183
-
184
- notify(
185
- `${ NOTIFICATION_TYPE.COLLAB_USER_EXITED }-${ prevCollab.collaboratorInfo.id }`,
132
+ },
133
+ [ createNotice ]
134
+ )
135
+ );
136
+
137
+ useOnCollaboratorLeave(
138
+ effectivePostId,
139
+ effectivePostType,
140
+ useCallback(
141
+ ( collaborator: PostEditorAwarenessState ) => {
142
+ void createNotice(
143
+ 'info',
186
144
  sprintf(
187
145
  /* translators: %s: collaborator display name */
188
146
  __( '%s has left the post.' ),
189
- prevCollab.collaboratorInfo.name
190
- )
147
+ collaborator.collaboratorInfo.name
148
+ ),
149
+ {
150
+ id: `${ NOTIFICATION_TYPE.COLLAB_USER_EXITED }-${ collaborator.collaboratorInfo.id }`,
151
+ type: 'snackbar',
152
+ isDismissible: false,
153
+ }
191
154
  );
192
- }
193
- }
194
- }, [
195
- activeCollaborators,
196
- prevCollaborators,
197
- isCollaborationEnabled,
198
- createNotice,
199
- ] );
200
-
201
- /*
202
- * Detect remote save events via the CRDT state map. The savedByClientId
203
- * is a Y.Doc client ID which maps to a collaborator via clientId.
204
- */
205
- useEffect( () => {
206
- if (
207
- ! isCollaborationEnabled ||
208
- ! NOTIFICATIONS_CONFIG.postUpdated ||
209
- ! lastPostSave ||
210
- ! postStatus
211
- ) {
212
- return;
213
- }
214
-
215
- if ( prevPostSave && lastPostSave.savedAt === prevPostSave.savedAt ) {
216
- return;
217
- }
218
-
219
- const saver = activeCollaborators.find(
220
- ( c ) => c.clientId === lastPostSave.savedByClientId && ! c.isMe
221
- );
222
-
223
- if ( ! saver ) {
224
- return;
225
- }
226
-
227
- // Prefer the remote status from Y.Doc (accurate at save time) over
228
- // the local Redux value, which may not have synced yet.
229
- const effectiveStatus =
230
- lastPostSave.postStatus ?? postStatus ?? 'draft';
231
-
232
- // prevPostSave is null on the first save this session, so fall back
233
- // to the Redux status (still pre-save when the notification fires).
234
- const prevStatus = prevPostSave?.postStatus ?? postStatus;
235
- const isFirstPublish =
236
- ! ( prevStatus && PUBLISHED_STATUSES.includes( prevStatus ) ) &&
237
- PUBLISHED_STATUSES.includes( effectiveStatus );
155
+ },
156
+ [ createNotice ]
157
+ )
158
+ );
159
+
160
+ useOnPostSave(
161
+ effectivePostId,
162
+ effectivePostType,
163
+ useCallback(
164
+ (
165
+ saveEvent: PostSaveEvent,
166
+ saver: PostEditorAwarenessState,
167
+ prevEvent: PostSaveEvent | null
168
+ ) => {
169
+ if ( ! postStatus ) {
170
+ return;
171
+ }
238
172
 
239
- const message = getPostUpdatedMessage(
240
- saver.collaboratorInfo.name,
241
- effectiveStatus,
242
- isFirstPublish
243
- );
173
+ // Prefer the remote status from Y.Doc (accurate at save time)
174
+ // over the local Redux value, which may not have synced yet.
175
+ const effectiveStatus =
176
+ saveEvent.postStatus ?? postStatus ?? 'draft';
177
+
178
+ // Use the previous save event's status when available for
179
+ // accurate first-publish detection across rapid saves.
180
+ const prevStatus = prevEvent?.postStatus ?? postStatus;
181
+ const isFirstPublish =
182
+ ! (
183
+ prevStatus && PUBLISHED_STATUSES.includes( prevStatus )
184
+ ) && PUBLISHED_STATUSES.includes( effectiveStatus );
185
+
186
+ const message = getPostUpdatedMessage(
187
+ saver.collaboratorInfo.name,
188
+ effectiveStatus,
189
+ isFirstPublish
190
+ );
244
191
 
245
- void createNotice( 'info', message, {
246
- id: `${ NOTIFICATION_TYPE.COLLAB_POST_UPDATED }-${ saver.collaboratorInfo.id }`,
247
- type: 'snackbar',
248
- isDismissible: false,
249
- } );
250
- }, [
251
- lastPostSave,
252
- prevPostSave,
253
- activeCollaborators,
254
- isCollaborationEnabled,
255
- postStatus,
256
- createNotice,
257
- ] );
192
+ void createNotice( 'info', message, {
193
+ id: `${ NOTIFICATION_TYPE.COLLAB_POST_UPDATED }-${ saver.collaboratorInfo.id }`,
194
+ type: 'snackbar',
195
+ isDismissible: false,
196
+ } );
197
+ },
198
+ [ createNotice, postStatus ]
199
+ )
200
+ );
258
201
  }
@@ -25,7 +25,7 @@
25
25
  border-radius: $grid-unit-05;
26
26
 
27
27
  @media not (prefers-reduced-motion) {
28
- transition: all 0.1s ease-out;
28
+ transition: background-color 0.1s ease-out;
29
29
  }
30
30
 
31
31
  &:hover {
@@ -7,7 +7,7 @@ import clsx from 'clsx';
7
7
  * WordPress dependencies
8
8
  */
9
9
  import { InterfaceSkeleton, ComplementaryArea } from '@wordpress/interface';
10
- import { useSelect } from '@wordpress/data';
10
+ import { useSelect, useDispatch } from '@wordpress/data';
11
11
  import { __ } from '@wordpress/i18n';
12
12
  import { store as preferencesStore } from '@wordpress/preferences';
13
13
  import { BlockBreadcrumb, BlockToolbar } from '@wordpress/block-editor';
@@ -82,6 +82,7 @@ export default function EditorInterface( {
82
82
  stylesPath,
83
83
  showStylebook,
84
84
  isRevisionsMode,
85
+ showDiff,
85
86
  } = useSelect( ( select ) => {
86
87
  const { get } = select( preferencesStore );
87
88
  const {
@@ -94,6 +95,7 @@ export default function EditorInterface( {
94
95
  getStylesPath,
95
96
  getShowStylebook,
96
97
  isRevisionsMode: _isRevisionsMode,
98
+ isShowingRevisionDiff,
97
99
  } = unlock( select( editorStore ) );
98
100
  const editorSettings = getEditorSettings();
99
101
 
@@ -121,8 +123,11 @@ export default function EditorInterface( {
121
123
  getCurrentPostType() === 'attachment' &&
122
124
  window?.__experimentalMediaEditor,
123
125
  isRevisionsMode: _isRevisionsMode(),
126
+ showDiff: isShowingRevisionDiff(),
124
127
  };
125
128
  }, [] );
129
+ const { setShowRevisionDiff } = unlock( useDispatch( editorStore ) );
130
+
126
131
  // Runs unconditionally so join/leave/save notifications are dispatched
127
132
  // regardless of viewport width or whether the header centre area is visible.
128
133
  useCollaboratorNotifications( postId, postType );
@@ -152,9 +157,6 @@ export default function EditorInterface( {
152
157
  [ entitiesSavedStatesCallback ]
153
158
  );
154
159
 
155
- // Local state for diff toggle in revisions mode.
156
- const [ showDiff, setShowDiff ] = useState( true );
157
-
158
160
  // When in revisions mode, render the revisions interface.
159
161
  if ( isRevisionsMode ) {
160
162
  return (
@@ -164,10 +166,10 @@ export default function EditorInterface( {
164
166
  header={
165
167
  <RevisionsHeader
166
168
  showDiff={ showDiff }
167
- onToggleDiff={ () => setShowDiff( ! showDiff ) }
169
+ onToggleDiff={ () => setShowRevisionDiff( ! showDiff ) }
168
170
  />
169
171
  }
170
- content={ <RevisionsCanvas showDiff={ showDiff } /> }
172
+ content={ <RevisionsCanvas /> }
171
173
  sidebar={ <ComplementaryArea.Slot scope="core" /> }
172
174
  />
173
175
  );
@@ -85,7 +85,10 @@ export default function InserterSidebar() {
85
85
  showMostUsedBlocks={ showMostUsedBlocks }
86
86
  showInserterHelpPanel
87
87
  shouldFocusBlock={ isMobileViewport }
88
- rootClientId={ blockSectionRootClientId }
88
+ rootClientId={
89
+ blockSectionRootClientId ?? inserter.rootClientId
90
+ }
91
+ __experimentalInsertionIndex={ inserter.insertionIndex }
89
92
  onSelect={ inserter.onSelect }
90
93
  __experimentalInitialTab={ inserter.tab }
91
94
  __experimentalInitialCategory={ inserter.category }
@@ -60,7 +60,7 @@ export const getItemPriority = ( name, searchValue ) => {
60
60
  */
61
61
  export function PageAttributesParent() {
62
62
  const { editPost } = useDispatch( editorStore );
63
- const [ fieldValue, setFieldValue ] = useState( false );
63
+ const [ fieldValue, setFieldValue ] = useState( '' );
64
64
  const {
65
65
  isHierarchical,
66
66
  parentPostId,
@@ -16,6 +16,7 @@ import { addAction, removeAction } from '@wordpress/hooks';
16
16
  import { useInstanceId } from '@wordpress/compose';
17
17
  import { store as coreStore } from '@wordpress/core-data';
18
18
  import { unlock } from '../../lock-unlock';
19
+ import { DOCUMENT_SIZE_LIMIT_EXCEEDED } from '../../utils/sync-error-messages';
19
20
 
20
21
  /**
21
22
  * Internal dependencies
@@ -23,14 +24,31 @@ import { unlock } from '../../lock-unlock';
23
24
  import { store as editorStore } from '../../store';
24
25
 
25
26
  function CollaborationContext() {
26
- const isCollaborationSupported = useSelect( ( select ) => {
27
- return unlock( select( coreStore ) ).isCollaborationSupported();
28
- }, [] );
27
+ const { isCollaborationSupported, syncConnectionStatus } = useSelect(
28
+ ( select ) => {
29
+ const selectors = unlock( select( coreStore ) );
30
+ return {
31
+ isCollaborationSupported: selectors.isCollaborationSupported(),
32
+ syncConnectionStatus: selectors.getSyncConnectionStatus(),
33
+ };
34
+ },
35
+ []
36
+ );
29
37
 
30
38
  if ( isCollaborationSupported ) {
31
39
  return null;
32
40
  }
33
41
 
42
+ if ( DOCUMENT_SIZE_LIMIT_EXCEEDED === syncConnectionStatus?.error?.code ) {
43
+ return (
44
+ <p>
45
+ { __(
46
+ 'Because this post is too large for real-time collaboration, only one person can edit at a time.'
47
+ ) }
48
+ </p>
49
+ );
50
+ }
51
+
34
52
  return (
35
53
  <p>
36
54
  { __(
@@ -28,6 +28,22 @@ import { unlock } from '../../lock-unlock';
28
28
 
29
29
  const { parseRawBlock } = unlock( blocksPrivateApis );
30
30
 
31
+ /**
32
+ * Safely stringifies a value for display and comparison.
33
+ *
34
+ * @param {*} value The value to stringify.
35
+ * @return {string} The stringified value.
36
+ */
37
+ function stringifyValue( value ) {
38
+ if ( value === null || value === undefined ) {
39
+ return '';
40
+ }
41
+ if ( typeof value === 'object' ) {
42
+ return JSON.stringify( value, null, 2 );
43
+ }
44
+ return String( value );
45
+ }
46
+
31
47
  /**
32
48
  * Calculate text similarity using word diff (semantically meaningful).
33
49
  * Returns ratio of unchanged words to total words.
@@ -65,7 +81,7 @@ function pairSimilarBlocks( blocks ) {
65
81
 
66
82
  // Separate blocks by status, tracking original indices.
67
83
  blocks.forEach( ( block, index ) => {
68
- const status = block.__revisionDiffStatus;
84
+ const status = block.__revisionDiffStatus?.status;
69
85
  if ( status === 'removed' ) {
70
86
  removed.push( { block, index } );
71
87
  } else if ( status === 'added' ) {
@@ -120,7 +136,7 @@ function pairSimilarBlocks( blocks ) {
120
136
  // Create modified block with previous content stored.
121
137
  modifications.set( bestMatch.index, {
122
138
  ...bestMatch.block,
123
- __revisionDiffStatus: 'modified',
139
+ __revisionDiffStatus: { status: 'modified' },
124
140
  __previousRawBlock: rem.block,
125
141
  } );
126
142
  }
@@ -176,14 +192,14 @@ function diffRawBlocks( currentRaw, previousRaw ) {
176
192
  for ( let i = 0; i < part.count; i++ ) {
177
193
  result.push( {
178
194
  ...currentRaw[ currIdx++ ],
179
- __revisionDiffStatus: 'added',
195
+ __revisionDiffStatus: { status: 'added' },
180
196
  } );
181
197
  }
182
198
  } else if ( part.removed ) {
183
199
  for ( let i = 0; i < part.count; i++ ) {
184
200
  result.push( {
185
201
  ...previousRaw[ prevIdx++ ],
186
- __revisionDiffStatus: 'removed',
202
+ __revisionDiffStatus: { status: 'removed' },
187
203
  } );
188
204
  }
189
205
  } else {
@@ -481,26 +497,28 @@ function applyRichTextDiff( currentRichText, previousRichText ) {
481
497
  }
482
498
 
483
499
  /**
484
- * Apply rich text diff to all rich-text attributes of a block.
485
- * Compares each rich-text attribute between current and previous parsed blocks.
500
+ * Apply diffs to a modified block's attributes.
501
+ * - Rich-text attributes: applies inline diff formatting (ins/del marks).
502
+ * - Other attributes: computes word-level diffs for the sidebar panel.
486
503
  *
487
504
  * @param {Object} currentBlock Current parsed block.
488
505
  * @param {Object} previousBlock Previous parsed block.
506
+ * @param {Object} diffStatus The __revisionDiffStatus object to attach changedAttributes to.
489
507
  */
490
- function applyRichTextDiffToBlock( currentBlock, previousBlock ) {
508
+ function applyDiffToBlock( currentBlock, previousBlock, diffStatus ) {
491
509
  const blockType = getBlockType( currentBlock.name );
492
510
  if ( ! blockType ) {
493
511
  return;
494
512
  }
495
513
 
496
- // Find rich-text attributes and compare
514
+ const changedAttributes = {};
515
+
497
516
  for ( const [ attrName, attrDef ] of Object.entries(
498
517
  blockType.attributes
499
518
  ) ) {
500
519
  if ( attrDef.source === 'rich-text' ) {
501
520
  const currentRichText = currentBlock.attributes[ attrName ];
502
521
  const previousRichText = previousBlock.attributes[ attrName ];
503
-
504
522
  if (
505
523
  currentRichText instanceof RichTextData &&
506
524
  previousRichText instanceof RichTextData
@@ -510,8 +528,22 @@ function applyRichTextDiffToBlock( currentBlock, previousBlock ) {
510
528
  previousRichText
511
529
  );
512
530
  }
531
+ } else {
532
+ const currStr = stringifyValue(
533
+ currentBlock.attributes[ attrName ]
534
+ );
535
+ const prevStr = stringifyValue(
536
+ previousBlock.attributes[ attrName ]
537
+ );
538
+ if ( currStr !== prevStr ) {
539
+ changedAttributes[ attrName ] = diffWords( prevStr, currStr );
540
+ }
513
541
  }
514
542
  }
543
+
544
+ if ( Object.keys( changedAttributes ).length > 0 ) {
545
+ diffStatus.changedAttributes = changedAttributes;
546
+ }
515
547
  }
516
548
 
517
549
  /**
@@ -525,18 +557,25 @@ function applyRichTextDiffToBlock( currentBlock, previousBlock ) {
525
557
  function applyDiffRecursively( parsedBlock, rawBlock ) {
526
558
  // Copy diff status from raw block to parsed block.
527
559
  if ( rawBlock.__revisionDiffStatus ) {
528
- parsedBlock.__revisionDiffStatus = rawBlock.__revisionDiffStatus;
529
- }
530
-
531
- // Apply rich text diff if this block is modified and has a previous raw block.
532
- if (
533
- rawBlock.__revisionDiffStatus === 'modified' &&
534
- rawBlock.__previousRawBlock
535
- ) {
536
- const previousParsed = parseRawBlock( rawBlock.__previousRawBlock );
537
- if ( previousParsed ) {
538
- applyRichTextDiffToBlock( parsedBlock, previousParsed );
560
+ // Apply diffs if this block is modified and has a previous raw block.
561
+ if (
562
+ rawBlock.__revisionDiffStatus.status === 'modified' &&
563
+ rawBlock.__previousRawBlock
564
+ ) {
565
+ const previousParsed = parseRawBlock( rawBlock.__previousRawBlock );
566
+ if ( previousParsed ) {
567
+ applyDiffToBlock(
568
+ parsedBlock,
569
+ previousParsed,
570
+ rawBlock.__revisionDiffStatus
571
+ );
572
+ }
539
573
  }
574
+
575
+ parsedBlock.__revisionDiffStatus = rawBlock.__revisionDiffStatus;
576
+ // Also store in attributes so it survives block-editor store normalization.
577
+ parsedBlock.attributes.__revisionDiffStatus =
578
+ rawBlock.__revisionDiffStatus;
540
579
  }
541
580
 
542
581
  // Recursively process inner blocks.
@@ -32,10 +32,10 @@ const { useBlockElementRef } = unlock( blockEditorPrivateApis );
32
32
  function collectDiffBlocks( blocks ) {
33
33
  const result = [];
34
34
  for ( const block of blocks ) {
35
- if ( block.__revisionDiffStatus ) {
35
+ if ( block.__revisionDiffStatus?.status ) {
36
36
  result.push( {
37
37
  clientId: block.clientId,
38
- status: block.__revisionDiffStatus,
38
+ status: block.__revisionDiffStatus.status,
39
39
  } );
40
40
  }
41
41
  if ( block.innerBlocks?.length ) {