{"_id":"@plannotator/markdown-editor","_rev":"5-7cc0851f2ba1028889f317e17a07b8bc","name":"@plannotator/markdown-editor","dist-tags":{"latest":"0.4.0"},"versions":{"0.1.0":{"name":"@plannotator/markdown-editor","version":"0.1.0","keywords":["codemirror","editor","live-preview","markdown","plannotator","react","wysiwyg"],"license":"MIT","_id":"@plannotator/markdown-editor@0.1.0","maintainers":[{"name":"backnotprop","email":"backnotprop@gmail.com"}],"homepage":"https://github.com/plannotator/markdown-editor#readme","bugs":{"url":"https://github.com/plannotator/markdown-editor/issues"},"dist":{"shasum":"8e39126540678283da73af919171b12b18176f91","tarball":"https://registry.npmjs.org/@plannotator/markdown-editor/-/markdown-editor-0.1.0.tgz","fileCount":13,"integrity":"sha512-tLc7ZeUcnsJCxvS3HNa+R0cP/ZikjGuP8nD1+4JeDUUd/5IB0upygcJRCC0OHZ4V6CJ5C9PdKGwnTp88Y72sog==","signatures":[{"sig":"MEUCIFNDsQ1NET5sbrumebMduZ/l8LdYjWmGvz3Gkvz5C9mVAiEA8KfjSh2o2h/+KPSJwj001Rl68uI1sNF9IfyHLaVsrCs=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":21957},"type":"module","_from":"file:plannotator-markdown-editor-0.1.0.tgz","engines":{"node":">=20"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./testing":{"types":"./dist/testing.d.ts","import":"./dist/testing.js"},"./styles.css":"./dist/styles/markdown-editor.css","./themes/plannotator.css":"./dist/styles/themes/plannotator.css"},"scripts":{"lint":"oxlint","test":"vitest run","build":"tsc -p tsconfig.build.json && node scripts/copy-styles.mjs","format":"oxfmt --write .","lint:fix":"oxlint --fix","typecheck":"tsc --noEmit -p tsconfig.json","format:check":"oxfmt --check ."},"_npmUser":{"name":"backnotprop","email":"backnotprop@gmail.com"},"_resolved":"/private/var/folders/xg/f67qq94s1qsbxl_nlh9hd_xc0000gn/T/4d27c63899fa3d8e6759da764d01fdea/plannotator-markdown-editor-0.1.0.tgz","_integrity":"sha512-tLc7ZeUcnsJCxvS3HNa+R0cP/ZikjGuP8nD1+4JeDUUd/5IB0upygcJRCC0OHZ4V6CJ5C9PdKGwnTp88Y72sog==","repository":{"url":"git+https://github.com/plannotator/markdown-editor.git","type":"git"},"_npmVersion":"11.12.1","description":"Byte-faithful live-preview markdown editor (CodeMirror 6 / atomic-editor) — the raw markdown is the source of truth; rendering is decoration-only. Built for Plannotator's edit mode, reusable anywhere.","directories":{},"sideEffects":["**/*.css"],"_nodeVersion":"24.15.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"oxfmt":"^0.43.0","react":"^19.2.0","oxlint":"^1.58.0","vitest":"^3.0.0","happy-dom":"^20.0.0","react-dom":"^19.2.0","typescript":"6.0.2","@types/react":"^19.2.0","@types/react-dom":"^19.2.0","@codemirror/language":"^6.11.0","@atomic-editor/editor":"^0.4.3","@codemirror/lang-json":"^6.0.2","@codemirror/lang-yaml":"^6.1.3","@codemirror/lang-python":"^6.2.1","@codemirror/legacy-modes":"^6.5.3","@codemirror/lang-javascript":"^6.2.5"},"peerDependencies":{"react":"^18.0.0 || ^19.0.0","react-dom":"^18.0.0 || ^19.0.0","@codemirror/language":"^6.0.0","@atomic-editor/editor":"^0.4.0","@codemirror/lang-json":"^6.0.0","@codemirror/lang-yaml":"^6.0.0","@codemirror/lang-python":"^6.0.0","@codemirror/legacy-modes":"^6.0.0","@codemirror/lang-javascript":"^6.0.0"},"peerDependenciesMeta":{"@codemirror/lang-json":{"optional":true},"@codemirror/lang-yaml":{"optional":true},"@codemirror/lang-python":{"optional":true},"@codemirror/legacy-modes":{"optional":true},"@codemirror/lang-javascript":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/markdown-editor_0.1.0_1781215753645_0.2112478117654657","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@plannotator/markdown-editor","version":"0.2.0","keywords":["codemirror","editor","live-preview","markdown","plannotator","react","wysiwyg"],"license":"MIT","_id":"@plannotator/markdown-editor@0.2.0","maintainers":[{"name":"backnotprop","email":"backnotprop@gmail.com"}],"homepage":"https://github.com/plannotator/markdown-editor#readme","bugs":{"url":"https://github.com/plannotator/markdown-editor/issues"},"dist":{"shasum":"cd1ac473a1fc0118c1d019a8f669a8d0d119a9d3","tarball":"https://registry.npmjs.org/@plannotator/markdown-editor/-/markdown-editor-0.2.0.tgz","fileCount":13,"integrity":"sha512-ENy6mMZSJWU6NnEsG/15Ia/oZLMSUfwd0skdmDyJcY4HGFESAZ2CTApHfgO2fGTQAaoOafSQ6Dh28QxWxY9duA==","signatures":[{"sig":"MEQCIAOYQubSkIQlUEFO8hhgmuQDh9IY82bYlAZ87gK1DfvoAiAcwqZwfzcBqjEczKhDb3nr2Hu5gDjdTL8Lk66u6ZjcBA==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":22390},"type":"module","_from":"file:plannotator-markdown-editor-0.2.0.tgz","engines":{"node":">=20"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./testing":{"types":"./dist/testing.d.ts","import":"./dist/testing.js"},"./styles.css":"./dist/styles/markdown-editor.css","./themes/plannotator.css":"./dist/styles/themes/plannotator.css"},"scripts":{"lint":"oxlint","test":"vitest run","build":"tsc -p tsconfig.build.json && node scripts/copy-styles.mjs","format":"oxfmt --write .","lint:fix":"oxlint --fix","typecheck":"tsc --noEmit -p tsconfig.json","format:check":"oxfmt --check ."},"_npmUser":{"name":"backnotprop","email":"backnotprop@gmail.com"},"_resolved":"/private/var/folders/xg/f67qq94s1qsbxl_nlh9hd_xc0000gn/T/8e43fe326f86cc3de639976449242066/plannotator-markdown-editor-0.2.0.tgz","_integrity":"sha512-ENy6mMZSJWU6NnEsG/15Ia/oZLMSUfwd0skdmDyJcY4HGFESAZ2CTApHfgO2fGTQAaoOafSQ6Dh28QxWxY9duA==","repository":{"url":"git+https://github.com/plannotator/markdown-editor.git","type":"git"},"_npmVersion":"11.12.1","description":"Live-preview markdown editor for React (CodeMirror 6, atomic-editor). Raw markdown is the source of truth: rendering is decoration only, edits round-trip byte-identical.","directories":{},"sideEffects":["**/*.css"],"_nodeVersion":"24.15.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"oxfmt":"^0.43.0","react":"^19.2.0","oxlint":"^1.58.0","vitest":"^3.0.0","happy-dom":"^20.0.0","react-dom":"^19.2.0","typescript":"6.0.2","@types/react":"^19.2.0","@types/react-dom":"^19.2.0","@codemirror/language":"^6.11.0","@codemirror/lang-json":"^6.0.2","@codemirror/lang-yaml":"^6.1.3","@codemirror/lang-python":"^6.2.1","@codemirror/legacy-modes":"^6.5.3","@plannotator/atomic-editor":"^0.5.0","@codemirror/lang-javascript":"^6.2.5"},"peerDependencies":{"react":"^18.0.0 || ^19.0.0","react-dom":"^18.0.0 || ^19.0.0","@codemirror/language":"^6.0.0","@codemirror/lang-json":"^6.0.0","@codemirror/lang-yaml":"^6.0.0","@codemirror/lang-python":"^6.0.0","@codemirror/legacy-modes":"^6.0.0","@plannotator/atomic-editor":"^0.5.0","@codemirror/lang-javascript":"^6.0.0"},"peerDependenciesMeta":{"@codemirror/lang-json":{"optional":true},"@codemirror/lang-yaml":{"optional":true},"@codemirror/lang-python":{"optional":true},"@codemirror/legacy-modes":{"optional":true},"@codemirror/lang-javascript":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/markdown-editor_0.2.0_1783028665317_0.027050041426467386","host":"s3://npm-registry-packages-npm-production"}},"0.3.0":{"name":"@plannotator/markdown-editor","version":"0.3.0","keywords":["codemirror","editor","live-preview","markdown","plannotator","react","wysiwyg"],"license":"MIT","_id":"@plannotator/markdown-editor@0.3.0","maintainers":[{"name":"backnotprop","email":"backnotprop@gmail.com"}],"homepage":"https://github.com/plannotator/markdown-editor#readme","bugs":{"url":"https://github.com/plannotator/markdown-editor/issues"},"dist":{"shasum":"504e56d8862acbfb66daef660676a73e986b8fd2","tarball":"https://registry.npmjs.org/@plannotator/markdown-editor/-/markdown-editor-0.3.0.tgz","fileCount":13,"integrity":"sha512-EnhaXk5HfFrfwXFEvfsKkaoUM2Zb2VFVnR/NwXHd7gk/IoPuLG6NOxGorvUTQaz6ThNG4zDQwRj/j88UD9RbQA==","signatures":[{"sig":"MEYCIQCIEduCWCtp7kU8gM3curtessBQ7AprGqILuK5hW/hsTgIhAK8UyORVO2s9S0tGqEnsA5H2xRarccEDdKnWlrztcM54","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":24224},"type":"module","_from":"file:plannotator-markdown-editor-0.3.0.tgz","engines":{"node":">=20"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./testing":{"types":"./dist/testing.d.ts","import":"./dist/testing.js"},"./styles.css":"./dist/styles/markdown-editor.css","./themes/plannotator.css":"./dist/styles/themes/plannotator.css"},"scripts":{"lint":"oxlint","test":"vitest run","build":"tsc -p tsconfig.build.json && node scripts/copy-styles.mjs","format":"oxfmt --write .","lint:fix":"oxlint --fix","typecheck":"tsc --noEmit -p tsconfig.json","format:check":"oxfmt --check ."},"_npmUser":{"name":"backnotprop","email":"backnotprop@gmail.com"},"_resolved":"/private/var/folders/xg/f67qq94s1qsbxl_nlh9hd_xc0000gn/T/2b80acf9c6cb9f86d8689edfcf793f7d/plannotator-markdown-editor-0.3.0.tgz","_integrity":"sha512-EnhaXk5HfFrfwXFEvfsKkaoUM2Zb2VFVnR/NwXHd7gk/IoPuLG6NOxGorvUTQaz6ThNG4zDQwRj/j88UD9RbQA==","repository":{"url":"git+https://github.com/plannotator/markdown-editor.git","type":"git"},"_npmVersion":"11.12.1","description":"Live-preview markdown editor for React (CodeMirror 6, atomic-editor). Raw markdown is the source of truth: rendering is decoration only, edits round-trip byte-identical.","directories":{},"sideEffects":["**/*.css"],"_nodeVersion":"24.15.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"oxfmt":"^0.43.0","react":"^19.2.0","oxlint":"^1.58.0","vitest":"^3.0.0","happy-dom":"^20.0.0","react-dom":"^19.2.0","typescript":"6.0.2","@types/react":"^19.2.0","@codemirror/view":"^6.43.0","@types/react-dom":"^19.2.0","@codemirror/state":"^6.7.0","@codemirror/language":"^6.11.0","@codemirror/lang-json":"^6.0.2","@codemirror/lang-yaml":"^6.1.3","@codemirror/lang-python":"^6.2.1","@codemirror/legacy-modes":"^6.5.3","@plannotator/atomic-editor":"^0.5.0","@codemirror/lang-javascript":"^6.2.5"},"peerDependencies":{"react":"^18.0.0 || ^19.0.0","react-dom":"^18.0.0 || ^19.0.0","@codemirror/state":"^6.0.0","@codemirror/language":"^6.0.0","@codemirror/lang-json":"^6.0.0","@codemirror/lang-yaml":"^6.0.0","@codemirror/lang-python":"^6.0.0","@codemirror/legacy-modes":"^6.0.0","@plannotator/atomic-editor":"^0.5.0","@codemirror/lang-javascript":"^6.0.0"},"peerDependenciesMeta":{"@codemirror/lang-json":{"optional":true},"@codemirror/lang-yaml":{"optional":true},"@codemirror/lang-python":{"optional":true},"@codemirror/legacy-modes":{"optional":true},"@codemirror/lang-javascript":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/markdown-editor_0.3.0_1783485199846_0.2992800214779201","host":"s3://npm-registry-packages-npm-production"}},"0.3.2":{"name":"@plannotator/markdown-editor","version":"0.3.2","keywords":["codemirror","editor","live-preview","markdown","plannotator","react","wysiwyg"],"license":"MIT","_id":"@plannotator/markdown-editor@0.3.2","maintainers":[{"name":"backnotprop","email":"backnotprop@gmail.com"}],"homepage":"https://github.com/plannotator/markdown-editor#readme","bugs":{"url":"https://github.com/plannotator/markdown-editor/issues"},"dist":{"shasum":"c327d584ce4f8c0515dca7a7441ff190822c3630","tarball":"https://registry.npmjs.org/@plannotator/markdown-editor/-/markdown-editor-0.3.2.tgz","fileCount":13,"integrity":"sha512-AEe6IcJz26nmfVQzJAzHHR0YBQ0+lzjF7xbnKRmJ99DIPFI90wJDoVo0vqs/cTX9PTA0vWymlD0xOp7+qYw4DA==","signatures":[{"sig":"MEQCIGS/rtyFKerxTFMKjGP4qGThvoISl1afq79sjePztQu+AiAL5+1Vry0FjhGXTf6qMznTII2PfhflO5DPUK4RNgZCtQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":24920},"type":"module","_from":"file:plannotator-markdown-editor-0.3.2.tgz","engines":{"node":">=20"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./testing":{"types":"./dist/testing.d.ts","import":"./dist/testing.js"},"./styles.css":"./dist/styles/markdown-editor.css","./themes/plannotator.css":"./dist/styles/themes/plannotator.css"},"scripts":{"lint":"oxlint","test":"vitest run","build":"tsc -p tsconfig.build.json && node scripts/copy-styles.mjs","format":"oxfmt --write .","lint:fix":"oxlint --fix","typecheck":"tsc --noEmit -p tsconfig.json","format:check":"oxfmt --check ."},"_npmUser":{"name":"backnotprop","email":"backnotprop@gmail.com"},"_resolved":"/private/var/folders/xg/f67qq94s1qsbxl_nlh9hd_xc0000gn/T/cc144c85b9c164791c282b4691762e97/plannotator-markdown-editor-0.3.2.tgz","_integrity":"sha512-AEe6IcJz26nmfVQzJAzHHR0YBQ0+lzjF7xbnKRmJ99DIPFI90wJDoVo0vqs/cTX9PTA0vWymlD0xOp7+qYw4DA==","repository":{"url":"git+https://github.com/plannotator/markdown-editor.git","type":"git"},"_npmVersion":"11.12.1","description":"Live-preview markdown editor for React (CodeMirror 6, atomic-editor). Raw markdown is the source of truth: rendering is decoration only, edits round-trip byte-identical.","directories":{},"sideEffects":["**/*.css"],"_nodeVersion":"24.15.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"oxfmt":"^0.43.0","react":"^19.2.0","oxlint":"^1.58.0","vitest":"^3.0.0","happy-dom":"^20.0.0","react-dom":"^19.2.0","typescript":"6.0.2","@types/react":"^19.2.0","@codemirror/view":"^6.43.0","@types/react-dom":"^19.2.0","@codemirror/state":"^6.7.0","@codemirror/language":"^6.11.0","@codemirror/lang-json":"^6.0.2","@codemirror/lang-yaml":"^6.1.3","@codemirror/lang-python":"^6.2.1","@codemirror/legacy-modes":"^6.5.3","@plannotator/atomic-editor":"^0.5.0","@codemirror/lang-javascript":"^6.2.5"},"peerDependencies":{"react":"^18.0.0 || ^19.0.0","react-dom":"^18.0.0 || ^19.0.0","@codemirror/state":"^6.0.0","@codemirror/language":"^6.0.0","@codemirror/lang-json":"^6.0.0","@codemirror/lang-yaml":"^6.0.0","@codemirror/lang-python":"^6.0.0","@codemirror/legacy-modes":"^6.0.0","@plannotator/atomic-editor":"^0.5.0 || ^0.6.0 || ^0.7.0","@codemirror/lang-javascript":"^6.0.0"},"peerDependenciesMeta":{"@codemirror/lang-json":{"optional":true},"@codemirror/lang-yaml":{"optional":true},"@codemirror/lang-python":{"optional":true},"@codemirror/legacy-modes":{"optional":true},"@codemirror/lang-javascript":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/markdown-editor_0.3.2_1783623417890_0.06985104499974071","host":"s3://npm-registry-packages-npm-production"}},"0.4.0":{"name":"@plannotator/markdown-editor","version":"0.4.0","description":"Live-preview markdown editor for React (CodeMirror 6, atomic-editor). Raw markdown is the source of truth: rendering is decoration only, edits round-trip byte-identical.","keywords":["codemirror","editor","live-preview","markdown","plannotator","react","wysiwyg"],"homepage":"https://github.com/plannotator/markdown-editor#readme","bugs":{"url":"https://github.com/plannotator/markdown-editor/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/plannotator/markdown-editor.git"},"type":"module","sideEffects":["**/*.css"],"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./testing":{"types":"./dist/testing.d.ts","import":"./dist/testing.js"},"./styles.css":"./dist/styles/markdown-editor.css","./themes/plannotator.css":"./dist/styles/themes/plannotator.css"},"publishConfig":{"access":"public"},"devDependencies":{"@codemirror/lang-javascript":"^6.2.5","@codemirror/lang-json":"^6.0.2","@codemirror/lang-python":"^6.2.1","@codemirror/lang-yaml":"^6.1.3","@codemirror/language":"^6.11.0","@codemirror/legacy-modes":"^6.5.3","@codemirror/state":"^6.7.0","@codemirror/view":"^6.43.0","@plannotator/atomic-editor":"^0.8.0","@types/react":"^19.2.0","@types/react-dom":"^19.2.0","happy-dom":"^20.0.0","oxfmt":"^0.43.0","oxlint":"^1.58.0","react":"^19.2.0","react-dom":"^19.2.0","typescript":"6.0.2","vitest":"^3.0.0"},"peerDependencies":{"@codemirror/lang-javascript":"^6.0.0","@codemirror/lang-json":"^6.0.0","@codemirror/lang-python":"^6.0.0","@codemirror/lang-yaml":"^6.0.0","@codemirror/language":"^6.0.0","@codemirror/legacy-modes":"^6.0.0","@codemirror/state":"^6.0.0","@plannotator/atomic-editor":"^0.8.0","react":"^18.0.0 || ^19.0.0","react-dom":"^18.0.0 || ^19.0.0"},"peerDependenciesMeta":{"@codemirror/lang-javascript":{"optional":true},"@codemirror/lang-json":{"optional":true},"@codemirror/lang-python":{"optional":true},"@codemirror/lang-yaml":{"optional":true},"@codemirror/legacy-modes":{"optional":true}},"engines":{"node":">=20"},"scripts":{"build":"tsc -p tsconfig.build.json && node scripts/copy-styles.mjs","lint":"oxlint","lint:fix":"oxlint --fix","format":"oxfmt --write .","format:check":"oxfmt --check .","typecheck":"tsc --noEmit -p tsconfig.json","test":"vitest run"},"_id":"@plannotator/markdown-editor@0.4.0","_integrity":"sha512-pXJj912NFY2KBUhQXusoTlGv169NGEDKLVo2bjTCkH+SPIzCMxQWwygdMvMzpNstsieZNDs6NY81fc+XBl5lCA==","_resolved":"/Users/ramos/oss/release-artifacts/plannotator-markdown-editor-0.4.0.tgz","_from":"file:plannotator-markdown-editor-0.4.0.tgz","_nodeVersion":"24.15.0","_npmVersion":"11.12.1","dist":{"integrity":"sha512-pXJj912NFY2KBUhQXusoTlGv169NGEDKLVo2bjTCkH+SPIzCMxQWwygdMvMzpNstsieZNDs6NY81fc+XBl5lCA==","shasum":"09f3c6cc5bb518dd2e20b47c421e216086310e09","tarball":"https://registry.npmjs.org/@plannotator/markdown-editor/-/markdown-editor-0.4.0.tgz","fileCount":17,"unpackedSize":33215,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIHoUC/SIKg8iTBABdfkvHTygIKMRs1W6sNLwnN3fIIrUAiEAxjTaWsGKZd5oOfOkquzIcoqffLB30LbaLqc2rmGs6W4="}]},"_npmUser":{"name":"backnotprop","email":"backnotprop@gmail.com"},"directories":{},"maintainers":[{"name":"backnotprop","email":"backnotprop@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/markdown-editor_0.4.0_1783662803929_0.4699520531032948"},"_hasShrinkwrap":false}},"time":{"created":"2026-06-11T22:09:13.451Z","modified":"2026-07-10T05:53:24.208Z","0.1.0":"2026-06-11T22:09:13.786Z","0.2.0":"2026-07-02T21:44:25.460Z","0.3.0":"2026-07-08T04:33:19.987Z","0.3.2":"2026-07-09T18:56:58.038Z","0.4.0":"2026-07-10T05:53:24.073Z"},"bugs":{"url":"https://github.com/plannotator/markdown-editor/issues"},"license":"MIT","homepage":"https://github.com/plannotator/markdown-editor#readme","keywords":["codemirror","editor","live-preview","markdown","plannotator","react","wysiwyg"],"repository":{"type":"git","url":"git+https://github.com/plannotator/markdown-editor.git"},"description":"Live-preview markdown editor for React (CodeMirror 6, atomic-editor). Raw markdown is the source of truth: rendering is decoration only, edits round-trip byte-identical.","maintainers":[{"name":"backnotprop","email":"backnotprop@gmail.com"}],"readme":"# @plannotator/markdown-editor\n\nLive-preview markdown editor for React, built on [our fork](https://github.com/plannotator/atomic-editor) of [atomic-editor](https://github.com/kenforthewin/atomic-editor) (CodeMirror 6). The raw markdown text is the source of truth. Rendering is decoration only, so loading a document and reading it back is byte-identical. A one-word edit produces a one-word diff.\n\nWe pulled this out of [Plannotator](https://github.com/backnotprop/plannotator)'s edit mode, where user edits travel to AI agents as unified diffs. That workflow breaks the moment an editor normalizes markdown it didn't touch: bullet markers, escaping, spacing. This package makes the guarantee reusable and enforces it by test.\n\n## Install\n\n```sh\npnpm add @plannotator/markdown-editor\n# optional, for the default fenced-code grammar set:\npnpm add @codemirror/lang-javascript @codemirror/lang-python @codemirror/lang-json @codemirror/lang-yaml @codemirror/legacy-modes\n```\n\nReact and the CodeMirror packages are peer dependencies. pnpm, npm 7+, and bun install them automatically. They're peers (not bundled) because your app must share one copy of React and one copy of CM6 with the editor: the `codeLanguages` prop takes `LanguageDescription` objects you build from your own `@codemirror/language`, and CM6 breaks when two copies of `@codemirror/state` coexist.\n\n## Use\n\n```tsx\nimport { useRef } from \"react\";\nimport { MarkdownEditor, type MarkdownEditorHandle } from \"@plannotator/markdown-editor\";\nimport \"@plannotator/markdown-editor/themes/plannotator.css\"; // or your own theme\n\nfunction Editor({ doc }: { doc: string }) {\n\tconst handle = useRef<MarkdownEditorHandle | null>(null);\n\treturn (\n\t\t// Needs a height-bounded parent. The editor fills it and scrolls internally.\n\t\t<div style={{ height: \"80vh\" }}>\n\t\t\t<MarkdownEditor\n\t\t\t\tmarkdown={doc}\n\t\t\t\tdocumentId=\"my-doc\"\n\t\t\t\teditorHandleRef={handle}\n\t\t\t\tmode=\"dark\"\n\t\t\t\tonLinkClick={(url) => window.open(url)}\n\t\t\t/>\n\t\t</div>\n\t);\n}\n\n// Read the current text whenever you need it:\nconst text = handle.current?.getMarkdown();\n```\n\n### Frozen document diff\n\n`MarkdownDiff` renders two revisions as one read-only document: newer text in place, additions\nhighlighted, deletions struck through where they used to be, and a keyboard-accessible change\noverview at the review pane's right edge. The full document remains visible; unchanged regions are\nnever collapsed.\n\n```tsx\nimport { MarkdownDiff } from \"@plannotator/markdown-editor\";\nimport \"@plannotator/markdown-editor/themes/plannotator.css\";\n\n<div style={{ height: \"80vh\" }}>\n\t<MarkdownDiff\n\t\toriginalMarkdown={savedRevision}\n\t\tmodifiedMarkdown={currentRevision}\n\t\tdocumentId={`${documentId}:${fromVersion}:${toVersion}`}\n\t\tmode=\"dark\"\n\t/>\n</div>;\n```\n\nThe complete diff surface is unconstrained by default so the overview rail stays attached to the\nreview pane edge. Set `maxWidth` to constrain the document and rail together, or pass\n`showOverview={false}` when the host supplies its own navigation. The `extensions` seam is the same\none used by `MarkdownEditor`, including its capture-on-mount contract.\n\nHow it behaves:\n\n- **Uncontrolled after mount.** `markdown` is read once, then the editor owns the text. Read it back with `editorHandleRef.current.getMarkdown()`. Swap documents by changing `documentId`.\n- **`mode`**: pass your app's resolved `'dark' | 'light'`. The light palette switches via `data-theme=\"light\"` on the wrapper.\n- **`codeLanguages`**: defaults to a small js/ts/python/json/yaml/shell set (`DEFAULT_CODE_LANGUAGES`). Pass your own `LanguageDescription[]` to change it. Watch out: bundlers with `inlineDynamicImports` (single-file builds) inline every listed grammar.\n- **`extensions`**: extra CodeMirror 6 extensions, forwarded verbatim to the editor and appended after its built-ins. This is the hook for live collaboration (e.g. `y-codemirror.next`), custom keymaps, or update listeners — the package stays provider-agnostic and ships no collaboration code of its own. Build extensions against your own CM6 packages (one shared copy of `@codemirror/state`, as above). Extensions that rewrite document text void the byte-fidelity guarantee; the contract covers what the editor itself does.\n\n    The engine ships two opt-in UI extensions (atomic-editor ≥ 0.6.0) that plug straight in:\n\n    ```tsx\n    import { slashCommands, selectionToolbar } from \"@plannotator/atomic-editor\";\n\n    <MarkdownEditor extensions={[slashCommands(), selectionToolbar()]} ... />\n    ```\n\n    `slashCommands()` is a Notion-style insert menu on `/` at the start of a line; `selectionToolbar()` is a floating bold/italic/strike/code/link bar over selected text (works multi-line and inside table cells). Both are themeable via the `--atomic-editor-menu-*` CSS variables and documented in the [atomic-editor changelog](https://github.com/plannotator/atomic-editor/blob/main/CHANGELOG.md).\n\n- **`className` / `cardClassName`**: extra classes on the wrapper and inner card, for stacking, shadows, or padding your app needs.\n\n## Theming\n\natomic-editor reads `--atomic-editor-*` CSS custom properties. Set them on `.pn-markdown-editor` or any ancestor. A ready-made mapping for the Plannotator token system (`--foreground`, `--card`, `--primary`, and so on) ships at `@plannotator/markdown-editor/themes/plannotator.css`. Copy it as the starting point for your own.\n\nOne gotcha that cost us a debugging session. atomic-editor declares its light palette directly on `.atomic-cm-editor` under `[data-theme=\"light\"]`, which beats variables inherited from a wrapper. Your theme must re-declare its overrides under `.pn-markdown-editor[data-theme='light'] .atomic-cm-editor { ... }` or they silently lose in light mode. The bundled theme does this.\n\n## The fidelity guarantee (test it in your app)\n\n```ts\n// vitest — full config below; jsdom won't work, CM6 needs layout\nimport { roundTrip } from \"@plannotator/markdown-editor/testing\";\n\ntest(\"my corpus round-trips byte-identically\", async () => {\n\tfor (const doc of myDocuments) {\n\t\texpect(await roundTrip(doc)).toBe(doc);\n\t}\n});\n```\n\n```ts\n// vitest.config.ts\nexport default defineConfig({\n\ttest: {\n\t\tenvironment: \"happy-dom\",\n\t\tsetupFiles: [\"./setup.ts\"], // must set: globalThis.IS_REACT_ACT_ENVIRONMENT = true\n\t\tserver: {\n\t\t\tdeps: {\n\t\t\t\t// Both packages must run through Vite: the editor's dist uses\n\t\t\t\t// extensionless ESM imports Node's resolver rejects, and once\n\t\t\t\t// Node loads this package natively the editor underneath is\n\t\t\t\t// loaded natively too.\n\t\t\t\tinline: [\"@plannotator/markdown-editor\", \"@plannotator/atomic-editor\"],\n\t\t\t},\n\t\t},\n\t},\n});\n```\n\nIf this fails on your content, file an issue. It's the package's core contract. Plannotator runs it against a 150-document sample of real plan history on every change.\n\n## Relationship to atomic-editor\n\nThis is a thin wrapper over [`@plannotator/atomic-editor`](https://github.com/plannotator/atomic-editor), our fork of [atomic-editor](https://github.com/kenforthewin/atomic-editor). We forked because we needed parser-level changes the wrapper couldn't provide (first: YAML frontmatter support, which upstream misparses as a rule plus heading). The fork tracks upstream closely, and we offer our changes back as PRs — it's a delta, not a divergence.\n\n## Development\n\n```sh\npnpm install\npnpm lint && pnpm typecheck && pnpm test && pnpm build\n```\n\nConventions: oxlint and oxfmt (tabs, width 100), strict TypeScript, vitest with happy-dom. CI runs every gate on every PR.\n\n## License\n\nMIT\n","readmeFilename":"README.md"}