{"_id":"tiptap-track-changes","_rev":"4-63a072b3768f6152d08918e170c7759d","name":"tiptap-track-changes","dist-tags":{"latest":"0.2.1"},"versions":{"0.1.0":{"name":"tiptap-track-changes","version":"0.1.0","keywords":["tiptap","prosemirror","track-changes","suggesting","editor","rich-text","diff","review","collaboration","google-docs"],"license":"MIT","_id":"tiptap-track-changes@0.1.0","maintainers":[{"name":"sungkhum","email":"sungkhum@gmail.com"}],"homepage":"https://github.com/sungkhum/tip-tap-track-changes","bugs":{"url":"https://github.com/sungkhum/tip-tap-track-changes/issues"},"dist":{"shasum":"32080ca61b272206ee039236ad40548077276b60","tarball":"https://registry.npmjs.org/tiptap-track-changes/-/tiptap-track-changes-0.1.0.tgz","fileCount":7,"integrity":"sha512-G8RK8eHGmdLnWyPdXYjvwRV7XOgSouSPms6tlZb95HCBxDvs4wDq9mYQTS2PnHT1l+zfVpF3YiErt7V32/TwXw==","signatures":[{"sig":"MEUCIHllliAWsi6+iW17XG7d1UCV3aeQP3D3bAJp6eB6cv/rAiEAyeuvtGfFqwHacCvfWt6H9U99PUZTzPXnKXmRk+OwOHM=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":108602},"main":"dist/index.js","types":"dist/index.d.ts","module":"dist/index.mjs","exports":{".":{"import":{"types":"./dist/index.d.mts","default":"./dist/index.mjs"},"require":{"types":"./dist/index.d.ts","default":"./dist/index.js"}}},"gitHead":"24c3c72c81c84bdc12716037d1b292b19e787d3f","scripts":{"dev":"cd demo && npx vite","test":"vitest run","build":"tsup src/index.ts --format cjs,esm --dts --clean","typecheck":"tsc --noEmit","test:watch":"vitest","prepublishOnly":"npm run build && npm test"},"_npmUser":{"name":"sungkhum","email":"sungkhum@gmail.com"},"repository":{"url":"git+https://github.com/sungkhum/tip-tap-track-changes.git","type":"git"},"_npmVersion":"10.9.2","description":"Open-source track changes (suggesting mode) extension for Tiptap","directories":{},"sideEffects":false,"_nodeVersion":"22.15.0","_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.0.0","jsdom":"^26.0.0","vitest":"^3.0.0","@tiptap/pm":"^2.11.0","typescript":"^5.0.0","@tiptap/core":"^2.11.0","@tiptap/extension-bold":"^2.11.0","@tiptap/extension-text":"^2.11.0","@tiptap/extension-italic":"^2.11.0","@tiptap/extension-strike":"^2.11.0","@tiptap/extension-heading":"^2.11.0","@tiptap/extension-history":"^2.11.0","@tiptap/extension-document":"^2.11.0","@tiptap/extension-paragraph":"^2.11.0","@tiptap/extension-underline":"^2.11.0"},"peerDependencies":{"@tiptap/pm":"^2.0.0","@tiptap/core":"^2.0.0"},"_npmOperationalInternal":{"tmp":"tmp/tiptap-track-changes_0.1.0_1773070256761_0.2854930194177048","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"name":"tiptap-track-changes","version":"0.1.1","keywords":["tiptap","prosemirror","track-changes","suggesting","editor","rich-text","diff","review","collaboration","google-docs"],"license":"MIT","_id":"tiptap-track-changes@0.1.1","maintainers":[{"name":"sungkhum","email":"sungkhum@gmail.com"}],"homepage":"https://github.com/sungkhum/tip-tap-track-changes","bugs":{"url":"https://github.com/sungkhum/tip-tap-track-changes/issues"},"dist":{"shasum":"41a5e02cc3d99c48c4f9d4259866e35e297a70df","tarball":"https://registry.npmjs.org/tiptap-track-changes/-/tiptap-track-changes-0.1.1.tgz","fileCount":7,"integrity":"sha512-vlcAIh58rA14AYiXzofWC7xgOlUiC9oQexk2Vjhv0bUtCMve3L/rnugHNxEgdhcwOu//r4px1CsADM100tJzpQ==","signatures":[{"sig":"MEQCIBONly4epbozb874DNctpyChq654+eTNvZXCrsz1Ql2PAiBehFKHlcFVHiYWngQoBTTttQBuKc9NHneIB+luhC5BzQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":124502},"main":"dist/index.js","types":"dist/index.d.ts","module":"dist/index.mjs","exports":{".":{"import":{"types":"./dist/index.d.mts","default":"./dist/index.mjs"},"require":{"types":"./dist/index.d.ts","default":"./dist/index.js"}}},"gitHead":"083cb090180187c3b4bd2a1472bc49fb80b1f23e","scripts":{"dev":"cd demo && npx vite","test":"vitest run","build":"tsup src/index.ts --format cjs,esm --dts --clean","typecheck":"tsc --noEmit","test:watch":"vitest","prepublishOnly":"npm run build && npm test"},"_npmUser":{"name":"sungkhum","email":"sungkhum@gmail.com"},"repository":{"url":"git+https://github.com/sungkhum/tip-tap-track-changes.git","type":"git"},"_npmVersion":"10.9.2","description":"Open-source track changes (suggesting mode) extension for Tiptap","directories":{},"sideEffects":false,"_nodeVersion":"22.15.0","_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.0.0","jsdom":"^26.0.0","vitest":"^3.0.0","@tiptap/pm":"^2.11.0","typescript":"^5.0.0","@tiptap/core":"^2.11.0","@tiptap/extension-bold":"^2.11.0","@tiptap/extension-text":"^2.11.0","@tiptap/extension-italic":"^2.11.0","@tiptap/extension-strike":"^2.11.0","@tiptap/extension-heading":"^2.11.0","@tiptap/extension-history":"^2.11.0","@tiptap/extension-document":"^2.11.0","@tiptap/extension-paragraph":"^2.11.0","@tiptap/extension-underline":"^2.11.0"},"peerDependencies":{"@tiptap/pm":"^2.0.0","@tiptap/core":"^2.0.0"},"_npmOperationalInternal":{"tmp":"tmp/tiptap-track-changes_0.1.1_1773462721244_0.07448468971060951","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"tiptap-track-changes","version":"0.2.0","keywords":["tiptap","prosemirror","track-changes","suggesting","editor","rich-text","diff","review","collaboration","google-docs"],"license":"MIT","_id":"tiptap-track-changes@0.2.0","maintainers":[{"name":"sungkhum","email":"sungkhum@gmail.com"}],"homepage":"https://github.com/sungkhum/tip-tap-track-changes","bugs":{"url":"https://github.com/sungkhum/tip-tap-track-changes/issues"},"dist":{"shasum":"befd1afb9bc1e8f3b58cdc6d196da23b584d8eb1","tarball":"https://registry.npmjs.org/tiptap-track-changes/-/tiptap-track-changes-0.2.0.tgz","fileCount":7,"integrity":"sha512-+Bk9GBvBFIfWKWEbSlqb1lU9jj93pmysDMecE0g+hExCGGISt7qrrS3p4KbHfKavw+QCz98yPxRhGSXLjk8cWA==","signatures":[{"sig":"MEYCIQDme5GNPwuemxxQScGtjWRIkHqZJCO/zcsPhvG7T/QfzQIhANIs0Evt03RqnNds7+2FJZduMWFpAD3wpAYRpshPKFB8","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":124408},"main":"dist/index.js","types":"dist/index.d.ts","module":"dist/index.mjs","exports":{".":{"import":{"types":"./dist/index.d.mts","default":"./dist/index.mjs"},"require":{"types":"./dist/index.d.ts","default":"./dist/index.js"}}},"gitHead":"e6090437ce2b486431c3814e85cd810e010cfce9","scripts":{"dev":"cd demo && npx vite","test":"vitest run","build":"tsup src/index.ts --format cjs,esm --dts --clean","typecheck":"tsc --noEmit","test:watch":"vitest","prepublishOnly":"npm run build && npm test"},"_npmUser":{"name":"sungkhum","email":"sungkhum@gmail.com"},"repository":{"url":"git+https://github.com/sungkhum/tip-tap-track-changes.git","type":"git"},"_npmVersion":"10.9.2","description":"Open-source track changes (suggesting mode) extension for Tiptap","directories":{},"sideEffects":false,"_nodeVersion":"22.15.0","_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.0.0","jsdom":"^26.0.0","vitest":"^3.0.0","@tiptap/pm":"^3.20.0","typescript":"^5.0.0","@tiptap/core":"^3.20.0","@tiptap/extension-bold":"^3.20.0","@tiptap/extension-text":"^3.20.0","@tiptap/extension-italic":"^3.20.0","@tiptap/extension-strike":"^3.20.0","@tiptap/extension-heading":"^3.20.0","@tiptap/extension-history":"^3.20.0","@tiptap/extension-document":"^3.20.0","@tiptap/extension-paragraph":"^3.20.0","@tiptap/extension-underline":"^3.20.0"},"peerDependencies":{"@tiptap/pm":"^2.0.0 || ^3.0.0","@tiptap/core":"^2.0.0 || ^3.0.0"},"_npmOperationalInternal":{"tmp":"tmp/tiptap-track-changes_0.2.0_1773539736679_0.3025365753704632","host":"s3://npm-registry-packages-npm-production"}},"0.2.1":{"name":"tiptap-track-changes","version":"0.2.1","description":"Open-source track changes (suggesting mode) extension for Tiptap","main":"dist/index.js","module":"dist/index.mjs","types":"dist/index.d.ts","sideEffects":false,"exports":{".":{"import":{"types":"./dist/index.d.mts","default":"./dist/index.mjs"},"require":{"types":"./dist/index.d.ts","default":"./dist/index.js"}}},"repository":{"type":"git","url":"git+https://github.com/sungkhum/tiptap-track-changes.git"},"homepage":"https://github.com/sungkhum/tiptap-track-changes","bugs":{"url":"https://github.com/sungkhum/tiptap-track-changes/issues"},"scripts":{"build":"tsup src/index.ts --format cjs,esm --dts --clean","dev":"cd demo && npx vite","test":"vitest run","test:watch":"vitest","typecheck":"tsc --noEmit","prepublishOnly":"npm run build && npm test"},"peerDependencies":{"@tiptap/core":"^2.0.0 || ^3.0.0","@tiptap/pm":"^2.0.0 || ^3.0.0"},"devDependencies":{"@tiptap/core":"^3.20.0","@tiptap/extension-bold":"^3.20.0","@tiptap/extension-document":"^3.20.0","@tiptap/extension-heading":"^3.20.0","@tiptap/extension-history":"^3.20.0","@tiptap/extension-italic":"^3.20.0","@tiptap/extension-paragraph":"^3.20.0","@tiptap/extension-strike":"^3.20.0","@tiptap/extension-text":"^3.20.0","@tiptap/extension-underline":"^3.20.0","@tiptap/pm":"^3.20.0","jsdom":"^26.0.0","tsup":"^8.0.0","typescript":"^5.0.0","vitest":"^3.0.0"},"license":"MIT","keywords":["tiptap","prosemirror","track-changes","suggesting","editor","rich-text","diff","review","collaboration","google-docs"],"_id":"tiptap-track-changes@0.2.1","gitHead":"9d950348ee880a8baadc45821c2319be43801684","_nodeVersion":"22.15.0","_npmVersion":"10.9.2","dist":{"integrity":"sha512-FIyh5nB1UX/u4uxTGxAP+I4t2xQff8XnZvqTHVboSY9mePcWoVErovK11zxQbr8ZD4u3AZjpdaLN4U8iIlKMWw==","shasum":"1e30a5ed92dc98d2d6dc92a17dc08bbf79fd94b3","tarball":"https://registry.npmjs.org/tiptap-track-changes/-/tiptap-track-changes-0.2.1.tgz","fileCount":7,"unpackedSize":124400,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQDAz1UgBp39FPkS6q42AaU2OD6orGIFV0Iryyp67pM6FgIhALSstwT5mUmL53X6EB72MbVo4DpGNoES48PBrnOiQKeH"}]},"_npmUser":{"name":"sungkhum","email":"sungkhum@gmail.com"},"directories":{},"maintainers":[{"name":"sungkhum","email":"sungkhum@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/tiptap-track-changes_0.2.1_1773767927320_0.11472758163209451"},"_hasShrinkwrap":false}},"time":{"created":"2026-03-09T15:30:56.761Z","modified":"2026-03-17T17:18:47.602Z","0.1.0":"2026-03-09T15:30:57.180Z","0.1.1":"2026-03-14T04:32:01.404Z","0.2.0":"2026-03-15T01:55:36.828Z","0.2.1":"2026-03-17T17:18:47.487Z"},"bugs":{"url":"https://github.com/sungkhum/tiptap-track-changes/issues"},"license":"MIT","homepage":"https://github.com/sungkhum/tiptap-track-changes","keywords":["tiptap","prosemirror","track-changes","suggesting","editor","rich-text","diff","review","collaboration","google-docs"],"repository":{"type":"git","url":"git+https://github.com/sungkhum/tiptap-track-changes.git"},"description":"Open-source track changes (suggesting mode) extension for Tiptap","maintainers":[{"name":"sungkhum","email":"sungkhum@gmail.com"}],"readme":"# tiptap-track-changes\n\n[![npm version](https://img.shields.io/npm/v/tiptap-track-changes.svg)](https://www.npmjs.com/package/tiptap-track-changes)\n[![license](https://img.shields.io/npm/l/tiptap-track-changes.svg)](https://github.com/sungkhum/tiptap-track-changes/blob/main/LICENSE)\n\nOpen-source track changes / suggesting mode extension for [Tiptap](https://tiptap.dev). Adds Google Docs-style change tracking to any Tiptap editor — insertions, deletions, replacements, and format changes are recorded as inline marks that can be individually accepted or rejected.\n\n**[Live Demo](https://sungkhum.github.io/tiptap-track-changes/)** | **[npm](https://www.npmjs.com/package/tiptap-track-changes)**\n\n![tiptap-track-changes demo showing multi-author suggesting mode with review sidebar](demo/screenshot.jpg)\n\n## Install\n\n```bash\nnpm install tiptap-track-changes\n```\n\n```bash\nyarn add tiptap-track-changes\n```\n\n```bash\npnpm add tiptap-track-changes\n```\n\n> Peer dependencies: `@tiptap/core` and `@tiptap/pm` (v2+). You probably already have these if you're using Tiptap.\n\n## Features\n\n- **Three editor modes**: `edit` (direct changes), `suggest` (tracked proposals), `view` (read-only)\n- **Inline change tracking**: Insertions, deletions, replacements, and format changes (bold, italic, etc.)\n- **Per-change accept/reject**: Accept or reject individual changes, or batch accept/reject all\n- **Multi-author support**: Each author gets a name, ID, and color — changes are attributed and color-coded\n- **Node-level tracking**: Paragraph splits (Enter key), block boundary deletions (Backspace/Delete across paragraphs), and block type changes (paragraph to heading)\n- **Undo/redo integration**: Tracked changes work correctly with Tiptap's built-in history extension\n- **Complex script support**: Tested with RTL (Arabic, Hebrew), Khmer, Thai, CJK, Devanagari, and other complex scripts\n- **200+ tests**: Comprehensive test suite covering edge cases, multi-author scenarios, and complex scripts\n\n## Quick Start\n\n```ts\nimport { Editor } from '@tiptap/core'\nimport Document from '@tiptap/extension-document'\nimport Paragraph from '@tiptap/extension-paragraph'\nimport Text from '@tiptap/extension-text'\nimport History from '@tiptap/extension-history'\nimport { TrackChangesExtension } from 'tiptap-track-changes'\n\nconst editor = new Editor({\n  element: document.querySelector('#editor'),\n  extensions: [\n    Document,\n    Paragraph,\n    Text,\n    History,\n    TrackChangesExtension.configure({\n      author: {\n        id: 'user-1',\n        name: 'Alice',\n        color: '#2d5fce',\n      },\n      mode: 'suggest', // start in suggesting mode\n      onStatusChange: (changeId, status) => {\n        console.log(`Change ${changeId} was ${status}`)\n      },\n    }),\n  ],\n  content: '<p>Hello world</p>',\n})\n```\n\nAny edits made while in `suggest` mode are tracked as proposed changes with inline visual markers.\n\n## Commands\n\n```ts\n// Switch modes\neditor.commands.setSuggestMode()\neditor.commands.setEditMode()\neditor.commands.setViewMode()\neditor.commands.setTrackChangesMode('suggest') // programmatic\n\n// Change author (e.g., when switching users)\neditor.commands.setTrackChangesAuthor({\n  id: 'user-2',\n  name: 'Bob',\n  color: '#c4362c',\n})\n\n// Accept/reject individual changes\neditor.commands.acceptChange('change-id-here')\neditor.commands.rejectChange('change-id-here')\n\n// Accept/reject all pending changes\neditor.commands.acceptAll()\neditor.commands.rejectAll()\n```\n\n## Reading Changes\n\n```ts\nimport {\n  getTrackedChanges,\n  getGroupedChanges,\n  getPendingChangeCount,\n  getBaseText,\n  getResultText,\n} from 'tiptap-track-changes'\n\n// Get all individual tracked change marks\nconst changes = getTrackedChanges(editor)\n// => TrackedChangeInfo[] with changeId, type, authorId, from, to, text, etc.\n\n// Get changes grouped by changeId (insertion + deletion = replacement)\nconst groups = getGroupedChanges(editor)\n// => Map<string, TrackedChangeInfo[]>\n\n// Count pending changes\nconst count = getPendingChangeCount(editor) // => number\n\n// Get document text with all changes rejected (original) or accepted (result)\nconst original = getBaseText(editor)\nconst result = getResultText(editor)\n```\n\n## Types\n\n```ts\ninterface ChangeAuthor {\n  id: string\n  name: string\n  color: string\n}\n\ntype TrackChangesMode = 'edit' | 'suggest' | 'view'\n\ninterface TrackChangesOptions {\n  author: ChangeAuthor\n  mode?: TrackChangesMode\n  onStatusChange?: (changeId: string, status: 'accepted' | 'rejected') => void\n}\n\ninterface TrackedChangeInfo {\n  changeId: string\n  type: 'insertion' | 'deletion' | 'formatChange' | 'nodeChange'\n  authorId: string\n  authorName: string\n  authorColor: string\n  timestamp: string\n  from: number\n  to: number\n  text: string\n  formatAdded?: string\n  formatRemoved?: string\n}\n```\n\n## How It Works\n\nIn **suggest mode**, the extension intercepts text input, deletions, Enter key, and format toggles. Instead of modifying the document directly, it wraps changes in inline marks:\n\n- **Insertions**: Text is inserted and wrapped in an `insertion` mark (rendered as `<ins>`)\n- **Deletions**: Text is kept in the document but wrapped in a `deletion` mark (rendered as `<del>` with CSS to hide it visually)\n- **Replacements**: A deletion mark on the old text + an insertion mark on the new text, sharing the same `changeId`\n- **Format changes**: The format is applied, and a `formatChange` mark records what was added/removed\n\nWhen a change is **accepted**, its marks are removed (insertions become normal text, deletions are removed from the document). When **rejected**, the opposite happens (insertions are removed, deletions become normal text).\n\n## Running the Demo Locally\n\nThe `demo/` directory contains a full interactive demo with a review sidebar, timeline view, keyboard navigation, and pre-built scenarios.\n\n```bash\ngit clone https://github.com/sungkhum/tiptap-track-changes.git\ncd tiptap-track-changes\nnpm install\ncd demo && npm install\ncd .. && npm run dev\n```\n\nOr try the **[hosted demo](https://sungkhum.github.io/tiptap-track-changes/)** directly.\n\n## Running Tests\n\n```bash\nnpm test          # single run\nnpm run test:watch # watch mode\n```\n\n## Contributing\n\nContributions are welcome. Please open an issue first to discuss what you'd like to change.\n\n1. Fork the repository\n2. Create your feature branch (`git checkout -b feature/my-feature`)\n3. Run the tests (`npm test`)\n4. Commit your changes\n5. Open a pull request\n\n## License\n\n[MIT](LICENSE)\n","readmeFilename":"README.md"}