@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
@@ -5,6 +5,7 @@ import { sprintf, __ } from '@wordpress/i18n';
5
5
  import { Button } from '@wordpress/components';
6
6
  import { useSelect, useDispatch } from '@wordpress/data';
7
7
  import { backup } from '@wordpress/icons';
8
+ import { addQueryArgs } from '@wordpress/url';
8
9
 
9
10
  /**
10
11
  * Internal dependencies
@@ -16,11 +17,16 @@ import { unlock } from '../../lock-unlock';
16
17
 
17
18
  function usePostLastRevisionInfo() {
18
19
  return useSelect( ( select ) => {
19
- const { getCurrentPostLastRevisionId, getCurrentPostRevisionsCount } =
20
- select( editorStore );
20
+ const {
21
+ getCurrentPostLastRevisionId,
22
+ getCurrentPostRevisionsCount,
23
+ getEditorSettings,
24
+ } = select( editorStore );
21
25
  return {
22
26
  lastRevisionId: getCurrentPostLastRevisionId(),
23
27
  revisionsCount: getCurrentPostRevisionsCount(),
28
+ disableVisualRevisions:
29
+ !! getEditorSettings().disableVisualRevisions,
24
30
  };
25
31
  }, [] );
26
32
  }
@@ -31,14 +37,23 @@ function usePostLastRevisionInfo() {
31
37
  * @return {React.ReactNode} The rendered component.
32
38
  */
