@wordpress/editor 14.40.2-next.v.202602241322.0 → 14.40.2

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 (464) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/build/components/collab-sidebar/index.cjs +7 -4
  3. package/build/components/collab-sidebar/index.cjs.map +2 -2
  4. package/build/components/collab-sidebar/utils.cjs +13 -15
  5. package/build/components/collab-sidebar/utils.cjs.map +2 -2
  6. package/build/components/collaborators-overlay/avatar-iframe-styles.cjs +141 -0
  7. package/build/components/collaborators-overlay/avatar-iframe-styles.cjs.map +7 -0
  8. package/build/components/collaborators-overlay/collaborator-styles.cjs +38 -2
  9. package/build/components/collaborators-overlay/collaborator-styles.cjs.map +2 -2
  10. package/build/components/collaborators-overlay/compute-selection.cjs +185 -0
  11. package/build/components/collaborators-overlay/compute-selection.cjs.map +7 -0
  12. package/build/components/collaborators-overlay/cursor-dom-utils.cjs +243 -0
  13. package/build/components/collaborators-overlay/cursor-dom-utils.cjs.map +7 -0
  14. package/build/components/collaborators-overlay/cursor-registry.cjs +86 -0
  15. package/build/components/collaborators-overlay/cursor-registry.cjs.map +7 -0
  16. package/build/components/collaborators-overlay/index.cjs +7 -2
  17. package/build/components/collaborators-overlay/index.cjs.map +2 -2
  18. package/build/components/collaborators-overlay/overlay-iframe-styles.cjs +148 -0
  19. package/build/components/collaborators-overlay/overlay-iframe-styles.cjs.map +7 -0
  20. package/build/components/collaborators-overlay/overlay.cjs +146 -236
  21. package/build/components/collaborators-overlay/overlay.cjs.map +3 -3
  22. package/build/components/collaborators-overlay/timing-utils.cjs +46 -0
  23. package/build/components/collaborators-overlay/timing-utils.cjs.map +7 -0
  24. package/build/components/collaborators-overlay/use-block-highlighting.cjs +91 -42
  25. package/build/components/collaborators-overlay/use-block-highlighting.cjs.map +2 -2
  26. package/build/components/collaborators-overlay/use-debounced-recompute.cjs +49 -0
  27. package/build/components/collaborators-overlay/use-debounced-recompute.cjs.map +7 -0
  28. package/build/components/collaborators-overlay/use-render-cursors.cjs +82 -172
  29. package/build/components/collaborators-overlay/use-render-cursors.cjs.map +3 -3
  30. package/build/components/collaborators-presence/avatar/component.cjs +121 -0
  31. package/build/components/collaborators-presence/avatar/component.cjs.map +7 -0
  32. package/build/components/collaborators-presence/avatar/index.cjs +37 -0
  33. package/build/components/collaborators-presence/avatar/index.cjs.map +7 -0
  34. package/build/components/collaborators-presence/avatar/types.cjs +19 -0
  35. package/build/components/collaborators-presence/avatar/types.cjs.map +7 -0
  36. package/build/components/collaborators-presence/avatar/use-image-loading-status.cjs +44 -0
  37. package/build/components/collaborators-presence/avatar/use-image-loading-status.cjs.map +7 -0
  38. package/build/components/collaborators-presence/avatar-group/component.cjs +78 -0
  39. package/build/components/collaborators-presence/avatar-group/component.cjs.map +7 -0
  40. package/build/components/collaborators-presence/avatar-group/index.cjs +37 -0
  41. package/build/components/collaborators-presence/avatar-group/index.cjs.map +7 -0
  42. package/build/components/collaborators-presence/avatar-group/types.cjs +19 -0
  43. package/build/components/collaborators-presence/avatar-group/types.cjs.map +7 -0
  44. package/build/components/collaborators-presence/index.cjs +66 -19
  45. package/build/components/collaborators-presence/index.cjs.map +3 -3
  46. package/build/components/collaborators-presence/list.cjs +60 -38
  47. package/build/components/collaborators-presence/list.cjs.map +3 -3
  48. package/build/components/collaborators-presence/use-collaborator-notifications.cjs +149 -0
  49. package/build/components/collaborators-presence/use-collaborator-notifications.cjs.map +7 -0
  50. package/build/components/editor-interface/index.cjs +11 -6
  51. package/build/components/editor-interface/index.cjs.map +2 -2
  52. package/build/components/inserter-sidebar/index.cjs +2 -1
  53. package/build/components/inserter-sidebar/index.cjs.map +2 -2
  54. package/build/components/post-last-revision/index.cjs +27 -6
  55. package/build/components/post-last-revision/index.cjs.map +2 -2
  56. package/build/components/post-locked-modal/index.cjs +49 -17
  57. package/build/components/post-locked-modal/index.cjs.map +3 -3
  58. package/build/components/post-revisions-preview/block-diff.cjs +130 -34
  59. package/build/components/post-revisions-preview/block-diff.cjs.map +2 -2
  60. package/build/components/post-revisions-preview/diff-markers.cjs +6 -5
  61. package/build/components/post-revisions-preview/diff-markers.cjs.map +2 -2
  62. package/build/components/post-revisions-preview/revisions-canvas.cjs +16 -60
  63. package/build/components/post-revisions-preview/revisions-canvas.cjs.map +3 -3
  64. package/build/components/post-revisions-preview/revisions-header.cjs +33 -26
  65. package/build/components/post-revisions-preview/revisions-header.cjs.map +3 -3
  66. package/build/components/post-revisions-preview/revisions-slider.cjs +104 -39
  67. package/build/components/post-revisions-preview/revisions-slider.cjs.map +3 -3
  68. package/build/components/preferences-modal/index.cjs +36 -4
  69. package/build/components/preferences-modal/index.cjs.map +2 -2
  70. package/build/components/provider/disable-non-page-content-blocks.cjs +31 -28
  71. package/build/components/provider/disable-non-page-content-blocks.cjs.map +3 -3
  72. package/build/components/provider/index.cjs +17 -5
  73. package/build/components/provider/index.cjs.map +2 -2
  74. package/build/components/provider/use-block-editor-settings.cjs +20 -4
  75. package/build/components/provider/use-block-editor-settings.cjs.map +3 -3
  76. package/build/components/provider/{use-post-content-blocks.cjs → use-post-content-block-types.cjs} +8 -19
  77. package/build/components/provider/use-post-content-block-types.cjs.map +7 -0
  78. package/build/components/provider/use-revision-blocks.cjs +106 -0
  79. package/build/components/provider/use-revision-blocks.cjs.map +7 -0
  80. package/build/components/revision-block-diff/index.cjs +61 -0
  81. package/build/components/revision-block-diff/index.cjs.map +7 -0
  82. package/build/components/revision-diff-panel/index.cjs +68 -0
  83. package/build/components/revision-diff-panel/index.cjs.map +7 -0
  84. package/build/components/revision-fields-diff/index.cjs +96 -0
  85. package/build/components/revision-fields-diff/index.cjs.map +7 -0
  86. package/build/components/sidebar/header.cjs +1 -1
  87. package/build/components/sidebar/header.cjs.map +2 -2
  88. package/build/components/sidebar/index.cjs +7 -1
  89. package/build/components/sidebar/index.cjs.map +3 -3
  90. package/build/components/style-book/index.cjs +4 -3
  91. package/build/components/style-book/index.cjs.map +2 -2
  92. package/build/components/sync-connection-error-modal/index.cjs +187 -0
  93. package/build/components/sync-connection-error-modal/index.cjs.map +7 -0
  94. package/build/components/sync-connection-error-modal/use-retry-countdown.cjs +84 -0
  95. package/build/components/sync-connection-error-modal/use-retry-countdown.cjs.map +7 -0
  96. package/build/components/template-content-panel/index.cjs +35 -31
  97. package/build/components/template-content-panel/index.cjs.map +3 -3
  98. package/build/components/template-part-menu-items/convert-to-regular.cjs +54 -11
  99. package/build/components/template-part-menu-items/convert-to-regular.cjs.map +3 -3
  100. package/build/components/visual-editor/index.cjs +6 -15
  101. package/build/components/visual-editor/index.cjs.map +2 -2
  102. package/build/store/private-actions.cjs +46 -2
  103. package/build/store/private-actions.cjs.map +2 -2
  104. package/build/store/private-selectors.cjs +156 -21
  105. package/build/store/private-selectors.cjs.map +2 -2
  106. package/build/store/reducer.cjs +27 -0
  107. package/build/store/reducer.cjs.map +2 -2
  108. package/build/store/selectors.cjs +0 -14
  109. package/build/store/selectors.cjs.map +2 -2
  110. package/build/utils/media-finalize/index.cjs +43 -0
  111. package/build/utils/media-finalize/index.cjs.map +7 -0
  112. package/build/utils/media-upload/on-success.cjs +46 -0
  113. package/build/utils/media-upload/on-success.cjs.map +7 -0
  114. package/build/utils/sync-error-messages.cjs +32 -15
  115. package/build/utils/sync-error-messages.cjs.map +3 -3
  116. package/build-module/components/collab-sidebar/index.mjs +7 -4
  117. package/build-module/components/collab-sidebar/index.mjs.map +2 -2
  118. package/build-module/components/collab-sidebar/utils.mjs +13 -15
  119. package/build-module/components/collab-sidebar/utils.mjs.map +2 -2
  120. package/build-module/components/collaborators-overlay/avatar-iframe-styles.mjs +128 -0
  121. package/build-module/components/collaborators-overlay/avatar-iframe-styles.mjs.map +7 -0
  122. package/build-module/components/collaborators-overlay/collaborator-styles.mjs +25 -1
  123. package/build-module/components/collaborators-overlay/collaborator-styles.mjs.map +2 -2
  124. package/build-module/components/collaborators-overlay/compute-selection.mjs +166 -0
  125. package/build-module/components/collaborators-overlay/compute-selection.mjs.map +7 -0
  126. package/build-module/components/collaborators-overlay/cursor-dom-utils.mjs +213 -0
  127. package/build-module/components/collaborators-overlay/cursor-dom-utils.mjs.map +7 -0
  128. package/build-module/components/collaborators-overlay/cursor-registry.mjs +61 -0
  129. package/build-module/components/collaborators-overlay/cursor-registry.mjs.map +7 -0
  130. package/build-module/components/collaborators-overlay/index.mjs +7 -2
  131. package/build-module/components/collaborators-overlay/index.mjs.map +2 -2
  132. package/build-module/components/collaborators-overlay/overlay-iframe-styles.mjs +130 -0
  133. package/build-module/components/collaborators-overlay/overlay-iframe-styles.mjs.map +7 -0
  134. package/build-module/components/collaborators-overlay/overlay.mjs +137 -237
  135. package/build-module/components/collaborators-overlay/overlay.mjs.map +2 -2
  136. package/build-module/components/collaborators-overlay/timing-utils.mjs +21 -0
  137. package/build-module/components/collaborators-overlay/timing-utils.mjs.map +7 -0
  138. package/build-module/components/collaborators-overlay/use-block-highlighting.mjs +93 -45
  139. package/build-module/components/collaborators-overlay/use-block-highlighting.mjs.map +2 -2
  140. package/build-module/components/collaborators-overlay/use-debounced-recompute.mjs +24 -0
  141. package/build-module/components/collaborators-overlay/use-debounced-recompute.mjs.map +7 -0
  142. package/build-module/components/collaborators-overlay/use-render-cursors.mjs +83 -176
  143. package/build-module/components/collaborators-overlay/use-render-cursors.mjs.map +2 -2
  144. package/build-module/components/collaborators-presence/avatar/component.mjs +90 -0
  145. package/build-module/components/collaborators-presence/avatar/component.mjs.map +7 -0
  146. package/build-module/components/collaborators-presence/avatar/index.mjs +6 -0
  147. package/build-module/components/collaborators-presence/avatar/index.mjs.map +7 -0
  148. package/build-module/components/collaborators-presence/avatar/types.mjs +1 -0
  149. package/build-module/components/collaborators-presence/avatar/types.mjs.map +7 -0
  150. package/build-module/components/collaborators-presence/avatar/use-image-loading-status.mjs +19 -0
  151. package/build-module/components/collaborators-presence/avatar/use-image-loading-status.mjs.map +7 -0
  152. package/build-module/components/collaborators-presence/avatar-group/component.mjs +47 -0
  153. package/build-module/components/collaborators-presence/avatar-group/component.mjs.map +7 -0
  154. package/build-module/components/collaborators-presence/avatar-group/index.mjs +6 -0
  155. package/build-module/components/collaborators-presence/avatar-group/index.mjs.map +7 -0
  156. package/build-module/components/collaborators-presence/avatar-group/types.mjs +1 -0
  157. package/build-module/components/collaborators-presence/avatar-group/types.mjs.map +7 -0
  158. package/build-module/components/collaborators-presence/index.mjs +57 -23
  159. package/build-module/components/collaborators-presence/index.mjs.map +2 -2
  160. package/build-module/components/collaborators-presence/list.mjs +52 -44
  161. package/build-module/components/collaborators-presence/list.mjs.map +2 -2
  162. package/build-module/components/collaborators-presence/use-collaborator-notifications.mjs +126 -0
  163. package/build-module/components/collaborators-presence/use-collaborator-notifications.mjs.map +7 -0
  164. package/build-module/components/editor-interface/index.mjs +12 -7
  165. package/build-module/components/editor-interface/index.mjs.map +2 -2
  166. package/build-module/components/inserter-sidebar/index.mjs +2 -1
  167. package/build-module/components/inserter-sidebar/index.mjs.map +2 -2
  168. package/build-module/components/post-last-revision/index.mjs +27 -6
  169. package/build-module/components/post-last-revision/index.mjs.map +2 -2
  170. package/build-module/components/post-locked-modal/index.mjs +49 -17
  171. package/build-module/components/post-locked-modal/index.mjs.map +2 -2
  172. package/build-module/components/post-revisions-preview/block-diff.mjs +130 -34
  173. package/build-module/components/post-revisions-preview/block-diff.mjs.map +2 -2
  174. package/build-module/components/post-revisions-preview/diff-markers.mjs +6 -5
  175. package/build-module/components/post-revisions-preview/diff-markers.mjs.map +2 -2
  176. package/build-module/components/post-revisions-preview/revisions-canvas.mjs +18 -65
  177. package/build-module/components/post-revisions-preview/revisions-canvas.mjs.map +2 -2
  178. package/build-module/components/post-revisions-preview/revisions-header.mjs +36 -33
  179. package/build-module/components/post-revisions-preview/revisions-header.mjs.map +2 -2
  180. package/build-module/components/post-revisions-preview/revisions-slider.mjs +112 -42
  181. package/build-module/components/post-revisions-preview/revisions-slider.mjs.map +2 -2
  182. package/build-module/components/preferences-modal/index.mjs +36 -4
  183. package/build-module/components/preferences-modal/index.mjs.map +2 -2
  184. package/build-module/components/provider/disable-non-page-content-blocks.mjs +31 -28
  185. package/build-module/components/provider/disable-non-page-content-blocks.mjs.map +2 -2
  186. package/build-module/components/provider/index.mjs +17 -5
  187. package/build-module/components/provider/index.mjs.map +2 -2
  188. package/build-module/components/provider/use-block-editor-settings.mjs +20 -4
  189. package/build-module/components/provider/use-block-editor-settings.mjs.map +2 -2
  190. package/build-module/components/provider/use-post-content-block-types.mjs +23 -0
  191. package/build-module/components/provider/use-post-content-block-types.mjs.map +7 -0
  192. package/build-module/components/provider/use-revision-blocks.mjs +81 -0
  193. package/build-module/components/provider/use-revision-blocks.mjs.map +7 -0
  194. package/build-module/components/revision-block-diff/index.mjs +30 -0
  195. package/build-module/components/revision-block-diff/index.mjs.map +7 -0
  196. package/build-module/components/revision-diff-panel/index.mjs +37 -0
  197. package/build-module/components/revision-diff-panel/index.mjs.map +7 -0
  198. package/build-module/components/revision-fields-diff/index.mjs +65 -0
  199. package/build-module/components/revision-fields-diff/index.mjs.map +7 -0
  200. package/build-module/components/sidebar/header.mjs +1 -1
  201. package/build-module/components/sidebar/header.mjs.map +2 -2
  202. package/build-module/components/sidebar/index.mjs +7 -1
  203. package/build-module/components/sidebar/index.mjs.map +2 -2
  204. package/build-module/components/style-book/index.mjs +4 -3
  205. package/build-module/components/style-book/index.mjs.map +2 -2
  206. package/build-module/components/sync-connection-error-modal/index.mjs +170 -0
  207. package/build-module/components/sync-connection-error-modal/index.mjs.map +7 -0
  208. package/build-module/components/sync-connection-error-modal/use-retry-countdown.mjs +59 -0
  209. package/build-module/components/sync-connection-error-modal/use-retry-countdown.mjs.map +7 -0
  210. package/build-module/components/template-content-panel/index.mjs +25 -31
  211. package/build-module/components/template-content-panel/index.mjs.map +2 -2
  212. package/build-module/components/template-part-menu-items/convert-to-regular.mjs +60 -14
  213. package/build-module/components/template-part-menu-items/convert-to-regular.mjs.map +2 -2
  214. package/build-module/components/visual-editor/index.mjs +6 -15
  215. package/build-module/components/visual-editor/index.mjs.map +2 -2
  216. package/build-module/store/private-actions.mjs +44 -2
  217. package/build-module/store/private-actions.mjs.map +2 -2
  218. package/build-module/store/private-selectors.mjs +155 -21
  219. package/build-module/store/private-selectors.mjs.map +2 -2
  220. package/build-module/store/reducer.mjs +25 -0
  221. package/build-module/store/reducer.mjs.map +2 -2
  222. package/build-module/store/selectors.mjs +0 -13
  223. package/build-module/store/selectors.mjs.map +2 -2
  224. package/build-module/utils/media-finalize/index.mjs +12 -0
  225. package/build-module/utils/media-finalize/index.mjs.map +7 -0
  226. package/build-module/utils/media-upload/on-success.mjs +25 -0
  227. package/build-module/utils/media-upload/on-success.mjs.map +7 -0
  228. package/build-module/utils/sync-error-messages.mjs +27 -15
  229. package/build-module/utils/sync-error-messages.mjs.map +3 -3
  230. package/build-style/style-rtl.css +388 -132
  231. package/build-style/style.css +388 -132
  232. package/build-types/components/autocompleters/link.d.ts +8 -0
  233. package/build-types/components/autocompleters/link.d.ts.map +1 -0
  234. package/build-types/components/collab-sidebar/add-note-menu-item.d.ts +5 -0
  235. package/build-types/components/collab-sidebar/add-note-menu-item.d.ts.map +1 -0
  236. package/build-types/components/collab-sidebar/add-note.d.ts +6 -0
  237. package/build-types/components/collab-sidebar/add-note.d.ts.map +1 -0
  238. package/build-types/components/collab-sidebar/board-store.d.ts +9 -0
  239. package/build-types/components/collab-sidebar/board-store.d.ts.map +1 -0
  240. package/build-types/components/collab-sidebar/floating-container.d.ts +8 -0
  241. package/build-types/components/collab-sidebar/floating-container.d.ts.map +1 -0
  242. package/build-types/components/collab-sidebar/index.d.ts.map +1 -1
  243. package/build-types/components/collab-sidebar/note-byline.d.ts +10 -0
  244. package/build-types/components/collab-sidebar/note-byline.d.ts.map +1 -0
  245. package/build-types/components/collab-sidebar/note-card.d.ts +8 -0
  246. package/build-types/components/collab-sidebar/note-card.d.ts.map +1 -0
  247. package/build-types/components/collab-sidebar/note-form.d.ts +7 -0
  248. package/build-types/components/collab-sidebar/note-form.d.ts.map +1 -0
  249. package/build-types/components/collab-sidebar/note-indicator-toolbar.d.ts +5 -0
  250. package/build-types/components/collab-sidebar/note-indicator-toolbar.d.ts.map +1 -0
  251. package/build-types/components/collab-sidebar/note-thread.d.ts +11 -0
  252. package/build-types/components/collab-sidebar/note-thread.d.ts.map +1 -0
  253. package/build-types/components/collab-sidebar/note.d.ts +9 -0
  254. package/build-types/components/collab-sidebar/note.d.ts.map +1 -0
  255. package/build-types/components/collab-sidebar/notes.d.ts +7 -0
  256. package/build-types/components/collab-sidebar/notes.d.ts.map +1 -0
  257. package/build-types/components/collab-sidebar/utils.d.ts.map +1 -1
  258. package/build-types/components/collaborators-overlay/avatar-iframe-styles.d.ts +11 -0
  259. package/build-types/components/collaborators-overlay/avatar-iframe-styles.d.ts.map +1 -0
  260. package/build-types/components/collaborators-overlay/collaborator-styles.d.ts +17 -2
  261. package/build-types/components/collaborators-overlay/collaborator-styles.d.ts.map +1 -1
  262. package/build-types/components/collaborators-overlay/compute-selection.d.ts +24 -0
  263. package/build-types/components/collaborators-overlay/compute-selection.d.ts.map +1 -0
  264. package/build-types/components/collaborators-overlay/cursor-dom-utils.d.ts +72 -0
  265. package/build-types/components/collaborators-overlay/cursor-dom-utils.d.ts.map +1 -0
  266. package/build-types/components/collaborators-overlay/cursor-registry.d.ts +36 -0
  267. package/build-types/components/collaborators-overlay/cursor-registry.d.ts.map +1 -0
  268. package/build-types/components/collaborators-overlay/index.d.ts +7 -4
  269. package/build-types/components/collaborators-overlay/index.d.ts.map +1 -1
  270. package/build-types/components/collaborators-overlay/overlay-iframe-styles.d.ts +6 -0
  271. package/build-types/components/collaborators-overlay/overlay-iframe-styles.d.ts.map +1 -0
  272. package/build-types/components/collaborators-overlay/overlay.d.ts +4 -1
  273. package/build-types/components/collaborators-overlay/overlay.d.ts.map +1 -1
  274. package/build-types/components/collaborators-overlay/timing-utils.d.ts +11 -0
  275. package/build-types/components/collaborators-overlay/timing-utils.d.ts.map +1 -0
  276. package/build-types/components/collaborators-overlay/use-block-highlighting.d.ts +21 -5
  277. package/build-types/components/collaborators-overlay/use-block-highlighting.d.ts.map +1 -1
  278. package/build-types/components/collaborators-overlay/use-debounced-recompute.d.ts +10 -0
  279. package/build-types/components/collaborators-overlay/use-debounced-recompute.d.ts.map +1 -0
  280. package/build-types/components/collaborators-overlay/use-render-cursors.d.ts +6 -1
  281. package/build-types/components/collaborators-overlay/use-render-cursors.d.ts.map +1 -1
  282. package/build-types/components/collaborators-presence/avatar/component.d.ts +7 -0
  283. package/build-types/components/collaborators-presence/avatar/component.d.ts.map +1 -0
  284. package/build-types/components/collaborators-presence/avatar/index.d.ts +3 -0
  285. package/build-types/components/collaborators-presence/avatar/index.d.ts.map +1 -0
  286. package/build-types/components/collaborators-presence/avatar/types.d.ts +66 -0
  287. package/build-types/components/collaborators-presence/avatar/types.d.ts.map +1 -0
  288. package/build-types/components/collaborators-presence/avatar/use-image-loading-status.d.ts +17 -0
  289. package/build-types/components/collaborators-presence/avatar/use-image-loading-status.d.ts.map +1 -0
  290. package/build-types/components/collaborators-presence/avatar-group/component.d.ts +7 -0
  291. package/build-types/components/collaborators-presence/avatar-group/component.d.ts.map +1 -0
  292. package/build-types/components/collaborators-presence/avatar-group/index.d.ts +3 -0
  293. package/build-types/components/collaborators-presence/avatar-group/index.d.ts.map +1 -0
  294. package/build-types/components/collaborators-presence/avatar-group/types.d.ts +14 -0
  295. package/build-types/components/collaborators-presence/avatar-group/types.d.ts.map +1 -0
  296. package/build-types/components/collaborators-presence/index.d.ts.map +1 -1
  297. package/build-types/components/collaborators-presence/list.d.ts +6 -2
  298. package/build-types/components/collaborators-presence/list.d.ts.map +1 -1
  299. package/build-types/components/collaborators-presence/use-collaborator-notifications.d.ts +9 -0
  300. package/build-types/components/collaborators-presence/use-collaborator-notifications.d.ts.map +1 -0
  301. package/build-types/components/editor-interface/index.d.ts.map +1 -1
  302. package/build-types/components/inserter-sidebar/index.d.ts.map +1 -1
  303. package/build-types/components/media/media-editor-modal.d.ts +15 -0
  304. package/build-types/components/media/media-editor-modal.d.ts.map +1 -0
  305. package/build-types/components/post-last-revision/index.d.ts.map +1 -1
  306. package/build-types/components/post-locked-modal/index.d.ts.map +1 -1
  307. package/build-types/components/post-revisions-panel/index.d.ts +2 -0
  308. package/build-types/components/post-revisions-panel/index.d.ts.map +1 -0
  309. package/build-types/components/post-revisions-preview/block-diff.d.ts.map +1 -1
  310. package/build-types/components/post-revisions-preview/diff-markers.d.ts.map +1 -1
  311. package/build-types/components/post-revisions-preview/revisions-canvas.d.ts +2 -5
  312. package/build-types/components/post-revisions-preview/revisions-canvas.d.ts.map +1 -1
  313. package/build-types/components/post-revisions-preview/revisions-header.d.ts.map +1 -1
  314. package/build-types/components/post-revisions-preview/revisions-slider.d.ts +1 -1
  315. package/build-types/components/post-revisions-preview/revisions-slider.d.ts.map +1 -1
  316. package/build-types/components/post-text-editor/utils.d.ts +29 -0
  317. package/build-types/components/post-text-editor/utils.d.ts.map +1 -0
  318. package/build-types/components/provider/disable-non-page-content-blocks.d.ts.map +1 -1
  319. package/build-types/components/provider/index.d.ts.map +1 -1
  320. package/build-types/components/provider/use-block-editor-settings.d.ts.map +1 -1
  321. package/build-types/components/provider/use-network-reconnect.d.ts +8 -0
  322. package/build-types/components/provider/use-network-reconnect.d.ts.map +1 -0
  323. package/build-types/components/provider/use-post-content-block-types.d.ts +9 -0
  324. package/build-types/components/provider/use-post-content-block-types.d.ts.map +1 -0
  325. package/build-types/components/provider/use-revision-blocks.d.ts +10 -0
  326. package/build-types/components/provider/use-revision-blocks.d.ts.map +1 -0
  327. package/build-types/components/revision-block-diff/index.d.ts +6 -0
  328. package/build-types/components/revision-block-diff/index.d.ts.map +1 -0
  329. package/build-types/components/revision-diff-panel/index.d.ts +14 -0
  330. package/build-types/components/revision-diff-panel/index.d.ts.map +1 -0
  331. package/build-types/components/revision-fields-diff/index.d.ts +6 -0
  332. package/build-types/components/revision-fields-diff/index.d.ts.map +1 -0
  333. package/build-types/components/sidebar/dataform-post-summary.d.ts +4 -0
  334. package/build-types/components/sidebar/dataform-post-summary.d.ts.map +1 -0
  335. package/build-types/components/sidebar/index.d.ts.map +1 -1
  336. package/build-types/components/sidebar/post-revision-summary.d.ts +2 -0
  337. package/build-types/components/sidebar/post-revision-summary.d.ts.map +1 -0
  338. package/build-types/components/style-book/index.d.ts +2 -1
  339. package/build-types/components/style-book/index.d.ts.map +1 -1
  340. package/build-types/components/sync-connection-error-modal/index.d.ts +8 -0
  341. package/build-types/components/sync-connection-error-modal/index.d.ts.map +1 -0
  342. package/build-types/components/sync-connection-error-modal/use-retry-countdown.d.ts +11 -0
  343. package/build-types/components/sync-connection-error-modal/use-retry-countdown.d.ts.map +1 -0
  344. package/build-types/components/template-actions-panel/block-theme-content.d.ts +2 -0
  345. package/build-types/components/template-actions-panel/block-theme-content.d.ts.map +1 -0
  346. package/build-types/components/template-actions-panel/classic-theme-content.d.ts +2 -0
  347. package/build-types/components/template-actions-panel/classic-theme-content.d.ts.map +1 -0
  348. package/build-types/components/template-actions-panel/index.d.ts +2 -0
  349. package/build-types/components/template-actions-panel/index.d.ts.map +1 -0
  350. package/build-types/components/template-content-panel/index.d.ts.map +1 -1
  351. package/build-types/components/template-part-menu-items/convert-to-regular.d.ts.map +1 -1
  352. package/build-types/components/upload-progress-snackbar/index.d.ts +19 -0
  353. package/build-types/components/upload-progress-snackbar/index.d.ts.map +1 -0
  354. package/build-types/components/upload-progress-snackbar/stories/index.story.d.ts +28 -0
  355. package/build-types/components/upload-progress-snackbar/stories/index.story.d.ts.map +1 -0
  356. package/build-types/components/upload-progress-snackbar/tracker.d.ts +41 -0
  357. package/build-types/components/upload-progress-snackbar/tracker.d.ts.map +1 -0
  358. package/build-types/components/visual-editor/index.d.ts.map +1 -1
  359. package/build-types/store/private-actions.d.ts +12 -0
  360. package/build-types/store/private-actions.d.ts.map +1 -1
  361. package/build-types/store/private-selectors.d.ts +45 -0
  362. package/build-types/store/private-selectors.d.ts.map +1 -1
  363. package/build-types/store/reducer.d.ts +21 -0
  364. package/build-types/store/reducer.d.ts.map +1 -1
  365. package/build-types/store/selectors.d.ts +0 -10
  366. package/build-types/store/selectors.d.ts.map +1 -1
  367. package/build-types/utils/media-delete/index.d.ts +2 -0
  368. package/build-types/utils/media-delete/index.d.ts.map +1 -0
  369. package/build-types/utils/media-finalize/index.d.ts +2 -0
  370. package/build-types/utils/media-finalize/index.d.ts.map +1 -0
  371. package/build-types/utils/media-upload/on-success.d.ts +9 -0
  372. package/build-types/utils/media-upload/on-success.d.ts.map +1 -0
  373. package/build-types/utils/sync-error-messages.d.ts +17 -3
  374. package/build-types/utils/sync-error-messages.d.ts.map +1 -1
  375. package/package.json +45 -44
  376. package/src/components/collab-sidebar/index.js +7 -4
  377. package/src/components/collab-sidebar/utils.js +9 -10
  378. package/src/components/collaborators-overlay/avatar-iframe-styles.ts +134 -0
  379. package/src/components/collaborators-overlay/collaborator-styles.ts +43 -2
  380. package/src/components/collaborators-overlay/compute-selection.ts +315 -0
  381. package/src/components/collaborators-overlay/cursor-dom-utils.ts +382 -0
  382. package/src/components/collaborators-overlay/cursor-registry.ts +96 -0
  383. package/src/components/collaborators-overlay/index.tsx +12 -4
  384. package/src/components/collaborators-overlay/overlay-iframe-styles.ts +131 -0
  385. package/src/components/collaborators-overlay/overlay.tsx +145 -229
  386. package/src/components/collaborators-overlay/timing-utils.ts +30 -0
  387. package/src/components/collaborators-overlay/use-block-highlighting.ts +152 -64
  388. package/src/components/collaborators-overlay/use-debounced-recompute.ts +32 -0
  389. package/src/components/collaborators-overlay/use-render-cursors.ts +120 -282
  390. package/src/components/collaborators-presence/avatar/component.tsx +123 -0
  391. package/src/components/collaborators-presence/avatar/index.ts +2 -0
  392. package/src/components/collaborators-presence/avatar/styles.scss +184 -0
  393. package/src/components/collaborators-presence/avatar/test/index.tsx +389 -0
  394. package/src/components/collaborators-presence/avatar/types.ts +66 -0
  395. package/src/components/collaborators-presence/avatar/use-image-loading-status.ts +36 -0
  396. package/src/components/collaborators-presence/avatar-group/component.tsx +55 -0
  397. package/src/components/collaborators-presence/avatar-group/index.ts +2 -0
  398. package/src/components/collaborators-presence/avatar-group/styles.scss +33 -0
  399. package/src/components/collaborators-presence/avatar-group/test/index.tsx +139 -0
  400. package/src/components/collaborators-presence/avatar-group/types.ts +13 -0
  401. package/src/components/collaborators-presence/index.tsx +43 -12
  402. package/src/components/collaborators-presence/list.tsx +68 -40
  403. package/src/components/collaborators-presence/styles/collaborators-list.scss +26 -19
  404. package/src/components/collaborators-presence/styles/collaborators-presence.scss +6 -2
  405. package/src/components/collaborators-presence/test/use-collaborator-notifications.ts +397 -0
  406. package/src/components/collaborators-presence/use-collaborator-notifications.ts +203 -0
  407. package/src/components/document-bar/style.scss +1 -1
  408. package/src/components/editor-interface/index.js +13 -6
  409. package/src/components/inserter-sidebar/index.js +4 -1
  410. package/src/components/post-last-revision/index.js +35 -6
  411. package/src/components/post-last-revision/style.scss +0 -3
  412. package/src/components/post-locked-modal/index.js +69 -25
  413. package/src/components/post-panel-row/style.scss +1 -0
  414. package/src/components/post-revisions-preview/block-diff.js +224 -61
  415. package/src/components/post-revisions-preview/diff-markers.js +15 -12
  416. package/src/components/post-revisions-preview/revisions-canvas.js +23 -86
  417. package/src/components/post-revisions-preview/revisions-header.js +35 -37
  418. package/src/components/post-revisions-preview/revisions-slider.js +138 -62
  419. package/src/components/post-revisions-preview/style.scss +3 -3
  420. package/src/components/post-revisions-preview/test/block-diff.js +202 -35
  421. package/src/components/preferences-modal/index.js +37 -7
  422. package/src/components/provider/disable-non-page-content-blocks.js +42 -40
  423. package/src/components/provider/index.js +20 -2
  424. package/src/components/provider/use-block-editor-settings.js +21 -1
  425. package/src/components/provider/use-post-content-block-types.js +30 -0
  426. package/src/components/provider/use-revision-blocks.js +105 -0
  427. package/src/components/revision-block-diff/index.js +39 -0
  428. package/src/components/revision-diff-panel/index.js +59 -0
  429. package/src/components/revision-diff-panel/style.scss +13 -0
  430. package/src/components/revision-fields-diff/index.js +91 -0
  431. package/src/components/sidebar/header.js +1 -1
  432. package/src/components/sidebar/index.js +4 -0
  433. package/src/components/style-book/index.js +4 -2
  434. package/src/components/sync-connection-error-modal/index.tsx +253 -0
  435. package/src/components/sync-connection-error-modal/style.scss +14 -0
  436. package/src/components/sync-connection-error-modal/use-retry-countdown.ts +93 -0
  437. package/src/components/template-content-panel/index.js +30 -38
  438. package/src/components/template-part-menu-items/convert-to-regular.js +74 -12
  439. package/src/components/visual-editor/index.js +6 -23
  440. package/src/store/private-actions.js +70 -1
  441. package/src/store/private-selectors.js +216 -28
  442. package/src/store/reducer.js +41 -0
  443. package/src/store/selectors.js +0 -19
  444. package/src/style.scss +4 -1
  445. package/src/utils/media-finalize/index.js +11 -0
  446. package/src/utils/media-finalize/test/index.js +34 -0
  447. package/src/utils/media-upload/on-success.js +34 -0
  448. package/src/utils/sync-error-messages.ts +72 -0
  449. package/src/utils/test/sync-error-messages.js +34 -0
  450. package/build/components/provider/use-post-content-blocks.cjs.map +0 -7
  451. package/build/components/sync-connection-modal/index.cjs +0 -130
  452. package/build/components/sync-connection-modal/index.cjs.map +0 -7
  453. package/build-module/components/provider/use-post-content-blocks.mjs +0 -34
  454. package/build-module/components/provider/use-post-content-blocks.mjs.map +0 -7
  455. package/build-module/components/sync-connection-modal/index.mjs +0 -114
  456. package/build-module/components/sync-connection-modal/index.mjs.map +0 -7
  457. package/build-types/components/provider/use-post-content-blocks.d.ts +0 -2
  458. package/build-types/components/provider/use-post-content-blocks.d.ts.map +0 -1
  459. package/build-types/components/sync-connection-modal/index.d.ts +0 -8
  460. package/build-types/components/sync-connection-modal/index.d.ts.map +0 -1
  461. package/src/components/provider/use-post-content-blocks.js +0 -42
  462. package/src/components/sync-connection-modal/index.js +0 -140
  463. package/src/components/sync-connection-modal/style.scss +0 -11
  464. package/src/utils/sync-error-messages.js +0 -53
