diff --git a/apps/cli/package.json b/apps/cli/package.json index 12fff63ff7..b11f8e4008 100644 --- a/apps/cli/package.json +++ b/apps/cli/package.json @@ -1,6 +1,6 @@ { "name": "@superdoc-dev/cli", - "version": "0.19.0", + "version": "0.20.1", "type": "module", "bin": { "superdoc": "./dist/index.js" diff --git a/apps/cli/platforms/cli-darwin-arm64/package.json b/apps/cli/platforms/cli-darwin-arm64/package.json index c2b415a4b0..685b84d0d9 100644 --- a/apps/cli/platforms/cli-darwin-arm64/package.json +++ b/apps/cli/platforms/cli-darwin-arm64/package.json @@ -1,6 +1,6 @@ { "name": "@superdoc-dev/cli-darwin-arm64", - "version": "0.19.0", + "version": "0.20.1", "os": [ "darwin" ], diff --git a/apps/cli/platforms/cli-darwin-x64/package.json b/apps/cli/platforms/cli-darwin-x64/package.json index 5d662a405a..808a891a41 100644 --- a/apps/cli/platforms/cli-darwin-x64/package.json +++ b/apps/cli/platforms/cli-darwin-x64/package.json @@ -1,6 +1,6 @@ { "name": "@superdoc-dev/cli-darwin-x64", - "version": "0.19.0", + "version": "0.20.1", "os": [ "darwin" ], diff --git a/apps/cli/platforms/cli-linux-arm64/package.json b/apps/cli/platforms/cli-linux-arm64/package.json index ad7a17360f..7626b8b640 100644 --- a/apps/cli/platforms/cli-linux-arm64/package.json +++ b/apps/cli/platforms/cli-linux-arm64/package.json @@ -1,6 +1,6 @@ { "name": "@superdoc-dev/cli-linux-arm64", - "version": "0.19.0", + "version": "0.20.1", "os": [ "linux" ], diff --git a/apps/cli/platforms/cli-linux-x64/package.json b/apps/cli/platforms/cli-linux-x64/package.json index 5a162a921d..02151a8e82 100644 --- a/apps/cli/platforms/cli-linux-x64/package.json +++ b/apps/cli/platforms/cli-linux-x64/package.json @@ -1,6 +1,6 @@ { "name": "@superdoc-dev/cli-linux-x64", - "version": "0.19.0", + "version": "0.20.1", "os": [ "linux" ], diff --git a/apps/cli/platforms/cli-windows-x64/package.json b/apps/cli/platforms/cli-windows-x64/package.json index 40687c81bf..2e53b75c03 100644 --- a/apps/cli/platforms/cli-windows-x64/package.json +++ b/apps/cli/platforms/cli-windows-x64/package.json @@ -1,6 +1,6 @@ { "name": "@superdoc-dev/cli-windows-x64", - "version": "0.19.0", + "version": "0.20.1", "os": [ "win32" ], diff --git a/apps/mcp/package.json b/apps/mcp/package.json index 54a003a735..901d6d0759 100644 --- a/apps/mcp/package.json +++ b/apps/mcp/package.json @@ -1,6 +1,6 @@ { "name": "@superdoc-dev/mcp", - "version": "0.14.0", + "version": "0.15.1", "type": "module", "engines": { "node": ">=20" diff --git a/apps/vscode-ext/package.json b/apps/vscode-ext/package.json index 73c14a9c1e..b0fc7cb645 100644 --- a/apps/vscode-ext/package.json +++ b/apps/vscode-ext/package.json @@ -2,7 +2,7 @@ "name": "superdoc-vscode-ext", "displayName": "SuperDoc - Word Document Editor", "description": "Open and edit Word documents (.docx) directly in VS Code using SuperDoc", - "version": "2.14.0", + "version": "2.15.1", "publisher": "superdoc-dev", "icon": "logo.png", "homepage": "https://superdoc.dev", diff --git a/packages/react/package.json b/packages/react/package.json index c53f577038..71b5174a0b 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -1,6 +1,6 @@ { "name": "@superdoc-dev/react", - "version": "1.13.0", + "version": "1.14.1", "description": "Official React wrapper for the SuperDoc document editor", "type": "module", "main": "./dist/index.cjs", diff --git a/packages/sdk/langs/node/package.json b/packages/sdk/langs/node/package.json index 628ed357e5..3a7f9f5c5c 100644 --- a/packages/sdk/langs/node/package.json +++ b/packages/sdk/langs/node/package.json @@ -1,6 +1,6 @@ { "name": "@superdoc-dev/sdk", - "version": "1.18.0", + "version": "1.19.1", "private": false, "type": "module", "main": "./dist/index.cjs", diff --git a/packages/sdk/langs/node/platforms/sdk-darwin-arm64/package.json b/packages/sdk/langs/node/platforms/sdk-darwin-arm64/package.json index 2d488c1a30..6d2b9d0799 100644 --- a/packages/sdk/langs/node/platforms/sdk-darwin-arm64/package.json +++ b/packages/sdk/langs/node/platforms/sdk-darwin-arm64/package.json @@ -1,6 +1,6 @@ { "name": "@superdoc-dev/sdk-darwin-arm64", - "version": "1.18.0", + "version": "1.19.1", "os": [ "darwin" ], diff --git a/packages/sdk/langs/node/platforms/sdk-darwin-x64/package.json b/packages/sdk/langs/node/platforms/sdk-darwin-x64/package.json index 3cb22347ff..f420d61fb6 100644 --- a/packages/sdk/langs/node/platforms/sdk-darwin-x64/package.json +++ b/packages/sdk/langs/node/platforms/sdk-darwin-x64/package.json @@ -1,6 +1,6 @@ { "name": "@superdoc-dev/sdk-darwin-x64", - "version": "1.18.0", + "version": "1.19.1", "os": [ "darwin" ], diff --git a/packages/sdk/langs/node/platforms/sdk-linux-arm64/package.json b/packages/sdk/langs/node/platforms/sdk-linux-arm64/package.json index 3473942a6d..94bb26ddf7 100644 --- a/packages/sdk/langs/node/platforms/sdk-linux-arm64/package.json +++ b/packages/sdk/langs/node/platforms/sdk-linux-arm64/package.json @@ -1,6 +1,6 @@ { "name": "@superdoc-dev/sdk-linux-arm64", - "version": "1.18.0", + "version": "1.19.1", "os": [ "linux" ], diff --git a/packages/sdk/langs/node/platforms/sdk-linux-x64/package.json b/packages/sdk/langs/node/platforms/sdk-linux-x64/package.json index 0bc8acecdc..d299dfab70 100644 --- a/packages/sdk/langs/node/platforms/sdk-linux-x64/package.json +++ b/packages/sdk/langs/node/platforms/sdk-linux-x64/package.json @@ -1,6 +1,6 @@ { "name": "@superdoc-dev/sdk-linux-x64", - "version": "1.18.0", + "version": "1.19.1", "os": [ "linux" ], diff --git a/packages/sdk/langs/node/platforms/sdk-windows-x64/package.json b/packages/sdk/langs/node/platforms/sdk-windows-x64/package.json index 70d5e093f1..779e0b4511 100644 --- a/packages/sdk/langs/node/platforms/sdk-windows-x64/package.json +++ b/packages/sdk/langs/node/platforms/sdk-windows-x64/package.json @@ -1,6 +1,6 @@ { "name": "@superdoc-dev/sdk-windows-x64", - "version": "1.18.0", + "version": "1.19.1", "os": [ "win32" ], diff --git a/packages/sdk/langs/python/platforms/superdoc-sdk-cli-darwin-arm64/pyproject.toml b/packages/sdk/langs/python/platforms/superdoc-sdk-cli-darwin-arm64/pyproject.toml index 144f4fb90a..537be3b7bd 100644 --- a/packages/sdk/langs/python/platforms/superdoc-sdk-cli-darwin-arm64/pyproject.toml +++ b/packages/sdk/langs/python/platforms/superdoc-sdk-cli-darwin-arm64/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "superdoc-sdk-cli-darwin-arm64" -version = "1.18.0" +version = "1.19.1" description = "SuperDoc CLI binary for macOS ARM64 (Apple Silicon)" readme = "README.md" requires-python = ">=3.9" diff --git a/packages/sdk/langs/python/platforms/superdoc-sdk-cli-darwin-x64/pyproject.toml b/packages/sdk/langs/python/platforms/superdoc-sdk-cli-darwin-x64/pyproject.toml index f32001365a..fd5bb047b0 100644 --- a/packages/sdk/langs/python/platforms/superdoc-sdk-cli-darwin-x64/pyproject.toml +++ b/packages/sdk/langs/python/platforms/superdoc-sdk-cli-darwin-x64/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "superdoc-sdk-cli-darwin-x64" -version = "1.18.0" +version = "1.19.1" description = "SuperDoc CLI binary for macOS x64 (Intel)" readme = "README.md" requires-python = ">=3.9" diff --git a/packages/sdk/langs/python/platforms/superdoc-sdk-cli-linux-arm64/pyproject.toml b/packages/sdk/langs/python/platforms/superdoc-sdk-cli-linux-arm64/pyproject.toml index 7ecdb6419d..f8eb9370b7 100644 --- a/packages/sdk/langs/python/platforms/superdoc-sdk-cli-linux-arm64/pyproject.toml +++ b/packages/sdk/langs/python/platforms/superdoc-sdk-cli-linux-arm64/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "superdoc-sdk-cli-linux-arm64" -version = "1.18.0" +version = "1.19.1" description = "SuperDoc CLI binary for Linux ARM64" readme = "README.md" requires-python = ">=3.9" diff --git a/packages/sdk/langs/python/platforms/superdoc-sdk-cli-linux-x64/pyproject.toml b/packages/sdk/langs/python/platforms/superdoc-sdk-cli-linux-x64/pyproject.toml index cf9f9be2dd..9ce66fce70 100644 --- a/packages/sdk/langs/python/platforms/superdoc-sdk-cli-linux-x64/pyproject.toml +++ b/packages/sdk/langs/python/platforms/superdoc-sdk-cli-linux-x64/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "superdoc-sdk-cli-linux-x64" -version = "1.18.0" +version = "1.19.1" description = "SuperDoc CLI binary for Linux x64" readme = "README.md" requires-python = ">=3.9" diff --git a/packages/sdk/langs/python/platforms/superdoc-sdk-cli-windows-x64/pyproject.toml b/packages/sdk/langs/python/platforms/superdoc-sdk-cli-windows-x64/pyproject.toml index 9db3f7d01d..1e9ff98628 100644 --- a/packages/sdk/langs/python/platforms/superdoc-sdk-cli-windows-x64/pyproject.toml +++ b/packages/sdk/langs/python/platforms/superdoc-sdk-cli-windows-x64/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "superdoc-sdk-cli-windows-x64" -version = "1.18.0" +version = "1.19.1" description = "SuperDoc CLI binary for Windows x64" readme = "README.md" requires-python = ">=3.9" diff --git a/packages/sdk/langs/python/pyproject.toml b/packages/sdk/langs/python/pyproject.toml index 2c631e4f2e..2c245c7423 100644 --- a/packages/sdk/langs/python/pyproject.toml +++ b/packages/sdk/langs/python/pyproject.toml @@ -4,18 +4,18 @@ build-backend = "setuptools.build_meta" [project] name = "superdoc-sdk" -version = "1.18.0" +version = "1.19.1" description = "SuperDoc SDK (CLI-backed)" readme = "README.md" requires-python = ">=3.9" license = "AGPL-3.0" authors = [{ name = "SuperDoc" }] dependencies = [ - "superdoc-sdk-cli-darwin-arm64==1.18.0; platform_system == 'Darwin' and (platform_machine == 'arm64' or platform_machine == 'aarch64' or platform_machine == 'ARM64')", - "superdoc-sdk-cli-darwin-x64==1.18.0; platform_system == 'Darwin' and (platform_machine == 'x86_64' or platform_machine == 'AMD64' or platform_machine == 'amd64')", - "superdoc-sdk-cli-linux-x64==1.18.0; platform_system == 'Linux' and (platform_machine == 'x86_64' or platform_machine == 'AMD64' or platform_machine == 'amd64')", - "superdoc-sdk-cli-linux-arm64==1.18.0; platform_system == 'Linux' and (platform_machine == 'arm64' or platform_machine == 'aarch64' or platform_machine == 'ARM64')", - "superdoc-sdk-cli-windows-x64==1.18.0; platform_system == 'Windows' and (platform_machine == 'x86_64' or platform_machine == 'AMD64' or platform_machine == 'amd64')", + "superdoc-sdk-cli-darwin-arm64==1.19.1; platform_system == 'Darwin' and (platform_machine == 'arm64' or platform_machine == 'aarch64' or platform_machine == 'ARM64')", + "superdoc-sdk-cli-darwin-x64==1.19.1; platform_system == 'Darwin' and (platform_machine == 'x86_64' or platform_machine == 'AMD64' or platform_machine == 'amd64')", + "superdoc-sdk-cli-linux-x64==1.19.1; platform_system == 'Linux' and (platform_machine == 'x86_64' or platform_machine == 'AMD64' or platform_machine == 'amd64')", + "superdoc-sdk-cli-linux-arm64==1.19.1; platform_system == 'Linux' and (platform_machine == 'arm64' or platform_machine == 'aarch64' or platform_machine == 'ARM64')", + "superdoc-sdk-cli-windows-x64==1.19.1; platform_system == 'Windows' and (platform_machine == 'x86_64' or platform_machine == 'AMD64' or platform_machine == 'amd64')", ] [tool.setuptools] diff --git a/packages/sdk/package.json b/packages/sdk/package.json index f345ad24bf..b61ab6c105 100644 --- a/packages/sdk/package.json +++ b/packages/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@superdoc-dev/sdk-workspace", - "version": "1.18.0", + "version": "1.19.1", "private": true, "type": "module", "scripts": { diff --git a/packages/sdk/version.json b/packages/sdk/version.json index 8244fe58e2..5475785bef 100644 --- a/packages/sdk/version.json +++ b/packages/sdk/version.json @@ -1,3 +1,3 @@ { - "sdkVersion": "1.18.0" + "sdkVersion": "1.19.1" } diff --git a/packages/super-editor/src/editors/v1/core/Editor.track-changes-dispatch.test.js b/packages/super-editor/src/editors/v1/core/Editor.track-changes-dispatch.test.js index b004173b6b..ea037f63f9 100644 --- a/packages/super-editor/src/editors/v1/core/Editor.track-changes-dispatch.test.js +++ b/packages/super-editor/src/editors/v1/core/Editor.track-changes-dispatch.test.js @@ -585,6 +585,48 @@ describe('Editor dispatch tracked-change meta', () => { expect(markEntries(editor, TrackInsertMarkName).some(({ text }) => text.includes('PASTE'))).toBe(false); }); + it('preserves review marks carried by a pasted slice inside another user tracked insertion while local track mode is off', () => { + ({ editor } = initTestEditor({ + mode: 'text', + content: '

', + user: BOB, + useImmediateSetTimeout: false, + })); + setDocumentWithTrackedInsertion(editor); + + // Pasting a SuperDoc slice that already carries its own tracked-review marks + // (a copied suggestion) into the interior of another suggestion must keep the + // pasted marks. Only the ENCLOSING mark inherited from the neighbour is stripped; + // a blanket strip-by-type would silently drop the pasted review metadata. + const { schema } = editor; + const pastedMark = schema.marks[TrackInsertMarkName].create({ + id: 'pasted-insert', + author: 'Carol Author', + authorEmail: 'carol@example.com', + date: FIXED_DATE, + }); + const openSlice = new Slice( + Fragment.from( + schema.nodes.paragraph.create( + { sdBlockId: 'paste-src' }, + schema.nodes.run.create({}, schema.text('COPIED', [pastedMark])), + ), + ), + 2, + 2, + ); + + const insertPos = findTextRange(editor, INSERTED_TEXT).from + 4; + editor.dispatch( + editor.state.tr.replaceRange(insertPos, insertPos, openSlice).setMeta('inputType', 'insertFromPaste'), + ); + + // The pasted suggestion keeps its own review id (it is not the enclosing mark). + expect(textForMarkId(editor, TrackInsertMarkName, 'pasted-insert')).toBe('COPIED'); + // The enclosing foreign insertion did not absorb the pasted text into its id. + expect(textForMarkId(editor, TrackInsertMarkName, FOREIGN_INSERT_ID)).toBe(INSERTED_TEXT); + }); + it('inserts plain text on direct insert inside another user tracked deletion while local track mode is off', () => { ({ editor } = initTestEditor({ mode: 'text', diff --git a/packages/super-editor/src/editors/v1/core/Editor.ts b/packages/super-editor/src/editors/v1/core/Editor.ts index ceb2e6fc4d..6b96ce2999 100644 --- a/packages/super-editor/src/editors/v1/core/Editor.ts +++ b/packages/super-editor/src/editors/v1/core/Editor.ts @@ -426,16 +426,35 @@ const transactionTouchesTrackedReviewState = (state: EditorState, tr: Transactio return tr.steps.some((step, index) => stepTouchesTrackedReviewState(step, docs[index] ?? state.doc)); }; +// The tracked-review mark instances that ENCLOSE a collapsed position (present on +// both nodeBefore and nodeAfter). These are exactly the marks PM mark-inheritance +// copies onto text inserted strictly inside the span. Returning the specific +// instances (matched by type + attrs) lets the cleanup strip only the inherited +// marks - marks a pasted slice carries in (a different review id) are left intact. +const enclosingTrackedReviewMarksAtCollapsedPosition = (doc: PmNode, pos: number): PmMark[] => { + const boundedPos = Math.max(0, Math.min(doc.content.size, pos)); + const $pos = doc.resolve(boundedPos); + const afterKeys = trackedReviewMarkKeysForNode($pos.nodeAfter); + if (!afterKeys.size) return []; + const enclosing: PmMark[] = []; + for (const mark of $pos.nodeBefore?.marks ?? []) { + const key = trackedReviewMarkKey(mark); + if (key && afterKeys.has(key)) enclosing.push(mark); + } + return enclosing; +}; + // Editing-mode collapsed insert strictly inside a tracked review span: returns -// the inserted range (in the post-step doc) so the inherited suggestion mark can -// be stripped from the new text. Marks are inclusive:false, so a strictly-interior -// insert still inherits the neighbour's trackInsert/trackDelete via PM mark -// inheritance; without the strip the typed chars would silently join the -// suggestion (struck-through inside a deletion / counted as someone else's insert). +// the inserted range (in the post-step doc) plus the enclosing mark instances so +// the inherited suggestion mark can be stripped from the new text. Marks are +// inclusive:false, so a strictly-interior insert still inherits the neighbour's +// trackInsert/trackDelete via PM mark inheritance; without the strip the typed +// chars would silently join the suggestion (struck-through inside a deletion / +// counted as someone else's insert). const collapsedInsertInsideTrackedReviewMarkRange = ( state: EditorState, tr: Transaction, -): { from: number; to: number } | null => { +): { from: number; to: number; marks: PmMark[] } | null => { if (!tr.docChanged || tr.steps.length !== 1) return null; const [step] = tr.steps; @@ -444,14 +463,15 @@ const collapsedInsertInsideTrackedReviewMarkRange = ( const docs = (tr as unknown as { docs?: PmNode[] }).docs ?? []; const docBeforeStep = docs[0] ?? state.doc; - if (!collapsedPositionIsInsideTrackedReviewMark(docBeforeStep, step.from)) return null; + const marks = enclosingTrackedReviewMarksAtCollapsedPosition(docBeforeStep, step.from); + if (!marks.length) return null; // Use slice.size (content.size - openStart - openEnd) - the size ProseMirror // actually maps the inserted range to (ReplaceStep.getMap uses slice.size). For // an open slice (a multi-paragraph / rich-HTML collapsed paste) content.size // overshoots, so the mark cleanup would strip trackInsert/trackDelete from the // existing suggestion text after the paste. - return { from: step.from, to: step.from + step.slice.size }; + return { from: step.from, to: step.from + step.slice.size, marks }; }; // Best-effort heuristic for labeling a content-control event's `source`. A @@ -3488,17 +3508,22 @@ export class Editor extends EventEmitter { } // PM mark inheritance (inclusive:false) still copies the neighbour // suggestion's trackInsert/trackDelete/trackFormat onto a strictly-interior - // insert. Strip those marks from the inserted range (and clear them from - // stored marks) so the typed chars stay plain and the existing suggestion - // is simply split around them. Composition-meta-agnostic: deferral never - // runs in editing mode, so this normalizes both IME and non-IME inserts. + // insert. Strip ONLY those enclosing mark instances from the inserted range + // (matched by type + attrs) so the typed chars stay plain and the existing + // suggestion is simply split around them. Removing by instance - not by type - + // preserves review marks a pasted SuperDoc slice carries in (a different + // review id), which a blanket type-strip would silently drop. Stored marks are + // cleared by type so continued typing stays plain; that only affects the next + // typed char, never the already-inserted content. Composition-meta-agnostic: + // deferral never runs in editing mode, so this normalizes IME and non-IME inserts. if (plainInsertRange) { + for (const mark of plainInsertRange.marks) { + transactionToApply.removeMark(plainInsertRange.from, plainInsertRange.to, mark); + } const schemaMarks = prevState.schema.marks; for (const markName of [TrackInsertMarkName, TrackDeleteMarkName, TrackFormatMarkName]) { const markType = schemaMarks[markName]; - if (!markType) continue; - transactionToApply.removeMark(plainInsertRange.from, plainInsertRange.to, markType); - transactionToApply.removeStoredMark(markType); + if (markType) transactionToApply.removeStoredMark(markType); } } if (shouldTrack && forceTrackChanges && !this.options.user) { diff --git a/packages/superdoc/package.json b/packages/superdoc/package.json index 31fae7b078..0d76a75dfd 100644 --- a/packages/superdoc/package.json +++ b/packages/superdoc/package.json @@ -1,7 +1,7 @@ { "name": "superdoc", "type": "module", - "version": "1.42.0", + "version": "1.43.1", "license": "AGPL-3.0", "repository": { "type": "git",