33
39
  function PostLastRevision() {
34
- const { lastRevisionId, revisionsCount } = usePostLastRevisionInfo();
40
+ const { lastRevisionId, revisionsCount, disableVisualRevisions } =
41
+ usePostLastRevisionInfo();
35
42
  const { setCurrentRevisionId } = unlock( useDispatch( editorStore ) );
36
43
 
44
+ const buttonProps = disableVisualRevisions
45
+ ? {
46
+ href: addQueryArgs( 'revision.php', {
47
+ revision: lastRevisionId,
48
+ } ),
49
+ }
50
+ : { onClick: () => setCurrentRevisionId( lastRevisionId ) };
51
+
37
52
  return (
38
53
  <PostLastRevisionCheck>
39
54
  <Button
40
55
  __next40pxDefaultSize
41
- onClick={ () => setCurrentRevisionId( lastRevisionId ) }
56
+ { ...buttonProps }
42
57
  className="editor-post-last-revision__title"
43
58
  icon={ backup }
44
59
  iconPosition="right"
@@ -53,16 +68,30 @@ function PostLastRevision() {
53
68
  }
54
69
 
55
70
  export function PrivatePostLastRevision() {
56
- const { lastRevisionId, revisionsCount } = usePostLastRevisionInfo();
71
+ const { lastRevisionId, revisionsCount, disableVisualRevisions } =
72
+ usePostLastRevisionInfo();
57
73
  const { setCurrentRevisionId } = unlock( useDispatch( editorStore ) );
58
74
 
75
+ const buttonProps = disableVisualRevisions
76
+ ? {
77
+ href: addQueryArgs( 'revision.php', {
78
+ revision: lastRevisionId,
79
+ } ),
80
+ }
81
+ : { onClick: () => setCurrentRevisionId( lastRevisionId ) };
82
+
59
83
  return (
60
84
  <PostLastRevisionCheck>
61
85
  <PostPanelRow label={ __( 'Revisions' ) }>
62
86
  <Button
63
- onClick={ () => setCurrentRevisionId( lastRevisionId ) }
87
+ { ...buttonProps }
64
88
  className="editor-private-post-last-revision__button"
65
89
  text={ revisionsCount }
90
+ aria-label={ sprintf(
91
+ /* translators: %s: number of revisions. */
92
+ __( 'Open revisions screen: %s revisions' ),
93
+ revisionsCount
94
+ ) }
66
95
  variant="tertiary"
67
96
  size="compact"
68
97
  />
@@ -32,6 +32,3 @@
32
32
  }
33
33
  }
34
34
 
35
- .editor-private-post-last-revision__button {
36
- display: inline-block;
37
- }
@@ -15,12 +15,52 @@ import { useEffect, createInterpolateElement } from '@wordpress/element';
15
15
  import { addAction, removeAction } from '@wordpress/hooks';
16
16
  import { useInstanceId } from '@wordpress/compose';
17
17
  import { store as coreStore } from '@wordpress/core-data';
18
+ import { unlock } from '../../lock-unlock';
19
+ import { DOCUMENT_SIZE_LIMIT_EXCEEDED } from '../../utils/sync-error-messages';
18
20
 
19
21
  /**
20
22
  * Internal dependencies
21
23
  */
22
24
  import { store as editorStore } from '../../store';
23
25
 
26
+ function CollaborationContext() {
27
+ const { isCollaborationSupported, syncConnectionStatus } = useSelect(
28
+ ( select ) => {
29
+ const {
30
+ isCollaborationSupported: isSupported,
31
+ getSyncConnectionStatus,
32
+ } = unlock( select( coreStore ) );
33
+ return {
34
+ isCollaborationSupported: isSupported(),
35
+ syncConnectionStatus: getSyncConnectionStatus(),
36
+ };
37
+ },
38
+ []
39
+ );
40
+
41
+ if ( isCollaborationSupported ) {
42
+ return null;
43
+ }
44
+
45
+ if ( DOCUMENT_SIZE_LIMIT_EXCEEDED === syncConnectionStatus?.error?.code ) {
46
+ return (
47
+ <p>
48
+ { __(
49
+ 'Because this post is too large for real-time collaboration, only one person can edit at a time.'
50
+ ) }
51
+ </p>
52
+ );
53
+ }
54
+
55
+ return (
56
+ <p>
57
+ { __(
58
+ 'Because this post uses plugins that aren’t compatible with real-time collaboration, only one person can edit at a time.'
59
+ ) }
60
+ </p>
61
+ );
62
+ }
63
+
24
64
  function PostLockedModal() {
25
65
  const instanceId = useInstanceId( PostLockedModal );
26
66
  const hookName = 'core/editor/post-locked-modal-' + instanceId;
@@ -37,7 +77,6 @@ function PostLockedModal() {
37
77
  previewLink,
38
78
  } = useSelect( ( select ) => {
39
79
  const {
40
- isCollaborationEnabledForCurrentPost,
41
80
  isPostLocked,
42
81
  isPostLockTakeover,
43
82
  getPostLockUser,
@@ -46,7 +85,8 @@ function PostLockedModal() {
46
85
  getEditedPostAttribute,
47
86
  getEditedPostPreviewLink,
48
87
  getEditorSettings,
49
- } = select( editorStore );
88
+ isCollaborationEnabledForCurrentPost,
89
+ } = unlock( select( editorStore ) );
50
90
  const { getPostType } = select( coreStore );
51
91
  return {
52
92
  isCollaborationEnabled: isCollaborationEnabledForCurrentPost(),
@@ -197,29 +237,32 @@ function PostLockedModal() {
197
237
  ) }
198
238
  <div>
199
239
  { !! isTakeover && (
200
- <p>
201
- { createInterpolateElement(
202
- userDisplayName
203
- ? sprintf(
204
- /* translators: %s: user's display name */
205
- __(
206
- '<strong>%s</strong> now has editing control of this post (<PreviewLink />). Don’t worry, your changes up to this moment have been saved.'
207
- ),
208
- userDisplayName
209
- )
210
- : __(
211
- 'Another user now has editing control of this post (<PreviewLink />). Don’t worry, your changes up to this moment have been saved.'
212
- ),
213
- {
214
- strong: <strong />,
215
- PreviewLink: (
216
- <ExternalLink href={ previewLink }>
217
- { __( 'preview' ) }
218
- </ExternalLink>
219
- ),
220
- }
221
- ) }
222
- </p>
240
+ <>
241
+ <p>
242
+ { createInterpolateElement(
243
+ userDisplayName
244
+ ? sprintf(
245
+ /* translators: %s: user's display name */
246
+ __(
247
+ '<strong>%s</strong> now has editing control of this post (<PreviewLink />). Don’t worry, your changes up to this moment have been saved.'
248
+ ),
249
+ userDisplayName
250
+ )
251
+ : __(
252
+ 'Another user now has editing control of this post (<PreviewLink />). Don’t worry, your changes up to this moment have been saved.'
253
+ ),
254
+ {
255
+ strong: <strong />,
256
+ PreviewLink: (
257
+ <ExternalLink href={ previewLink }>
258
+ { __( 'preview' ) }
259
+ </ExternalLink>
260
+ ),
261
+ }
262
+ ) }
263
+ </p>
264
+ <CollaborationContext />
265
+ </>
223
266
  ) }
224
267
  { ! isTakeover && (
225
268
  <>
@@ -246,6 +289,7 @@ function PostLockedModal() {
246
289
  }
247
290
  ) }
248
291
  </p>
292
+ <CollaborationContext />
249
293
  <p>
250
294
  { __(
251
295
  'If you take over, the other user will lose editing control to the post, but their changes will be saved.'
@@ -16,6 +16,7 @@
16
16
  padding: 6px 0; // Matches button to ensure alignment
17
17
  line-height: $grid-unit-05 * 5;
18
18
  hyphens: auto;
19
+ word-break: break-word;
19
20
  }
20
21
 
21
22
  .editor-post-panel__row-control {
@@ -29,8 +29,35 @@ import { unlock } from '../../lock-unlock';
29
29
  const { parseRawBlock } = unlock( blocksPrivateApis );
30
30
 
31
31
  /**
32
- * Calculate text similarity using word diff (semantically meaningful).
33
- * Returns ratio of unchanged words to total words.
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
+
47
+ /**
48
+ * Calculate text similarity using word-set overlap.
49
+ *
50
+ * Uses a variant of the Jaccard index (https://en.wikipedia.org/wiki/Jaccard_index)
51
+ * called the overlap coefficient (https://en.wikipedia.org/wiki/Overlap_coefficient)
52
+ * where we divide by the larger set size rather than the union. This ensures that
53
+ * a small edit to a long paragraph scores high — the few changed words don't
54
+ * dilute the score.
55
+ *
56
+ * This replaces the previous diffWords-based similarity which was O(n*m) per pair.
57
+ * The word-set approach is O(n) where n is the number of words.
58
+ *
59
+ * Words are extracted using Intl.Segmenter for proper multilingual support
60
+ * (CJK, Thai, etc.) rather than splitting on whitespace.
34
61
  *
35
62
  * @param {string} text1 First text to compare.
36
63
  * @param {string} text2 Second text to compare.
@@ -44,17 +71,53 @@ function textSimilarity( text1, text2 ) {
44
71
  return 0;
45
72
  }
46
73
 
47
- const changes = diffWords( text1, text2 );
48
- const unchanged = changes
49
- .filter( ( c ) => ! c.added && ! c.removed )
50
- .reduce( ( sum, c ) => sum + c.value.length, 0 );
51
- const total = Math.max( text1.length, text2.length );
52
- return total > 0 ? unchanged / total : 0;
74
+ const segmenter = new Intl.Segmenter( undefined, {
75
+ granularity: 'word',
76
+ } );
77
+ // Safari's Intl.Segmenter returns isWordLike: false for numeric segments,
78
+ // so fall back to a Unicode-aware regex for letters and numbers.
79
+ const wordLikeRegex = /[\p{L}\p{N}]/u;
80
+ const getWords = ( text ) => {
81
+ const words = [];
82
+ for ( const { segment, isWordLike } of segmenter.segment( text ) ) {
83
+ if ( isWordLike || wordLikeRegex.test( segment ) ) {
84
+ words.push( segment );
85
+ }
86
+ }
87
+ return words;
88
+ };
89
+ const words1 = getWords( text1 );
90
+ const words2 = getWords( text2 );
91
+
92
+ if ( words1.length === 0 && words2.length === 0 ) {
93
+ return 1;
94
+ }
95
+
96
+ const set1 = new Set( words1 );
97
+ let intersection = 0;
98
+ for ( const word of words2 ) {
99
+ if ( set1.has( word ) ) {
100
+ intersection++;
101
+ }
102
+ }
103
+
104
+ const total = Math.max( words1.length, words2.length );
105
+ return total > 0 ? intersection / total : 0;
53
106
  }
54
107
 
55
108
  /**
56
109
  * Post-process diff result to pair similar removed/added blocks as modifications.
57
- * This catches modifications that LCS missed due to content changes.
110
+ *
111
+ * After LCS diffing, a block whose content changed appears as a separate "removed"
112
+ * and "added" entry (since the full block signature differs). This function detects
113
+ * such pairs and merges them into a single "modified" block with inline diff.
114
+ *
115
+ * Two pairing strategies are used:
116
+ * 1. When exactly one block of a given type was removed and one was added,
117
+ * they are paired directly — no ambiguity, no similarity check needed.
118
+ * 2. When multiple candidates exist, textSimilarity (overlap coefficient) is
119
+ * used to find the best match. Blocks must share at least 50% of their
120
+ * words to be paired, preventing unrelated paragraphs from being merged.
58
121
  *
59
122
  * @param {Array} blocks Raw blocks with diff status.
60
123
  * @return {Array} Blocks with similar pairs converted to modifications.
@@ -65,7 +128,7 @@ function pairSimilarBlocks( blocks ) {
65
128
 
66
129
  // Separate blocks by status, tracking original indices.
67
130
  blocks.forEach( ( block, index ) => {
68
- const status = block.__revisionDiffStatus;
131
+ const status = block.__revisionDiffStatus?.status;
69
132
  if ( status === 'removed' ) {
70
133
  removed.push( { block, index } );
71
134
  } else if ( status === 'added' ) {
@@ -78,62 +141,139 @@ function pairSimilarBlocks( blocks ) {
78
141
  return blocks;
79
142
  }
80
143
 
81
- const pairedRemoved = new Set(); // Indices of removed blocks that were paired.
82
- const modifications = new Map(); // Map from added block index to modified block.
83
- const SIMILARITY_THRESHOLD = 0.3;
144
+ const pairedRemoved = new Set(); // Indices of removed blocks filtered out.
145
+ const pairedAdded = new Set(); // Indices of added blocks filtered out.
146
+ const modifications = new Map(); // Index → modified block.
147
+ const SIMILARITY_THRESHOLD = 0.5;
148
+
149
+ // Group candidates by block name for efficient lookup.
150
+ const addedByName = new Map();
151
+ for ( const add of added ) {
152
+ const name = add.block.blockName;
153
+ if ( ! addedByName.has( name ) ) {
154
+ addedByName.set( name, [] );
155
+ }
156
+ addedByName.get( name ).push( add );
157
+ }
158
+ const removedByName = new Map();
159
+ for ( const rem of removed ) {
160
+ const name = rem.block.blockName;
161
+ if ( ! removedByName.has( name ) ) {
162
+ removedByName.set( name, [] );
163
+ }
164
+ removedByName.get( name ).push( rem );
165
+ }
84
166
 
85
167
  // For each removed block, find best matching added block.
168
+ // Track the highest added index paired so far — new pairings must
169
+ // not go backwards, or the removed/added text order would break.
170
+ let maxPairedAddedIndex = -1;
171
+
86
172
  for ( const rem of removed ) {
87
- let bestMatch = null;
88
- let bestScore = 0;
173
+ const candidates = addedByName.get( rem.block.blockName ) || [];
174
+ const sameNameRemoved = removedByName.get( rem.block.blockName ) || [];
175
+ const unpaired = candidates.filter(
176
+ ( add ) =>
177
+ ! modifications.has( add.index ) &&
178
+ add.index > maxPairedAddedIndex
179
+ );
89
180
 
90
- for ( const add of added ) {
91
- if ( modifications.has( add.index ) ) {
92
- continue;
93
- }
94
- if ( add.block.blockName !== rem.block.blockName ) {
95
- continue;
96
- }
181
+ if ( unpaired.length === 0 ) {
182
+ continue;
183
+ }
97
184
 
98
- const score = textSimilarity(
99
- rem.block.innerHTML || '',
100
- add.block.innerHTML || ''
101
- );
102
- // If content is identical (score=1), only pair if attrs differ.
103
- // Otherwise identical blocks are just position swaps, not modifications.
185
+ let bestMatch = null;
186
+
187
+ // If there's exactly one removed and one added of this type,
188
+ // pair them directly — no ambiguity, no similarity check needed.
189
+ if ( sameNameRemoved.length === 1 && unpaired.length === 1 ) {
190
+ const add = unpaired[ 0 ];
104
191
  const attrsMatch =
105
192
  JSON.stringify( rem.block.attrs ) ===
106
193
  JSON.stringify( add.block.attrs );
107
- if (
108
- score > bestScore &&
109
- score > SIMILARITY_THRESHOLD &&
110
- ( score < 1 || ! attrsMatch )
111
- ) {
112
- bestScore = score;
194
+ // Only skip pairing if both content and attrs are identical
195
+ // (position swap, not a modification).
196
+ const contentMatch =
197
+ ( rem.block.innerHTML || '' ) === ( add.block.innerHTML || '' );
198
+ if ( ! contentMatch || ! attrsMatch ) {
113
199
  bestMatch = add;
114
200
  }
201
+ } else {
202
+ // Multiple candidates — use similarity to find best match.
203
+ let bestScore = 0;
204
+ for ( const add of unpaired ) {
205
+ const score = textSimilarity(
206
+ rem.block.innerHTML || '',
207
+ add.block.innerHTML || ''
208
+ );
209
+ // Skip identical blocks (score=1 with same attrs) — those
210
+ // are position swaps, not modifications. They should show
211
+ // as separate removed + added, not as a no-op "modified".
212
+ const attrsMatch =
213
+ JSON.stringify( rem.block.attrs ) ===
214
+ JSON.stringify( add.block.attrs );
215
+ if (
216
+ score > bestScore &&
217
+ score > SIMILARITY_THRESHOLD &&
218
+ ( score < 1 || ! attrsMatch )
219
+ ) {
220
+ bestScore = score;
221
+ bestMatch = add;
222
+ }
223
+ }
115
224
  }
116
225
 
117
226
  if ( bestMatch ) {
118
- pairedRemoved.add( rem.index );
227
+ maxPairedAddedIndex = bestMatch.index;
119
228
 
120
- // Create modified block with previous content stored.
121
- modifications.set( bestMatch.index, {
229
+ const modifiedBlock = {
122
230
  ...bestMatch.block,
123
- __revisionDiffStatus: 'modified',
231
+ __revisionDiffStatus: { status: 'modified' },
124
232
  __previousRawBlock: rem.block,
125
- } );
233
+ };
234
+
235
+ // Decide where to place the modified block by checking
236
+ // what's between the removed and added positions.
237
+ // If there are unpaired added blocks between them,
238
+ // placing at the removed position would put the modified
239
+ // block before content that comes before it in the
240
+ // current revision — so use the added position.
241
+ // Otherwise, use the removed position to keep the
242
+ // previous revision's order intact.
243
+ const lo = Math.min( rem.index, bestMatch.index );
244
+ const hi = Math.max( rem.index, bestMatch.index );
245
+ let hasAddedBetween = false;
246
+ for ( let i = lo + 1; i < hi; i++ ) {
247
+ if (
248
+ blocks[ i ].__revisionDiffStatus?.status === 'added' &&
249
+ ! pairedAdded.has( i )
250
+ ) {
251
+ hasAddedBetween = true;
252
+ break;
253
+ }
254
+ }
255
+
256
+ if ( hasAddedBetween ) {
257
+ // Use the added position — don't jump before
258
+ // current-revision content.
259
+ modifications.set( bestMatch.index, modifiedBlock );
260
+ pairedRemoved.add( rem.index );
261
+ } else {
262
+ // Use the removed position — keep the previous
263
+ // revision's reading order.
264
+ modifications.set( rem.index, modifiedBlock );
265
+ pairedAdded.add( bestMatch.index );
266
+ }
126
267
  }
127
268
  }
128
269
 
129
- // Rebuild result: filter out paired removed, replace paired added with modified.
270
+ // Rebuild result: replace modification targets, filter out
271
+ // their paired counterparts.
130
272
  return blocks
131
273
  .map( ( block, index ) => {
132
- // Skip paired removed blocks.
133
- if ( pairedRemoved.has( index ) ) {
274
+ if ( pairedRemoved.has( index ) || pairedAdded.has( index ) ) {
134
275
  return null;
135
276
  }
136
- // Replace paired added blocks with modified version.
137
277
  if ( modifications.has( index ) ) {
138
278
  return modifications.get( index );
139
279
  }
@@ -176,14 +316,14 @@ function diffRawBlocks( currentRaw, previousRaw ) {
176
316
  for ( let i = 0; i < part.count; i++ ) {
177
317
  result.push( {
178
318
  ...currentRaw[ currIdx++ ],
179
- __revisionDiffStatus: 'added',
319
+ __revisionDiffStatus: { status: 'added' },
180
320
  } );
181
321
  }
182
322
  } else if ( part.removed ) {
183
323
  for ( let i = 0; i < part.count; i++ ) {
184
324
  result.push( {
185
325
  ...previousRaw[ prevIdx++ ],
186
- __revisionDiffStatus: 'removed',
326
+ __revisionDiffStatus: { status: 'removed' },
187
327
  } );
188
328
  }
189
329
  } else {
@@ -481,26 +621,28 @@ function applyRichTextDiff( currentRichText, previousRichText ) {
481
621
  }
482
622
 
483
623
  /**
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.
624
+ * Apply diffs to a modified block's attributes.
625
+ * - Rich-text attributes: applies inline diff formatting (ins/del marks).
626
+ * - Other attributes: computes word-level diffs for the sidebar panel.
486
627
  *
487
628
  * @param {Object} currentBlock Current parsed block.
488
629
  * @param {Object} previousBlock Previous parsed block.
630
+ * @param {Object} diffStatus The __revisionDiffStatus object to attach changedAttributes to.
489
631
  */
490
- function applyRichTextDiffToBlock( currentBlock, previousBlock ) {
632
+ function applyDiffToBlock( currentBlock, previousBlock, diffStatus ) {
491
633
  const blockType = getBlockType( currentBlock.name );
492
634
  if ( ! blockType ) {
493
635
  return;
494
636
  }
495
637
 
496
- // Find rich-text attributes and compare
638
+ const changedAttributes = {};
639
+
497
640
  for ( const [ attrName, attrDef ] of Object.entries(
498
641
  blockType.attributes
499
642
  ) ) {
500
643
  if ( attrDef.source === 'rich-text' ) {
501
644
  const currentRichText = currentBlock.attributes[ attrName ];
502
645
  const previousRichText = previousBlock.attributes[ attrName ];
503
-
504
646
  if (
505
647
  currentRichText instanceof RichTextData &&
506
648
  previousRichText instanceof RichTextData
@@ -510,8 +652,22 @@ function applyRichTextDiffToBlock( currentBlock, previousBlock ) {
510
652
  previousRichText
511
653
  );
512
654
  }
655
+ } else {
656
+ const currStr = stringifyValue(
657
+ currentBlock.attributes[ attrName ]
658
+ );
659
+ const prevStr = stringifyValue(
660
+ previousBlock.attributes[ attrName ]
661
+ );
662
+ if ( currStr !== prevStr ) {
663
+ changedAttributes[ attrName ] = diffWords( prevStr, currStr );
664
+ }
513
665
  }
514
666
  }
667
+
668
+ if ( Object.keys( changedAttributes ).length > 0 ) {
669
+ diffStatus.changedAttributes = changedAttributes;
670
+ }
515
671
  }
516
672
 
517
673
  /**
@@ -525,18 +681,25 @@ function applyRichTextDiffToBlock( currentBlock, previousBlock ) {
525
681
  function applyDiffRecursively( parsedBlock, rawBlock ) {
526
682
  // Copy diff status from raw block to parsed block.
527
683
  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 );
684
+ // Apply diffs if this block is modified and has a previous raw block.
685
+ if (
686
+ rawBlock.__revisionDiffStatus.status === 'modified' &&
687
+ rawBlock.__previousRawBlock
688
+ ) {
689
+ const previousParsed = parseRawBlock( rawBlock.__previousRawBlock );
690
+ if ( previousParsed ) {
691
+ applyDiffToBlock(
692
+ parsedBlock,
693
+ previousParsed,
694
+ rawBlock.__revisionDiffStatus
695
+ );
696
+ }
539
697
  }
698
+
699
+ parsedBlock.__revisionDiffStatus = rawBlock.__revisionDiffStatus;
700
+ // Also store in attributes so it survives block-editor store normalization.
701
+ parsedBlock.attributes.__revisionDiffStatus =
702
+ rawBlock.__revisionDiffStatus;
540
703
  }
541
704
 
542
705
  // Recursively process inner blocks.
@@ -15,6 +15,7 @@ import {
15
15
  store as blockEditorStore,
16
16
  } from '@wordpress/block-editor';
17
17
  import { __ } from '@wordpress/i18n';
18
+ import { Tooltip } from '@wordpress/components';
18
19
 
19
20
  /**
20
21
  * Internal dependencies
@@ -32,10 +33,10 @@ const { useBlockElementRef } = unlock( blockEditorPrivateApis );
32
33
  function collectDiffBlocks( blocks ) {
33
34
  const result = [];
34
35
  for ( const block of blocks ) {
35
- if ( block.__revisionDiffStatus ) {
36
+ if ( block.__revisionDiffStatus?.status ) {
36
37
  result.push( {
37
38
  clientId: block.clientId,
38
- status: block.__revisionDiffStatus,
39
+ status: block.__revisionDiffStatus.status,
39
40
  } );
40
41
  }
41
42
  if ( block.innerBlocks?.length ) {
@@ -97,15 +98,17 @@ function DiffMarkerButton( { clientId, status, subscribe } ) {
97
98
  }
98
99
 
99
100
  return (
100
- <button
101
- className={ `revision-diff-marker is-${ status }` }
102
- style={ {
103
- top: `${ position.top }%`,
104
- height: `${ Math.max( position.height, 0.5 ) }%`,
105
- } }
106
- onClick={ () => blockRef.current?.focus() }
107
- aria-label={ STATUS_LABELS[ status ] }
108
- />
101
+ <Tooltip text={ STATUS_LABELS[ status ] }>
102
+ <button
103
+ className={ `revision-diff-marker is-${ status }` }
104
+ style={ {
105
+ top: `${ position.top }%`,
106
+ height: `${ Math.max( position.height, 0.5 ) }%`,
107
+ } }
108
+ onClick={ () => blockRef.current?.focus() }
109
+ aria-label={ STATUS_LABELS[ status ] }
110
+ />
111
+ </Tooltip>
109
112
  );
110
113
  }
111
114
 
@@ -145,7 +148,7 @@ export function useDiffMarkers() {
145
148
  key="diff-markers"
146
149
  className="revision-diff-markers"
147
150
  role="navigation"
148
- aria-label={ __( 'Diff markers' ) }
151
+ aria-label={ __( 'Document changes' ) }
149
152
  >
150
153
  { isMounted &&
151
154
  diffBlocks.map( ( { clientId, status } ) => (