@@ -0,0 +1,397 @@
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import { renderHook } from '@testing-library/react';
5
+
6
+ /**
7
+ * WordPress dependencies
8
+ */
9
+ import { useDispatch, useSelect } from '@wordpress/data';
10
+
11
+ /**
12
+ * Internal dependencies
13
+ */
14
+ import { useCollaboratorNotifications } from '../use-collaborator-notifications';
15
+
16
+ // --- Mocks ---
17
+
18
+ const mockCreateNotice = jest.fn();
19
+ let mockOnJoinCallback: Function | null = null;
20
+ let mockOnLeaveCallback: Function | null = null;
21
+ let mockOnPostSaveCallback: Function | null = null;
22
+ let lastJoinPostId: unknown;
23
+ let lastLeavePostId: unknown;
24
+ let lastSavePostId: unknown;
25
+ let mockEditorState = {
26
+ postStatus: 'draft',
27
+ isCollaborationEnabled: true,
28
+ showCollaborationNotifications: true,
29
+ };
30
+
31
+ jest.mock( '@wordpress/data', () => ( {
32
+ useSelect: jest.fn(),
33
+ useDispatch: jest.fn(),
34
+ } ) );
35
+
36
+ jest.mock( '@wordpress/notices', () => ( {
37
+ store: 'core/notices',
38
+ } ) );
39
+
40
+ jest.mock( '@wordpress/preferences', () => ( {
41
+ store: 'core/preferences',
42
+ } ) );
43
+
44
+ // Mock the editor store to prevent deep import chain (blocks, rich-text, etc.)
45
+ jest.mock( '../../../store', () => ( {
46
+ store: 'core/editor',
47
+ } ) );
48
+
49
+ // Mock the private APIs and unlock mechanism
50
+ jest.mock( '@wordpress/core-data', () => ( {
51
+ privateApis: {},
52
+ } ) );
53
+
54
+ jest.mock( '../../../lock-unlock', () => ( {
55
+ unlock: jest.fn( ( value: unknown ) => ( {
56
+ ...( value as object ),
57
+ useOnCollaboratorJoin: jest.fn(
58
+ ( postId: unknown, _postType: unknown, callback: Function ) => {
59
+ lastJoinPostId = postId;
60
+ mockOnJoinCallback = callback;
61
+ }
62
+ ),
63
+ useOnCollaboratorLeave: jest.fn(
64
+ ( postId: unknown, _postType: unknown, callback: Function ) => {
65
+ lastLeavePostId = postId;
66
+ mockOnLeaveCallback = callback;
67
+ }
68
+ ),
69
+ useOnPostSave: jest.fn(
70
+ ( postId: unknown, _postType: unknown, callback: Function ) => {
71
+ lastSavePostId = postId;
72
+ mockOnPostSaveCallback = callback;
73
+ }
74
+ ),
75
+ } ) ),
76
+ } ) );
77
+
78
+ // --- Helpers ---
79
+
80
+ const BASE_ENTERED_AT = 1704067200000;
81
+
82
+ function makeCollaborator( overrides: Record< string, unknown > = {} ) {
83
+ return {
84
+ clientId: 1,
85
+ isMe: false,
86
+ isConnected: true,
87
+ collaboratorInfo: {
88
+ id: 100,
89
+ name: 'Alice',
90
+ slug: 'alice',
91
+ avatar_urls: {},
92
+ browserType: 'Chrome',
93
+ enteredAt: BASE_ENTERED_AT + 1000,
94
+ },
95
+ ...overrides,
96
+ };
97
+ }
98
+
99
+ function makeMe( overrides: Record< string, unknown > = {} ) {
100
+ return makeCollaborator( {
101
+ clientId: 999,
102
+ isMe: true,
103
+ collaboratorInfo: {
104
+ id: 1,
105
+ name: 'Me',
106
+ slug: 'me',
107
+ avatar_urls: {},
108
+ browserType: 'Chrome',
109
+ enteredAt: BASE_ENTERED_AT + 5000, // joined later than Alice
110
+ },
111
+ ...overrides,
112
+ } );
113
+ }
114
+
115
+ // --- Setup ---
116
+
117
+ function buildMockSelect() {
118
+ return ( storeKey: string ) => {
119
+ if ( storeKey === 'core/preferences' ) {
120
+ return {
121
+ get: ( scope: string, name: string ) => {
122
+ if (
123
+ scope === 'core' &&
124
+ name === 'showCollaborationNotifications'
125
+ ) {
126
+ return mockEditorState.showCollaborationNotifications;
127
+ }
128
+ return undefined;
129
+ },
130
+ };
131
+ }
132
+ return {
133
+ getCurrentPostAttribute: ( attr: string ) =>
134
+ attr === 'status' ? mockEditorState.postStatus : undefined,
135
+ isCollaborationEnabledForCurrentPost: () =>
136
+ mockEditorState.isCollaborationEnabled,
137
+ };
138
+ };
139
+ }
140
+
141
+ beforeEach( () => {
142
+ mockOnJoinCallback = null;
143
+ mockOnLeaveCallback = null;
144
+ mockOnPostSaveCallback = null;
145
+ lastJoinPostId = undefined;
146
+ lastLeavePostId = undefined;
147
+ lastSavePostId = undefined;
148
+ mockEditorState = {
149
+ postStatus: 'draft',
150
+ isCollaborationEnabled: true,
151
+ showCollaborationNotifications: true,
152
+ };
153
+ mockCreateNotice.mockClear();
154
+ ( useSelect as jest.Mock ).mockImplementation( ( selector: Function ) =>
155
+ selector( buildMockSelect() )
156
+ );
157
+ ( useDispatch as jest.Mock ).mockReturnValue( {
158
+ createNotice: mockCreateNotice,
159
+ } );
160
+ } );
161
+
162
+ // --- Tests ---
163
+
164
+ describe( 'useCollaboratorNotifications', () => {
165
+ describe( 'collaborator join notifications', () => {
166
+ it( 'fires join notification for a collaborator who joined after current user', () => {
167
+ const me = makeMe(); // enteredAt: BASE_ENTERED_AT + 5000
168
+ const bobJoinedAfter = makeCollaborator( {
169
+ clientId: 2,
170
+ collaboratorInfo: {
171
+ id: 200,
172
+ name: 'Bob',
173
+ slug: 'bob',
174
+ avatar_urls: {},
175
+ browserType: 'Firefox',
176
+ enteredAt: BASE_ENTERED_AT + 10000, // joined after me
177
+ },
178
+ } );
179
+
180
+ renderHook( () => useCollaboratorNotifications( 123, 'post' ) );
181
+
182
+ // Simulate the core-data hook firing the join callback
183
+ mockOnJoinCallback?.( bobJoinedAfter, me );
184
+
185
+ expect( mockCreateNotice ).toHaveBeenCalledWith(
186
+ 'info',
187
+ 'Bob has joined the post.',
188
+ expect.objectContaining( {
189
+ id: 'collab-user-entered-200',
190
+ } )
191
+ );
192
+ } );
193
+
194
+ it( 'skips join notification for collaborators who joined before current user', () => {
195
+ const me = makeMe(); // enteredAt: BASE_ENTERED_AT + 5000
196
+ const aliceJoinedFirst = makeCollaborator( {
197
+ collaboratorInfo: {
198
+ id: 100,
199
+ name: 'Alice',
200
+ slug: 'alice',
201
+ avatar_urls: {},
202
+ browserType: 'Chrome',
203
+ enteredAt: BASE_ENTERED_AT + 1000, // joined earlier than me
204
+ },
205
+ } );
206
+
207
+ renderHook( () => useCollaboratorNotifications( 123, 'post' ) );
208
+
209
+ // Simulate the core-data hook firing the join callback
210
+ mockOnJoinCallback?.( aliceJoinedFirst, me );
211
+
212
+ expect( mockCreateNotice ).not.toHaveBeenCalled();
213
+ } );
214
+ } );
215
+
216
+ describe( 'collaborator leave notifications', () => {
217
+ it( 'fires a leave notification when a collaborator leaves', () => {
218
+ const alice = makeCollaborator();
219
+ renderHook( () => useCollaboratorNotifications( 123, 'post' ) );
220
+
221
+ // Simulate the core-data hook firing the leave callback
222
+ mockOnLeaveCallback?.( alice );
223
+
224
+ expect( mockCreateNotice ).toHaveBeenCalledWith(
225
+ 'info',
226
+ 'Alice has left the post.',
227
+ expect.objectContaining( {
228
+ type: 'snackbar',
229
+ isDismissible: false,
230
+ id: 'collab-user-exited-100',
231
+ } )
232
+ );
233
+ } );
234
+ } );
235
+
236
+ describe( 'post updated notifications', () => {
237
+ it( 'fires a post updated notification when a collaborator saves (draft)', () => {
238
+ const alice = makeCollaborator();
239
+ renderHook( () => useCollaboratorNotifications( 123, 'post' ) );
240
+
241
+ // Simulate the core-data hook firing the save callback
242
+ mockOnPostSaveCallback?.(
243
+ {
244
+ savedAt: Date.now(),
245
+ savedByClientId: alice.clientId,
246
+ postStatus: undefined,
247
+ },
248
+ alice,
249
+ null
250
+ );
251
+
252
+ expect( mockCreateNotice ).toHaveBeenCalledWith(
253
+ 'info',
254
+ 'Draft saved by Alice.',
255
+ expect.objectContaining( {
256
+ type: 'snackbar',
257
+ isDismissible: false,
258
+ id: 'collab-post-updated-100',
259
+ } )
260
+ );
261
+ } );
262
+
263
+ it( 'fires a post updated notification with "Post updated" for published status', () => {
264
+ mockEditorState = {
265
+ ...mockEditorState,
266
+ postStatus: 'publish',
267
+ };
268
+ const alice = makeCollaborator();
269
+ renderHook( () => useCollaboratorNotifications( 123, 'post' ) );
270
+
271
+ mockOnPostSaveCallback?.(
272
+ {
273
+ savedAt: Date.now(),
274
+ savedByClientId: alice.clientId,
275
+ postStatus: 'publish',
276
+ },
277
+ alice,
278
+ null
279
+ );
280
+
281
+ expect( mockCreateNotice ).toHaveBeenCalledWith(
282
+ 'info',
283
+ 'Post updated by Alice.',
284
+ expect.objectContaining( {
285
+ id: 'collab-post-updated-100',
286
+ } )
287
+ );
288
+ } );
289
+
290
+ it( 'fires a "Post published" notification on first publish (no previous save)', () => {
291
+ mockEditorState = {
292
+ ...mockEditorState,
293
+ postStatus: 'draft',
294
+ };
295
+ const alice = makeCollaborator();
296
+ renderHook( () => useCollaboratorNotifications( 123, 'post' ) );
297
+
298
+ mockOnPostSaveCallback?.(
299
+ {
300
+ savedAt: Date.now(),
301
+ savedByClientId: alice.clientId,
302
+ postStatus: 'publish',
303
+ },
304
+ alice,
305
+ null
306
+ );
307
+
308
+ expect( mockCreateNotice ).toHaveBeenCalledWith(
309
+ 'info',
310
+ 'Post published by Alice.',
311
+ expect.objectContaining( {
312
+ id: 'collab-post-updated-100',
313
+ } )
314
+ );
315
+ } );
316
+
317
+ it( 'fires a "Post published" notification using prevEvent status for transition detection', () => {
318
+ // Redux postStatus may already be 'publish' by the time the
319
+ // callback fires. The prevEvent carries the accurate prior status.
320
+ mockEditorState = {
321
+ ...mockEditorState,
322
+ postStatus: 'publish',
323
+ };
324
+ const alice = makeCollaborator();
325
+ renderHook( () => useCollaboratorNotifications( 123, 'post' ) );
326
+
327
+ mockOnPostSaveCallback?.(
328
+ {
329
+ savedAt: Date.now() + 2000,
330
+ savedByClientId: alice.clientId,
331
+ postStatus: 'publish',
332
+ },
333
+ alice,
334
+ {
335
+ savedAt: Date.now() + 1000,
336
+ savedByClientId: alice.clientId,
337
+ postStatus: 'draft',
338
+ }
339
+ );
340
+
341
+ expect( mockCreateNotice ).toHaveBeenCalledWith(
342
+ 'info',
343
+ 'Post published by Alice.',
344
+ expect.objectContaining( {
345
+ id: 'collab-post-updated-100',
346
+ } )
347
+ );
348
+ } );
349
+
350
+ it( 'does not fire a notification when postStatus is undefined', () => {
351
+ mockEditorState = {
352
+ ...mockEditorState,
353
+ postStatus: undefined as any,
354
+ };
355
+ const alice = makeCollaborator();
356
+ renderHook( () => useCollaboratorNotifications( 123, 'post' ) );
357
+
358
+ mockOnPostSaveCallback?.(
359
+ {
360
+ savedAt: Date.now(),
361
+ savedByClientId: alice.clientId,
362
+ postStatus: undefined,
363
+ },
364
+ alice,
365
+ null
366
+ );
367
+
368
+ expect( mockCreateNotice ).not.toHaveBeenCalled();
369
+ } );
370
+ } );
371
+
372
+ describe( 'when notifications are disabled', () => {
373
+ it( 'passes null postId to hooks when showCollaborationNotifications preference is false', () => {
374
+ mockEditorState = {
375
+ ...mockEditorState,
376
+ showCollaborationNotifications: false,
377
+ };
378
+ renderHook( () => useCollaboratorNotifications( 123, 'post' ) );
379
+
380
+ expect( lastJoinPostId ).toBeNull();
381
+ expect( lastLeavePostId ).toBeNull();
382
+ expect( lastSavePostId ).toBeNull();
383
+ } );
384
+
385
+ it( 'passes null postId to hooks when collaboration is disabled', () => {
386
+ mockEditorState = {
387
+ ...mockEditorState,
388
+ isCollaborationEnabled: false,
389
+ };
390
+ renderHook( () => useCollaboratorNotifications( 123, 'post' ) );
391
+
392
+ expect( lastJoinPostId ).toBeNull();
393
+ expect( lastLeavePostId ).toBeNull();
394
+ expect( lastSavePostId ).toBeNull();
395
+ } );
396
+ } );
397
+ } );
@@ -0,0 +1,203 @@
1
+ /**
2
+ * WordPress dependencies
3
+ */
4
+ import { useDispatch, useSelect } from '@wordpress/data';
5
+ import { useCallback } from '@wordpress/element';
6
+ import { __, sprintf } from '@wordpress/i18n';
7
+ import { store as noticesStore } from '@wordpress/notices';
8
+ import {
9
+ privateApis,
10
+ type PostEditorAwarenessState,
11
+ type PostSaveEvent,
12
+ } from '@wordpress/core-data';
13
+ import { store as preferencesStore } from '@wordpress/preferences';
14
+
15
+ /**
16
+ * Internal dependencies
17
+ */
18
+ import { unlock } from '../../lock-unlock';
19
+ import { store as editorStore } from '../../store';
20
+
21
+ const { useOnCollaboratorJoin, useOnCollaboratorLeave, useOnPostSave } =
22
+ unlock( privateApis );
23
+
24
+ /**
25
+ * Notice IDs for each notification type. Using stable IDs prevents duplicate
26
+ * notices if the same event is processed more than once.
27
+ */
28
+ const NOTIFICATION_TYPE = {
29
+ COLLAB_POST_UPDATED: 'collab-post-updated',
30
+ COLLAB_USER_ENTERED: 'collab-user-entered',
31
+ COLLAB_USER_EXITED: 'collab-user-exited',
32
+ } as const;
33
+
34
+ const PUBLISHED_STATUSES = [ 'publish', 'private', 'future' ];
35
+
36
+ /**
37
+ * Returns the snackbar message for a post updated notification.
38
+ *
39
+ * @param name Display name of the collaborator who saved.
40
+ * @param status WordPress post status at the time of save.
41
+ * @param isFirstPublish Whether this save transitioned the post to published.
42
+ */
43
+ function getPostUpdatedMessage(
44
+ name: string,
45
+ status: string,
46
+ isFirstPublish: boolean
47
+ ): string {
48
+ if ( isFirstPublish ) {
49
+ /* translators: %s: collaborator display name */
50
+ return sprintf( __( 'Post published by %s.' ), name );
51
+ }
52
+ if ( PUBLISHED_STATUSES.includes( status ) ) {
53
+ /* translators: %s: collaborator display name */
54
+ return sprintf( __( 'Post updated by %s.' ), name );
55
+ }
56
+ /* translators: %s: collaborator display name */
57
+ return sprintf( __( 'Draft saved by %s.' ), name );
58
+ }
59
+
60
+ /**
61
+ * Hook that watches for collaborator join/leave events and remote save events,
62
+ * dispatching snackbar notices accordingly.
63
+ *
64
+ * @param postId The ID of the post being edited.
65
+ * @param postType The post type of the post being edited.
66
+ */
67
+ export function useCollaboratorNotifications(
68
+ postId: number | null,
69
+ postType: string | null
70
+ ): void {
71
+ const { postStatus, isCollaborationEnabled, showNotifications } = useSelect(
72
+ ( select ) => {
73
+ const {
74
+ getCurrentPostAttribute,
75
+ isCollaborationEnabledForCurrentPost,
76
+ } = unlock( select( editorStore ) );
77
+ return {
78
+ postStatus: getCurrentPostAttribute( 'status' ) as
79
+ | string
80
+ | undefined,
81
+ isCollaborationEnabled: isCollaborationEnabledForCurrentPost(),
82
+ showNotifications:
83
+ select( preferencesStore ).get(
84
+ 'core',
85
+ 'showCollaborationNotifications'
86
+ ) ?? true,
87
+ };
88
+ },
89
+ []
90
+ );
91
+
92
+ const { createNotice } = useDispatch( noticesStore );
93
+
94
+ // Pass null when collaboration is disabled or notifications are
95
+ // turned off to prevent the hooks from subscribing to awareness state.
96
+ const shouldSubscribe = isCollaborationEnabled && showNotifications;
97
+ const effectivePostId = shouldSubscribe ? postId : null;
98
+ const effectivePostType = shouldSubscribe ? postType : null;
99
+
100
+ useOnCollaboratorJoin(
101
+ effectivePostId,
102
+ effectivePostType,
103
+ useCallback(
104
+ (
105
+ collaborator: PostEditorAwarenessState,
106
+ me?: PostEditorAwarenessState
107
+ ) => {
108
+ /*
109
+ * Skip collaborators who were present before the current user
110
+ * joined. Their enteredAt is earlier than ours, meaning we're
111
+ * the newcomer.
112
+ */
113
+ if (
114
+ me &&
115
+ collaborator.collaboratorInfo.enteredAt <
116
+ me.collaboratorInfo.enteredAt
117
+ ) {
118
+ return;
119
+ }
120
+
121
+ void createNotice(
122
+ 'info',
123
+ sprintf(
124
+ /* translators: %s: collaborator display name */
125
+ __( '%s has joined the post.' ),
126
+ collaborator.collaboratorInfo.name
127
+ ),
128
+ {
129
+ id: `${ NOTIFICATION_TYPE.COLLAB_USER_ENTERED }-${ collaborator.collaboratorInfo.id }`,
130
+ type: 'snackbar',
131
+ isDismissible: false,
132
+ }
133
+ );
134
+ },
135
+ [ createNotice ]
136
+ )
137
+ );
138
+
139
+ useOnCollaboratorLeave(
140
+ effectivePostId,
141
+ effectivePostType,
142
+ useCallback(
143
+ ( collaborator: PostEditorAwarenessState ) => {
144
+ void createNotice(
145
+ 'info',
146
+ sprintf(
147
+ /* translators: %s: collaborator display name */
148
+ __( '%s has left the post.' ),
149
+ collaborator.collaboratorInfo.name
150
+ ),
151
+ {
152
+ id: `${ NOTIFICATION_TYPE.COLLAB_USER_EXITED }-${ collaborator.collaboratorInfo.id }`,
153
+ type: 'snackbar',
154
+ isDismissible: false,
155
+ }
156
+ );
157
+ },
158
+ [ createNotice ]
159
+ )
160
+ );
161
+
162
+ useOnPostSave(
163
+ effectivePostId,
164
+ effectivePostType,
165
+ useCallback(
166
+ (
167
+ saveEvent: PostSaveEvent,
168
+ saver: PostEditorAwarenessState,
169
+ prevEvent: PostSaveEvent | null
170
+ ) => {
171
+ if ( ! postStatus ) {
172
+ return;
173
+ }
174
+
175
+ // Prefer the remote status from Y.Doc (accurate at save time)
176
+ // over the local Redux value, which may not have synced yet.
177
+ const effectiveStatus =
178
+ saveEvent.postStatus ?? postStatus ?? 'draft';
179
+
180
+ // Use the previous save event's status when available for
181
+ // accurate first-publish detection across rapid saves.
182
+ const prevStatus = prevEvent?.postStatus ?? postStatus;
183
+ const isFirstPublish =
184
+ ! (
185
+ prevStatus && PUBLISHED_STATUSES.includes( prevStatus )
186
+ ) && PUBLISHED_STATUSES.includes( effectiveStatus );
187
+
188
+ const message = getPostUpdatedMessage(
189
+ saver.collaboratorInfo.name,
190
+ effectiveStatus,
191
+ isFirstPublish
192
+ );
193
+
194
+ void createNotice( 'info', message, {
195
+ id: `${ NOTIFICATION_TYPE.COLLAB_POST_UPDATED }-${ saver.collaboratorInfo.id }`,
196
+ type: 'snackbar',
197
+ isDismissible: false,
198
+ } );
199
+ },
200
+ [ createNotice, postStatus ]
201
+ )
202
+ );
203
+ }
@@ -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';
@@ -27,6 +27,7 @@ import InserterSidebar from '../inserter-sidebar';
27
27
  import ListViewSidebar from '../list-view-sidebar';
