@wordpress/editor 14.48.0 → 14.48.1
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.
- package/CHANGELOG.md +25 -1
- package/build/components/block-removal-warnings/index.cjs +0 -3
- package/build/components/block-removal-warnings/index.cjs.map +2 -2
- package/build/components/collab-sidebar/note-indicator-toolbar.cjs +49 -43
- package/build/components/collab-sidebar/note-indicator-toolbar.cjs.map +3 -3
- package/build/components/collaborators-overlay/use-block-highlighting.cjs +1 -8
- package/build/components/collaborators-overlay/use-block-highlighting.cjs.map +3 -3
- package/build/components/collaborators-overlay/use-render-cursors.cjs +1 -7
- package/build/components/collaborators-overlay/use-render-cursors.cjs.map +3 -3
- package/build/components/more-menu/view-more-menu-group.cjs +1 -2
- package/build/components/more-menu/view-more-menu-group.cjs.map +2 -2
- package/build/components/page-attributes/parent.cjs +1 -0
- package/build/components/page-attributes/parent.cjs.map +2 -2
- package/build/components/post-publish-button/index.cjs +114 -157
- package/build/components/post-publish-button/index.cjs.map +3 -3
- package/build/components/post-revisions-preview/block-diff.cjs +21 -9
- package/build/components/post-revisions-preview/block-diff.cjs.map +2 -2
- package/build/components/post-revisions-preview/preserve-client-ids.cjs +2 -2
- package/build/components/post-revisions-preview/preserve-client-ids.cjs.map +2 -2
- package/build/components/provider/index.cjs +2 -0
- package/build/components/provider/index.cjs.map +3 -3
- package/build/components/provider/use-network-reconnect.cjs +51 -0
- package/build/components/provider/use-network-reconnect.cjs.map +7 -0
- package/build/components/revision-fields-diff/index.cjs +2 -2
- package/build/components/revision-fields-diff/index.cjs.map +2 -2
- package/build/components/sidebar/index.cjs +1 -4
- package/build/components/sidebar/index.cjs.map +2 -2
- package/build/components/template-actions-panel/block-theme-content.cjs +7 -1
- package/build/components/template-actions-panel/block-theme-content.cjs.map +2 -2
- package/build/components/upload-progress-snackbar/index.cjs +161 -0
- package/build/components/upload-progress-snackbar/index.cjs.map +7 -0
- package/build/components/upload-progress-snackbar/tracker.cjs +90 -0
- package/build/components/upload-progress-snackbar/tracker.cjs.map +7 -0
- package/build/private-apis.cjs +2 -0
- package/build/private-apis.cjs.map +3 -3
- package/build/store/selectors.cjs +1 -2
- package/build/store/selectors.cjs.map +2 -2
- package/build/utils/media-upload/index.cjs +16 -0
- package/build/utils/media-upload/index.cjs.map +3 -3
- package/build-module/components/block-removal-warnings/index.mjs +0 -3
- package/build-module/components/block-removal-warnings/index.mjs.map +2 -2
- package/build-module/components/collab-sidebar/note-indicator-toolbar.mjs +53 -44
- package/build-module/components/collab-sidebar/note-indicator-toolbar.mjs.map +2 -2
- package/build-module/components/collaborators-overlay/use-block-highlighting.mjs +1 -8
- package/build-module/components/collaborators-overlay/use-block-highlighting.mjs.map +2 -2
- package/build-module/components/collaborators-overlay/use-render-cursors.mjs +1 -7
- package/build-module/components/collaborators-overlay/use-render-cursors.mjs.map +2 -2
- package/build-module/components/more-menu/view-more-menu-group.mjs +1 -2
- package/build-module/components/more-menu/view-more-menu-group.mjs.map +2 -2
- package/build-module/components/page-attributes/parent.mjs +1 -0
- package/build-module/components/page-attributes/parent.mjs.map +2 -2
- package/build-module/components/post-publish-button/index.mjs +116 -159
- package/build-module/components/post-publish-button/index.mjs.map +2 -2
- package/build-module/components/post-revisions-preview/block-diff.mjs +20 -8
- package/build-module/components/post-revisions-preview/block-diff.mjs.map +2 -2
- package/build-module/components/post-revisions-preview/preserve-client-ids.mjs +1 -1
- package/build-module/components/post-revisions-preview/preserve-client-ids.mjs.map +1 -1
- package/build-module/components/provider/index.mjs +2 -0
- package/build-module/components/provider/index.mjs.map +2 -2
- package/build-module/components/provider/use-network-reconnect.mjs +30 -0
- package/build-module/components/provider/use-network-reconnect.mjs.map +7 -0
- package/build-module/components/revision-fields-diff/index.mjs +2 -2
- package/build-module/components/revision-fields-diff/index.mjs.map +2 -2
- package/build-module/components/sidebar/index.mjs +2 -11
- package/build-module/components/sidebar/index.mjs.map +2 -2
- package/build-module/components/template-actions-panel/block-theme-content.mjs +7 -1
- package/build-module/components/template-actions-panel/block-theme-content.mjs.map +2 -2
- package/build-module/components/upload-progress-snackbar/index.mjs +135 -0
- package/build-module/components/upload-progress-snackbar/index.mjs.map +7 -0
- package/build-module/components/upload-progress-snackbar/tracker.mjs +61 -0
- package/build-module/components/upload-progress-snackbar/tracker.mjs.map +7 -0
- package/build-module/private-apis.mjs +2 -0
- package/build-module/private-apis.mjs.map +2 -2
- package/build-module/store/selectors.mjs +1 -2
- package/build-module/store/selectors.mjs.map +2 -2
- package/build-module/utils/media-upload/index.mjs +19 -0
- package/build-module/utils/media-upload/index.mjs.map +2 -2
- package/build-style/style-rtl.css +454 -81
- package/build-style/style.css +454 -81
- package/build-types/components/block-removal-warnings/index.d.ts.map +1 -1
- package/build-types/components/collab-sidebar/add-comment.d.ts +6 -0
- package/build-types/components/collab-sidebar/add-comment.d.ts.map +1 -0
- package/build-types/components/collab-sidebar/comment-author-info.d.ts +8 -0
- package/build-types/components/collab-sidebar/comment-author-info.d.ts.map +1 -0
- package/build-types/components/collab-sidebar/comment-form.d.ts +9 -0
- package/build-types/components/collab-sidebar/comment-form.d.ts.map +1 -0
- package/build-types/components/collab-sidebar/comment-indicator-toolbar.d.ts +6 -0
- package/build-types/components/collab-sidebar/comment-indicator-toolbar.d.ts.map +1 -0
- package/build-types/components/collab-sidebar/comment-menu-item.d.ts +6 -0
- package/build-types/components/collab-sidebar/comment-menu-item.d.ts.map +1 -0
- package/build-types/components/collab-sidebar/comments.d.ts +10 -0
- package/build-types/components/collab-sidebar/comments.d.ts.map +1 -0
- package/build-types/components/collab-sidebar/note-indicator-toolbar.d.ts.map +1 -1
- package/build-types/components/collaborators-overlay/use-block-highlighting.d.ts +0 -3
- package/build-types/components/collaborators-overlay/use-block-highlighting.d.ts.map +1 -1
- package/build-types/components/collaborators-overlay/use-render-cursors.d.ts.map +1 -1
- package/build-types/components/document-bar/index.d.ts +2 -2
- package/build-types/components/document-bar/index.d.ts.map +1 -1
- package/build-types/components/global-styles-provider/index.d.ts +16 -0
- package/build-types/components/global-styles-provider/index.d.ts.map +1 -0
- package/build-types/components/media/index.d.ts +3 -0
- package/build-types/components/media/index.d.ts.map +1 -0
- package/build-types/components/media/metadata-panel.d.ts +12 -0
- package/build-types/components/media/metadata-panel.d.ts.map +1 -0
- package/build-types/components/media/preview.d.ts +9 -0
- package/build-types/components/media/preview.d.ts.map +1 -0
- package/build-types/components/more-menu/view-more-menu-group.d.ts.map +1 -1
- package/build-types/components/page-attributes/parent.d.ts.map +1 -1
- package/build-types/components/post-publish-button/index.d.ts +9 -9
- package/build-types/components/post-publish-button/index.d.ts.map +1 -1
- package/build-types/components/post-revisions-preview/block-diff.d.ts +3 -0
- package/build-types/components/post-revisions-preview/block-diff.d.ts.map +1 -1
- package/build-types/components/post-text-editor/index.d.ts +1 -1
- package/build-types/components/post-text-editor/index.d.ts.map +1 -1
- package/build-types/components/post-text-editor/utils.d.ts +29 -0
- package/build-types/components/post-text-editor/utils.d.ts.map +1 -0
- package/build-types/components/provider/index.d.ts.map +1 -1
- package/build-types/components/provider/use-network-reconnect.d.ts +8 -0
- package/build-types/components/provider/use-network-reconnect.d.ts.map +1 -0
- package/build-types/components/revision-fields-diff/index.d.ts +3 -0
- package/build-types/components/revision-fields-diff/index.d.ts.map +1 -1
- package/build-types/components/sidebar/index.d.ts.map +1 -1
- package/build-types/components/template-actions-panel/block-theme-content.d.ts.map +1 -1
- package/build-types/components/upload-progress-snackbar/index.d.ts +19 -0
- package/build-types/components/upload-progress-snackbar/index.d.ts.map +1 -0
- package/build-types/components/upload-progress-snackbar/stories/index.story.d.ts +28 -0
- package/build-types/components/upload-progress-snackbar/stories/index.story.d.ts.map +1 -0
- package/build-types/components/upload-progress-snackbar/tracker.d.ts +41 -0
- package/build-types/components/upload-progress-snackbar/tracker.d.ts.map +1 -0
- package/build-types/private-apis.d.ts.map +1 -1
- package/build-types/store/selectors.d.ts.map +1 -1
- package/build-types/utils/get-template-part-icon.d.ts.map +1 -1
- package/build-types/utils/media-upload/index.d.ts.map +1 -1
- package/package.json +53 -50
- package/src/components/README.md +1 -1
- package/src/components/block-removal-warnings/index.js +0 -7
- package/src/components/collab-sidebar/note-indicator-toolbar.js +73 -60
- package/src/components/collaborators-overlay/use-block-highlighting.ts +0 -9
- package/src/components/collaborators-overlay/use-render-cursors.ts +0 -8
- package/src/components/collaborators-presence/avatar/test/index.tsx +8 -3
- package/src/components/more-menu/view-more-menu-group.js +1 -2
- package/src/components/page-attributes/parent.js +1 -0
- package/src/components/post-publish-button/index.js +143 -192
- package/src/components/post-publish-button/test/index.js +137 -114
- package/src/components/post-revisions-preview/block-diff.js +63 -19
- package/src/components/post-revisions-preview/preserve-client-ids.js +1 -1
- package/src/components/post-revisions-preview/test/block-diff.js +109 -6
- package/src/components/provider/index.js +4 -0
- package/src/components/provider/test/use-network-reconnect.js +137 -0
- package/src/components/provider/use-network-reconnect.js +44 -0
- package/src/components/revision-fields-diff/index.js +7 -2
- package/src/components/sidebar/index.js +2 -11
- package/src/components/template-actions-panel/block-theme-content.js +10 -1
- package/src/components/upload-progress-snackbar/README.md +26 -0
- package/src/components/upload-progress-snackbar/index.js +216 -0
- package/src/components/upload-progress-snackbar/stories/index.story.tsx +85 -0
- package/src/components/upload-progress-snackbar/style.scss +30 -0
- package/src/components/upload-progress-snackbar/test/index.js +199 -0
- package/src/components/upload-progress-snackbar/tracker.js +105 -0
- package/src/private-apis.js +2 -0
- package/src/store/selectors.js +1 -3
- package/src/style.scss +1 -0
- package/src/utils/media-upload/index.js +27 -0
- package/src/components/commands/index.native.js +0 -2
- package/src/components/deprecated.native.js +0 -47
- package/src/components/editor-help/add-blocks.native.js +0 -40
- package/src/components/editor-help/customize-blocks.native.js +0 -40
- package/src/components/editor-help/help-detail-navigation-screen.native.js +0 -67
- package/src/components/editor-help/help-get-support-button.native.js +0 -38
- package/src/components/editor-help/help-section-title.native.js +0 -29
- package/src/components/editor-help/help-topic-row.native.js +0 -33
- package/src/components/editor-help/icon-move-blocks.native.js +0 -10
- package/src/components/editor-help/index.native.js +0 -208
- package/src/components/editor-help/intro-to-blocks.native.js +0 -91
- package/src/components/editor-help/move-blocks.native.js +0 -55
- package/src/components/editor-help/remove-blocks.native.js +0 -35
- package/src/components/editor-help/style.android.scss +0 -6
- package/src/components/editor-help/style.ios.scss +0 -6
- package/src/components/editor-help/test/index.native.js +0 -81
- package/src/components/editor-help/view-sections.native.js +0 -79
- package/src/components/error-boundary/index.native.js +0 -192
- package/src/components/error-boundary/style.native.scss +0 -116
- package/src/components/index.native.js +0 -15
- package/src/components/offline-status/index.native.js +0 -99
- package/src/components/offline-status/style.native.scss +0 -28
- package/src/components/offline-status/test/index.native.js +0 -108
- package/src/components/post-title/index.native.js +0 -282
- package/src/components/post-title/style.native.scss +0 -13
- package/src/components/post-title/test/__snapshots__/index.native.js.snap +0 -25
- package/src/components/post-title/test/index.native.js +0 -78
- package/src/components/provider/index.native.js +0 -497
- package/src/components/provider/use-block-editor-settings.native.js +0 -48
- package/src/components/template-part-menu-items/index.native.js +0 -3
- package/src/hooks/index.native.js +0 -0
- package/src/index.native.js +0 -16
- package/src/private-apis.native.js +0 -33
- package/src/store/actions.native.js +0 -27
- package/src/store/reducer.native.js +0 -94
- package/src/store/selectors.native.js +0 -57
- package/src/store/test/actions.native.js +0 -16
- package/src/store/test/reducer.native.js +0 -36
- package/src/store/test/selectors.native.js +0 -28
- package/src/utils/index.native.js +0 -6
- package/src/utils/media-sideload/index.native.js +0 -1
- package/src/utils/media-upload/index.native.js +0 -1
|
@@ -35,173 +35,130 @@ __export(post_publish_button_exports, {
|
|
|
35
35
|
});
|
|
36
36
|
module.exports = __toCommonJS(post_publish_button_exports);
|
|
37
37
|
var import_components = require("@wordpress/components");
|
|
38
|
-
var import_element = require("@wordpress/element");
|
|
39
38
|
var import_data = require("@wordpress/data");
|
|
40
|
-
var import_compose = require("@wordpress/compose");
|
|
41
39
|
var import_label = __toESM(require("./label.cjs"));
|
|
42
40
|
var import_store = require("../../store/index.cjs");
|
|
43
41
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
44
42
|
var noop = () => {
|
|
45
43
|
};
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
44
|
+
function PostPublishButton({
|
|
45
|
+
forceIsDirty,
|
|
46
|
+
isOpen,
|
|
47
|
+
isToggle,
|
|
48
|
+
onSubmit = noop,
|
|
49
|
+
onToggle,
|
|
50
|
+
setEntitiesSavedStatesCallback
|
|
51
|
+
}) {
|
|
52
|
+
const {
|
|
53
|
+
hasPublishAction,
|
|
54
|
+
isBeingScheduled,
|
|
55
|
+
isPostSavingLocked,
|
|
56
|
+
isPublishable,
|
|
57
|
+
isPublished,
|
|
58
|
+
isSaveable,
|
|
59
|
+
isSaving,
|
|
60
|
+
isAutoSaving,
|
|
61
|
+
visibility,
|
|
62
|
+
hasNonPostEntityChanges,
|
|
63
|
+
isSavingNonPostEntityChanges,
|
|
64
|
+
postStatus,
|
|
65
|
+
postStatusHasChanged,
|
|
66
|
+
postType,
|
|
67
|
+
postId
|
|
68
|
+
} = (0, import_data.useSelect)((select) => {
|
|
69
|
+
const store = select(import_store.store);
|
|
70
|
+
return {
|
|
71
|
+
isSaving: store.isSavingPost(),
|
|
72
|
+
isAutoSaving: store.isAutosavingPost(),
|
|
73
|
+
isBeingScheduled: store.isEditedPostBeingScheduled(),
|
|
74
|
+
visibility: store.getEditedPostVisibility(),
|
|
75
|
+
isSaveable: store.isEditedPostSaveable(),
|
|
76
|
+
isPostSavingLocked: store.isPostSavingLocked(),
|
|
77
|
+
isPublishable: store.isEditedPostPublishable(),
|
|
78
|
+
isPublished: store.isCurrentPostPublished(),
|
|
79
|
+
hasPublishAction: store.getCurrentPost()._links?.["wp:action-publish"] ?? false,
|
|
80
|
+
postType: store.getCurrentPostType(),
|
|
81
|
+
postId: store.getCurrentPostId(),
|
|
82
|
+
postStatus: store.getEditedPostAttribute("status"),
|
|
83
|
+
postStatusHasChanged: store.getPostEdits()?.status,
|
|
84
|
+
hasNonPostEntityChanges: store.hasNonPostEntityChanges(),
|
|
85
|
+
isSavingNonPostEntityChanges: store.isSavingNonPostEntityChanges()
|
|
68
86
|
};
|
|
87
|
+
}, []);
|
|
88
|
+
const { editPost, savePost } = (0, import_data.useDispatch)(import_store.store);
|
|
89
|
+
const savePostStatus = (status) => {
|
|
90
|
+
editPost({ status }, { undoIgnore: true });
|
|
91
|
+
savePost();
|
|
92
|
+
};
|
|
93
|
+
const createOnClick = (callback) => (...args) => {
|
|
94
|
+
if (hasNonPostEntityChanges && setEntitiesSavedStatesCallback) {
|
|
95
|
+
const onClose = (savedEntities) => {
|
|
96
|
+
if (savedEntities && savedEntities.some(
|
|
97
|
+
(elt) => elt.kind === "postType" && elt.name === postType && elt.key === postId
|
|
98
|
+
)) {
|
|
99
|
+
callback(...args);
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
setEntitiesSavedStatesCallback(() => onClose);
|
|
103
|
+
return noop;
|
|
104
|
+
}
|
|
105
|
+
return callback(...args);
|
|
106
|
+
};
|
|
107
|
+
const isButtonDisabled = isPostSavingLocked || (isSaving || !isSaveable || !isPublishable && !forceIsDirty) && (!hasNonPostEntityChanges || isSavingNonPostEntityChanges);
|
|
108
|
+
const isToggleDisabled = isPostSavingLocked || (isPublished || isSaving || !isSaveable || !isPublishable && !forceIsDirty) && (!hasNonPostEntityChanges || isSavingNonPostEntityChanges);
|
|
109
|
+
let publishStatus = "publish";
|
|
110
|
+
if (postStatusHasChanged) {
|
|
111
|
+
publishStatus = postStatus;
|
|
112
|
+
} else if (!hasPublishAction) {
|
|
113
|
+
publishStatus = "pending";
|
|
114
|
+
} else if (visibility === "private") {
|
|
115
|
+
publishStatus = "private";
|
|
116
|
+
} else if (isBeingScheduled) {
|
|
117
|
+
publishStatus = "future";
|
|
69
118
|
}
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
this.setState({ entitiesSavedStatesCallback: false }, () => {
|
|
74
|
-
if (savedEntities && savedEntities.some(
|
|
75
|
-
(elt) => elt.kind === "postType" && elt.name === postType && elt.key === postId
|
|
76
|
-
)) {
|
|
77
|
-
entitiesSavedStatesCallback();
|
|
78
|
-
}
|
|
79
|
-
});
|
|
80
|
-
}
|
|
81
|
-
render() {
|
|
82
|
-
const {
|
|
83
|
-
forceIsDirty,
|
|
84
|
-
hasPublishAction,
|
|
85
|
-
isBeingScheduled,
|
|
86
|
-
isOpen,
|
|
87
|
-
isPostSavingLocked,
|
|
88
|
-
isPublishable,
|
|
89
|
-
isPublished,
|
|
90
|
-
isSaveable,
|
|
91
|
-
isSaving,
|
|
92
|
-
isAutoSaving,
|
|
93
|
-
isToggle,
|
|
94
|
-
savePostStatus,
|
|
95
|
-
onSubmit = noop,
|
|
96
|
-
onToggle,
|
|
97
|
-
visibility,
|
|
98
|
-
hasNonPostEntityChanges,
|
|
99
|
-
isSavingNonPostEntityChanges,
|
|
100
|
-
postStatus,
|
|
101
|
-
postStatusHasChanged
|
|
102
|
-
} = this.props;
|
|
103
|
-
const isButtonDisabled = isPostSavingLocked || (isSaving || !isSaveable || !isPublishable && !forceIsDirty) && (!hasNonPostEntityChanges || isSavingNonPostEntityChanges);
|
|
104
|
-
const isToggleDisabled = isPostSavingLocked || (isPublished || isSaving || !isSaveable || !isPublishable && !forceIsDirty) && (!hasNonPostEntityChanges || isSavingNonPostEntityChanges);
|
|
105
|
-
let publishStatus = "publish";
|
|
106
|
-
if (postStatusHasChanged) {
|
|
107
|
-
publishStatus = postStatus;
|
|
108
|
-
} else if (!hasPublishAction) {
|
|
109
|
-
publishStatus = "pending";
|
|
110
|
-
} else if (visibility === "private") {
|
|
111
|
-
publishStatus = "private";
|
|
112
|
-
} else if (isBeingScheduled) {
|
|
113
|
-
publishStatus = "future";
|
|
119
|
+
const onClickButton = () => {
|
|
120
|
+
if (isButtonDisabled) {
|
|
121
|
+
return;
|
|
114
122
|
}
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
123
|
+
onSubmit();
|
|
124
|
+
savePostStatus(publishStatus);
|
|
125
|
+
};
|
|
126
|
+
const onClickToggle = () => {
|
|
127
|
+
if (isToggleDisabled) {
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
onToggle();
|
|
131
|
+
};
|
|
132
|
+
const buttonProps = {
|
|
133
|
+
"aria-disabled": isButtonDisabled,
|
|
134
|
+
className: "editor-post-publish-button",
|
|
135
|
+
isBusy: !isAutoSaving && isSaving,
|
|
136
|
+
variant: "primary",
|
|
137
|
+
onClick: createOnClick(onClickButton),
|
|
138
|
+
"aria-haspopup": hasNonPostEntityChanges ? "dialog" : void 0
|
|
139
|
+
};
|
|
140
|
+
const toggleProps = {
|
|
141
|
+
"aria-disabled": isToggleDisabled,
|
|
142
|
+
"aria-expanded": isOpen,
|
|
143
|
+
className: "editor-post-publish-panel__toggle",
|
|
144
|
+
isBusy: isSaving && isPublished,
|
|
145
|
+
variant: "primary",
|
|
146
|
+
size: "compact",
|
|
147
|
+
onClick: createOnClick(onClickToggle),
|
|
148
|
+
"aria-haspopup": hasNonPostEntityChanges ? "dialog" : void 0
|
|
149
|
+
};
|
|
150
|
+
const componentProps = isToggle ? toggleProps : buttonProps;
|
|
151
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
152
|
+
import_components.Button,
|
|
153
|
+
{
|
|
154
|
+
...componentProps,
|
|
155
|
+
className: `${componentProps.className} editor-post-publish-button__button`,
|
|
142
156
|
size: "compact",
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
import_components.Button,
|
|
149
|
-
{
|
|
150
|
-
...componentProps,
|
|
151
|
-
className: `${componentProps.className} editor-post-publish-button__button`,
|
|
152
|
-
size: "compact",
|
|
153
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_label.default, {})
|
|
154
|
-
}
|
|
155
|
-
) });
|
|
156
|
-
}
|
|
157
|
-
};
|
|
158
|
-
var post_publish_button_default = (0, import_compose.compose)([
|
|
159
|
-
(0, import_data.withSelect)((select) => {
|
|
160
|
-
const {
|
|
161
|
-
isSavingPost,
|
|
162
|
-
isAutosavingPost,
|
|
163
|
-
isEditedPostBeingScheduled,
|
|
164
|
-
getEditedPostVisibility,
|
|
165
|
-
isCurrentPostPublished,
|
|
166
|
-
isEditedPostSaveable,
|
|
167
|
-
isEditedPostPublishable,
|
|
168
|
-
isPostSavingLocked,
|
|
169
|
-
getCurrentPost,
|
|
170
|
-
getCurrentPostType,
|
|
171
|
-
getCurrentPostId,
|
|
172
|
-
hasNonPostEntityChanges,
|
|
173
|
-
isSavingNonPostEntityChanges,
|
|
174
|
-
getEditedPostAttribute,
|
|
175
|
-
getPostEdits
|
|
176
|
-
} = select(import_store.store);
|
|
177
|
-
return {
|
|
178
|
-
isSaving: isSavingPost(),
|
|
179
|
-
isAutoSaving: isAutosavingPost(),
|
|
180
|
-
isBeingScheduled: isEditedPostBeingScheduled(),
|
|
181
|
-
visibility: getEditedPostVisibility(),
|
|
182
|
-
isSaveable: isEditedPostSaveable(),
|
|
183
|
-
isPostSavingLocked: isPostSavingLocked(),
|
|
184
|
-
isPublishable: isEditedPostPublishable(),
|
|
185
|
-
isPublished: isCurrentPostPublished(),
|
|
186
|
-
hasPublishAction: getCurrentPost()._links?.["wp:action-publish"] ?? false,
|
|
187
|
-
postType: getCurrentPostType(),
|
|
188
|
-
postId: getCurrentPostId(),
|
|
189
|
-
postStatus: getEditedPostAttribute("status"),
|
|
190
|
-
postStatusHasChanged: getPostEdits()?.status,
|
|
191
|
-
hasNonPostEntityChanges: hasNonPostEntityChanges(),
|
|
192
|
-
isSavingNonPostEntityChanges: isSavingNonPostEntityChanges()
|
|
193
|
-
};
|
|
194
|
-
}),
|
|
195
|
-
(0, import_data.withDispatch)((dispatch) => {
|
|
196
|
-
const { editPost, savePost } = dispatch(import_store.store);
|
|
197
|
-
return {
|
|
198
|
-
savePostStatus: (status) => {
|
|
199
|
-
editPost({ status }, { undoIgnore: true });
|
|
200
|
-
savePost();
|
|
201
|
-
}
|
|
202
|
-
};
|
|
203
|
-
})
|
|
204
|
-
])(PostPublishButton);
|
|
157
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_label.default, {})
|
|
158
|
+
}
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
var post_publish_button_default = PostPublishButton;
|
|
205
162
|
// Annotate the CommonJS export names for ESM import in node:
|
|
206
163
|
0 && (module.exports = {
|
|
207
164
|
PostPublishButton
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/components/post-publish-button/index.js"],
|
|
4
|
-
"sourcesContent": ["/**\n * WordPress dependencies\n */\nimport { Button } from '@wordpress/components';\nimport {
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAGA,wBAAuB;AACvB,
|
|
6
|
-
"names": ["
|
|
4
|
+
"sourcesContent": ["/**\n * WordPress dependencies\n */\nimport { Button } from '@wordpress/components';\nimport { useDispatch, useSelect } from '@wordpress/data';\n\n/**\n * Internal dependencies\n */\nimport PublishButtonLabel from './label';\nimport { store as editorStore } from '../../store';\n\nconst noop = () => {};\n\nexport function PostPublishButton( {\n\tforceIsDirty,\n\tisOpen,\n\tisToggle,\n\tonSubmit = noop,\n\tonToggle,\n\tsetEntitiesSavedStatesCallback,\n} ) {\n\tconst {\n\t\thasPublishAction,\n\t\tisBeingScheduled,\n\t\tisPostSavingLocked,\n\t\tisPublishable,\n\t\tisPublished,\n\t\tisSaveable,\n\t\tisSaving,\n\t\tisAutoSaving,\n\t\tvisibility,\n\t\thasNonPostEntityChanges,\n\t\tisSavingNonPostEntityChanges,\n\t\tpostStatus,\n\t\tpostStatusHasChanged,\n\t\tpostType,\n\t\tpostId,\n\t} = useSelect( ( select ) => {\n\t\tconst store = select( editorStore );\n\t\treturn {\n\t\t\tisSaving: store.isSavingPost(),\n\t\t\tisAutoSaving: store.isAutosavingPost(),\n\t\t\tisBeingScheduled: store.isEditedPostBeingScheduled(),\n\t\t\tvisibility: store.getEditedPostVisibility(),\n\t\t\tisSaveable: store.isEditedPostSaveable(),\n\t\t\tisPostSavingLocked: store.isPostSavingLocked(),\n\t\t\tisPublishable: store.isEditedPostPublishable(),\n\t\t\tisPublished: store.isCurrentPostPublished(),\n\t\t\thasPublishAction:\n\t\t\t\tstore.getCurrentPost()._links?.[ 'wp:action-publish' ] ?? false,\n\t\t\tpostType: store.getCurrentPostType(),\n\t\t\tpostId: store.getCurrentPostId(),\n\t\t\tpostStatus: store.getEditedPostAttribute( 'status' ),\n\t\t\tpostStatusHasChanged: store.getPostEdits()?.status,\n\t\t\thasNonPostEntityChanges: store.hasNonPostEntityChanges(),\n\t\t\tisSavingNonPostEntityChanges: store.isSavingNonPostEntityChanges(),\n\t\t};\n\t}, [] );\n\n\tconst { editPost, savePost } = useDispatch( editorStore );\n\n\tconst savePostStatus = ( status ) => {\n\t\teditPost( { status }, { undoIgnore: true } );\n\t\tsavePost();\n\t};\n\n\tconst createOnClick =\n\t\t( callback ) =>\n\t\t( ...args ) => {\n\t\t\t// If a post with non-post entities is published, but the user\n\t\t\t// elects to not save changes to the non-post entities, those\n\t\t\t// entities will still be dirty when the Publish button is clicked.\n\t\t\t// We also need to check that the `setEntitiesSavedStatesCallback`\n\t\t\t// prop was passed. See https://github.com/WordPress/gutenberg/pull/37383\n\t\t\tif ( hasNonPostEntityChanges && setEntitiesSavedStatesCallback ) {\n\t\t\t\t// The modal for multiple entity saving will open. If the post\n\t\t\t\t// entity is checked when it closes, run the held callback.\n\t\t\t\tconst onClose = ( savedEntities ) => {\n\t\t\t\t\tif (\n\t\t\t\t\t\tsavedEntities &&\n\t\t\t\t\t\tsavedEntities.some(\n\t\t\t\t\t\t\t( elt ) =>\n\t\t\t\t\t\t\t\telt.kind === 'postType' &&\n\t\t\t\t\t\t\t\telt.name === postType &&\n\t\t\t\t\t\t\t\telt.key === postId\n\t\t\t\t\t\t)\n\t\t\t\t\t) {\n\t\t\t\t\t\tcallback( ...args );\n\t\t\t\t\t}\n\t\t\t\t};\n\n\t\t\t\t// Open the save panel by setting its callback.\n\t\t\t\t// To set a function on the useState hook, we must set it\n\t\t\t\t// with another function (() => myFunction). Passing the\n\t\t\t\t// function on its own will cause an error when called.\n\t\t\t\tsetEntitiesSavedStatesCallback( () => onClose );\n\t\t\t\treturn noop;\n\t\t\t}\n\n\t\t\treturn callback( ...args );\n\t\t};\n\n\tconst isButtonDisabled =\n\t\tisPostSavingLocked ||\n\t\t( ( isSaving ||\n\t\t\t! isSaveable ||\n\t\t\t( ! isPublishable && ! forceIsDirty ) ) &&\n\t\t\t( ! hasNonPostEntityChanges || isSavingNonPostEntityChanges ) );\n\n\tconst isToggleDisabled =\n\t\tisPostSavingLocked ||\n\t\t( ( isPublished ||\n\t\t\tisSaving ||\n\t\t\t! isSaveable ||\n\t\t\t( ! isPublishable && ! forceIsDirty ) ) &&\n\t\t\t( ! hasNonPostEntityChanges || isSavingNonPostEntityChanges ) );\n\n\t// If the new status has not changed explicitly, we derive it from\n\t// other factors, like having a publish action, etc.. We need to preserve\n\t// this because it affects when to show the pre and post publish panels.\n\t// If it has changed though explicitly, we need to respect that.\n\tlet publishStatus = 'publish';\n\tif ( postStatusHasChanged ) {\n\t\tpublishStatus = postStatus;\n\t} else if ( ! hasPublishAction ) {\n\t\tpublishStatus = 'pending';\n\t} else if ( visibility === 'private' ) {\n\t\tpublishStatus = 'private';\n\t} else if ( isBeingScheduled ) {\n\t\tpublishStatus = 'future';\n\t}\n\n\tconst onClickButton = () => {\n\t\tif ( isButtonDisabled ) {\n\t\t\treturn;\n\t\t}\n\t\tonSubmit();\n\t\tsavePostStatus( publishStatus );\n\t};\n\n\t// Callback to open the publish panel.\n\tconst onClickToggle = () => {\n\t\tif ( isToggleDisabled ) {\n\t\t\treturn;\n\t\t}\n\t\tonToggle();\n\t};\n\n\tconst buttonProps = {\n\t\t'aria-disabled': isButtonDisabled,\n\t\tclassName: 'editor-post-publish-button',\n\t\tisBusy: ! isAutoSaving && isSaving,\n\t\tvariant: 'primary',\n\t\tonClick: createOnClick( onClickButton ),\n\t\t'aria-haspopup': hasNonPostEntityChanges ? 'dialog' : undefined,\n\t};\n\n\tconst toggleProps = {\n\t\t'aria-disabled': isToggleDisabled,\n\t\t'aria-expanded': isOpen,\n\t\tclassName: 'editor-post-publish-panel__toggle',\n\t\tisBusy: isSaving && isPublished,\n\t\tvariant: 'primary',\n\t\tsize: 'compact',\n\t\tonClick: createOnClick( onClickToggle ),\n\t\t'aria-haspopup': hasNonPostEntityChanges ? 'dialog' : undefined,\n\t};\n\tconst componentProps = isToggle ? toggleProps : buttonProps;\n\treturn (\n\t\t<Button\n\t\t\t{ ...componentProps }\n\t\t\tclassName={ `${ componentProps.className } editor-post-publish-button__button` }\n\t\t\tsize=\"compact\"\n\t\t>\n\t\t\t<PublishButtonLabel />\n\t\t</Button>\n\t);\n}\n\n/**\n * Renders the publish button.\n */\nexport default PostPublishButton;\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["editorStore", "PublishButtonLabel"]
|
|
7
7
|
}
|
|
@@ -23,14 +23,16 @@ __export(block_diff_exports, {
|
|
|
23
23
|
diffRevisionContent: () => diffRevisionContent
|
|
24
24
|
});
|
|
25
25
|
module.exports = __toCommonJS(block_diff_exports);
|
|
26
|
-
var
|
|
27
|
-
var import_word = require("diff/lib/diff/word");
|
|
26
|
+
var import_diff = require("diff");
|
|
28
27
|
var import_block_serialization_default_parser = require("@wordpress/block-serialization-default-parser");
|
|
29
28
|
var import_blocks = require("@wordpress/blocks");
|
|
30
29
|
var import_rich_text = require("@wordpress/rich-text");
|
|
31
30
|
var import_i18n = require("@wordpress/i18n");
|
|
32
31
|
var import_lock_unlock = require("../../lock-unlock.cjs");
|
|
33
32
|
var { parseRawBlock } = (0, import_lock_unlock.unlock)(import_blocks.privateApis);
|
|
33
|
+
function isWhitespaceRawBlock(rawBlock) {
|
|
34
|
+
return rawBlock.blockName === null && (!rawBlock.innerHTML || !rawBlock.innerHTML.trim());
|
|
35
|
+
}
|
|
34
36
|
function stringifyValue(value) {
|
|
35
37
|
if (value === null || value === void 0) {
|
|
36
38
|
return "";
|
|
@@ -150,14 +152,19 @@ function pairSimilarBlocks(blocks) {
|
|
|
150
152
|
};
|
|
151
153
|
const lo = Math.min(rem.index, bestMatch.index);
|
|
152
154
|
const hi = Math.max(rem.index, bestMatch.index);
|
|
153
|
-
let
|
|
155
|
+
let crossesCurrentContent = false;
|
|
154
156
|
for (let i = lo + 1; i < hi; i++) {
|
|
155
|
-
|
|
156
|
-
|
|
157
|
+
const status = blocks[i].__revisionDiffStatus?.status;
|
|
158
|
+
if (status === void 0) {
|
|
159
|
+
crossesCurrentContent = true;
|
|
160
|
+
break;
|
|
161
|
+
}
|
|
162
|
+
if (status === "added" && !pairedAdded.has(i)) {
|
|
163
|
+
crossesCurrentContent = true;
|
|
157
164
|
break;
|
|
158
165
|
}
|
|
159
166
|
}
|
|
160
|
-
if (
|
|
167
|
+
if (crossesCurrentContent) {
|
|
161
168
|
modifications.set(bestMatch.index, modifiedBlock);
|
|
162
169
|
pairedRemoved.add(rem.index);
|
|
163
170
|
} else {
|
|
@@ -177,6 +184,8 @@ function pairSimilarBlocks(blocks) {
|
|
|
177
184
|
}).filter(Boolean);
|
|
178
185
|
}
|
|
179
186
|
function diffRawBlocks(currentRaw, previousRaw) {
|
|
187
|
+
currentRaw = currentRaw.filter((b) => !isWhitespaceRawBlock(b));
|
|
188
|
+
previousRaw = previousRaw.filter((b) => !isWhitespaceRawBlock(b));
|
|
180
189
|
const createBlockSignature = (rawBlock) => JSON.stringify({
|
|
181
190
|
name: rawBlock.blockName,
|
|
182
191
|
attrs: rawBlock.attrs,
|
|
@@ -188,7 +197,7 @@ function diffRawBlocks(currentRaw, previousRaw) {
|
|
|
188
197
|
});
|
|
189
198
|
const currentSigs = currentRaw.map(createBlockSignature);
|
|
190
199
|
const previousSigs = previousRaw.map(createBlockSignature);
|
|
191
|
-
const diff = (0,
|
|
200
|
+
const diff = (0, import_diff.diffArrays)(previousSigs, currentSigs);
|
|
192
201
|
const result = [];
|
|
193
202
|
let currIdx = 0;
|
|
194
203
|
let prevIdx = 0;
|
|
@@ -316,7 +325,7 @@ function describeFormatChange(currentFormats, previousFormats, currIdx, prevIdx)
|
|
|
316
325
|
function applyRichTextDiff(currentRichText, previousRichText) {
|
|
317
326
|
const currentText = currentRichText.toPlainText();
|
|
318
327
|
const previousText = previousRichText.toPlainText();
|
|
319
|
-
const textDiff = (0,
|
|
328
|
+
const textDiff = (0, import_diff.diffWordsWithSpace)(previousText, currentText);
|
|
320
329
|
let result = (0, import_rich_text.create)({ text: "" });
|
|
321
330
|
let currentIdx = 0;
|
|
322
331
|
let previousIdx = 0;
|
|
@@ -436,7 +445,10 @@ function applyDiffToBlock(currentBlock, previousBlock, diffStatus) {
|
|
|
436
445
|
previousBlock.attributes[attrName]
|
|
437
446
|
);
|
|
438
447
|
if (currStr !== prevStr) {
|
|
439
|
-
changedAttributes[attrName] = (0,
|
|
448
|
+
changedAttributes[attrName] = (0, import_diff.diffWordsWithSpace)(
|
|
449
|
+
prevStr,
|
|
450
|
+
currStr
|
|
451
|
+
);
|
|
440
452
|
}
|
|
441
453
|
}
|
|
442
454
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/components/post-revisions-preview/block-diff.js"],
|
|
4
|
-
"sourcesContent": ["/**\n * External dependencies\n */\nimport { diffArrays } from 'diff/lib/diff/array';\nimport { diffWords } from 'diff/lib/diff/word';\n\n/**\n * WordPress dependencies\n */\nimport { parse as grammarParse } from '@wordpress/block-serialization-default-parser';\nimport {\n\tprivateApis as blocksPrivateApis,\n\tgetBlockType,\n} from '@wordpress/blocks';\nimport {\n\tRichTextData,\n\tcreate,\n\tslice,\n\tconcat,\n\tapplyFormat,\n} from '@wordpress/rich-text';\nimport { __, _n, sprintf } from '@wordpress/i18n';\n\n/**\n * Internal dependencies\n */\nimport { unlock } from '../../lock-unlock';\n\nconst { parseRawBlock } = unlock( blocksPrivateApis );\n\n/**\n * Safely stringifies a value for display and comparison.\n *\n * @param {*} value The value to stringify.\n * @return {string} The stringified value.\n */\nfunction stringifyValue( value ) {\n\tif ( value === null || value === undefined ) {\n\t\treturn '';\n\t}\n\tif ( typeof value === 'object' ) {\n\t\treturn JSON.stringify( value, null, 2 );\n\t}\n\treturn String( value );\n}\n\n/**\n * Calculate text similarity using word-set overlap.\n *\n * Uses a variant of the Jaccard index (https://en.wikipedia.org/wiki/Jaccard_index)\n * called the overlap coefficient (https://en.wikipedia.org/wiki/Overlap_coefficient)\n * where we divide by the larger set size rather than the union. This ensures that\n * a small edit to a long paragraph scores high \u2014 the few changed words don't\n * dilute the score.\n *\n * This replaces the previous diffWords-based similarity which was O(n*m) per pair.\n * The word-set approach is O(n) where n is the number of words.\n *\n * Words are extracted using Intl.Segmenter for proper multilingual support\n * (CJK, Thai, etc.) rather than splitting on whitespace.\n *\n * @param {string} text1 First text to compare.\n * @param {string} text2 Second text to compare.\n * @return {number} Similarity score between 0 and 1.\n */\nfunction textSimilarity( text1, text2 ) {\n\tif ( ! text1 && ! text2 ) {\n\t\treturn 1;\n\t}\n\tif ( ! text1 || ! text2 ) {\n\t\treturn 0;\n\t}\n\n\tconst segmenter = new Intl.Segmenter( undefined, {\n\t\tgranularity: 'word',\n\t} );\n\t// Safari's Intl.Segmenter returns isWordLike: false for numeric segments,\n\t// so fall back to a Unicode-aware regex for letters and numbers.\n\tconst wordLikeRegex = /[\\p{L}\\p{N}]/u;\n\tconst getWords = ( text ) => {\n\t\tconst words = [];\n\t\tfor ( const { segment, isWordLike } of segmenter.segment( text ) ) {\n\t\t\tif ( isWordLike || wordLikeRegex.test( segment ) ) {\n\t\t\t\twords.push( segment );\n\t\t\t}\n\t\t}\n\t\treturn words;\n\t};\n\tconst words1 = getWords( text1 );\n\tconst words2 = getWords( text2 );\n\n\tif ( words1.length === 0 && words2.length === 0 ) {\n\t\treturn 1;\n\t}\n\n\tconst set1 = new Set( words1 );\n\tlet intersection = 0;\n\tfor ( const word of words2 ) {\n\t\tif ( set1.has( word ) ) {\n\t\t\tintersection++;\n\t\t}\n\t}\n\n\tconst total = Math.max( words1.length, words2.length );\n\treturn total > 0 ? intersection / total : 0;\n}\n\n/**\n * Post-process diff result to pair similar removed/added blocks as modifications.\n *\n * After LCS diffing, a block whose content changed appears as a separate \"removed\"\n * and \"added\" entry (since the full block signature differs). This function detects\n * such pairs and merges them into a single \"modified\" block with inline diff.\n *\n * Two pairing strategies are used:\n * 1. When exactly one block of a given type was removed and one was added,\n * they are paired directly \u2014 no ambiguity, no similarity check needed.\n * 2. When multiple candidates exist, textSimilarity (overlap coefficient) is\n * used to find the best match. Blocks must share at least 50% of their\n * words to be paired, preventing unrelated paragraphs from being merged.\n *\n * @param {Array} blocks Raw blocks with diff status.\n * @return {Array} Blocks with similar pairs converted to modifications.\n */\nfunction pairSimilarBlocks( blocks ) {\n\tconst removed = [];\n\tconst added = [];\n\n\t// Separate blocks by status, tracking original indices.\n\tblocks.forEach( ( block, index ) => {\n\t\tconst status = block.__revisionDiffStatus?.status;\n\t\tif ( status === 'removed' ) {\n\t\t\tremoved.push( { block, index } );\n\t\t} else if ( status === 'added' ) {\n\t\t\tadded.push( { block, index } );\n\t\t}\n\t} );\n\n\t// If no removed or no added, nothing to pair.\n\tif ( removed.length === 0 || added.length === 0 ) {\n\t\treturn blocks;\n\t}\n\n\tconst pairedRemoved = new Set(); // Indices of removed blocks filtered out.\n\tconst pairedAdded = new Set(); // Indices of added blocks filtered out.\n\tconst modifications = new Map(); // Index \u2192 modified block.\n\tconst SIMILARITY_THRESHOLD = 0.5;\n\n\t// Group candidates by block name for efficient lookup.\n\tconst addedByName = new Map();\n\tfor ( const add of added ) {\n\t\tconst name = add.block.blockName;\n\t\tif ( ! addedByName.has( name ) ) {\n\t\t\taddedByName.set( name, [] );\n\t\t}\n\t\taddedByName.get( name ).push( add );\n\t}\n\tconst removedByName = new Map();\n\tfor ( const rem of removed ) {\n\t\tconst name = rem.block.blockName;\n\t\tif ( ! removedByName.has( name ) ) {\n\t\t\tremovedByName.set( name, [] );\n\t\t}\n\t\tremovedByName.get( name ).push( rem );\n\t}\n\n\t// For each removed block, find best matching added block.\n\t// Track the highest added index paired so far \u2014 new pairings must\n\t// not go backwards, or the removed/added text order would break.\n\tlet maxPairedAddedIndex = -1;\n\n\tfor ( const rem of removed ) {\n\t\tconst candidates = addedByName.get( rem.block.blockName ) || [];\n\t\tconst sameNameRemoved = removedByName.get( rem.block.blockName ) || [];\n\t\tconst unpaired = candidates.filter(\n\t\t\t( add ) =>\n\t\t\t\t! modifications.has( add.index ) &&\n\t\t\t\tadd.index > maxPairedAddedIndex\n\t\t);\n\n\t\tif ( unpaired.length === 0 ) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tlet bestMatch = null;\n\n\t\t// If there's exactly one removed and one added of this type,\n\t\t// pair them directly \u2014 no ambiguity, no similarity check needed.\n\t\tif ( sameNameRemoved.length === 1 && unpaired.length === 1 ) {\n\t\t\tconst add = unpaired[ 0 ];\n\t\t\tconst attrsMatch =\n\t\t\t\tJSON.stringify( rem.block.attrs ) ===\n\t\t\t\tJSON.stringify( add.block.attrs );\n\t\t\t// Only skip pairing if both content and attrs are identical\n\t\t\t// (position swap, not a modification).\n\t\t\tconst contentMatch =\n\t\t\t\t( rem.block.innerHTML || '' ) === ( add.block.innerHTML || '' );\n\t\t\tif ( ! contentMatch || ! attrsMatch ) {\n\t\t\t\tbestMatch = add;\n\t\t\t}\n\t\t} else {\n\t\t\t// Multiple candidates \u2014 use similarity to find best match.\n\t\t\tlet bestScore = 0;\n\t\t\tfor ( const add of unpaired ) {\n\t\t\t\tconst score = textSimilarity(\n\t\t\t\t\trem.block.innerHTML || '',\n\t\t\t\t\tadd.block.innerHTML || ''\n\t\t\t\t);\n\t\t\t\t// Skip identical blocks (score=1 with same attrs) \u2014 those\n\t\t\t\t// are position swaps, not modifications. They should show\n\t\t\t\t// as separate removed + added, not as a no-op \"modified\".\n\t\t\t\tconst attrsMatch =\n\t\t\t\t\tJSON.stringify( rem.block.attrs ) ===\n\t\t\t\t\tJSON.stringify( add.block.attrs );\n\t\t\t\tif (\n\t\t\t\t\tscore > bestScore &&\n\t\t\t\t\tscore > SIMILARITY_THRESHOLD &&\n\t\t\t\t\t( score < 1 || ! attrsMatch )\n\t\t\t\t) {\n\t\t\t\t\tbestScore = score;\n\t\t\t\t\tbestMatch = add;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif ( bestMatch ) {\n\t\t\tmaxPairedAddedIndex = bestMatch.index;\n\n\t\t\tconst modifiedBlock = {\n\t\t\t\t...bestMatch.block,\n\t\t\t\t__revisionDiffStatus: { status: 'modified' },\n\t\t\t\t__previousRawBlock: rem.block,\n\t\t\t};\n\n\t\t\t// Decide where to place the modified block by checking\n\t\t\t// what's between the removed and added positions.\n\t\t\t// If there are unpaired added blocks between them,\n\t\t\t// placing at the removed position would put the modified\n\t\t\t// block before content that comes before it in the\n\t\t\t// current revision \u2014 so use the added position.\n\t\t\t// Otherwise, use the removed position to keep the\n\t\t\t// previous revision's order intact.\n\t\t\tconst lo = Math.min( rem.index, bestMatch.index );\n\t\t\tconst hi = Math.max( rem.index, bestMatch.index );\n\t\t\tlet hasAddedBetween = false;\n\t\t\tfor ( let i = lo + 1; i < hi; i++ ) {\n\t\t\t\tif (\n\t\t\t\t\tblocks[ i ].__revisionDiffStatus?.status === 'added' &&\n\t\t\t\t\t! pairedAdded.has( i )\n\t\t\t\t) {\n\t\t\t\t\thasAddedBetween = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif ( hasAddedBetween ) {\n\t\t\t\t// Use the added position \u2014 don't jump before\n\t\t\t\t// current-revision content.\n\t\t\t\tmodifications.set( bestMatch.index, modifiedBlock );\n\t\t\t\tpairedRemoved.add( rem.index );\n\t\t\t} else {\n\t\t\t\t// Use the removed position \u2014 keep the previous\n\t\t\t\t// revision's reading order.\n\t\t\t\tmodifications.set( rem.index, modifiedBlock );\n\t\t\t\tpairedAdded.add( bestMatch.index );\n\t\t\t}\n\t\t}\n\t}\n\n\t// Rebuild result: replace modification targets, filter out\n\t// their paired counterparts.\n\treturn blocks\n\t\t.map( ( block, index ) => {\n\t\t\tif ( pairedRemoved.has( index ) || pairedAdded.has( index ) ) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tif ( modifications.has( index ) ) {\n\t\t\t\treturn modifications.get( index );\n\t\t\t}\n\t\t\treturn block;\n\t\t} )\n\t\t.filter( Boolean );\n}\n\n/**\n * Diff raw block arrays using LCS, recursively handling innerBlocks.\n * Detects modifications when exactly 1 block is removed and 1 is added\n * with the same blockName (1:1 replacement = modification).\n *\n * @param {Array} currentRaw Current revision's raw blocks.\n * @param {Array} previousRaw Previous revision's raw blocks.\n * @return {Array} Merged raw blocks with diff status injected.\n */\nfunction diffRawBlocks( currentRaw, previousRaw ) {\n\tconst createBlockSignature = ( rawBlock ) =>\n\t\tJSON.stringify( {\n\t\t\tname: rawBlock.blockName,\n\t\t\tattrs: rawBlock.attrs,\n\t\t\t// Use innerContent filtered to non-null and non-whitespace-only strings.\n\t\t\t// This excludes whitespace between inner blocks which changes based on count.\n\t\t\thtml: ( rawBlock.innerContent || [] ).filter(\n\t\t\t\t( c ) => c !== null && c.trim() !== ''\n\t\t\t),\n\t\t} );\n\tconst currentSigs = currentRaw.map( createBlockSignature );\n\tconst previousSigs = previousRaw.map( createBlockSignature );\n\n\tconst diff = diffArrays( previousSigs, currentSigs );\n\n\tconst result = [];\n\tlet currIdx = 0;\n\tlet prevIdx = 0;\n\n\tfor ( const part of diff ) {\n\t\tif ( part.added ) {\n\t\t\tfor ( let i = 0; i < part.count; i++ ) {\n\t\t\t\tresult.push( {\n\t\t\t\t\t...currentRaw[ currIdx++ ],\n\t\t\t\t\t__revisionDiffStatus: { status: 'added' },\n\t\t\t\t} );\n\t\t\t}\n\t\t} else if ( part.removed ) {\n\t\t\tfor ( let i = 0; i < part.count; i++ ) {\n\t\t\t\tresult.push( {\n\t\t\t\t\t...previousRaw[ prevIdx++ ],\n\t\t\t\t\t__revisionDiffStatus: { status: 'removed' },\n\t\t\t\t} );\n\t\t\t}\n\t\t} else {\n\t\t\t// Matched blocks - recursively diff their innerBlocks.\n\t\t\tfor ( let i = 0; i < part.count; i++ ) {\n\t\t\t\tconst currBlock = currentRaw[ currIdx++ ];\n\t\t\t\tconst prevBlock = previousRaw[ prevIdx++ ];\n\n\t\t\t\t// Recursively diff inner blocks.\n\t\t\t\tconst diffedInnerBlocks = diffRawBlocks(\n\t\t\t\t\tcurrBlock.innerBlocks || [],\n\t\t\t\t\tprevBlock.innerBlocks || []\n\t\t\t\t);\n\n\t\t\t\tresult.push( {\n\t\t\t\t\t...currBlock,\n\t\t\t\t\tinnerBlocks: diffedInnerBlocks,\n\t\t\t\t} );\n\t\t\t}\n\t\t}\n\t}\n\n\t// Post-process to pair similar removed/added blocks as modifications.\n\treturn pairSimilarBlocks( result );\n}\n\n/**\n * Check if formatting has changed at specific character indices.\n *\n * @param {Array} currentFormats Current formats array.\n * @param {Array} previousFormats Previous formats array.\n * @param {number} currentIndex Character index in current.\n * @param {number} previousIndex Character index in previous.\n * @return {boolean} True if formatting changed at these indices.\n */\nfunction hasFormatChangedAtIndex(\n\tcurrentFormats,\n\tpreviousFormats,\n\tcurrentIndex,\n\tpreviousIndex\n) {\n\tconst currFmts = currentFormats[ currentIndex ] || [];\n\tconst prevFmts = previousFormats[ previousIndex ] || [];\n\n\tif ( currFmts.length !== prevFmts.length ) {\n\t\treturn true;\n\t}\n\n\t// Check if each format in current exists in previous\n\tfor ( const fmt of currFmts ) {\n\t\tconst match = prevFmts.find(\n\t\t\t( pf ) =>\n\t\t\t\tpf.type === fmt.type &&\n\t\t\t\tJSON.stringify( pf.attributes ) ===\n\t\t\t\t\tJSON.stringify( fmt.attributes )\n\t\t);\n\t\tif ( ! match ) {\n\t\t\treturn true;\n\t\t}\n\t}\n\n\treturn false;\n}\n\n/**\n * Analyze what formatting changed between two character positions.\n * Returns both the change type (for styling) and a description (for tooltip).\n *\n * @param {Array} currentFormats Current formats array.\n * @param {Array} previousFormats Previous formats array.\n * @param {number} currIdx Character index in current.\n * @param {number} prevIdx Character index in previous.\n * @return {{ type: 'added'|'removed'|'changed', description: string }} Change info.\n */\nfunction describeFormatChange(\n\tcurrentFormats,\n\tpreviousFormats,\n\tcurrIdx,\n\tprevIdx\n) {\n\tconst currFmts = currentFormats[ currIdx ] || [];\n\tconst prevFmts = previousFormats[ prevIdx ] || [];\n\n\tlet addedCount = 0;\n\tlet removedCount = 0;\n\tlet changedCount = 0;\n\n\t// Find added formats and attribute changes\n\tfor ( const fmt of currFmts ) {\n\t\tconst match = prevFmts.find( ( pf ) => pf.type === fmt.type );\n\t\tif ( ! match ) {\n\t\t\taddedCount++;\n\t\t} else if (\n\t\t\tJSON.stringify( fmt.attributes ) !==\n\t\t\tJSON.stringify( match.attributes )\n\t\t) {\n\t\t\tchangedCount++;\n\t\t}\n\t}\n\n\t// Find removed formats\n\tfor ( const fmt of prevFmts ) {\n\t\tconst match = currFmts.find( ( cf ) => cf.type === fmt.type );\n\t\tif ( ! match ) {\n\t\t\tremovedCount++;\n\t\t}\n\t}\n\n\t// Determine primary change type for styling\n\tif ( addedCount > 0 && removedCount === 0 && changedCount === 0 ) {\n\t\treturn {\n\t\t\ttype: 'added',\n\t\t\tdescription: sprintf(\n\t\t\t\t/* translators: %d: number of formats added */\n\t\t\t\t_n( '%d format added', '%d formats added', addedCount ),\n\t\t\t\taddedCount\n\t\t\t),\n\t\t};\n\t}\n\tif ( removedCount > 0 && addedCount === 0 && changedCount === 0 ) {\n\t\treturn {\n\t\t\ttype: 'removed',\n\t\t\tdescription: sprintf(\n\t\t\t\t/* translators: %d: number of formats removed */\n\t\t\t\t_n( '%d format removed', '%d formats removed', removedCount ),\n\t\t\t\tremovedCount\n\t\t\t),\n\t\t};\n\t}\n\n\t// Mixed or attribute-only changes\n\tconst parts = [];\n\tif ( addedCount > 0 ) {\n\t\tparts.push(\n\t\t\tsprintf(\n\t\t\t\t/* translators: %d: number of formats added */\n\t\t\t\t_n( '%d format added', '%d formats added', addedCount ),\n\t\t\t\taddedCount\n\t\t\t)\n\t\t);\n\t}\n\tif ( removedCount > 0 ) {\n\t\tparts.push(\n\t\t\tsprintf(\n\t\t\t\t/* translators: %d: number of formats removed */\n\t\t\t\t_n( '%d format removed', '%d formats removed', removedCount ),\n\t\t\t\tremovedCount\n\t\t\t)\n\t\t);\n\t}\n\tif ( changedCount > 0 ) {\n\t\tparts.push(\n\t\t\tsprintf(\n\t\t\t\t/* translators: %d: number of formats changed */\n\t\t\t\t_n( '%d format changed', '%d formats changed', changedCount ),\n\t\t\t\tchangedCount\n\t\t\t)\n\t\t);\n\t}\n\treturn {\n\t\ttype: 'changed',\n\t\tdescription: parts.join( ', ' ) || __( 'Formatting changed' ),\n\t};\n}\n\n/**\n * Apply inline diff formatting comparing two RichTextData values.\n * - Text changes: apply revision/diff-removed and revision/diff-added formats\n * - Format-only changes (text unchanged): apply revision/diff-format-changed format\n *\n * @param {RichTextData} currentRichText Current revision's rich text.\n * @param {RichTextData} previousRichText Previous revision's rich text.\n * @return {RichTextData} New rich text with diff formatting applied.\n */\nfunction applyRichTextDiff( currentRichText, previousRichText ) {\n\tconst currentText = currentRichText.toPlainText();\n\tconst previousText = previousRichText.toPlainText();\n\n\t// Diff the plain text (words for cleaner output)\n\tconst textDiff = diffWords( previousText, currentText );\n\n\tlet result = create( { text: '' } );\n\tlet currentIdx = 0;\n\tlet previousIdx = 0;\n\n\tfor ( const part of textDiff ) {\n\t\tif ( part.removed ) {\n\t\t\t// Text deleted - slice from PREVIOUS, apply <del>\n\t\t\tconst removedSlice = slice(\n\t\t\t\tpreviousRichText,\n\t\t\t\tpreviousIdx,\n\t\t\t\tpreviousIdx + part.value.length\n\t\t\t);\n\t\t\tconst formatted = applyFormat(\n\t\t\t\tremovedSlice,\n\t\t\t\t{\n\t\t\t\t\ttype: 'revision/diff-removed',\n\t\t\t\t\tattributes: { title: __( 'Removed' ) },\n\t\t\t\t},\n\t\t\t\t0,\n\t\t\t\tpart.value.length\n\t\t\t);\n\t\t\tresult = concat( result, formatted );\n\t\t\tpreviousIdx += part.value.length;\n\t\t} else if ( part.added ) {\n\t\t\t// Text added - slice from CURRENT, apply <ins>\n\t\t\tconst addedSlice = slice(\n\t\t\t\tcurrentRichText,\n\t\t\t\tcurrentIdx,\n\t\t\t\tcurrentIdx + part.value.length\n\t\t\t);\n\t\t\tconst formatted = applyFormat(\n\t\t\t\taddedSlice,\n\t\t\t\t{\n\t\t\t\t\ttype: 'revision/diff-added',\n\t\t\t\t\tattributes: { title: __( 'Added' ) },\n\t\t\t\t},\n\t\t\t\t0,\n\t\t\t\tpart.value.length\n\t\t\t);\n\t\t\tresult = concat( result, formatted );\n\t\t\tcurrentIdx += part.value.length;\n\t\t} else {\n\t\t\t// Text unchanged - check formatting at each character position.\n\t\t\t// Only apply <mark> to specific ranges where formatting differs.\n\t\t\tconst currentFormats = currentRichText.formats || [];\n\t\t\tconst previousFormats = previousRichText.formats || [];\n\t\t\tconst len = part.value.length;\n\n\t\t\t// Helper to check format change at offset within this unchanged part.\n\t\t\tconst checkFormatChanged = ( offset ) =>\n\t\t\t\thasFormatChangedAtIndex(\n\t\t\t\t\tcurrentFormats,\n\t\t\t\t\tpreviousFormats,\n\t\t\t\t\tcurrentIdx + offset,\n\t\t\t\t\tpreviousIdx + offset\n\t\t\t\t);\n\n\t\t\t// Find ranges of characters grouped by whether format changed.\n\t\t\tlet rangeStart = 0;\n\t\t\tlet rangeFormatChanged = checkFormatChanged( 0 );\n\n\t\t\tfor ( let i = 1; i <= len; i++ ) {\n\t\t\t\tconst formatChanged = i < len && checkFormatChanged( i );\n\n\t\t\t\t// When format-changed status changes or we reach the end, emit range.\n\t\t\t\tif ( i === len || formatChanged !== rangeFormatChanged ) {\n\t\t\t\t\tconst rangeSlice = slice(\n\t\t\t\t\t\tcurrentRichText,\n\t\t\t\t\t\tcurrentIdx + rangeStart,\n\t\t\t\t\t\tcurrentIdx + i\n\t\t\t\t\t);\n\n\t\t\t\t\tif ( rangeFormatChanged ) {\n\t\t\t\t\t\t// Get type and description of what changed\n\t\t\t\t\t\tconst { type, description } = describeFormatChange(\n\t\t\t\t\t\t\tcurrentFormats,\n\t\t\t\t\t\t\tpreviousFormats,\n\t\t\t\t\t\t\tcurrentIdx + rangeStart,\n\t\t\t\t\t\t\tpreviousIdx + rangeStart\n\t\t\t\t\t\t);\n\n\t\t\t\t\t\t// Map change type to format type for styling\n\t\t\t\t\t\tconst formatType = {\n\t\t\t\t\t\t\tadded: 'revision/diff-format-added',\n\t\t\t\t\t\t\tremoved: 'revision/diff-format-removed',\n\t\t\t\t\t\t\tchanged: 'revision/diff-format-changed',\n\t\t\t\t\t\t}[ type ];\n\n\t\t\t\t\t\tconst marked = applyFormat(\n\t\t\t\t\t\t\trangeSlice,\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\ttype: formatType,\n\t\t\t\t\t\t\t\tattributes: { title: description },\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t0,\n\t\t\t\t\t\t\ti - rangeStart\n\t\t\t\t\t\t);\n\t\t\t\t\t\tresult = concat( result, marked );\n\t\t\t\t\t} else {\n\t\t\t\t\t\tresult = concat( result, rangeSlice );\n\t\t\t\t\t}\n\n\t\t\t\t\trangeStart = i;\n\t\t\t\t\trangeFormatChanged = formatChanged;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tcurrentIdx += part.value.length;\n\t\t\tpreviousIdx += part.value.length;\n\t\t}\n\t}\n\n\treturn new RichTextData( result );\n}\n\n/**\n * Apply diffs to a modified block's attributes.\n * - Rich-text attributes: applies inline diff formatting (ins/del marks).\n * - Other attributes: computes word-level diffs for the sidebar panel.\n *\n * @param {Object} currentBlock Current parsed block.\n * @param {Object} previousBlock Previous parsed block.\n * @param {Object} diffStatus The __revisionDiffStatus object to attach changedAttributes to.\n */\nfunction applyDiffToBlock( currentBlock, previousBlock, diffStatus ) {\n\tconst blockType = getBlockType( currentBlock.name );\n\tif ( ! blockType ) {\n\t\treturn;\n\t}\n\n\tconst changedAttributes = {};\n\n\tfor ( const [ attrName, attrDef ] of Object.entries(\n\t\tblockType.attributes\n\t) ) {\n\t\tif ( attrDef.source === 'rich-text' ) {\n\t\t\tconst currentRichText = currentBlock.attributes[ attrName ];\n\t\t\tconst previousRichText = previousBlock.attributes[ attrName ];\n\t\t\tif (\n\t\t\t\tcurrentRichText instanceof RichTextData &&\n\t\t\t\tpreviousRichText instanceof RichTextData\n\t\t\t) {\n\t\t\t\tcurrentBlock.attributes[ attrName ] = applyRichTextDiff(\n\t\t\t\t\tcurrentRichText,\n\t\t\t\t\tpreviousRichText\n\t\t\t\t);\n\t\t\t}\n\t\t} else {\n\t\t\tconst currStr = stringifyValue(\n\t\t\t\tcurrentBlock.attributes[ attrName ]\n\t\t\t);\n\t\t\tconst prevStr = stringifyValue(\n\t\t\t\tpreviousBlock.attributes[ attrName ]\n\t\t\t);\n\t\t\tif ( currStr !== prevStr ) {\n\t\t\t\tchangedAttributes[ attrName ] = diffWords( prevStr, currStr );\n\t\t\t}\n\t\t}\n\t}\n\n\tif ( Object.keys( changedAttributes ).length > 0 ) {\n\t\tdiffStatus.changedAttributes = changedAttributes;\n\t}\n}\n\n/**\n * Recursively apply diff status and rich text diff to blocks in the tree.\n * Copies __revisionDiffStatus from raw blocks to parsed blocks and applies\n * rich text diffs to modified blocks.\n *\n * @param {Object} parsedBlock Parsed block (with inner blocks).\n * @param {Object} rawBlock Raw block (with __revisionDiffStatus and __previousRawBlock).\n */\nfunction applyDiffRecursively( parsedBlock, rawBlock ) {\n\t// Copy diff status from raw block to parsed block.\n\tif ( rawBlock.__revisionDiffStatus ) {\n\t\t// Apply diffs if this block is modified and has a previous raw block.\n\t\tif (\n\t\t\trawBlock.__revisionDiffStatus.status === 'modified' &&\n\t\t\trawBlock.__previousRawBlock\n\t\t) {\n\t\t\tconst previousParsed = parseRawBlock( rawBlock.__previousRawBlock );\n\t\t\tif ( previousParsed ) {\n\t\t\t\tapplyDiffToBlock(\n\t\t\t\t\tparsedBlock,\n\t\t\t\t\tpreviousParsed,\n\t\t\t\t\trawBlock.__revisionDiffStatus\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\tparsedBlock.__revisionDiffStatus = rawBlock.__revisionDiffStatus;\n\t\t// Also store in attributes so it survives block-editor store normalization.\n\t\tparsedBlock.attributes.__revisionDiffStatus =\n\t\t\trawBlock.__revisionDiffStatus;\n\t}\n\n\t// Recursively process inner blocks.\n\tif ( parsedBlock.innerBlocks && rawBlock.innerBlocks ) {\n\t\tfor ( let i = 0; i < parsedBlock.innerBlocks.length; i++ ) {\n\t\t\tconst parsedInner = parsedBlock.innerBlocks[ i ];\n\t\t\tconst rawInner = rawBlock.innerBlocks[ i ];\n\t\t\tif ( parsedInner && rawInner ) {\n\t\t\t\tapplyDiffRecursively( parsedInner, rawInner );\n\t\t\t}\n\t\t}\n\t}\n}\n\n/**\n * Diff two revision contents at the grammar level.\n *\n * @param {string} currentContent Current revision's raw content.\n * @param {string} previousContent Previous revision's raw content.\n * @return {Array} Array of parsed blocks with diff status attributes.\n */\nexport function diffRevisionContent( currentContent, previousContent ) {\n\t// Grammar parse both contents.\n\tconst currentRaw = grammarParse( currentContent || '' );\n\tconst previousRaw = grammarParse( previousContent || '' );\n\n\t// Diff the raw block arrays.\n\tconst mergedRaw = diffRawBlocks( currentRaw, previousRaw );\n\n\t// Parse each raw block and apply diff status.\n\treturn mergedRaw\n\t\t.map( ( rawBlock ) => {\n\t\t\tconst parsed = parseRawBlock( rawBlock );\n\t\t\tif ( parsed ) {\n\t\t\t\tapplyDiffRecursively( parsed, rawBlock );\n\t\t\t}\n\t\t\treturn parsed;\n\t\t} )\n\t\t.filter( Boolean );\n}\n"],
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;
|
|
4
|
+
"sourcesContent": ["/**\n * External dependencies\n */\n/*\n * `diffWordsWithSpace` preserves the v4-style per-word output. v6+\n * stopped treating whitespace as a token in `diffWords`, which coalesces\n * adjacent word changes into a single removed/added pair.\n */\nimport { diffArrays, diffWordsWithSpace } from 'diff';\n\n/**\n * WordPress dependencies\n */\nimport { parse as grammarParse } from '@wordpress/block-serialization-default-parser';\nimport {\n\tprivateApis as blocksPrivateApis,\n\tgetBlockType,\n} from '@wordpress/blocks';\nimport {\n\tRichTextData,\n\tcreate,\n\tslice,\n\tconcat,\n\tapplyFormat,\n} from '@wordpress/rich-text';\nimport { __, _n, sprintf } from '@wordpress/i18n';\n\n/**\n * Internal dependencies\n */\nimport { unlock } from '../../lock-unlock';\n\nconst { parseRawBlock } = unlock( blocksPrivateApis );\n\n/**\n * Whether a grammar-parsed raw block is a whitespace-only freeform pseudo-block\n * (the `\\n\\n` between block markers, etc). These are stripped from both arrays\n * before LCS to keep the matching pivot stable: under `diff` v6's tie-breaker,\n * a whitespace block could otherwise be selected as the LCS anchor in\n * `[paragraph, whitespace, paragraph]` swaps, mis-pairing the surrounding\n * paragraphs in `pairSimilarBlocks`. Whitespace pseudo-blocks don't render\n * anyway (`parseRawBlock` returns undefined for them), so dropping them\n * before the diff has no user-visible effect.\n *\n * @param {Object} rawBlock A raw block from `@wordpress/block-serialization-default-parser`.\n * @return {boolean} True if the block should be excluded from LCS matching.\n */\nfunction isWhitespaceRawBlock( rawBlock ) {\n\treturn (\n\t\trawBlock.blockName === null &&\n\t\t( ! rawBlock.innerHTML || ! rawBlock.innerHTML.trim() )\n\t);\n}\n\n/**\n * Safely stringifies a value for display and comparison.\n *\n * @param {*} value The value to stringify.\n * @return {string} The stringified value.\n */\nfunction stringifyValue( value ) {\n\tif ( value === null || value === undefined ) {\n\t\treturn '';\n\t}\n\tif ( typeof value === 'object' ) {\n\t\treturn JSON.stringify( value, null, 2 );\n\t}\n\treturn String( value );\n}\n\n/**\n * Calculate text similarity using word-set overlap.\n *\n * Uses a variant of the Jaccard index (https://en.wikipedia.org/wiki/Jaccard_index)\n * called the overlap coefficient (https://en.wikipedia.org/wiki/Overlap_coefficient)\n * where we divide by the larger set size rather than the union. This ensures that\n * a small edit to a long paragraph scores high \u2014 the few changed words don't\n * dilute the score.\n *\n * This replaces the previous diffWords-based similarity which was O(n*m) per pair.\n * The word-set approach is O(n) where n is the number of words.\n *\n * Words are extracted using Intl.Segmenter for proper multilingual support\n * (CJK, Thai, etc.) rather than splitting on whitespace.\n *\n * @param {string} text1 First text to compare.\n * @param {string} text2 Second text to compare.\n * @return {number} Similarity score between 0 and 1.\n */\nfunction textSimilarity( text1, text2 ) {\n\tif ( ! text1 && ! text2 ) {\n\t\treturn 1;\n\t}\n\tif ( ! text1 || ! text2 ) {\n\t\treturn 0;\n\t}\n\n\tconst segmenter = new Intl.Segmenter( undefined, {\n\t\tgranularity: 'word',\n\t} );\n\t// Safari's Intl.Segmenter returns isWordLike: false for numeric segments,\n\t// so fall back to a Unicode-aware regex for letters and numbers.\n\tconst wordLikeRegex = /[\\p{L}\\p{N}]/u;\n\tconst getWords = ( text ) => {\n\t\tconst words = [];\n\t\tfor ( const { segment, isWordLike } of segmenter.segment( text ) ) {\n\t\t\tif ( isWordLike || wordLikeRegex.test( segment ) ) {\n\t\t\t\twords.push( segment );\n\t\t\t}\n\t\t}\n\t\treturn words;\n\t};\n\tconst words1 = getWords( text1 );\n\tconst words2 = getWords( text2 );\n\n\tif ( words1.length === 0 && words2.length === 0 ) {\n\t\treturn 1;\n\t}\n\n\tconst set1 = new Set( words1 );\n\tlet intersection = 0;\n\tfor ( const word of words2 ) {\n\t\tif ( set1.has( word ) ) {\n\t\t\tintersection++;\n\t\t}\n\t}\n\n\tconst total = Math.max( words1.length, words2.length );\n\treturn total > 0 ? intersection / total : 0;\n}\n\n/**\n * Post-process diff result to pair similar removed/added blocks as modifications.\n *\n * After LCS diffing, a block whose content changed appears as a separate \"removed\"\n * and \"added\" entry (since the full block signature differs). This function detects\n * such pairs and merges them into a single \"modified\" block with inline diff.\n *\n * Two pairing strategies are used:\n * 1. When exactly one block of a given type was removed and one was added,\n * they are paired directly \u2014 no ambiguity, no similarity check needed.\n * 2. When multiple candidates exist, textSimilarity (overlap coefficient) is\n * used to find the best match. Blocks must share at least 50% of their\n * words to be paired, preventing unrelated paragraphs from being merged.\n *\n * @param {Array} blocks Raw blocks with diff status.\n * @return {Array} Blocks with similar pairs converted to modifications.\n */\nfunction pairSimilarBlocks( blocks ) {\n\tconst removed = [];\n\tconst added = [];\n\n\t// Separate blocks by status, tracking original indices.\n\tblocks.forEach( ( block, index ) => {\n\t\tconst status = block.__revisionDiffStatus?.status;\n\t\tif ( status === 'removed' ) {\n\t\t\tremoved.push( { block, index } );\n\t\t} else if ( status === 'added' ) {\n\t\t\tadded.push( { block, index } );\n\t\t}\n\t} );\n\n\t// If no removed or no added, nothing to pair.\n\tif ( removed.length === 0 || added.length === 0 ) {\n\t\treturn blocks;\n\t}\n\n\tconst pairedRemoved = new Set(); // Indices of removed blocks filtered out.\n\tconst pairedAdded = new Set(); // Indices of added blocks filtered out.\n\tconst modifications = new Map(); // Index \u2192 modified block.\n\tconst SIMILARITY_THRESHOLD = 0.5;\n\n\t// Group candidates by block name for efficient lookup.\n\tconst addedByName = new Map();\n\tfor ( const add of added ) {\n\t\tconst name = add.block.blockName;\n\t\tif ( ! addedByName.has( name ) ) {\n\t\t\taddedByName.set( name, [] );\n\t\t}\n\t\taddedByName.get( name ).push( add );\n\t}\n\tconst removedByName = new Map();\n\tfor ( const rem of removed ) {\n\t\tconst name = rem.block.blockName;\n\t\tif ( ! removedByName.has( name ) ) {\n\t\t\tremovedByName.set( name, [] );\n\t\t}\n\t\tremovedByName.get( name ).push( rem );\n\t}\n\n\t// For each removed block, find best matching added block.\n\t// Track the highest added index paired so far \u2014 new pairings must\n\t// not go backwards, or the removed/added text order would break.\n\tlet maxPairedAddedIndex = -1;\n\n\tfor ( const rem of removed ) {\n\t\tconst candidates = addedByName.get( rem.block.blockName ) || [];\n\t\tconst sameNameRemoved = removedByName.get( rem.block.blockName ) || [];\n\t\tconst unpaired = candidates.filter(\n\t\t\t( add ) =>\n\t\t\t\t! modifications.has( add.index ) &&\n\t\t\t\tadd.index > maxPairedAddedIndex\n\t\t);\n\n\t\tif ( unpaired.length === 0 ) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tlet bestMatch = null;\n\n\t\t// If there's exactly one removed and one added of this type,\n\t\t// pair them directly \u2014 no ambiguity, no similarity check needed.\n\t\tif ( sameNameRemoved.length === 1 && unpaired.length === 1 ) {\n\t\t\tconst add = unpaired[ 0 ];\n\t\t\tconst attrsMatch =\n\t\t\t\tJSON.stringify( rem.block.attrs ) ===\n\t\t\t\tJSON.stringify( add.block.attrs );\n\t\t\t// Only skip pairing if both content and attrs are identical\n\t\t\t// (position swap, not a modification).\n\t\t\tconst contentMatch =\n\t\t\t\t( rem.block.innerHTML || '' ) === ( add.block.innerHTML || '' );\n\t\t\tif ( ! contentMatch || ! attrsMatch ) {\n\t\t\t\tbestMatch = add;\n\t\t\t}\n\t\t} else {\n\t\t\t// Multiple candidates \u2014 use similarity to find best match.\n\t\t\tlet bestScore = 0;\n\t\t\tfor ( const add of unpaired ) {\n\t\t\t\tconst score = textSimilarity(\n\t\t\t\t\trem.block.innerHTML || '',\n\t\t\t\t\tadd.block.innerHTML || ''\n\t\t\t\t);\n\t\t\t\t// Skip identical blocks (score=1 with same attrs) \u2014 those\n\t\t\t\t// are position swaps, not modifications. They should show\n\t\t\t\t// as separate removed + added, not as a no-op \"modified\".\n\t\t\t\tconst attrsMatch =\n\t\t\t\t\tJSON.stringify( rem.block.attrs ) ===\n\t\t\t\t\tJSON.stringify( add.block.attrs );\n\t\t\t\tif (\n\t\t\t\t\tscore > bestScore &&\n\t\t\t\t\tscore > SIMILARITY_THRESHOLD &&\n\t\t\t\t\t( score < 1 || ! attrsMatch )\n\t\t\t\t) {\n\t\t\t\t\tbestScore = score;\n\t\t\t\t\tbestMatch = add;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif ( bestMatch ) {\n\t\t\tmaxPairedAddedIndex = bestMatch.index;\n\n\t\t\tconst modifiedBlock = {\n\t\t\t\t...bestMatch.block,\n\t\t\t\t__revisionDiffStatus: { status: 'modified' },\n\t\t\t\t__previousRawBlock: rem.block,\n\t\t\t};\n\n\t\t\t// Decide where to place the modified block by checking\n\t\t\t// what's between the removed and added positions. If any\n\t\t\t// block between them is in the current revision (an\n\t\t\t// unchanged block, or an unpaired added block), placing\n\t\t\t// the modification at the removed position would put it\n\t\t\t// before content that already comes before it in the\n\t\t\t// current revision \u2014 so use the added position instead.\n\t\t\t// Otherwise, use the removed position to keep the previous\n\t\t\t// revision's reading order intact.\n\t\t\t//\n\t\t\t// 'removed' blocks (and added blocks already absorbed via\n\t\t\t// `pairedAdded`) aren't checked because they aren't in the\n\t\t\t// current revision and so don't count as crossing it.\n\t\t\tconst lo = Math.min( rem.index, bestMatch.index );\n\t\t\tconst hi = Math.max( rem.index, bestMatch.index );\n\t\t\tlet crossesCurrentContent = false;\n\t\t\tfor ( let i = lo + 1; i < hi; i++ ) {\n\t\t\t\tconst status = blocks[ i ].__revisionDiffStatus?.status;\n\t\t\t\tif ( status === undefined ) {\n\t\t\t\t\tcrossesCurrentContent = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tif ( status === 'added' && ! pairedAdded.has( i ) ) {\n\t\t\t\t\tcrossesCurrentContent = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif ( crossesCurrentContent ) {\n\t\t\t\t// Use the added position \u2014 don't jump before\n\t\t\t\t// current-revision content.\n\t\t\t\tmodifications.set( bestMatch.index, modifiedBlock );\n\t\t\t\tpairedRemoved.add( rem.index );\n\t\t\t} else {\n\t\t\t\t// Use the removed position \u2014 keep the previous\n\t\t\t\t// revision's reading order.\n\t\t\t\tmodifications.set( rem.index, modifiedBlock );\n\t\t\t\tpairedAdded.add( bestMatch.index );\n\t\t\t}\n\t\t}\n\t}\n\n\t// Rebuild result: replace modification targets, filter out\n\t// their paired counterparts.\n\treturn blocks\n\t\t.map( ( block, index ) => {\n\t\t\tif ( pairedRemoved.has( index ) || pairedAdded.has( index ) ) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tif ( modifications.has( index ) ) {\n\t\t\t\treturn modifications.get( index );\n\t\t\t}\n\t\t\treturn block;\n\t\t} )\n\t\t.filter( Boolean );\n}\n\n/**\n * Diff raw block arrays using LCS, recursively handling innerBlocks.\n * Detects modifications when exactly 1 block is removed and 1 is added\n * with the same blockName (1:1 replacement = modification).\n *\n * Whitespace-only freeform pseudo-blocks are filtered at every recursive\n * level so this function is safe to call directly with raw output from\n * `@wordpress/block-serialization-default-parser`. The duplicate work for\n * inner-block recursion is negligible and keeps the contract self-contained.\n *\n * @param {Array} currentRaw Current revision's raw blocks.\n * @param {Array} previousRaw Previous revision's raw blocks.\n * @return {Array} Merged raw blocks with diff status injected.\n */\nfunction diffRawBlocks( currentRaw, previousRaw ) {\n\t// Strip whitespace-only freeform pseudo-blocks before LCS \u2014 see\n\t// `isWhitespaceRawBlock` for why.\n\tcurrentRaw = currentRaw.filter( ( b ) => ! isWhitespaceRawBlock( b ) );\n\tpreviousRaw = previousRaw.filter( ( b ) => ! isWhitespaceRawBlock( b ) );\n\n\tconst createBlockSignature = ( rawBlock ) =>\n\t\tJSON.stringify( {\n\t\t\tname: rawBlock.blockName,\n\t\t\tattrs: rawBlock.attrs,\n\t\t\t// Use innerContent filtered to non-null and non-whitespace-only strings.\n\t\t\t// This excludes whitespace between inner blocks which changes based on count.\n\t\t\thtml: ( rawBlock.innerContent || [] ).filter(\n\t\t\t\t( c ) => c !== null && c.trim() !== ''\n\t\t\t),\n\t\t} );\n\tconst currentSigs = currentRaw.map( createBlockSignature );\n\tconst previousSigs = previousRaw.map( createBlockSignature );\n\n\tconst diff = diffArrays( previousSigs, currentSigs );\n\n\tconst result = [];\n\tlet currIdx = 0;\n\tlet prevIdx = 0;\n\n\tfor ( const part of diff ) {\n\t\tif ( part.added ) {\n\t\t\tfor ( let i = 0; i < part.count; i++ ) {\n\t\t\t\tresult.push( {\n\t\t\t\t\t...currentRaw[ currIdx++ ],\n\t\t\t\t\t__revisionDiffStatus: { status: 'added' },\n\t\t\t\t} );\n\t\t\t}\n\t\t} else if ( part.removed ) {\n\t\t\tfor ( let i = 0; i < part.count; i++ ) {\n\t\t\t\tresult.push( {\n\t\t\t\t\t...previousRaw[ prevIdx++ ],\n\t\t\t\t\t__revisionDiffStatus: { status: 'removed' },\n\t\t\t\t} );\n\t\t\t}\n\t\t} else {\n\t\t\t// Matched blocks - recursively diff their innerBlocks.\n\t\t\tfor ( let i = 0; i < part.count; i++ ) {\n\t\t\t\tconst currBlock = currentRaw[ currIdx++ ];\n\t\t\t\tconst prevBlock = previousRaw[ prevIdx++ ];\n\n\t\t\t\t// Recursively diff inner blocks.\n\t\t\t\tconst diffedInnerBlocks = diffRawBlocks(\n\t\t\t\t\tcurrBlock.innerBlocks || [],\n\t\t\t\t\tprevBlock.innerBlocks || []\n\t\t\t\t);\n\n\t\t\t\tresult.push( {\n\t\t\t\t\t...currBlock,\n\t\t\t\t\tinnerBlocks: diffedInnerBlocks,\n\t\t\t\t} );\n\t\t\t}\n\t\t}\n\t}\n\n\t// Post-process to pair similar removed/added blocks as modifications.\n\treturn pairSimilarBlocks( result );\n}\n\n/**\n * Check if formatting has changed at specific character indices.\n *\n * @param {Array} currentFormats Current formats array.\n * @param {Array} previousFormats Previous formats array.\n * @param {number} currentIndex Character index in current.\n * @param {number} previousIndex Character index in previous.\n * @return {boolean} True if formatting changed at these indices.\n */\nfunction hasFormatChangedAtIndex(\n\tcurrentFormats,\n\tpreviousFormats,\n\tcurrentIndex,\n\tpreviousIndex\n) {\n\tconst currFmts = currentFormats[ currentIndex ] || [];\n\tconst prevFmts = previousFormats[ previousIndex ] || [];\n\n\tif ( currFmts.length !== prevFmts.length ) {\n\t\treturn true;\n\t}\n\n\t// Check if each format in current exists in previous\n\tfor ( const fmt of currFmts ) {\n\t\tconst match = prevFmts.find(\n\t\t\t( pf ) =>\n\t\t\t\tpf.type === fmt.type &&\n\t\t\t\tJSON.stringify( pf.attributes ) ===\n\t\t\t\t\tJSON.stringify( fmt.attributes )\n\t\t);\n\t\tif ( ! match ) {\n\t\t\treturn true;\n\t\t}\n\t}\n\n\treturn false;\n}\n\n/**\n * Analyze what formatting changed between two character positions.\n * Returns both the change type (for styling) and a description (for tooltip).\n *\n * @param {Array} currentFormats Current formats array.\n * @param {Array} previousFormats Previous formats array.\n * @param {number} currIdx Character index in current.\n * @param {number} prevIdx Character index in previous.\n * @return {{ type: 'added'|'removed'|'changed', description: string }} Change info.\n */\nfunction describeFormatChange(\n\tcurrentFormats,\n\tpreviousFormats,\n\tcurrIdx,\n\tprevIdx\n) {\n\tconst currFmts = currentFormats[ currIdx ] || [];\n\tconst prevFmts = previousFormats[ prevIdx ] || [];\n\n\tlet addedCount = 0;\n\tlet removedCount = 0;\n\tlet changedCount = 0;\n\n\t// Find added formats and attribute changes\n\tfor ( const fmt of currFmts ) {\n\t\tconst match = prevFmts.find( ( pf ) => pf.type === fmt.type );\n\t\tif ( ! match ) {\n\t\t\taddedCount++;\n\t\t} else if (\n\t\t\tJSON.stringify( fmt.attributes ) !==\n\t\t\tJSON.stringify( match.attributes )\n\t\t) {\n\t\t\tchangedCount++;\n\t\t}\n\t}\n\n\t// Find removed formats\n\tfor ( const fmt of prevFmts ) {\n\t\tconst match = currFmts.find( ( cf ) => cf.type === fmt.type );\n\t\tif ( ! match ) {\n\t\t\tremovedCount++;\n\t\t}\n\t}\n\n\t// Determine primary change type for styling\n\tif ( addedCount > 0 && removedCount === 0 && changedCount === 0 ) {\n\t\treturn {\n\t\t\ttype: 'added',\n\t\t\tdescription: sprintf(\n\t\t\t\t/* translators: %d: number of formats added */\n\t\t\t\t_n( '%d format added', '%d formats added', addedCount ),\n\t\t\t\taddedCount\n\t\t\t),\n\t\t};\n\t}\n\tif ( removedCount > 0 && addedCount === 0 && changedCount === 0 ) {\n\t\treturn {\n\t\t\ttype: 'removed',\n\t\t\tdescription: sprintf(\n\t\t\t\t/* translators: %d: number of formats removed */\n\t\t\t\t_n( '%d format removed', '%d formats removed', removedCount ),\n\t\t\t\tremovedCount\n\t\t\t),\n\t\t};\n\t}\n\n\t// Mixed or attribute-only changes\n\tconst parts = [];\n\tif ( addedCount > 0 ) {\n\t\tparts.push(\n\t\t\tsprintf(\n\t\t\t\t/* translators: %d: number of formats added */\n\t\t\t\t_n( '%d format added', '%d formats added', addedCount ),\n\t\t\t\taddedCount\n\t\t\t)\n\t\t);\n\t}\n\tif ( removedCount > 0 ) {\n\t\tparts.push(\n\t\t\tsprintf(\n\t\t\t\t/* translators: %d: number of formats removed */\n\t\t\t\t_n( '%d format removed', '%d formats removed', removedCount ),\n\t\t\t\tremovedCount\n\t\t\t)\n\t\t);\n\t}\n\tif ( changedCount > 0 ) {\n\t\tparts.push(\n\t\t\tsprintf(\n\t\t\t\t/* translators: %d: number of formats changed */\n\t\t\t\t_n( '%d format changed', '%d formats changed', changedCount ),\n\t\t\t\tchangedCount\n\t\t\t)\n\t\t);\n\t}\n\treturn {\n\t\ttype: 'changed',\n\t\tdescription: parts.join( ', ' ) || __( 'Formatting changed' ),\n\t};\n}\n\n/**\n * Apply inline diff formatting comparing two RichTextData values.\n * - Text changes: apply revision/diff-removed and revision/diff-added formats\n * - Format-only changes (text unchanged): apply revision/diff-format-changed format\n *\n * @param {RichTextData} currentRichText Current revision's rich text.\n * @param {RichTextData} previousRichText Previous revision's rich text.\n * @return {RichTextData} New rich text with diff formatting applied.\n */\nfunction applyRichTextDiff( currentRichText, previousRichText ) {\n\tconst currentText = currentRichText.toPlainText();\n\tconst previousText = previousRichText.toPlainText();\n\n\t// Diff the plain text (words for cleaner output).\n\tconst textDiff = diffWordsWithSpace( previousText, currentText );\n\n\tlet result = create( { text: '' } );\n\tlet currentIdx = 0;\n\tlet previousIdx = 0;\n\n\tfor ( const part of textDiff ) {\n\t\tif ( part.removed ) {\n\t\t\t// Text deleted - slice from PREVIOUS, apply <del>\n\t\t\tconst removedSlice = slice(\n\t\t\t\tpreviousRichText,\n\t\t\t\tpreviousIdx,\n\t\t\t\tpreviousIdx + part.value.length\n\t\t\t);\n\t\t\tconst formatted = applyFormat(\n\t\t\t\tremovedSlice,\n\t\t\t\t{\n\t\t\t\t\ttype: 'revision/diff-removed',\n\t\t\t\t\tattributes: { title: __( 'Removed' ) },\n\t\t\t\t},\n\t\t\t\t0,\n\t\t\t\tpart.value.length\n\t\t\t);\n\t\t\tresult = concat( result, formatted );\n\t\t\tpreviousIdx += part.value.length;\n\t\t} else if ( part.added ) {\n\t\t\t// Text added - slice from CURRENT, apply <ins>\n\t\t\tconst addedSlice = slice(\n\t\t\t\tcurrentRichText,\n\t\t\t\tcurrentIdx,\n\t\t\t\tcurrentIdx + part.value.length\n\t\t\t);\n\t\t\tconst formatted = applyFormat(\n\t\t\t\taddedSlice,\n\t\t\t\t{\n\t\t\t\t\ttype: 'revision/diff-added',\n\t\t\t\t\tattributes: { title: __( 'Added' ) },\n\t\t\t\t},\n\t\t\t\t0,\n\t\t\t\tpart.value.length\n\t\t\t);\n\t\t\tresult = concat( result, formatted );\n\t\t\tcurrentIdx += part.value.length;\n\t\t} else {\n\t\t\t// Text unchanged - check formatting at each character position.\n\t\t\t// Only apply <mark> to specific ranges where formatting differs.\n\t\t\tconst currentFormats = currentRichText.formats || [];\n\t\t\tconst previousFormats = previousRichText.formats || [];\n\t\t\tconst len = part.value.length;\n\n\t\t\t// Helper to check format change at offset within this unchanged part.\n\t\t\tconst checkFormatChanged = ( offset ) =>\n\t\t\t\thasFormatChangedAtIndex(\n\t\t\t\t\tcurrentFormats,\n\t\t\t\t\tpreviousFormats,\n\t\t\t\t\tcurrentIdx + offset,\n\t\t\t\t\tpreviousIdx + offset\n\t\t\t\t);\n\n\t\t\t// Find ranges of characters grouped by whether format changed.\n\t\t\tlet rangeStart = 0;\n\t\t\tlet rangeFormatChanged = checkFormatChanged( 0 );\n\n\t\t\tfor ( let i = 1; i <= len; i++ ) {\n\t\t\t\tconst formatChanged = i < len && checkFormatChanged( i );\n\n\t\t\t\t// When format-changed status changes or we reach the end, emit range.\n\t\t\t\tif ( i === len || formatChanged !== rangeFormatChanged ) {\n\t\t\t\t\tconst rangeSlice = slice(\n\t\t\t\t\t\tcurrentRichText,\n\t\t\t\t\t\tcurrentIdx + rangeStart,\n\t\t\t\t\t\tcurrentIdx + i\n\t\t\t\t\t);\n\n\t\t\t\t\tif ( rangeFormatChanged ) {\n\t\t\t\t\t\t// Get type and description of what changed\n\t\t\t\t\t\tconst { type, description } = describeFormatChange(\n\t\t\t\t\t\t\tcurrentFormats,\n\t\t\t\t\t\t\tpreviousFormats,\n\t\t\t\t\t\t\tcurrentIdx + rangeStart,\n\t\t\t\t\t\t\tpreviousIdx + rangeStart\n\t\t\t\t\t\t);\n\n\t\t\t\t\t\t// Map change type to format type for styling\n\t\t\t\t\t\tconst formatType = {\n\t\t\t\t\t\t\tadded: 'revision/diff-format-added',\n\t\t\t\t\t\t\tremoved: 'revision/diff-format-removed',\n\t\t\t\t\t\t\tchanged: 'revision/diff-format-changed',\n\t\t\t\t\t\t}[ type ];\n\n\t\t\t\t\t\tconst marked = applyFormat(\n\t\t\t\t\t\t\trangeSlice,\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\ttype: formatType,\n\t\t\t\t\t\t\t\tattributes: { title: description },\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t0,\n\t\t\t\t\t\t\ti - rangeStart\n\t\t\t\t\t\t);\n\t\t\t\t\t\tresult = concat( result, marked );\n\t\t\t\t\t} else {\n\t\t\t\t\t\tresult = concat( result, rangeSlice );\n\t\t\t\t\t}\n\n\t\t\t\t\trangeStart = i;\n\t\t\t\t\trangeFormatChanged = formatChanged;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tcurrentIdx += part.value.length;\n\t\t\tpreviousIdx += part.value.length;\n\t\t}\n\t}\n\n\treturn new RichTextData( result );\n}\n\n/**\n * Apply diffs to a modified block's attributes.\n * - Rich-text attributes: applies inline diff formatting (ins/del marks).\n * - Other attributes: computes word-level diffs for the sidebar panel.\n *\n * @param {Object} currentBlock Current parsed block.\n * @param {Object} previousBlock Previous parsed block.\n * @param {Object} diffStatus The __revisionDiffStatus object to attach changedAttributes to.\n */\nfunction applyDiffToBlock( currentBlock, previousBlock, diffStatus ) {\n\tconst blockType = getBlockType( currentBlock.name );\n\tif ( ! blockType ) {\n\t\treturn;\n\t}\n\n\tconst changedAttributes = {};\n\n\tfor ( const [ attrName, attrDef ] of Object.entries(\n\t\tblockType.attributes\n\t) ) {\n\t\tif ( attrDef.source === 'rich-text' ) {\n\t\t\tconst currentRichText = currentBlock.attributes[ attrName ];\n\t\t\tconst previousRichText = previousBlock.attributes[ attrName ];\n\t\t\tif (\n\t\t\t\tcurrentRichText instanceof RichTextData &&\n\t\t\t\tpreviousRichText instanceof RichTextData\n\t\t\t) {\n\t\t\t\tcurrentBlock.attributes[ attrName ] = applyRichTextDiff(\n\t\t\t\t\tcurrentRichText,\n\t\t\t\t\tpreviousRichText\n\t\t\t\t);\n\t\t\t}\n\t\t} else {\n\t\t\tconst currStr = stringifyValue(\n\t\t\t\tcurrentBlock.attributes[ attrName ]\n\t\t\t);\n\t\t\tconst prevStr = stringifyValue(\n\t\t\t\tpreviousBlock.attributes[ attrName ]\n\t\t\t);\n\t\t\tif ( currStr !== prevStr ) {\n\t\t\t\tchangedAttributes[ attrName ] = diffWordsWithSpace(\n\t\t\t\t\tprevStr,\n\t\t\t\t\tcurrStr\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\n\tif ( Object.keys( changedAttributes ).length > 0 ) {\n\t\tdiffStatus.changedAttributes = changedAttributes;\n\t}\n}\n\n/**\n * Recursively apply diff status and rich text diff to blocks in the tree.\n * Copies __revisionDiffStatus from raw blocks to parsed blocks and applies\n * rich text diffs to modified blocks.\n *\n * @param {Object} parsedBlock Parsed block (with inner blocks).\n * @param {Object} rawBlock Raw block (with __revisionDiffStatus and __previousRawBlock).\n */\nfunction applyDiffRecursively( parsedBlock, rawBlock ) {\n\t// Copy diff status from raw block to parsed block.\n\tif ( rawBlock.__revisionDiffStatus ) {\n\t\t// Apply diffs if this block is modified and has a previous raw block.\n\t\tif (\n\t\t\trawBlock.__revisionDiffStatus.status === 'modified' &&\n\t\t\trawBlock.__previousRawBlock\n\t\t) {\n\t\t\tconst previousParsed = parseRawBlock( rawBlock.__previousRawBlock );\n\t\t\tif ( previousParsed ) {\n\t\t\t\tapplyDiffToBlock(\n\t\t\t\t\tparsedBlock,\n\t\t\t\t\tpreviousParsed,\n\t\t\t\t\trawBlock.__revisionDiffStatus\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\tparsedBlock.__revisionDiffStatus = rawBlock.__revisionDiffStatus;\n\t\t// Also store in attributes so it survives block-editor store normalization.\n\t\tparsedBlock.attributes.__revisionDiffStatus =\n\t\t\trawBlock.__revisionDiffStatus;\n\t}\n\n\t// Recursively process inner blocks.\n\tif ( parsedBlock.innerBlocks && rawBlock.innerBlocks ) {\n\t\tfor ( let i = 0; i < parsedBlock.innerBlocks.length; i++ ) {\n\t\t\tconst parsedInner = parsedBlock.innerBlocks[ i ];\n\t\t\tconst rawInner = rawBlock.innerBlocks[ i ];\n\t\t\tif ( parsedInner && rawInner ) {\n\t\t\t\tapplyDiffRecursively( parsedInner, rawInner );\n\t\t\t}\n\t\t}\n\t}\n}\n\n/**\n * Diff two revision contents at the grammar level.\n *\n * @param {string} currentContent Current revision's raw content.\n * @param {string} previousContent Previous revision's raw content.\n * @return {Array} Array of parsed blocks with diff status attributes.\n */\nexport function diffRevisionContent( currentContent, previousContent ) {\n\t// Grammar parse both contents.\n\tconst currentRaw = grammarParse( currentContent || '' );\n\tconst previousRaw = grammarParse( previousContent || '' );\n\n\t// Diff the raw block arrays.\n\tconst mergedRaw = diffRawBlocks( currentRaw, previousRaw );\n\n\t// Parse each raw block and apply diff status.\n\treturn mergedRaw\n\t\t.map( ( rawBlock ) => {\n\t\t\tconst parsed = parseRawBlock( rawBlock );\n\t\t\tif ( parsed ) {\n\t\t\t\tapplyDiffRecursively( parsed, rawBlock );\n\t\t\t}\n\t\t\treturn parsed;\n\t\t} )\n\t\t.filter( Boolean );\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["blocksPrivateApis", "grammarParse"]
|
|
7
7
|
}
|
|
@@ -23,14 +23,14 @@ __export(preserve_client_ids_exports, {
|
|
|
23
23
|
preserveClientIds: () => preserveClientIds
|
|
24
24
|
});
|
|
25
25
|
module.exports = __toCommonJS(preserve_client_ids_exports);
|
|
26
|
-
var
|
|
26
|
+
var import_diff = require("diff");
|
|
27
27
|
function preserveClientIds(newBlocks, prevBlocks) {
|
|
28
28
|
if (!prevBlocks?.length || !newBlocks?.length) {
|
|
29
29
|
return newBlocks;
|
|
30
30
|
}
|
|
31
31
|
const newSigs = newBlocks.map((block) => block.name);
|
|
32
32
|
const prevSigs = prevBlocks.map((block) => block.name);
|
|
33
|
-
const diffResult = (0,
|
|
33
|
+
const diffResult = (0, import_diff.diffArrays)(prevSigs, newSigs);
|
|
34
34
|
let newIndex = 0;
|
|
35
35
|
let prevIndex = 0;
|
|
36
36
|
const result = [];
|