28
28
  import { RevisionsHeader, RevisionsCanvas } from '../post-revisions-preview';
29
29
  import { CollaboratorsOverlay } from '../collaborators-overlay';
30
+ import { useCollaboratorNotifications } from '../collaborators-presence/use-collaborator-notifications';
30
31
  import SavePublishPanels from '../save-publish-panels';
31
32
  import TextEditor from '../text-editor';
32
33
  import VisualEditor from '../visual-editor';
@@ -81,6 +82,7 @@ export default function EditorInterface( {
81
82
  stylesPath,
82
83
  showStylebook,
83
84
  isRevisionsMode,
85
+ showDiff,
84
86
  } = useSelect( ( select ) => {
85
87
  const { get } = select( preferencesStore );
86
88
  const {
@@ -93,6 +95,7 @@ export default function EditorInterface( {
93
95
  getStylesPath,
94
96
  getShowStylebook,
95
97
  isRevisionsMode: _isRevisionsMode,
98
+ isShowingRevisionDiff,
96
99
  } = unlock( select( editorStore ) );
97
100
  const editorSettings = getEditorSettings();
98
101
 
@@ -120,8 +123,15 @@ export default function EditorInterface( {
120
123
  getCurrentPostType() === 'attachment' &&
121
124
  window?.__experimentalMediaEditor,
122
125
  isRevisionsMode: _isRevisionsMode(),
126
+ showDiff: isShowingRevisionDiff(),
123
127
  };
124
128
  }, [] );
129
+ const { setShowRevisionDiff } = unlock( useDispatch( editorStore ) );
130
+
131
+ // Runs unconditionally so join/leave/save notifications are dispatched
132
+ // regardless of viewport width or whether the header centre area is visible.
133
+ useCollaboratorNotifications( postId, postType );
134
+
125
135
  const isLargeViewport = useViewportMatch( 'medium' );
126
136
  const secondarySidebarLabel = isListViewOpened
127
137
  ? __( 'Document Overview' )
@@ -147,9 +157,6 @@ export default function EditorInterface( {
147
157
  [ entitiesSavedStatesCallback ]
148
158
  );
149
159
 
150
- // Local state for diff toggle in revisions mode.
151
- const [ showDiff, setShowDiff ] = useState( true );
152
-
153
160
  // When in revisions mode, render the revisions interface.
154
161
  if ( isRevisionsMode ) {
155
162
  return (
@@ -159,10 +166,10 @@ export default function EditorInterface( {
159
166
  header={
160
167
  <RevisionsHeader
161
168
  showDiff={ showDiff }
162
- onToggleDiff={ () => setShowDiff( ! showDiff ) }
169
+ onToggleDiff={ () => setShowRevisionDiff( ! showDiff ) }
163
170
  />
164
171
  }
165
- content={ <RevisionsCanvas showDiff={ showDiff } /> }
172
+ content={ <RevisionsCanvas /> }
166
173
  sidebar={ <ComplementaryArea.Slot scope="core" /> }
167
174
  />
168
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 }