{"_id":"svedit","_rev":"24-ec87037d0d87b2463e606e49d46e60d5","name":"svedit","dist-tags":{"latest":"0.15.2"},"versions":{"0.1.0":{"name":"svedit","version":"0.1.0","license":"MIT","_id":"svedit@0.1.0","maintainers":[{"name":"michael","email":"michael@letsken.com"}],"dist":{"shasum":"0a09f998ab91bdf6673ad555267450bebd736e9a","tarball":"https://registry.npmjs.org/svedit/-/svedit-0.1.0.tgz","fileCount":49,"integrity":"sha512-wAIabCWSeCiADnKiYwEYcZ28I1vcaFFKe7sXTxiVlfeBDNZN31uj+w1tVa16X1jQtO2onjGxI8tfoZxbKUBF1A==","signatures":[{"sig":"MEYCIQDw8YWOO199X2gaR0KaAG/A76waXck003jrE0cM35svlQIhAO44as/opEGSR7EiZz5j+ndJZpIhf6ZBNLq6Ey0PbA3I","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":241980},"type":"module","types":"./dist/index.d.ts","svelte":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","svelte":"./dist/index.js"}},"gitHead":"fe62f17252a0ccee1eeb541245730d451a4418d8","scripts":{"dev":"vite dev","lint":"prettier --check . && eslint .","test":"npm run test:unit -- --run","build":"vite build","check":"svelte-kit sync && svelte-check --tsconfig ./jsconfig.json","format":"prettier --write .","prepack":"svelte-kit sync && svelte-package && publint","prepare":"svelte-kit sync || echo ''","preview":"vite preview","test:unit":"vitest","check:watch":"svelte-kit sync && svelte-check --tsconfig ./jsconfig.json --watch"},"_npmUser":{"name":"michael","email":"michael@letsken.com"},"_npmVersion":"10.9.2","description":"Svedit (think Svelte Edit) is a tiny library for building **rich content editors** with Svelte 5. You can model your content in JSON, render it with custom Svelte components, and edit it directly in the layout.","directories":{},"sideEffects":["**/*.css"],"_nodeVersion":"22.17.0","_hasShrinkwrap":false,"devDependencies":{"vite":"^7.0.4","eslint":"^9.18.0","nanoid":"^5.1.5","svelte":"^5.0.0","vitest":"^3.2.3","globals":"^16.0.0","publint":"^0.3.2","prettier":"^3.4.2","@eslint/js":"^9.18.0","svelte-check":"^4.0.0","@sveltejs/kit":"^2.21.2","@eslint/compat":"^1.2.5","@vitest/browser":"^3.2.4","@sveltejs/package":"^2.0.0","eslint-plugin-svelte":"^3.0.0","vitest-browser-svelte":"^1.1.0","@sveltejs/adapter-auto":"^6.0.0","eslint-config-prettier":"^10.0.1","prettier-plugin-svelte":"^3.3.3","@typescript-eslint/parser":"^8.39.1","@sveltejs/vite-plugin-svelte":"^6.0.0","@typescript-eslint/eslint-plugin":"^8.39.1"},"peerDependencies":{"svelte":"^5.0.0"},"_npmOperationalInternal":{"tmp":"tmp/svedit_0.1.0_1755612179745_0.6890302065379181","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"name":"svedit","version":"0.1.1","license":"MIT","_id":"svedit@0.1.1","maintainers":[{"name":"michael","email":"michael@letsken.com"}],"dist":{"shasum":"f3fc87fd8541f56205813b1f57a7e549114a40cb","tarball":"https://registry.npmjs.org/svedit/-/svedit-0.1.1.tgz","fileCount":49,"integrity":"sha512-GJckyCTXu1JM0H6HlQDhU1Q2o1xx7AOnKjB6fEj39aPVdksM4GYzdNA3rF1IwNeDCdCwLyRGiEjNKTciE2CXew==","signatures":[{"sig":"MEYCIQDWmwLgzt73yD37M8kX4UHGc0ZTCkeZBEzU4Qj7YwWwzwIhANRkqHaESzuBg6s8KfmEZG6R9i0Mjo8i76U5ZgCWOSBo","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":244518},"type":"module","types":"./dist/index.d.ts","svelte":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","svelte":"./dist/index.js"}},"gitHead":"b6893adf45f884fd431e741b6b2f7fc1c10ef520","scripts":{"dev":"vite dev","lint":"prettier --check . && eslint .","test":"npm run test:unit -- --run","build":"vite build","check":"svelte-kit sync && svelte-check --tsconfig ./jsconfig.json","format":"prettier --write .","prepack":"svelte-kit sync && svelte-package && publint","prepare":"svelte-kit sync || echo ''","preview":"vite preview","test:unit":"vitest","check:watch":"svelte-kit sync && svelte-check --tsconfig ./jsconfig.json --watch"},"_npmUser":{"name":"michael","email":"michael@letsken.com"},"_npmVersion":"10.9.2","description":"Svedit (think Svelte Edit) is a tiny library for building **rich content editors** with Svelte 5. You can model your content in JSON, render it with custom Svelte components, and edit it directly in the layout.","directories":{},"sideEffects":["**/*.css"],"_nodeVersion":"22.17.0","_hasShrinkwrap":false,"devDependencies":{"vite":"^7.0.4","eslint":"^9.18.0","nanoid":"^5.1.5","svelte":"^5.0.0","vitest":"^3.2.3","globals":"^16.0.0","publint":"^0.3.2","prettier":"^3.4.2","@eslint/js":"^9.18.0","svelte-check":"^4.0.0","@sveltejs/kit":"^2.21.2","@eslint/compat":"^1.2.5","@vitest/browser":"^3.2.4","@sveltejs/package":"^2.0.0","eslint-plugin-svelte":"^3.0.0","vitest-browser-svelte":"^1.1.0","@sveltejs/adapter-auto":"^6.0.0","eslint-config-prettier":"^10.0.1","prettier-plugin-svelte":"^3.3.3","@typescript-eslint/parser":"^8.39.1","@sveltejs/vite-plugin-svelte":"^6.0.0","@typescript-eslint/eslint-plugin":"^8.39.1"},"peerDependencies":{"svelte":"^5.0.0"},"_npmOperationalInternal":{"tmp":"tmp/svedit_0.1.1_1755635277133_0.26818138605138064","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"svedit","version":"0.2.0","license":"MIT","_id":"svedit@0.2.0","maintainers":[{"name":"michael","email":"michael@letsken.com"}],"dist":{"shasum":"855407f67ea633b3950291c6d68c64ead24e3b50","tarball":"https://registry.npmjs.org/svedit/-/svedit-0.2.0.tgz","fileCount":49,"integrity":"sha512-UbDCQOIghkXTkxoqI4GhSqJ/CHaNHO90CrxZOndqTRSz4AYb9yCe8s+EjOlXoiLSLyH4rMC1tYi6HKigxXEFug==","signatures":[{"sig":"MEQCICf2X5Qm9nt4sxXyu4YDYurpjc5NGW9HxdwOB0VW6wM8AiBiPNB4kcPGkD/PTZHtLkjL5gw/sIO4BSbggRdhnKjM2g==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":246344},"type":"module","types":"./dist/index.d.ts","svelte":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","svelte":"./dist/index.js"}},"gitHead":"0af9b7298e9e0b739dbabe3629bf8053efcda0f8","scripts":{"dev":"vite dev","lint":"prettier --check . && eslint .","test":"npm run test:unit -- --run","build":"vite build","check":"svelte-kit sync && svelte-check --tsconfig ./jsconfig.json","format":"prettier --write .","prepack":"svelte-kit sync && svelte-package && publint","prepare":"svelte-kit sync || echo ''","preview":"vite preview","test:unit":"vitest","check:watch":"svelte-kit sync && svelte-check --tsconfig ./jsconfig.json --watch"},"_npmUser":{"name":"michael","email":"michael@letsken.com"},"_npmVersion":"10.9.2","description":"Svedit (think Svelte Edit) is a tiny library for building **rich content editors** with Svelte 5. You can model your content in JSON, render it with custom Svelte components, and edit it directly in the layout.","directories":{},"sideEffects":["**/*.css"],"_nodeVersion":"22.17.0","_hasShrinkwrap":false,"devDependencies":{"vite":"^7.0.4","eslint":"^9.18.0","nanoid":"^5.1.5","svelte":"^5.0.0","vitest":"^3.2.3","globals":"^16.0.0","publint":"^0.3.2","prettier":"^3.4.2","@eslint/js":"^9.18.0","svelte-check":"^4.0.0","@sveltejs/kit":"^2.21.2","@eslint/compat":"^1.2.5","@vitest/browser":"^3.2.4","@sveltejs/package":"^2.0.0","eslint-plugin-svelte":"^3.0.0","vitest-browser-svelte":"^1.1.0","@sveltejs/adapter-auto":"^6.0.0","eslint-config-prettier":"^10.0.1","prettier-plugin-svelte":"^3.3.3","@typescript-eslint/parser":"^8.39.1","@sveltejs/vite-plugin-svelte":"^6.0.0","@typescript-eslint/eslint-plugin":"^8.39.1"},"peerDependencies":{"svelte":"^5.0.0"},"_npmOperationalInternal":{"tmp":"tmp/svedit_0.2.0_1755796143224_0.2254599272204938","host":"s3://npm-registry-packages-npm-production"}},"0.3.0":{"name":"svedit","version":"0.3.0","license":"MIT","_id":"svedit@0.3.0","maintainers":[{"name":"michael","email":"michael@letsken.com"}],"dist":{"shasum":"e7802c402e422bf03aed603500f628bd1b91b144","tarball":"https://registry.npmjs.org/svedit/-/svedit-0.3.0.tgz","fileCount":49,"integrity":"sha512-CEP6zrRkevKMTQ758SpfkIkb4o1HmWZlsfHYIRD7WDQucbMo2Zzp0coTeDX4HQkWA6b4D6Rj+dyMJHsreuyJyQ==","signatures":[{"sig":"MEYCIQDKRvEYb3nk6BAr20ywHy33QYx6YnFNqmQZWCtMhvGI9wIhAIrRpVyhFLbNWlUGGbaOHfNLIKHPgy6oO3pkKURIU/l8","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":301803},"type":"module","types":"./dist/index.d.ts","svelte":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","svelte":"./dist/index.js"}},"gitHead":"c233ae26dcb45c6e89e60486c8076469a9b26546","scripts":{"dev":"vite dev","lint":"prettier --check . && eslint .","test":"npm run test:unit -- --run","build":"vite build","check":"svelte-kit sync && svelte-check --tsconfig ./jsconfig.json","format":"prettier --write .","prepack":"svelte-kit sync && svelte-package && publint","prepare":"svelte-kit sync || echo ''","preview":"vite preview","test:unit":"vitest","check:watch":"svelte-kit sync && svelte-check --tsconfig ./jsconfig.json --watch"},"_npmUser":{"name":"michael","email":"michael@letsken.com"},"_npmVersion":"10.9.2","description":"Svedit (think Svelte Edit) is a tiny library for building **rich content editors** with Svelte 5. You can model your content in JSON, render it with custom Svelte components, and edit it directly in the layout.","directories":{},"sideEffects":["**/*.css"],"_nodeVersion":"22.17.0","_hasShrinkwrap":false,"devDependencies":{"vite":"^7.0.4","eslint":"^9.18.0","nanoid":"^5.1.5","svelte":"^5.0.0","vitest":"^3.2.3","globals":"^16.0.0","publint":"^0.3.2","prettier":"^3.4.2","@eslint/js":"^9.18.0","svelte-check":"^4.0.0","@sveltejs/kit":"^2.21.2","@eslint/compat":"^1.2.5","@vitest/browser":"^3.2.4","@sveltejs/package":"^2.0.0","eslint-plugin-svelte":"^3.0.0","vitest-browser-svelte":"^1.1.0","@sveltejs/adapter-auto":"^6.0.0","eslint-config-prettier":"^10.0.1","prettier-plugin-svelte":"^3.3.3","@typescript-eslint/parser":"^8.39.1","@sveltejs/vite-plugin-svelte":"^6.0.0","@typescript-eslint/eslint-plugin":"^8.39.1"},"peerDependencies":{"svelte":"^5.0.0"},"_npmOperationalInternal":{"tmp":"tmp/svedit_0.3.0_1756927137395_0.04937167553506194","host":"s3://npm-registry-packages-npm-production"}},"0.4.0":{"name":"svedit","version":"0.4.0","license":"MIT","_id":"svedit@0.4.0","maintainers":[{"name":"michael","email":"michael@letsken.com"}],"dist":{"shasum":"93c97c822b4c671095ea6105318d4a3527251f77","tarball":"https://registry.npmjs.org/svedit/-/svedit-0.4.0.tgz","fileCount":49,"integrity":"sha512-boUcGnQL3zujE//AtfapREuguKtcivp7UeOZTqpI/F3FOTLy04+HMZ1VUqtNiuhoL8K04zxayEbVo/FPjJV9qw==","signatures":[{"sig":"MEUCIFWlC4R9Az0YT4dN+qGNlkJo/EoRZ0bl30flQ6wekRY1AiEAiaB1MJZuu4PeoJu7jArTfudTkJhbc4XRAS7FRwLQv6Q=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":317041},"type":"module","types":"./dist/index.d.ts","svelte":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","svelte":"./dist/index.js"}},"gitHead":"cc4ecd13778fa6ed987412a50d0b57116fadefe1","scripts":{"dev":"vite dev","lint":"prettier --check . && eslint .","test":"npm run test:unit -- --run","build":"vite build","check":"svelte-kit sync && svelte-check --tsconfig ./jsconfig.json","format":"prettier --write .","prepack":"svelte-kit sync && svelte-package && publint","prepare":"svelte-kit sync || echo ''","preview":"vite preview","test:unit":"vitest","check:watch":"svelte-kit sync && svelte-check --tsconfig ./jsconfig.json --watch"},"_npmUser":{"name":"michael","email":"michael@letsken.com"},"_npmVersion":"10.9.2","description":"Svedit (think Svelte Edit) is a tiny library for building **rich content editors** with Svelte 5. You can model your content in JSON, render it with custom Svelte components, and edit it directly in the layout.","directories":{},"sideEffects":["**/*.css"],"_nodeVersion":"22.17.0","_hasShrinkwrap":false,"devDependencies":{"vite":"^7.0.4","eslint":"^9.18.0","nanoid":"^5.1.5","svelte":"^5.0.0","vitest":"^3.2.3","globals":"^16.0.0","publint":"^0.3.2","prettier":"^3.4.2","@eslint/js":"^9.18.0","svelte-check":"^4.0.0","@sveltejs/kit":"^2.21.2","@eslint/compat":"^1.2.5","@vitest/browser":"^3.2.4","@sveltejs/package":"^2.0.0","eslint-plugin-svelte":"^3.0.0","vitest-browser-svelte":"^1.1.0","@sveltejs/adapter-auto":"^6.0.0","eslint-config-prettier":"^10.0.1","prettier-plugin-svelte":"^3.3.3","@typescript-eslint/parser":"^8.39.1","@sveltejs/vite-plugin-svelte":"^6.0.0","@typescript-eslint/eslint-plugin":"^8.39.1"},"peerDependencies":{"svelte":"^5.0.0"},"_npmOperationalInternal":{"tmp":"tmp/svedit_0.4.0_1757873983172_0.90336592314771","host":"s3://npm-registry-packages-npm-production"}},"0.5.0":{"name":"svedit","version":"0.5.0","license":"MIT","_id":"svedit@0.5.0","maintainers":[{"name":"michael","email":"michael@letsken.com"}],"dist":{"shasum":"52a7868f91da135ed5c5f2b0b9abe4e0b0cb3dfb","tarball":"https://registry.npmjs.org/svedit/-/svedit-0.5.0.tgz","fileCount":49,"integrity":"sha512-/WFmyiV2On3lcHZUGm5FYfFk7mWFF2z90x9DK0qc08QE9bv42seWcl7GptFrVlhmt/StLekONEWrjjhYLEyGLw==","signatures":[{"sig":"MEUCIQD506OpuNEIz8FWOt/dfpTyeQVp8K2p7IbrgCNYqODBGgIgGLyXlk5u9pgHiosXdnr2LO5JNz61oC0yKrygtmjMQDc=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":336955},"type":"module","types":"./dist/index.d.ts","svelte":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","svelte":"./dist/index.js"}},"gitHead":"4d76c9bab195f638d57a69d8500663fc6d971984","scripts":{"dev":"vite dev","lint":"prettier --check . && eslint .","test":"npm run test:unit -- --run","build":"vite build","check":"svelte-kit sync && svelte-check --tsconfig ./jsconfig.json","format":"prettier --write .","prepack":"svelte-kit sync && svelte-package && publint","prepare":"svelte-kit sync || echo ''","preview":"vite preview","test:unit":"vitest","check:watch":"svelte-kit sync && svelte-check --tsconfig ./jsconfig.json --watch"},"_npmUser":{"name":"michael","email":"michael@letsken.com"},"_npmVersion":"10.9.2","description":"Svedit (think Svelte Edit) is a a tiny library for building editable websites in Svelte. You can model your content in JSON, render it with custom Svelte components, and edit it directly in the layout.","directories":{},"sideEffects":["**/*.css"],"_nodeVersion":"22.17.0","_hasShrinkwrap":false,"devDependencies":{"vite":"^7.1.7","eslint":"^9.36.0","nanoid":"^5.1.6","svelte":"^5.39.8","vitest":"^3.2.4","globals":"^16.4.0","publint":"^0.3.13","prettier":"^3.6.2","@eslint/js":"^9.36.0","svelte-check":"^4.3.2","@sveltejs/kit":"^2.43.7","@eslint/compat":"^1.4.0","@vitest/browser":"^3.2.4","@sveltejs/package":"^2.5.4","eslint-plugin-svelte":"^3.12.4","vitest-browser-svelte":"^1.1.0","@sveltejs/adapter-auto":"^6.1.0","eslint-config-prettier":"^10.1.8","prettier-plugin-svelte":"^3.4.0","@typescript-eslint/parser":"^8.45.0","@sveltejs/vite-plugin-svelte":"^6.2.1","@typescript-eslint/eslint-plugin":"^8.45.0"},"peerDependencies":{"svelte":"^5.0.0"},"_npmOperationalInternal":{"tmp":"tmp/svedit_0.5.0_1759349092860_0.2060389526019073","host":"s3://npm-registry-packages-npm-production"}},"0.6.0":{"name":"svedit","version":"0.6.0","license":"MIT","_id":"svedit@0.6.0","maintainers":[{"name":"michael","email":"michael@letsken.com"}],"dist":{"shasum":"87e08ab79352961c2b9bb57e2756b444311001bd","tarball":"https://registry.npmjs.org/svedit/-/svedit-0.6.0.tgz","fileCount":61,"integrity":"sha512-rlfLdnB9azvuUjYALKIXwjHDPBYPWftBdglGV4fEdNKGdp0EcsWHbtuQFOiTXx1LiYOtNmWSK5/MuhI2Kn0/BA==","signatures":[{"sig":"MEUCIQCLA1AANEcJwY5Pawv+DRyLjSonO2+7LnGduRYP3Jhi5gIgYUEDMa2phmZndnP5j5Spjgn+8RqEE/4o4SJ4HXTZx4s=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":397288},"type":"module","types":"./dist/index.d.ts","svelte":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","svelte":"./dist/index.js"}},"gitHead":"7c7d4671f5908949d0461044ce9cf58be99e77e2","scripts":{"dev":"vite dev","lint":"prettier --check . && eslint .","test":"npm run test:unit -- --run","build":"vite build","check":"svelte-kit sync && svelte-check --tsconfig ./jsconfig.json","format":"prettier --write .","prepack":"svelte-kit sync && svelte-package && publint","prepare":"svelte-kit sync || echo ''","preview":"vite preview","test:unit":"vitest","check:watch":"svelte-kit sync && svelte-check --tsconfig ./jsconfig.json --watch"},"_npmUser":{"name":"michael","email":"michael@letsken.com"},"_npmVersion":"11.6.2","description":"Svedit (think Svelte Edit) is a tiny library for building editable websites in Svelte. You can model your content in JSON, render it with custom Svelte components, and (this is the kicker) site owners can **edit their site directly in the layout** — no CM","directories":{},"sideEffects":["**/*.css"],"_nodeVersion":"24.11.1","_hasShrinkwrap":false,"devDependencies":{"vite":"^7.2.2","eslint":"^9.39.1","nanoid":"^5.1.6","svelte":"^5.43.4","vitest":"^4.0.8","globals":"^16.5.0","publint":"^0.3.15","prettier":"^3.6.2","@eslint/js":"^9.39.1","typescript":"^5.9.3","svelte-check":"^4.3.3","@sveltejs/kit":"^2.48.4","@eslint/compat":"^1.4.1","@vitest/browser":"^4.0.8","@sveltejs/package":"^2.5.4","eslint-plugin-svelte":"^3.13.0","vitest-browser-svelte":"^2.0.1","@sveltejs/adapter-auto":"^7.0.0","eslint-config-prettier":"^10.1.8","prettier-plugin-svelte":"^3.4.0","@vitest/browser-preview":"^4.0.8","@typescript-eslint/parser":"^8.46.3","@sveltejs/vite-plugin-svelte":"^6.2.1","@typescript-eslint/eslint-plugin":"^8.46.3"},"peerDependencies":{"svelte":"^5.0.0"},"_npmOperationalInternal":{"tmp":"tmp/svedit_0.6.0_1764281255775_0.9057257546768209","host":"s3://npm-registry-packages-npm-production"}},"0.6.1":{"name":"svedit","version":"0.6.1","license":"MIT","_id":"svedit@0.6.1","maintainers":[{"name":"michael","email":"michael@letsken.com"}],"dist":{"shasum":"7450a29daee187eec81c2daf2bc61adfcf09a655","tarball":"https://registry.npmjs.org/svedit/-/svedit-0.6.1.tgz","fileCount":61,"integrity":"sha512-STxTFI0bBZUNNbbf99rsfVx+IsH8LBnX3YV8m/hNKZwxWkMJ/G3KdQn69bFhXvG96PbpLIqpJAi8zLZkBls3zw==","signatures":[{"sig":"MEYCIQDBkFX4phd8oBXDfwAMR6fztTQTVn/kHw+SyJF6oarzbAIhAIV6oISbtcHLpqSfz/D4xykB/L8LLTb6ZZEtGjxlaNlB","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":396652},"type":"module","types":"./dist/index.d.ts","svelte":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","svelte":"./dist/index.js"}},"gitHead":"3384a8b16306c2fe526a23f40913ac238335b663","scripts":{"dev":"vite dev","lint":"prettier --check . && eslint .","test":"npm run test:unit -- --run","build":"vite build","check":"svelte-kit sync && svelte-check --tsconfig ./jsconfig.json","format":"prettier --write .","prepack":"svelte-kit sync && svelte-package && publint","prepare":"svelte-kit sync || echo ''","preview":"vite preview","test:unit":"vitest","check:watch":"svelte-kit sync && svelte-check --tsconfig ./jsconfig.json --watch"},"_npmUser":{"name":"michael","email":"michael@letsken.com"},"_npmVersion":"11.6.2","description":"Svedit (think Svelte Edit) is a tiny library for building editable websites in Svelte. You can model your content in JSON, render it with custom Svelte components, and (this is the kicker) site owners can **edit their site directly in the layout** — no CM","directories":{},"sideEffects":["**/*.css"],"_nodeVersion":"24.11.1","_hasShrinkwrap":false,"devDependencies":{"vite":"^7.2.2","eslint":"^9.39.1","nanoid":"^5.1.6","svelte":"^5.43.4","vitest":"^4.0.8","globals":"^16.5.0","publint":"^0.3.15","prettier":"^3.6.2","@eslint/js":"^9.39.1","typescript":"^5.9.3","svelte-check":"^4.3.3","@sveltejs/kit":"^2.48.4","@eslint/compat":"^1.4.1","@vitest/browser":"^4.0.8","@sveltejs/package":"^2.5.4","eslint-plugin-svelte":"^3.13.0","vitest-browser-svelte":"^2.0.1","@sveltejs/adapter-auto":"^7.0.0","eslint-config-prettier":"^10.1.8","prettier-plugin-svelte":"^3.4.0","@vitest/browser-preview":"^4.0.8","@typescript-eslint/parser":"^8.46.3","@sveltejs/vite-plugin-svelte":"^6.2.1","@typescript-eslint/eslint-plugin":"^8.46.3"},"peerDependencies":{"svelte":"^5.0.0"},"_npmOperationalInternal":{"tmp":"tmp/svedit_0.6.1_1765901434213_0.6922009941457519","host":"s3://npm-registry-packages-npm-production"}},"0.6.2":{"name":"svedit","version":"0.6.2","license":"MIT","_id":"svedit@0.6.2","maintainers":[{"name":"michael","email":"michael@letsken.com"}],"dist":{"shasum":"53500fe6d56c92261baad70bce5c14b1e98d4f1e","tarball":"https://registry.npmjs.org/svedit/-/svedit-0.6.2.tgz","fileCount":61,"integrity":"sha512-0CqVb16Ua3y6hdkvQ50kc6X8Op07L09r6Z6UnbJyCf1J5Ajb5rpSls6SYTT0wKsiCeuV0IkwqUPdMjKXpjPJOA==","signatures":[{"sig":"MEUCIQCrSL4dR1fLWpaGWdGeLt8cmIZl6ToJGtfBqS6Ow7YsSgIgQuQBZ/mUaSArxyTAYzDh0P2t3GKz+yN0qd4E/b34/x0=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":397606},"type":"module","types":"./dist/index.d.ts","svelte":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","svelte":"./dist/index.js"}},"gitHead":"a642d7781ffde82657870802f20230b3e4f37504","scripts":{"dev":"vite dev","lint":"prettier --check . && eslint .","test":"npm run test:unit -- --run","build":"vite build","check":"svelte-kit sync && svelte-check --tsconfig ./jsconfig.json","format":"prettier --write .","prepack":"svelte-kit sync && svelte-package && publint","prepare":"svelte-kit sync || echo ''","preview":"vite preview","test:unit":"vitest","check:watch":"svelte-kit sync && svelte-check --tsconfig ./jsconfig.json --watch"},"_npmUser":{"name":"michael","email":"michael@letsken.com"},"_npmVersion":"11.6.2","description":"Svedit (think Svelte Edit) is a tiny library for building editable websites in Svelte. You can model your content in JSON, render it with custom Svelte components, and (this is the kicker) site owners can **edit their site directly in the layout** — no CM","directories":{},"sideEffects":["**/*.css"],"_nodeVersion":"24.11.1","_hasShrinkwrap":false,"devDependencies":{"vite":"^7.2.2","eslint":"^9.39.1","nanoid":"^5.1.6","svelte":"^5.43.4","vitest":"^4.0.8","globals":"^16.5.0","publint":"^0.3.15","prettier":"^3.6.2","@eslint/js":"^9.39.1","typescript":"^5.9.3","svelte-check":"^4.3.3","@sveltejs/kit":"^2.48.4","@eslint/compat":"^1.4.1","@vitest/browser":"^4.0.8","@sveltejs/package":"^2.5.4","eslint-plugin-svelte":"^3.13.0","vitest-browser-svelte":"^2.0.1","@sveltejs/adapter-auto":"^7.0.0","eslint-config-prettier":"^10.1.8","prettier-plugin-svelte":"^3.4.0","@vitest/browser-preview":"^4.0.8","@typescript-eslint/parser":"^8.46.3","@sveltejs/vite-plugin-svelte":"^6.2.1","@typescript-eslint/eslint-plugin":"^8.46.3"},"peerDependencies":{"svelte":"^5.0.0"},"_npmOperationalInternal":{"tmp":"tmp/svedit_0.6.2_1769209382552_0.321545658893607","host":"s3://npm-registry-packages-npm-production"}},"0.6.3":{"name":"svedit","version":"0.6.3","license":"MIT","_id":"svedit@0.6.3","maintainers":[{"name":"michael","email":"michael@letsken.com"}],"dist":{"shasum":"0bfe961ff418f9772410d8258614bffd88cceba1","tarball":"https://registry.npmjs.org/svedit/-/svedit-0.6.3.tgz","fileCount":61,"integrity":"sha512-gETax47LBpyvsrwdvYSub0Vw2aXyWdXaWXYHKbyzEL1jLfWQ/x2m/vs5+jUXjRf1FygHTLZywwDbh5jbyPdVyQ==","signatures":[{"sig":"MEYCIQC6smQx/lMRlQyPGV8N8IKb1g1U+s24gkX8b6ry0AaJ3wIhANY8Y/Unz+gXyJ1tYz9G0v/C2mKfhlqwUsrItQ40CIK5","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":397630},"type":"module","types":"./dist/index.d.ts","svelte":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","svelte":"./dist/index.js"}},"gitHead":"4a009a841e9de6c8884c60ff6dc08fadb86e06f5","scripts":{"dev":"vite dev","lint":"prettier --check . && eslint .","test":"npm run test:unit -- --run","build":"vite build","check":"svelte-kit sync && svelte-check --tsconfig ./jsconfig.json","format":"prettier --write .","prepack":"svelte-kit sync && svelte-package && publint","prepare":"svelte-kit sync || echo ''","preview":"vite preview","test:unit":"vitest","check:watch":"svelte-kit sync && svelte-check --tsconfig ./jsconfig.json --watch"},"_npmUser":{"name":"michael","email":"michael@letsken.com"},"_npmVersion":"11.6.2","description":"Svedit (think Svelte Edit) is a tiny library for building editable websites in Svelte. You can model your content in JSON, render it with custom Svelte components, and (this is the kicker) site owners can **edit their site directly in the layout** — no CM","directories":{},"sideEffects":["**/*.css"],"_nodeVersion":"24.11.1","_hasShrinkwrap":false,"devDependencies":{"vite":"^7.2.2","eslint":"^9.39.1","nanoid":"^5.1.6","svelte":"^5.43.4","vitest":"^4.0.8","globals":"^16.5.0","publint":"^0.3.15","prettier":"^3.6.2","@eslint/js":"^9.39.1","typescript":"^5.9.3","svelte-check":"^4.3.3","@sveltejs/kit":"^2.48.4","@eslint/compat":"^1.4.1","@vitest/browser":"^4.0.8","@sveltejs/package":"^2.5.4","eslint-plugin-svelte":"^3.13.0","vitest-browser-svelte":"^2.0.1","@sveltejs/adapter-auto":"^7.0.0","eslint-config-prettier":"^10.1.8","prettier-plugin-svelte":"^3.4.0","@vitest/browser-preview":"^4.0.8","@typescript-eslint/parser":"^8.46.3","@sveltejs/vite-plugin-svelte":"^6.2.1","@typescript-eslint/eslint-plugin":"^8.46.3"},"peerDependencies":{"svelte":"^5.0.0"},"_npmOperationalInternal":{"tmp":"tmp/svedit_0.6.3_1769280669453_0.11305472323462129","host":"s3://npm-registry-packages-npm-production"}},"0.6.4":{"name":"svedit","version":"0.6.4","license":"MIT","_id":"svedit@0.6.4","maintainers":[{"name":"michael","email":"michael@letsken.com"}],"dist":{"shasum":"0ab94e2e48b9148bd6d4362e8f795767e953c93e","tarball":"https://registry.npmjs.org/svedit/-/svedit-0.6.4.tgz","fileCount":61,"integrity":"sha512-QlhSacX8hlD1CwLRMGI003w2Js0T/3H7kouxqGcCiSr9+qukWOVUfFALahW/DJiTRLzxiTKYFOFl49kA0njcEw==","signatures":[{"sig":"MEQCIDYf1fonncB104AugtLo2hLJKyKrFpxIij6MVjPf+N34AiApHP35M8mV7aP608qeelFwvXTr/Ebju/bnSGlHC/0iHQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":398232},"type":"module","types":"./dist/index.d.ts","svelte":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","svelte":"./dist/index.js"}},"gitHead":"a1eec7ddb16ccfba503bf69e5016f17c8bbca24a","scripts":{"dev":"vite dev","lint":"prettier --check . && eslint .","test":"npm run test:unit -- --run","build":"vite build","check":"svelte-kit sync && svelte-check --tsconfig ./jsconfig.json","format":"prettier --write .","prepack":"svelte-kit sync && svelte-package && publint","prepare":"svelte-kit sync || echo ''","preview":"vite preview","test:unit":"vitest","check:watch":"svelte-kit sync && svelte-check --tsconfig ./jsconfig.json --watch"},"_npmUser":{"name":"michael","email":"michael@letsken.com"},"_npmVersion":"11.6.2","description":"Svedit (think Svelte Edit) is a tiny library for building editable websites in Svelte. You can model your content in JSON, render it with custom Svelte components, and (this is the kicker) site owners can **edit their site directly in the layout** — no CM","directories":{},"sideEffects":["**/*.css"],"_nodeVersion":"24.11.1","_hasShrinkwrap":false,"devDependencies":{"vite":"^7.2.2","eslint":"^9.39.1","nanoid":"^5.1.6","svelte":"^5.43.4","vitest":"^4.0.8","globals":"^16.5.0","publint":"^0.3.15","prettier":"^3.6.2","@eslint/js":"^9.39.1","typescript":"^5.9.3","svelte-check":"^4.3.3","@sveltejs/kit":"^2.48.4","@eslint/compat":"^1.4.1","@vitest/browser":"^4.0.8","@sveltejs/package":"^2.5.4","eslint-plugin-svelte":"^3.13.0","vitest-browser-svelte":"^2.0.1","@sveltejs/adapter-auto":"^7.0.0","eslint-config-prettier":"^10.1.8","prettier-plugin-svelte":"^3.4.0","@vitest/browser-preview":"^4.0.8","@typescript-eslint/parser":"^8.46.3","@sveltejs/vite-plugin-svelte":"^6.2.1","@typescript-eslint/eslint-plugin":"^8.46.3"},"peerDependencies":{"svelte":"^5.0.0"},"_npmOperationalInternal":{"tmp":"tmp/svedit_0.6.4_1770498210843_0.11651648921823199","host":"s3://npm-registry-packages-npm-production"}},"0.7.0":{"name":"svedit","version":"0.7.0","license":"MIT","_id":"svedit@0.7.0","maintainers":[{"name":"michael","email":"michael@letsken.com"}],"dist":{"shasum":"a79fbefe49b929001d3672dfb8637d9511c44a45","tarball":"https://registry.npmjs.org/svedit/-/svedit-0.7.0.tgz","fileCount":83,"integrity":"sha512-2d2R0UpJRoqyXmg6lEv4IMSMvo30K3g4w0TCiIPczMVuBAbsbrSy6cAxdUDccOw8vkZ2fIKW8mKrT7y9THScXw==","signatures":[{"sig":"MEUCIG49Ua8tUETx5pTdYNEqgQVGFeHZQ2J5m8uj96kjAK+YAiEAjJIyLpN4sONzvLeiYyUVQ8CzqW7kBe09BzFGL3gdwuQ=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":509661},"type":"module","types":"./dist/index.d.ts","svelte":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","svelte":"./dist/index.js"}},"gitHead":"ed5faf3e6bcec4f4d20a6ca5d40b02f1c6194d73","scripts":{"dev":"vite dev","lint":"prettier --check . && eslint .","test":"npm run test:unit -- --run","build":"vite build","check":"svelte-kit sync && svelte-check --tsconfig ./jsconfig.json","format":"prettier --write .","prepack":"svelte-kit sync && svelte-package && publint","prepare":"svelte-kit sync || echo ''","preview":"vite preview","test:unit":"vitest","check:watch":"svelte-kit sync && svelte-check --tsconfig ./jsconfig.json --watch"},"_npmUser":{"name":"michael","email":"michael@letsken.com"},"_npmVersion":"11.6.2","description":"Svedit (think Svelte Edit) is a tiny library for building editable websites in Svelte. You can model your content in JSON, render it with custom Svelte components, and (this is the kicker) site owners can **edit their site directly in the layout** — no CM","directories":{},"sideEffects":["**/*.css"],"_nodeVersion":"24.11.1","_hasShrinkwrap":false,"devDependencies":{"vite":"^8.0.0","eslint":"^10.0.3","nanoid":"^5.1.6","svelte":"^5.53.12","vitest":"^4.1.0","globals":"^17.4.0","publint":"^0.3.18","prettier":"^3.8.1","@eslint/js":"^10.0.1","typescript":"^5.9.3","svelte-check":"^4.4.5","@sveltejs/kit":"^2.55.0","@eslint/compat":"^2.0.3","@vitest/browser":"^4.1.0","@sveltejs/package":"^2.5.7","eslint-plugin-svelte":"^3.15.1","vitest-browser-svelte":"^2.1.0","@sveltejs/adapter-auto":"^7.0.0","eslint-config-prettier":"^10.1.8","prettier-plugin-svelte":"^3.5.1","@vitest/browser-preview":"^4.1.0","@typescript-eslint/parser":"^8.57.0","@sveltejs/vite-plugin-svelte":"^7.0.0","@typescript-eslint/eslint-plugin":"^8.57.0"},"peerDependencies":{"svelte":"^5.0.0"},"_npmOperationalInternal":{"tmp":"tmp/svedit_0.7.0_1773527457922_0.21877435703523807","host":"s3://npm-registry-packages-npm-production"}},"0.7.1":{"name":"svedit","version":"0.7.1","license":"MIT","_id":"svedit@0.7.1","maintainers":[{"name":"michael","email":"michael@letsken.com"}],"dist":{"shasum":"bb35bb8fee2782c2a708f4d986f16ee5fd97cab1","tarball":"https://registry.npmjs.org/svedit/-/svedit-0.7.1.tgz","fileCount":83,"integrity":"sha512-cKlZ9hROsZGCXQ/CY0WVbpeVdsXTd39Zbo6fUQqWnD853AN3G8oO42XkPp5J8rHlPNhPmO4YhLiPEjvYjOjFgQ==","signatures":[{"sig":"MEQCIGmVs/x4p8p5wjniFbZ0h+O6Ra1hJhCvpTVvHeR+TNhMAiBEX20ioC3i5/HILTSc7yFVT3jTme5Q5Bwtbirh/CY6mg==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":511085},"type":"module","types":"./dist/index.d.ts","svelte":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","svelte":"./dist/index.js"}},"gitHead":"695a7b4cdb44a152a1721e9420268910d2e74a27","scripts":{"dev":"vite dev","lint":"prettier --check . && eslint .","test":"npm run test:unit -- --run","build":"vite build","check":"svelte-kit sync && svelte-check --tsconfig ./jsconfig.json","format":"prettier --write .","prepack":"svelte-kit sync && svelte-package && publint","prepare":"svelte-kit sync || echo ''","preview":"vite preview","test:unit":"vitest","check:watch":"svelte-kit sync && svelte-check --tsconfig ./jsconfig.json --watch"},"_npmUser":{"name":"michael","email":"michael@letsken.com"},"_npmVersion":"11.9.0","description":"Svedit (think Svelte Edit) is a tiny library for building editable websites in Svelte. You can model your content in JSON, render it with custom Svelte components, and (this is the kicker) site owners can **edit their site directly in the layout** — no CM","directories":{},"sideEffects":["**/*.css"],"_nodeVersion":"24.14.0","_hasShrinkwrap":false,"devDependencies":{"vite":"^8.0.0","eslint":"^10.0.3","nanoid":"^5.1.6","svelte":"^5.53.12","vitest":"^4.1.0","globals":"^17.4.0","publint":"^0.3.18","prettier":"^3.8.1","@eslint/js":"^10.0.1","typescript":"^5.9.3","svelte-check":"^4.4.5","@sveltejs/kit":"^2.55.0","@eslint/compat":"^2.0.3","@vitest/browser":"^4.1.0","@sveltejs/package":"^2.5.7","eslint-plugin-svelte":"^3.15.1","vitest-browser-svelte":"^2.1.0","@sveltejs/adapter-auto":"^7.0.0","eslint-config-prettier":"^10.1.8","prettier-plugin-svelte":"^3.5.1","@vitest/browser-preview":"^4.1.0","@typescript-eslint/parser":"^8.57.0","@sveltejs/vite-plugin-svelte":"^7.0.0","@typescript-eslint/eslint-plugin":"^8.57.0"},"peerDependencies":{"svelte":"^5.0.0"},"_npmOperationalInternal":{"tmp":"tmp/svedit_0.7.1_1773876666548_0.6958896484401833","host":"s3://npm-registry-packages-npm-production"}},"0.8.0":{"name":"svedit","version":"0.8.0","license":"MIT","_id":"svedit@0.8.0","maintainers":[{"name":"michael","email":"michael@letsken.com"}],"dist":{"shasum":"632fc1750b9c1aa91e5d4f0a5bda00128e7cf10d","tarball":"https://registry.npmjs.org/svedit/-/svedit-0.8.0.tgz","fileCount":83,"integrity":"sha512-4JgM2fwEGk9hOevdsZKE/5UkElbTip3aZbv0w0HOlxiNTM3gfbCYoPNH10hEHNdzSITn52yO5WiyokCOukGM6A==","signatures":[{"sig":"MEQCIB12s+X83g76EdYNN57v0LykRNwbvovWaIwh+apnpAAKAiAcSqn/vWRwomY/A9SULbqbMgIiQ2CxPKRQQmkJxjNq5w==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":521423},"type":"module","types":"./dist/index.d.ts","svelte":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","svelte":"./dist/index.js"}},"gitHead":"d58b049dfe327051d1b078c1db80d69e77d27fef","scripts":{"dev":"vite dev","lint":"prettier --check . && eslint .","test":"npm run test:unit -- --run","build":"vite build","check":"svelte-kit sync && svelte-check --tsconfig ./jsconfig.json","format":"prettier --write .","prepack":"svelte-kit sync && svelte-package && publint","prepare":"svelte-kit sync || echo ''","preview":"vite preview","test:unit":"vitest","check:watch":"svelte-kit sync && svelte-check --tsconfig ./jsconfig.json --watch"},"_npmUser":{"name":"michael","email":"michael@letsken.com"},"_npmVersion":"11.9.0","description":"Svedit (think Svelte Edit) is a tiny library for building editable websites in Svelte. You can model your content in JSON, render it with custom Svelte components, and (this is the kicker) site owners can **edit their site directly in the layout** — no CM","directories":{},"sideEffects":["**/*.css"],"_nodeVersion":"24.14.0","_hasShrinkwrap":false,"devDependencies":{"vite":"^8.0.0","eslint":"^10.0.3","nanoid":"^5.1.6","svelte":"^5.53.12","vitest":"^4.1.0","globals":"^17.4.0","publint":"^0.3.18","prettier":"^3.8.1","@eslint/js":"^10.0.1","typescript":"^5.9.3","svelte-check":"^4.4.5","@sveltejs/kit":"^2.55.0","@eslint/compat":"^2.0.3","@vitest/browser":"^4.1.0","@sveltejs/package":"^2.5.7","eslint-plugin-svelte":"^3.15.1","vitest-browser-svelte":"^2.1.0","@sveltejs/adapter-auto":"^7.0.0","eslint-config-prettier":"^10.1.8","prettier-plugin-svelte":"^3.5.1","@vitest/browser-preview":"^4.1.0","@typescript-eslint/parser":"^8.57.0","@sveltejs/vite-plugin-svelte":"^7.0.0","@typescript-eslint/eslint-plugin":"^8.57.0"},"peerDependencies":{"svelte":"^5.0.0"},"_npmOperationalInternal":{"tmp":"tmp/svedit_0.8.0_1774386972785_0.9981944546796786","host":"s3://npm-registry-packages-npm-production"}},"0.9.0":{"name":"svedit","version":"0.9.0","license":"MIT","_id":"svedit@0.9.0","maintainers":[{"name":"michael","email":"michael@letsken.com"}],"dist":{"shasum":"16de37d7719ed78ede403918a7d136bb48f96e85","tarball":"https://registry.npmjs.org/svedit/-/svedit-0.9.0.tgz","fileCount":83,"integrity":"sha512-F1fcssrLF1k1bFOacgvWhtMkII2bouYhqciF1KpRBP50FGc8lK4kGzUo0EY4Qv7Ryns6AeK6GEjk+u8IiSDSSw==","signatures":[{"sig":"MEQCIGS+mXGqQZjKN0neXdF/XeCLmmPx5m/Ga69m5EvhAU6wAiB11ziaTxVC5CnVSBmAcnj7sykIqGuYep/cFaj0oE6ASg==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":524389},"type":"module","types":"./dist/index.d.ts","svelte":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","svelte":"./dist/index.js"}},"gitHead":"728e71c132e40a015fe35f3a0a7d58e3b9f3cf07","scripts":{"dev":"vite dev","lint":"prettier --check . && eslint .","test":"npm run test:unit -- --run","build":"vite build","check":"svelte-kit sync && svelte-check --tsconfig ./jsconfig.json","format":"prettier --write .","prepack":"svelte-kit sync && svelte-package && publint","prepare":"svelte-kit sync || echo ''","preview":"vite preview","test:unit":"vitest","check:watch":"svelte-kit sync && svelte-check --tsconfig ./jsconfig.json --watch"},"_npmUser":{"name":"michael","email":"michael@letsken.com"},"_npmVersion":"11.9.0","description":"Svedit (think Svelte Edit) is a tiny library for building editable websites in Svelte. You can model your content in JSON, render it with custom Svelte components, and (this is the kicker) site owners can **edit their site directly in the layout** — no CM","directories":{},"sideEffects":["**/*.css"],"_nodeVersion":"24.14.0","_hasShrinkwrap":false,"devDependencies":{"vite":"^8.0.0","eslint":"^10.0.3","nanoid":"^5.1.6","svelte":"^5.53.12","vitest":"^4.1.0","globals":"^17.4.0","publint":"^0.3.18","prettier":"^3.8.1","@eslint/js":"^10.0.1","typescript":"^5.9.3","svelte-check":"^4.4.5","@sveltejs/kit":"^2.55.0","@eslint/compat":"^2.0.3","@vitest/browser":"^4.1.0","@sveltejs/package":"^2.5.7","eslint-plugin-svelte":"^3.15.1","vitest-browser-svelte":"^2.1.0","@sveltejs/adapter-auto":"^7.0.0","eslint-config-prettier":"^10.1.8","prettier-plugin-svelte":"^3.5.1","@vitest/browser-preview":"^4.1.0","@typescript-eslint/parser":"^8.57.0","@sveltejs/vite-plugin-svelte":"^7.0.0","@typescript-eslint/eslint-plugin":"^8.57.0"},"peerDependencies":{"svelte":"^5.0.0"},"_npmOperationalInternal":{"tmp":"tmp/svedit_0.9.0_1774975349297_0.1854251381915788","host":"s3://npm-registry-packages-npm-production"}},"0.10.0":{"name":"svedit","version":"0.10.0","license":"MIT","_id":"svedit@0.10.0","maintainers":[{"name":"michael","email":"michael@letsken.com"}],"dist":{"shasum":"67e29b6ff6aee0eb144a3bf31a91de52f6709c57","tarball":"https://registry.npmjs.org/svedit/-/svedit-0.10.0.tgz","fileCount":85,"integrity":"sha512-fGSAnPEy221QdtEwZkotIFHsfQmWGxfT9Pm7qEt5Z72ABpNnMBP4/iiDt9d/JW+oglj+bvE4Tr9Zmpq1h/mq9A==","signatures":[{"sig":"MEUCIQD5NriwiAZt4oWj+bt9Ck5tYdrEjC3yBKeOVj0snorihQIgFGtY/qrn6QCQPvVyvRtc4v5KJUEdXBavDjunNbfyIik=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":630090},"type":"module","types":"./dist/index.d.ts","svelte":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","svelte":"./dist/index.js"}},"gitHead":"74baeaa7a3669ce4345bce92a6485c0975758ea9","scripts":{"dev":"vite dev","lint":"prettier --check . && eslint .","test":"npm run test:unit -- --run","build":"vite build","check":"svelte-kit sync && svelte-check --tsconfig ./jsconfig.json","format":"prettier --write .","prepack":"svelte-kit sync && svelte-package && publint","prepare":"svelte-kit sync || echo ''","preview":"vite preview","test:unit":"vitest","check:watch":"svelte-kit sync && svelte-check --tsconfig ./jsconfig.json --watch"},"_npmUser":{"name":"michael","email":"michael@letsken.com"},"_npmVersion":"11.9.0","description":"Svedit (think Svelte Edit) is a tiny library for building editable websites in Svelte. Model content in JSON, render it with custom components, and let site owners **edit directly in the layout** — no CMS needed.","directories":{},"sideEffects":["**/*.css"],"_nodeVersion":"24.14.0","_hasShrinkwrap":false,"devDependencies":{"vite":"^8.0.0","eslint":"^10.0.3","nanoid":"^5.1.6","svelte":"^5.53.12","vitest":"^4.1.0","globals":"^17.4.0","publint":"^0.3.18","prettier":"^3.8.1","@eslint/js":"^10.0.1","typescript":"^5.9.3","@types/node":"^25.6.0","svelte-check":"^4.4.5","@sveltejs/kit":"^2.55.0","@eslint/compat":"^2.0.3","@vitest/browser":"^4.1.0","@sveltejs/package":"^2.5.7","eslint-plugin-svelte":"^3.15.1","vitest-browser-svelte":"^2.1.0","@sveltejs/adapter-auto":"^7.0.0","eslint-config-prettier":"^10.1.8","prettier-plugin-svelte":"^3.5.1","@vitest/browser-preview":"^4.1.0","@typescript-eslint/parser":"^8.57.0","@sveltejs/vite-plugin-svelte":"^7.0.0","@typescript-eslint/eslint-plugin":"^8.57.0"},"peerDependencies":{"svelte":"^5.0.0"},"_npmOperationalInternal":{"tmp":"tmp/svedit_0.10.0_1779994602108_0.3998699296608499","host":"s3://npm-registry-packages-npm-production"}},"0.11.0":{"name":"svedit","version":"0.11.0","license":"MIT","_id":"svedit@0.11.0","maintainers":[{"name":"michael","email":"michael@letsken.com"}],"dist":{"shasum":"d8a5cdd3a8b3b109046f23e18b0aac196dfb6406","tarball":"https://registry.npmjs.org/svedit/-/svedit-0.11.0.tgz","fileCount":89,"integrity":"sha512-YIWkzx25GmPyNppkTlQkPeimZJyjLAoz5pIoZKozxdTvMLm1Syjcpx1NFRl0ry+bu/L0/0VyVVZ+628MenGCMA==","signatures":[{"sig":"MEYCIQCZ8A1UgP5po1FnNuNc6URZK4HhngQrCDYQuB8GbgRh3wIhAOf+gDjdnbY6pQvvrfESjIhNjUVxb4rU9wFAhZo5skh1","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":650130},"type":"module","types":"./dist/index.d.ts","svelte":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","svelte":"./dist/index.js"}},"gitHead":"4ab897ddd7f966825e379d5760c3a5e24d69e7b5","scripts":{"dev":"vite dev","lint":"prettier --check . && eslint .","test":"npm run test:unit -- --run","build":"vite build","check":"svelte-kit sync && svelte-check --tsconfig ./jsconfig.json","format":"prettier --write .","prepack":"svelte-kit sync && svelte-package && publint","prepare":"svelte-kit sync || echo ''","preview":"vite preview","test:unit":"vitest","check:watch":"svelte-kit sync && svelte-check --tsconfig ./jsconfig.json --watch"},"_npmUser":{"name":"michael","email":"michael@letsken.com"},"_npmVersion":"11.9.0","description":"Svedit (think Svelte Edit) is a tiny library for building editable websites in Svelte. Model content in JSON, render it with custom components, and let site owners **edit directly in the layout** — no CMS needed.","directories":{},"sideEffects":["**/*.css"],"_nodeVersion":"24.14.0","_hasShrinkwrap":false,"devDependencies":{"vite":"^8.0.0","eslint":"^10.0.3","nanoid":"^5.1.6","svelte":"^5.53.12","vitest":"^4.1.0","globals":"^17.4.0","publint":"^0.3.18","prettier":"^3.8.1","@eslint/js":"^10.0.1","typescript":"^5.9.3","@types/node":"^25.6.0","svelte-check":"^4.4.5","@sveltejs/kit":"^2.55.0","@eslint/compat":"^2.0.3","@vitest/browser":"^4.1.0","@sveltejs/package":"^2.5.7","eslint-plugin-svelte":"^3.15.1","vitest-browser-svelte":"^2.1.0","@sveltejs/adapter-auto":"^7.0.0","eslint-config-prettier":"^10.1.8","prettier-plugin-svelte":"^3.5.1","@vitest/browser-preview":"^4.1.0","@typescript-eslint/parser":"^8.57.0","@sveltejs/vite-plugin-svelte":"^7.0.0","@typescript-eslint/eslint-plugin":"^8.57.0"},"peerDependencies":{"svelte":"^5.0.0"},"_npmOperationalInternal":{"tmp":"tmp/svedit_0.11.0_1781784527611_0.8775974441555288","host":"s3://npm-registry-packages-npm-production"}},"0.11.1":{"name":"svedit","version":"0.11.1","license":"MIT","_id":"svedit@0.11.1","maintainers":[{"name":"michael","email":"michael@letsken.com"}],"dist":{"shasum":"f9fc05ee6d3468e67eec47356abdcd84e201fc4f","tarball":"https://registry.npmjs.org/svedit/-/svedit-0.11.1.tgz","fileCount":89,"integrity":"sha512-aETNSJkICdj/IUoyPBZvpNy8BdK5oXcobeHFmM73A+vH0m9g2laW7jK7grxEBftVxVAO8qUkjdkc9G3/T9RzgQ==","signatures":[{"sig":"MEYCIQDk/AGT/nz+5X+sVf+Av7Q48BWyijaHw4oyOCk1oCRdrAIhAO0lor5CLCFLzlMB/lZFAeAWEFlt7OOP2M59OTePCK4d","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":652372},"type":"module","types":"./dist/index.d.ts","svelte":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","svelte":"./dist/index.js"}},"gitHead":"1ffe36d7871d127fa4af931c23875a94c5b69309","scripts":{"dev":"vite dev","lint":"prettier --check . && eslint .","test":"npm run test:unit -- --run","build":"vite build","check":"svelte-kit sync && svelte-check --tsconfig ./jsconfig.json","format":"prettier --write .","prepack":"svelte-kit sync && svelte-package && publint","prepare":"svelte-kit sync || echo ''","preview":"vite preview","test:unit":"vitest","check:watch":"svelte-kit sync && svelte-check --tsconfig ./jsconfig.json --watch"},"_npmUser":{"name":"michael","email":"michael@letsken.com"},"_npmVersion":"11.9.0","description":"Svedit (think Svelte Edit) is a tiny library for building editable websites in Svelte. Model content in JSON, render it with custom components, and let site owners **edit directly in the layout** — no CMS needed.","directories":{},"sideEffects":["**/*.css"],"_nodeVersion":"24.14.0","_hasShrinkwrap":false,"devDependencies":{"vite":"^8.0.0","eslint":"^10.0.3","nanoid":"^5.1.6","svelte":"^5.53.12","vitest":"^4.1.0","globals":"^17.4.0","publint":"^0.3.18","prettier":"^3.8.1","@eslint/js":"^10.0.1","typescript":"^5.9.3","@types/node":"^25.6.0","svelte-check":"^4.4.5","@sveltejs/kit":"^2.55.0","@eslint/compat":"^2.0.3","@vitest/browser":"^4.1.0","@sveltejs/package":"^2.5.7","eslint-plugin-svelte":"^3.15.1","vitest-browser-svelte":"^2.1.0","@sveltejs/adapter-auto":"^7.0.0","eslint-config-prettier":"^10.1.8","prettier-plugin-svelte":"^3.5.1","@vitest/browser-preview":"^4.1.0","@typescript-eslint/parser":"^8.57.0","@sveltejs/vite-plugin-svelte":"^7.0.0","@typescript-eslint/eslint-plugin":"^8.57.0"},"peerDependencies":{"svelte":"^5.0.0"},"_npmOperationalInternal":{"tmp":"tmp/svedit_0.11.1_1782719462498_0.24202932417221423","host":"s3://npm-registry-packages-npm-production"}},"0.12.0":{"name":"svedit","version":"0.12.0","license":"MIT","_id":"svedit@0.12.0","maintainers":[{"name":"michael","email":"michael@letsken.com"}],"dist":{"shasum":"886d15b811d18ef855655f83cd5c4a8b0d26f3e7","tarball":"https://registry.npmjs.org/svedit/-/svedit-0.12.0.tgz","fileCount":89,"integrity":"sha512-I6niDShyOdN5Npv8azgqWhRx2bECC/P6/5eloP+2GPXOKXnpCnIR3P3M9AHvcF1uh6ln6ZBinXFe79S8M/quSA==","signatures":[{"sig":"MEUCIDubNG4D9yUabINkMa7eZJ/Tq3Xw2z1iigQ/HoBr8incAiEAtzGJLssfLCI7U455Zq8DspkOEPj/XnT0rCwX02vLFXg=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":740034},"type":"module","types":"./dist/index.d.ts","svelte":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","svelte":"./dist/index.js"}},"gitHead":"f4d35f1933cd409e31a43ddd9e05a2b33fbf164a","scripts":{"dev":"vite dev","lint":"prettier --check . && eslint .","test":"npm run test:unit -- --run","build":"vite build","check":"svelte-kit sync && svelte-check --tsconfig ./jsconfig.json","format":"prettier --write .","prepack":"svelte-kit sync && svelte-package && publint","prepare":"svelte-kit sync || echo ''","preview":"vite preview","test:unit":"vitest","check:watch":"svelte-kit sync && svelte-check --tsconfig ./jsconfig.json --watch"},"_npmUser":{"name":"michael","email":"michael@letsken.com"},"_npmVersion":"11.18.0","description":"Svedit (think Svelte Edit) is a tiny library for building editable websites in Svelte. Model content in JSON, render it with custom components, and let site owners **edit directly in the layout** — no CMS needed.","directories":{},"sideEffects":["**/*.css"],"_nodeVersion":"24.14.0","allowScripts":{"fsevents@2.3.3":true},"_hasShrinkwrap":false,"devDependencies":{"vite":"^8.0.0","eslint":"^10.0.3","nanoid":"^5.1.6","svelte":"^5.53.12","vitest":"^4.1.0","globals":"^17.4.0","publint":"^0.3.18","prettier":"^3.8.1","@eslint/js":"^10.0.1","typescript":"^5.9.3","@types/node":"^25.6.0","svelte-check":"^4.4.5","@sveltejs/kit":"^2.55.0","@eslint/compat":"^2.0.3","@vitest/browser":"^4.1.0","@sveltejs/package":"^2.5.7","eslint-plugin-svelte":"^3.15.1","vitest-browser-svelte":"^2.1.0","@sveltejs/adapter-auto":"^7.0.0","eslint-config-prettier":"^10.1.8","prettier-plugin-svelte":"^3.5.1","@vitest/browser-preview":"^4.1.0","@typescript-eslint/parser":"^8.57.0","@sveltejs/vite-plugin-svelte":"^7.0.0","@typescript-eslint/eslint-plugin":"^8.57.0"},"peerDependencies":{"svelte":"^5.0.0"},"_npmOperationalInternal":{"tmp":"tmp/svedit_0.12.0_1783530440614_0.8058782493418131","host":"s3://npm-registry-packages-npm-production"}},"0.13.0":{"name":"svedit","version":"0.13.0","license":"MIT","_id":"svedit@0.13.0","maintainers":[{"name":"michael","email":"michael@letsken.com"}],"dist":{"shasum":"4884b8c47ad4896ab1ead6fb94146ce5f171a1fc","tarball":"https://registry.npmjs.org/svedit/-/svedit-0.13.0.tgz","fileCount":91,"integrity":"sha512-CNlo6Biob6qP5GjpEWh8u/Jv0HjBmX0OG5+i5BkBsg/bdwQcZma/kWH3oZYxEVvK4vx5n9OLE1m7ZpOofc9tZA==","signatures":[{"sig":"MEYCIQCdwpZljx+iO/Kukf2kulUWOGVc9/RwYqjKgcq9pI/eAwIhAMTt9h+4mM77fLibL4taBzT5Cz8YcGydxqSAI/d55ZsK","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":777375},"type":"module","types":"./dist/index.d.ts","svelte":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","svelte":"./dist/index.js"}},"scripts":{"dev":"vite dev","lint":"prettier --check . && eslint .","test":"pnpm test:unit --run","build":"vite build","check":"svelte-kit sync && svelte-check --tsconfig ./tsconfig.json","format":"prettier --write .","preview":"vite preview","test:unit":"vitest","check:watch":"svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch","check:strict":"svelte-kit sync && svelte-check --tsconfig ./tsconfig.strict.json"},"_npmUser":{"name":"michael","email":"michael@letsken.com"},"directories":{},"sideEffects":["**/*.css"],"_nodeVersion":"24.14.0","allowScripts":{"fsevents@2.3.3":true},"_hasShrinkwrap":false,"devDependencies":{"vite":"^8.0.0","eslint":"^10.0.3","nanoid":"^5.1.6","svelte":"^5.53.12","vitest":"^4.1.0","globals":"^17.4.0","publint":"^0.3.18","prettier":"^3.8.1","@eslint/js":"^10.0.1","typescript":"^5.9.3","@types/node":"^25.6.0","svelte-check":"^4.4.5","@sveltejs/kit":"^2.55.0","@eslint/compat":"^2.0.3","@vitest/browser":"^4.1.0","@sveltejs/package":"^2.5.7","eslint-plugin-svelte":"^3.15.1","vitest-browser-svelte":"^2.1.0","@sveltejs/adapter-auto":"^7.0.0","eslint-config-prettier":"^10.1.8","prettier-plugin-svelte":"^3.5.1","@vitest/browser-preview":"^4.1.0","@typescript-eslint/parser":"^8.57.0","@sveltejs/vite-plugin-svelte":"^7.0.0","@typescript-eslint/eslint-plugin":"^8.57.0"},"peerDependencies":{"svelte":"^5.0.0"},"_npmOperationalInternal":{"tmp":"tmp/svedit_0.13.0_1784763087350_0.4526512650154053","host":"s3://npm-registry-packages-npm-production"}},"0.14.0":{"name":"svedit","version":"0.14.0","license":"MIT","_id":"svedit@0.14.0","maintainers":[{"name":"michael","email":"michael@letsken.com"}],"homepage":"https://svedit.dev","dist":{"shasum":"09b7d161869ed1c7528f03ec988096c21647f271","tarball":"https://registry.npmjs.org/svedit/-/svedit-0.14.0.tgz","fileCount":91,"integrity":"sha512-pY/s8nkkaFRWToWG4l+7EqIYRGK+Fw3rJHsZR6vejRgXZeY8x4YsY4a4/6yvXt96YRJcC/Ga/WJTUvgU4P1DPA==","signatures":[{"sig":"MEUCIQDBk6BjUMWC7ydp1ryxTi0Y0n6VAA2ctZPCK/jZ52BGBAIgNP/VAuqKbJVdeMLaMRD31gHCU2Q6x3vPgwN/3dYMkao=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":781189},"type":"module","types":"./dist/index.d.ts","svelte":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","svelte":"./dist/index.js"}},"scripts":{"dev":"vite dev","lint":"prettier --check . && eslint .","test":"pnpm test:unit --run","build":"vite build","check":"svelte-kit sync && svelte-check --tsconfig ./tsconfig.json","format":"prettier --write .","preview":"vite preview","test:unit":"vitest","check:watch":"svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch","check:strict":"svelte-kit sync && svelte-check --tsconfig ./tsconfig.strict.json"},"_npmUser":{"name":"michael","email":"michael@letsken.com"},"directories":{},"sideEffects":["**/*.css"],"_nodeVersion":"26.0.0","allowScripts":{"fsevents@2.3.3":true},"_hasShrinkwrap":false,"devDependencies":{"vite":"^8.0.0","eslint":"^10.0.3","nanoid":"^5.1.6","svelte":"^5.53.12","vitest":"^4.1.0","globals":"^17.4.0","publint":"^0.3.18","prettier":"^3.8.1","@eslint/js":"^10.0.1","typescript":"^5.9.3","@types/node":"^25.6.0","svelte-check":"^4.4.5","@sveltejs/kit":"^2.55.0","@eslint/compat":"^2.0.3","@vitest/browser":"^4.1.0","@sveltejs/package":"^2.5.7","eslint-plugin-svelte":"^3.15.1","vitest-browser-svelte":"^2.1.0","@sveltejs/adapter-auto":"^7.0.0","eslint-config-prettier":"^10.1.8","prettier-plugin-svelte":"^3.5.1","@vitest/browser-preview":"^4.1.0","@typescript-eslint/parser":"^8.57.0","@sveltejs/vite-plugin-svelte":"^7.0.0","@typescript-eslint/eslint-plugin":"^8.57.0"},"peerDependencies":{"svelte":"^5.0.0"},"_npmOperationalInternal":{"tmp":"tmp/svedit_0.14.0_1787582156559_0.6483371553696189","host":"s3://npm-registry-packages-npm-production"}},"0.15.0":{"name":"svedit","version":"0.15.0","license":"MIT","_id":"svedit@0.15.0","maintainers":[{"name":"michael","email":"michael@letsken.com"}],"homepage":"https://svedit.dev","dist":{"shasum":"0007f1fc34381a33f74cb55685038c04b967312e","tarball":"https://registry.npmjs.org/svedit/-/svedit-0.15.0.tgz","fileCount":103,"integrity":"sha512-26sIJuqKv21DNzTwjO1u98OnGn7PwAYNqzXh8d8K5GNC0gl8TC+uF3RPxR1dCow+hH5L791+kQwH6liME3BJDw==","signatures":[{"sig":"MEQCIG3ic5m1zLdjek8yaSKsqstTwBh1H3oqHPC0F7MvydGgAiAuvlPrEQ8N4IvlpBSnBnjB1tdWRtWBi2xH0mgg7Oe1Ew==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"sig":"MEUCIQCc2GGV2EEzC7NPth8PP9rQNiAIl8GwvfWo9+xcuQe9EQIgKhwHrU68poHOeCWHmYrBHzKmHLSLVq7UZ5XSnXdzxyU=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":827194},"type":"module","types":"./dist/index.d.ts","svelte":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","svelte":"./dist/index.js"}},"imports":{"#lib":"./src/lib/index.js","#lib/*":"./src/lib/*"},"scripts":{"dev":"vite dev","lint":"prettier --check . && eslint .","test":"pnpm test:unit --run","build":"vite build","check":"svelte-kit sync && svelte-check --tsconfig ./tsconfig.json","format":"prettier --write .","preview":"vite preview","test:unit":"vitest","check:watch":"svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch","check:strict":"svelte-kit sync && svelte-check --tsconfig ./tsconfig.strict.json"},"_npmUser":{"name":"michael","email":"michael@letsken.com"},"directories":{},"sideEffects":["**/*.css"],"_nodeVersion":"26.0.0","_hasShrinkwrap":false,"devDependencies":{"vite":"^8.3.2","eslint":"^10.11.0","nanoid":"^6.0.1","svelte":"^5.57.1","vitest":"^5.0.3","globals":"^17.13.0","publint":"^0.3.25","prettier":"^3.9.9","@eslint/js":"^10.0.1","typescript":"^6.0.3","@types/node":"^26.6.4","svelte-check":"^4.7.6","@sveltejs/kit":"^3.0.0","@eslint/compat":"^2.1.1","@sveltejs/package":"^3.0.0","eslint-plugin-svelte":"^3.23.0","@sveltejs/load-config":"^0.2.3","vitest-browser-svelte":"^3.1.0","eslint-config-prettier":"^10.1.8","prettier-plugin-svelte":"^4.1.1","@vitest/browser-preview":"^5.0.3","@sveltejs/adapter-static":"^4.0.0","@typescript-eslint/parser":"^8.71.0","@sveltejs/vite-plugin-svelte":"^7.3.1","@typescript-eslint/eslint-plugin":"^8.71.0"},"peerDependencies":{"svelte":"^5.0.0"},"_npmOperationalInternal":{"tmp":"tmp/svedit_0.15.0_1790902759287_0.05914949368629263","host":"s3://npm-registry-packages-npm-production"}},"0.15.1":{"name":"svedit","version":"0.15.1","license":"MIT","_id":"svedit@0.15.1","maintainers":[{"name":"michael","email":"michael@letsken.com"}],"homepage":"https://svedit.dev","dist":{"shasum":"7c8f05578328a6d55302615871a28b59e677a11a","tarball":"https://registry.npmjs.org/svedit/-/svedit-0.15.1.tgz","fileCount":103,"integrity":"sha512-bPEVDg6h9Yr9iG4Pr0L47HFyjDH3TwSZjS51YIeeQmM55JMdQIG9ILtXifurQv+dM/fDZ60odWNA02prChnjKQ==","signatures":[{"sig":"MEQCIA+RyC6dTfpFp2BPKFY4GrZbnsw6hHK/zv12Pj4jCrCMAiAE8FoCZ72vdfTizbo8NXWRwLAeIOJDgnD0MP+aBccHWA==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"sig":"MEUCIQDRBd/kQzoqrUAOmtsRDgeB23zvlDzFgVt6eP5uNEJftgIgH+fAUOuzyGBHbJJ1HPSGu2NCQAUAqe2HQcY+KrmA+EI=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":829731},"type":"module","types":"./dist/index.d.ts","svelte":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","svelte":"./dist/index.js"}},"imports":{"#lib":"./src/lib/index.js","#lib/*":"./src/lib/*"},"scripts":{"dev":"vite dev","lint":"prettier --check . && eslint .","test":"pnpm test:unit --run","build":"vite build","check":"svelte-kit sync && svelte-check --tsconfig ./tsconfig.json","format":"prettier --write .","preview":"vite preview","test:unit":"vitest","check:watch":"svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch","check:strict":"svelte-kit sync && svelte-check --tsconfig ./tsconfig.strict.json"},"_npmUser":{"name":"michael","email":"michael@letsken.com"},"directories":{},"sideEffects":["**/*.css"],"_hasShrinkwrap":false,"devDependencies":{"vite":"^8.3.2","eslint":"^10.11.0","nanoid":"^6.0.1","svelte":"^5.57.1","vitest":"^5.0.3","globals":"^17.13.0","publint":"^0.3.25","prettier":"^3.9.9","@eslint/js":"^10.0.1","typescript":"^6.0.3","@types/node":"^26.6.4","svelte-check":"^4.7.6","@sveltejs/kit":"^3.0.0","@eslint/compat":"^2.1.1","@sveltejs/package":"^3.0.0","eslint-plugin-svelte":"^3.23.0","@sveltejs/load-config":"^0.2.3","vitest-browser-svelte":"^3.1.0","eslint-config-prettier":"^10.1.8","prettier-plugin-svelte":"^4.1.1","@vitest/browser-preview":"^5.0.3","@sveltejs/adapter-static":"^4.0.0","@typescript-eslint/parser":"^8.71.0","@sveltejs/vite-plugin-svelte":"^7.3.1","@typescript-eslint/eslint-plugin":"^8.71.0"},"peerDependencies":{"svelte":"^5.0.0"},"_npmOperationalInternal":{"tmp":"tmp/svedit_0.15.1_1790954206645_0.6228477689435525","host":"s3://npm-registry-packages-npm-production"}},"0.15.2":{"_id":"svedit@0.15.2","dist":{"shasum":"092c88c4e9e7e28d6abdd44bcb5b2d3b4df9ed6e","tarball":"https://registry.npmjs.org/svedit/-/svedit-0.15.2.tgz","fileCount":103,"integrity":"sha512-TyLBCG+frvENnfR9dEzm0epv3d6n7rgTdC7iFJ2JChHxdpHjkStuibtMn7f1qSco+Mv879mhaHiIWWsDj0gs2A==","signatures":[{"sig":"MEQCIDP7ewjUbBCy1+mk95ExN1P6QNxemXWvbHiD73y/zLdwAiAVsAZXv7+FKRpvwdO3u8GSwKD9208aP/G1fxUpp2NLWA==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIFOd3lM802H0nkGbJL1aUfWr94sGPbBW06kleBSsNgSrAiEA6ebCzbHJ9I1sGiS2ADta/safVurjFCs8Jghwx5hMX28="}],"unpackedSize":832274},"name":"svedit","type":"module","types":"./dist/index.d.ts","svelte":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","svelte":"./dist/index.js"}},"imports":{"#lib":"./src/lib/index.js","#lib/*":"./src/lib/*"},"license":"MIT","scripts":{"dev":"vite dev","lint":"prettier --check . && eslint .","test":"pnpm test:unit --run","build":"vite build","check":"svelte-kit sync && svelte-check --tsconfig ./tsconfig.json","format":"prettier --write .","preview":"vite preview","test:unit":"vitest","check:watch":"svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch","check:strict":"svelte-kit sync && svelte-check --tsconfig ./tsconfig.strict.json"},"version":"0.15.2","_npmUser":{"name":"michael","email":"michael@letsken.com"},"homepage":"https://svedit.dev","repository":{"url":"git+https://github.com/michael/svedit.git","type":"git"},"description":"Structured, full-canvas editing for Svelte","directories":{},"maintainers":[{"name":"michael","email":"michael@letsken.com"}],"sideEffects":["**/*.css"],"_hasShrinkwrap":false,"devDependencies":{"vite":"^8.3.5","eslint":"^10.13.0","nanoid":"^6.0.2","svelte":"^5.57.3","vitest":"^5.0.5","globals":"^17.13.0","publint":"^0.3.25","prettier":"^3.9.10","@eslint/js":"^10.0.1","typescript":"^6.0.3","@types/node":"^26.6.6","svelte-check":"^4.7.7","@sveltejs/kit":"^3.1.0","@eslint/compat":"^2.1.1","@sveltejs/package":"^3.0.1","eslint-plugin-svelte":"^3.23.1","@sveltejs/load-config":"^0.2.3","vitest-browser-svelte":"^3.1.0","eslint-config-prettier":"^10.1.8","prettier-plugin-svelte":"^4.1.1","@vitest/browser-preview":"^5.0.5","@sveltejs/adapter-static":"^4.0.0","@typescript-eslint/parser":"^8.72.1","@sveltejs/vite-plugin-svelte":"^7.3.1","@typescript-eslint/eslint-plugin":"^8.72.1"},"peerDependencies":{"svelte":"^5.0.0"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/svedit_0.15.2_1793376869327_0.6835578815074657"}}},"time":{"created":"2025-08-19T14:02:59.744Z","modified":"2026-10-30T16:14:29.626Z","0.1.0":"2025-08-19T14:02:59.932Z","0.1.1":"2025-08-19T20:27:57.317Z","0.2.0":"2025-08-21T17:09:03.459Z","0.3.0":"2025-09-03T19:18:57.584Z","0.4.0":"2025-09-14T18:19:43.342Z","0.5.0":"2025-10-01T20:04:53.045Z","0.6.0":"2025-11-27T22:07:35.982Z","0.6.1":"2025-12-16T16:10:34.406Z","0.6.2":"2026-01-23T23:03:02.716Z","0.6.3":"2026-01-24T18:51:09.612Z","0.6.4":"2026-02-07T21:03:31.009Z","0.7.0":"2026-03-14T22:30:58.068Z","0.7.1":"2026-03-18T23:31:06.709Z","0.8.0":"2026-03-24T21:16:12.935Z","0.9.0":"2026-03-31T16:42:29.486Z","0.10.0":"2026-05-28T18:56:42.266Z","0.11.0":"2026-06-18T12:08:47.758Z","0.11.1":"2026-06-29T07:51:02.634Z","0.12.0":"2026-07-08T17:07:20.836Z","0.13.0":"2026-07-22T23:31:27.540Z","0.14.0":"2026-08-24T14:35:56.714Z","0.15.0":"2026-10-02T00:59:19.385Z","0.15.1":"2026-10-02T15:16:46.757Z","0.15.2":"2026-10-30T16:14:29.480Z"},"license":"MIT","homepage":"https://svedit.dev","maintainers":[{"name":"michael","email":"michael@letsken.com"}],"readme":"# Svedit\n\nSvedit is Editing for Svelte. It offers structured, full-canvas content editing. Model content in JSON, render it with custom components, and **edit it directly in the layout**.\n\n- Try the [Svedit demo](https://svedit.dev) — watch the debug output at the bottom of the page\n- Try [Editable](https://editable.website) — build Svelte websites you can edit on the page\n\n## Why Svedit?\n\nBecause Svelte‘s reactivity system is **the perfect fit** for building super-lightweight content editing experiences. In fact, they're so lightweight, **your content is your editor** — no context switching between a backend and the live site. Svedit just gives you the gluing pieces around **defining a custom document model** and **mapping DOM selections** to the internal model and vice versa.\n\n## Getting started\n\nThe fastest way to get started is to clone the `hello-svedit` template and turn it into your own project:\n\n```bash\ngit clone https://github.com/michael/hello-svedit\ncd hello-svedit\npnpm install\npnpm dev\n```\n\nNow make it your own. The next thing you probably want to do is define your own [node types](./src/routes/demo_schema.ts), add a [Toolbar](./src/routes/components/Toolbar.svelte), and render custom [Overlays](./src/routes/components/Overlays.svelte). For that just get inspired by the [Svedit demo code](./src/routes).\n\nYou can also install Svedit into an existing SvelteKit project with `pnpm add svedit`, but you'll need to set up the session, schema, config, and components yourself. See the [hello-svedit repo](https://github.com/michael/hello-svedit) or this repo's [`src/routes`](./src/routes) for reference.\n\n## Principles\n\n**Simplicity over completeness:** Svedit doesn't guess what your app needs or offer ready-made blocks. Instead, we keep the core lean and provide carefully crafted examples showing how to build anything on top — without compromising flexibility.\n\n**White-box library:** We expose the internals of the library to allow you to customize and extend it to your needs. That means a little bit more work upfront, but in return lets you control \"everything\" — the toolbar, the overlays, or how fast the node caret blinks.\n\n**Chromeless canvas:** Svedit keeps the editing canvas chromeless, meaning there are no UI elements like toolbars or menus mingled with the content. You can interact with text directly, but everything else happens via tools shown in separate overlays or in the fixed toolbar.\n\n**Native-first:** Svedit favors standardized native browser solutions over custom ones whenever possible — using browser-based rendering and the browser's selection APIs underneath, rather than reimplementing them (e.g. by building on [pretext](https://github.com/chenglou/pretext)). When we hit browser bugs, we try to get them fixed upstream (in collaboration with the W3C editing working group). Workarounds we add are intended to be temporary, until the underlying issue is fixed by browser vendors. And where we believe there's a better way to solve a problem than what's currently specified, we lobby for it to become a new (or improved) web standard.\n\n## How it works\n\nSvedit connects eight key pieces:\n\n1. **Schema** - Define your content structure (node types, properties, marks, annotations)\n2. **Document** - An actual document, containing a `document_id` and a flat map of nodes that hold the content\n3. **Session** - Manages the document, selection state, and history\n4. **Transaction** - Groups multiple document operations (create, delete, set) into a single atomic unit with undo/redo support\n5. **Transforms** - Higher-level composable functions that run inside a transaction (e.g., `break_text_node`, `join_text_node`) to modify the document.\n6. **Config** - Maps node types to components, provides inserters and commands\n7. **Components** - Render your content using Svelte (one component per node type)\n8. **Commands** - User actions (bold text, insert node, undo/redo) that modify the session\n\n**The flow:**\n\n- Define a schema → create a session → provide config → render with `<Svedit>` component\n- User interactions trigger commands → commands create transactions → which run transforms to modify the document → session applies the transaction → Svelte's reactivity updates the UI\n- Native DOM selections are mapped to Svedit's internal selection model\n\n## Schema\n\nYou can use a simple JSON-compatible schema definition language to enforce constraints on your documents. E.g. to make sure a page node always has a property body with references to nodes that are allowed within a page.\n\nFirst off, everything is a node. The page is a node, and so is a paragraph, a list, a list item, a nav and a nav item.\n\nEach node has a `kind` that determines its behavior:\n\n- `document`: A top-level node accessible via a route (e.g. a page, event)\n- `block`: A structured node that contains other nodes or properties\n- `text`: A node with editable text content (can be split and joined)\n- `mark`: A content-level range applied to text or a node array (bold, link, section, etc.). Marks are mutually exclusive and render in-place.\n- `annotation`: A metadata/overlay range applied to text or a node array (comments, markers, etc.). Annotations may overlap and are data-only.\n\n### Choosing between `text` and `block`\n\n`kind: 'text'` opts into the split/join system (`break_text_node`, `join_text_node`), which assumes:\n\n- The node has exactly **one** `text` property named **`content`**\n- Pressing Enter splits the node into two nodes of the same type\n- Pressing Backspace at position 0 joins it with the previous node\n\nWhen `break_text_node` splits through a mark or annotation, the right-hand occurrence receives an independent attachment node with the same properties. Editing one link or annotation therefore does not change the other. Ranges moved wholly to one side retain their ids. Subtree copying and pasting through `Transaction.build` also detach repeated attachment references in the copied data. These operations do not rewrite attachment references in existing stored documents.\n\nIf **any** of those assumptions don't hold, use `kind: 'block'`. Blocks can still have `text` properties with full editing support (typing, formatting, selection) — they just don't participate in split/join.\n\n> **Common mistake:** A quote node with `content` + `author` properties might seem like `kind: 'text'` because both fields are editable text. But splitting a quote into two half-quotes doesn't make sense, and `join_text_node` hard-codes `node.content` — so Backspace in the `author` field would join the wrong property with the previous block and drop the author text. The correct kind is `'block'`.\n\n<details>\n<summary>Decision tree for choosing a node kind</summary>\n\n```mermaid\nflowchart TD\n    Start([\"I need a new node type\"])\n    Q1{\"Is it a range applied within text or over child nodes?\"}\n    Q1a{\"Is it part of the content (formatting) rather than metadata?\"}\n    Q2{\"Is it a top-level routable entry point?\"}\n    Q3{\"Is text the node's entire purpose?\"}\n    Q4{\"Does it have exactly one text property named content?\"}\n    Q5{\"Does pressing Enter to split into two nodes make sense?\"}\n\n    KindMark[\"kind: mark e.g. bold, italic, link, section\"]\n    KindAnnotation[\"kind: annotation e.g. comment, marker\"]\n    KindDocument[\"kind: document e.g. page, article, event\"]\n    KindBlock[\"kind: block e.g. image+caption, quote+author, list, nav, embed\"]\n    KindText[\"kind: text e.g. paragraph, heading, list_item\"]\n\n    Start --> Q1\n    Q1 -->|Yes| Q1a\n    Q1a -->|Yes| KindMark\n    Q1a -->|\"No — it's metadata that may overlap\"| KindAnnotation\n    Q1 -->|No| Q2\n    Q2 -->|Yes| KindDocument\n    Q2 -->|No| Q3\n    Q3 -->|\"No — it has non-text properties or children\"| KindBlock\n    Q3 -->|Yes| Q4\n    Q4 -->|\"No — multiple text properties\"| KindBlock\n    Q4 -->|Yes| Q5\n    Q5 -->|\"No — splitting doesn't make sense\"| KindBlock\n    Q5 -->|Yes| KindText\n```\n\n</details>\n\nProperties of nodes can hold values:\n\n- `string`: A good old JavaScript string. Use optional `values` to restrict it to an ordered set of allowed strings.\n- `number`: Just like a number in JavaScript\n- `integer`: A number for which Number.isInteger(number) returns true. Optional `min` and `max` bounds are inclusive.\n- `boolean`: true or false\n- `datetime`: A date/time string parseable by `Date.parse()`\n- `string_array`: An array of strings\n- `number_array`: An array of numbers\n- `integer_array`: An array of integers\n- `boolean_array`: An array of booleans\n- `text`: Plain text content with marks (bold, italic, link etc.) and annotations (comments etc.). Text values use `{ content: '', marks: [], annotations: [] }`. Set `allow_newlines: true` to let users insert line breaks with Shift+Enter, or `false` to keep content single-line (e.g. for titles).\n\nOr references:\n\n- `node`: References a single node (e.g. an image node can reference a global asset node)\n- `node_array`: References a sequence of nodes (e.g. page.body references paragraph and list nodes)\n\n`node_array` properties use `node_types` for the child node types they can contain, plus optional `mark_types` for marks that can wrap ranges of child nodes and optional `annotation_types` for overlay annotations.\n\n```js\nconst document_schema = {\n\tpage: {\n\t\tkind: 'document',\n\t\tproperties: {\n\t\t\tbody: {\n\t\t\t\ttype: 'node_array',\n\t\t\t\tnode_types: ['nav', 'paragraph', 'list'],\n\t\t\t\tmark_types: ['section'],\n\t\t\t\tannotation_types: ['comment'],\n\t\t\t\tdefault_node_type: 'paragraph'\n\t\t\t}\n\t\t}\n\t},\n\tparagraph: {\n\t\tkind: 'text',\n\t\tproperties: {\n\t\t\tcontent: {\n\t\t\t\ttype: 'text',\n\t\t\t\tmark_types: ['strong', 'emphasis', 'link'],\n\t\t\t\tannotation_types: ['comment'],\n\t\t\t\tallow_newlines: true\n\t\t\t}\n\t\t}\n\t},\n\tlist_item: {\n\t\tkind: 'text',\n\t\tproperties: {\n\t\t\tcontent: {\n\t\t\t\ttype: 'text',\n\t\t\t\tmark_types: ['strong', 'emphasis', 'link'],\n\t\t\t\tallow_newlines: true\n\t\t\t}\n\t\t}\n\t},\n\tlist: {\n\t\tkind: 'block',\n\t\tproperties: {\n\t\t\tlist_items: {\n\t\t\t\ttype: 'node_array',\n\t\t\t\tnode_types: ['list_item'],\n\t\t\t\tdefault_node_type: 'list_item'\n\t\t\t}\n\t\t}\n\t},\n\tnav: {\n\t\tkind: 'block',\n\t\tproperties: {\n\t\t\tnav_items: {\n\t\t\t\ttype: 'node_array',\n\t\t\t\tnode_types: ['nav_item'],\n\t\t\t\tdefault_node_type: 'nav_item'\n\t\t\t}\n\t\t}\n\t},\n\tnav_item: {\n\t\tkind: 'block',\n\t\tproperties: {\n\t\t\turl: { type: 'string' },\n\t\t\tlabel: { type: 'string' }\n\t\t}\n\t},\n\tstrong: {\n\t\tkind: 'mark',\n\t\tproperties: {}\n\t},\n\temphasis: {\n\t\tkind: 'mark',\n\t\tproperties: {}\n\t},\n\tlink: {\n\t\tkind: 'mark',\n\t\tproperties: {\n\t\t\thref: { type: 'string' }\n\t\t}\n\t},\n\tsection: {\n\t\tkind: 'mark',\n\t\tproperties: {}\n\t},\n\tcomment: {\n\t\tkind: 'annotation',\n\t\tproperties: {}\n\t}\n};\n```\n\nMark types are defined as nodes with `kind: 'mark'`, annotation types as nodes with `kind: 'annotation'`. Simple marks like `strong` and `emphasis` have no properties, while marks like `link` can carry data (e.g. `href`). Each `text` or `node_array` property specifies which types are allowed via `mark_types` and `annotation_types` — this lets you control formatting per property (e.g. allow bold and links in body text, but only emphasis in titles). `mark_types` may only reference `kind: 'mark'` node types and `annotation_types` only `kind: 'annotation'` node types.\n\n## Document\n\nA document is a plain JavaScript object (POJO) with a `document_id` (the entry point) and a `nodes` object containing all content nodes.\n\nRules:\n\n- All nodes must be reachable from the document node (unreachable nodes are discarded)\n- No cyclic references allowed\n- Text properties use `{ content: '', marks: [], annotations: [] }`\n- Node array properties use `{ nodes: [], marks: [], annotations: [] }`\n\n### Node IDs\n\nNode IDs uniquely identify nodes inside a document. They are also used whenever one node references another node, for example in `node`, `node_array.nodes`, and mark or annotation `node_id` values.\n\nBecause Svedit also uses these IDs in HTML ids, document paths, and CSS selectors, IDs need to follow a simple, safe format:\n\n- It must be a string.\n- It must not be empty.\n- It must start with a letter (`A-Z`, `a-z`) or underscore (`_`).\n- After the first character, it may contain letters, numbers, underscores, or dashes.\n- It must not contain Svedit's path separator `__`.\n\nValid examples:\n\n```js\n'page_1';\n'story-hero';\n'_temporary_node';\n```\n\nInvalid examples:\n\n| ID          | Why invalid                                         |\n| ----------- | --------------------------------------------------- |\n| `1`         | Not a string                                        |\n| `'1'`       | Serialized paths would treat this as an array index |\n| `'1_page'`  | Starts with a number                                |\n| `'page.1'`  | `.` is not allowed                                  |\n| `'page__1'` | `__` is reserved as Svedit's path separator         |\n\nThe `doc.nodes` map key must also match the node's own `id`:\n\n```js\nnodes: {\n\tpage_1: {\n\t\tid: 'page_1',\n\t\ttype: 'page'\n\t\t// ...\n\t}\n}\n```\n\nIf you provide `config.generate_id`, every generated ID must follow the same rules. Svedit validates generated IDs immediately and throws if the provider returns an invalid value:\n\n```js\nconst session_config = {\n\tgenerate_id: () => `node_${crypto.randomUUID()}`\n};\n```\n\nProvide your own generator: use short custom-alphabet `nanoid` IDs for local documents, monotonic ULIDs (`ulid`) for sortable/synced IDs, or prefixed UUIDs for globally unique IDs.\n\nHere's an example document:\n\n```js\nconst doc = {\n\tdocument_id: 'page_1',\n\tnodes: {\n\t\tstrong_1: {\n\t\t\tid: 'strong_1',\n\t\t\ttype: 'strong'\n\t\t},\n\t\tsection_1: {\n\t\t\tid: 'section_1',\n\t\t\ttype: 'section'\n\t\t},\n\t\tnav_item_1: {\n\t\t\tid: 'nav_item_1',\n\t\t\ttype: 'nav_item',\n\t\t\turl: '/homepage',\n\t\t\tlabel: 'Home'\n\t\t},\n\t\tnav_1: {\n\t\t\tid: 'nav_1',\n\t\t\ttype: 'nav',\n\t\t\tnav_items: { nodes: ['nav_item_1'], marks: [], annotations: [] }\n\t\t},\n\t\tparagraph_1: {\n\t\t\tid: 'paragraph_1',\n\t\t\ttype: 'paragraph',\n\t\t\tcontent: {\n\t\t\t\tcontent: 'Hello world.',\n\t\t\t\tmarks: [{ start_offset: 0, end_offset: 5, node_id: 'strong_1' }],\n\t\t\t\tannotations: []\n\t\t\t}\n\t\t},\n\t\tlist_item_1: {\n\t\t\tid: 'list_item_1',\n\t\t\ttype: 'list_item',\n\t\t\tcontent: { content: 'First list item', marks: [], annotations: [] }\n\t\t},\n\t\tlist_item_2: {\n\t\t\tid: 'list_item_2',\n\t\t\ttype: 'list_item',\n\t\t\tcontent: { content: 'Second list item', marks: [], annotations: [] }\n\t\t},\n\t\tlist_1: {\n\t\t\tid: 'list_1',\n\t\t\ttype: 'list',\n\t\t\tlist_items: { nodes: ['list_item_1', 'list_item_2'], marks: [], annotations: [] }\n\t\t},\n\t\tpage_1: {\n\t\t\tid: 'page_1',\n\t\t\ttype: 'page',\n\t\t\tbody: {\n\t\t\t\tnodes: ['nav_1', 'paragraph_1', 'list_1'],\n\t\t\t\tmarks: [{ start_offset: 1, end_offset: 3, node_id: 'section_1' }],\n\t\t\t\tannotations: []\n\t\t\t}\n\t\t}\n\t}\n};\n```\n\n### Marks and annotations\n\nMarks and annotations are regular nodes attached to a range in a property value. Both use the same attachment shape:\n\n```js\n{\n\tstart_offset: 0,\n\tend_offset: 5,\n\tnode_id: 'strong_1'\n}\n```\n\nThey differ in semantics:\n\n- **Marks** are part of the content (bold, italic, link, section). They are mutually exclusive within a property and are rendered in-place by `<TextProperty>`/`<NodeArrayProperty>`.\n- **Annotations** are metadata layered over the content (comments, markers). They may overlap marks and each other, and are data-only: Svedit keeps and transforms the data (and adds CSS classes to covered node wrappers), but your app is responsible for interpreting it. Annotation types must not have registered components.\n\nFor `text` properties, offsets address character positions in the `content` string:\n\n```js\n{\n\tid: 'paragraph_1',\n\ttype: 'paragraph',\n\tcontent: {\n\t\tcontent: 'Hello world.',\n\t\tmarks: [{ start_offset: 0, end_offset: 5, node_id: 'strong_1' }],\n\t\tannotations: [{ start_offset: 3, end_offset: 8, node_id: 'comment_1' }]\n\t}\n}\n```\n\nFor `node_array` properties, offsets address node positions in the `nodes` array. This lets you wrap a contiguous group of child nodes with the same range model:\n\n```js\n{\n\tid: 'page_1',\n\ttype: 'page',\n\tbody: {\n\t\tnodes: ['paragraph_1', 'paragraph_2', 'paragraph_3'],\n\t\tmarks: [{ start_offset: 1, end_offset: 3, node_id: 'section_1' }],\n\t\tannotations: []\n\t}\n}\n```\n\nThe ranges are half-open: `start_offset` is included, `end_offset` is excluded. Marks must not overlap; annotations may overlap (including same-type annotations created through lower-level APIs).\n\n### Document schema changes\n\nSvedit validates documents against the current schema when you create a `Session`. If you change the schema, you are responsible for migrating existing documents before loading them.\n\nFor simple additive changes, defaults can help. When you add a new property, you can call `fill_document_defaults` before creating the session:\n\n```js\nimport { Session, fill_document_defaults } from 'svedit';\n\nconst document_schema = {\n\tparagraph: {\n\t\tkind: 'text',\n\t\tproperties: {\n\t\t\tlayout: {\n\t\t\t\ttype: 'string',\n\t\t\t\tvalues: ['image-left', 'image-right', 'stacked'],\n\t\t\t\tdefault: 'image-left'\n\t\t\t},\n\t\t\tcontent: {\n\t\t\t\ttype: 'text',\n\t\t\t\tallow_newlines: true\n\t\t\t}\n\t\t}\n\t}\n};\n\nconst migrated_doc = fill_document_defaults(existing_doc, document_schema);\nconst session = new Session(document_schema, migrated_doc, config);\n```\n\nThis only fills properties that are missing. Explicit schema `default` values are used when present; otherwise Svedit uses built-in defaults for value types such as strings, numbers, booleans, arrays, `node_array`, and `text`. Constraints such as `values`, `min`, and `max` do not change those defaults, so declare an explicit `default` when the built-in value would be invalid. Existing values are preserved, and the original document object is not mutated.\n\nDefaults make it safe to add new defaultable properties, but they are not a replacement for real document migrations. If you rename a property, remove a property, split one property into several, change node types, or need to transform existing data, write your own migration first and use `fill_document_defaults` only as a helper where appropriate.\n\n`tr.create` and `tr.build` also fill omitted defaults for newly-created nodes. For document migrations, declare explicit defaults whenever the built-in type default is not the value you want. Do not rely on default filling for schema changes that need a real migration.\n\n## Config\n\nDocuments need a config object that tells Svedit how to render and manipulate your content. See the full example in [`src/routes/demo_config.ts`](src/routes/demo_config.ts).\n\n### Importing pasted HTML\n\nSet `config.html_paste` to enable external HTML import. Without it, paste keeps its\nexisting plain-text behavior. Native Svedit clipboard data and media paste keep\ntheir existing paths and bypass this importer.\n\n```ts\nimport type { HtmlPasteConfig } from 'svedit';\n\nconst html_paste: HtmlPasteConfig = {\n\tblocks: {\n\t\tp: { type: 'paragraph', text_property: 'content' },\n\t\th1: { type: 'heading1', text_property: 'content' },\n\t\th2: { type: 'heading2', text_property: 'content' }\n\t},\n\tmarks: {\n\t\tbold: { type: 'strong' },\n\t\tlink: ({ href }) => ({ type: 'link', properties: { href } })\n\t}\n};\n\nconst config = { /* other configuration */ html_paste };\n```\n\nUse your app's schema names (the demo uses `heading_1`, for example). Blocks accept\n`p` and `h1` through `h6`. Each mapping names a text node type and its text property.\nA block mapping can also be a synchronous factory returning the same descriptor,\nwith extra node properties, or `null` to use the destination's default text node:\n\n```ts\nh1: ({ tag }) => ({\n\ttype: 'heading',\n\ttext_property: 'content',\n\tproperties: { level: Number(tag.slice(1)) }\n});\n```\n\n`<strong>` and `<b>` use the `bold` mapping. Link factories receive the decoded,\ntrimmed `href`; they may return `null` to discard link formatting. The importer\naccepts HTTP(S), mailto, tel, and relative links, and drops unsafe schemes.\nMappings describe data; they should not mutate the session. Svedit creates IDs\nand fills schema defaults through its normal insertion transaction.\n\nMap HTML lists separately because they contain an array of text nodes:\n\n```ts\nhtml_paste.lists = {\n\tul: {\n\t\ttype: 'list',\n\t\tchildren_property: 'list_items',\n\t\titem: { type: 'list_item', text_property: 'content' },\n\t\tproperties: { layout: 'square' }\n\t},\n\tol: {\n\t\ttype: 'list',\n\t\tchildren_property: 'list_items',\n\t\titem: { type: 'list_item', text_property: 'content' },\n\t\tproperties: { layout: 'decimal' }\n\t}\n};\n```\n\nAll type and property names come from your schema. Nested HTML lists flatten into\none list in item order, using the outer list's mapping. An item with multiple\nparagraphs becomes one text node. Unmapped or disallowed lists fall back to the\ndestination's default text nodes; text fields receive flattened list text.\n\nIf a destination requires a containing block, configure an optional wrapper:\n\n```ts\nhtml_paste.wrapper = { type: 'prose', children_property: 'body' };\n```\n\nThe wrapper is used only when blocks cannot be mapped directly to the destination\nand the destination allows its type. Its `children_property` must be a node array;\nblocks are mapped against that property's allowed node types. Wrapper mappings\nalso accept `properties` for schema-specific defaults.\n\nImport respects the destination's allowed node and mark types. Unmapped or\ndisallowed headings fall back to its default text node; unsupported marks keep\ntheir text. Marks are exclusive in Svedit, so a supported link takes precedence\nover bold where they overlap. Existing active-mark insertion behavior still applies.\n\nAt a node caret, HTML blocks become nodes. A single text block pasted into text\nretains the destination node type. A configured list follows the node insertion\npath, even when it contains only one item. Multiple blocks pasted into a text node\nfollow the existing multi-paragraph behavior: insert nodes after that node without splitting\nor replacing its text. In block fields such as titles and captions, blocks are\njoined with blank lines, or spaces when `allow_newlines` is false.\n\nThe importer recognizes semantic tags, common block wrappers, entities and\n`<br>`. Unsupported containers retain their text but do not import table\nstructure, images, or CSS formatting. Lists retain structure when a list mapping\nis configured. Script/style content is ignored. Empty or\nunusable HTML, and mapping errors, fall back to the existing plain-text path.\n\n### Exporting copied rich text\n\nCopy and cut include both the embedded `data-svedit` payload and readable HTML.\nConfigure `mark_html_exporters` to map your schema's marks to HTML. Unknown marks\nkeep their text; data-only annotations remain in the native payload.\n\n```ts\nimport { export_text_html, escape_html, safe_html_href } from 'svedit';\nimport type { MarkHtmlExporter } from 'svedit';\n\nconst mark_html_exporters = {\n\tstrong: (_node, content) => `<strong>${content}</strong>`,\n\tlink: (node, content) => {\n\t\tconst href = safe_html_href(node.href || '');\n\t\treturn href ? `<a href=\"${escape_html(href)}\">${content}</a>` : content;\n\t}\n} satisfies Record<string, MarkHtmlExporter>;\n\nconst config = {\n\tmark_html_exporters,\n\thtml_exporters: {\n\t\tparagraph: (node, session) => `<p>${export_text_html(node.content, session)}</p>`,\n\t\theading1: (node, session) => `<h1>${export_text_html(node.content, session)}</h1>`\n\t}\n};\n```\n\nText selections and the default node exporter use this automatically. Existing\ncustom `html_exporters` should call `export_text_html` for their text properties,\nas above. The helper escapes text, preserves line breaks with `<br>`, and uses\nSvedit's grapheme offsets. Mark exporters receive already escaped inline HTML;\nonly attribute values need escaping. Import and export mappings are separate\nbecause import factories cannot be reliably reversed.\n\n### Custom handlers\n\nTwo optional hooks are especially useful when integrating custom media workflows:\n\n- `handle_media_paste(session, pasted_media)`  \n  Called when media is pasted. You can upload/process files, replace an existing media property, or return a node payload to insert new content.  \n  See example implementation in [`src/routes/demo_config.ts`](src/routes/demo_config.ts).\n\n- `handle_property_deletion(session, path)`  \n  Called when deleting/cutting a property selection. Use it to define app-specific reset behavior (for example clearing an image property or resetting a referenced media node).  \n  See example implementation in [`src/routes/demo_config.ts`](src/routes/demo_config.ts).\n\n```js\nconst session_config = {\n  // ID generator for creating new nodes\n  generate_id: () => nanoid(),\n\n  // User-land overlays and optional system component overrides\n  system_components: { overlays: Overlays },\n\n  // Map node types to Svelte components\n  node_components: { page: Page, text: Text, story: Story, list: List, button: Button, ... },\n\n  // Functions that create and insert new nodes\n  inserters: {\n    text: (tr, content = { content: '', marks: [], annotations: [] }) => {\n      const text_id = nanoid();\n      tr.create({ id: text_id, type: 'text', content });\n      tr.insert_nodes([text_id]);\n    }\n  },\n\n  // Returns { commands, keymap } for the editor instance\n  create_commands_and_keymap: (context) => { ... },\n\n  // Optional: handle image paste events\n  handle_image_paste: (session, images) => { ... }\n};\n```\n\n**Key config options:**\n\n- **`generate_id`** - Function that generates unique IDs for new nodes\n- **`node_components`** - Maps each node type from your schema to a Svelte component\n- **`system_components`** - Optional overrides for internal editor components and a slot for your own overlays:\n  - `overlays` — A Svelte component rendered inside `<Svedit>` but outside the content canvas. Use it to add floating UI like link editors, image toolbars, or annotation popovers that appear near the current selection. See [`src/routes/components/Overlays.svelte`](src/routes/components/Overlays.svelte) for an example.\n  - `node_gap`, `node_gap_markers`, `node_selection_markers` — Override the default system components if you need custom visuals for node gaps or selection indicators.\n  - `node_gap_tools` — A component rendered inside the active node gap marker, next to the caret. Use it for gap-scoped tools like an insert button: it inherits the `--row` orientation variable from the node array, and it appears and disappears together with the caret. Receives `{ path, offset, is_first, is_last }` (see the `NodeGapToolsProps` type). The marker has `pointer-events: none`, so interactive tools must set `pointer-events: auto`. See [`src/routes/components/GapInsertTool.svelte`](src/routes/components/GapInsertTool.svelte) for an example.\n- **`inserters`** - Functions that create blank nodes of each type and set up the selection\n- **`create_commands_and_keymap`** - Factory function that creates commands and keybindings for an editor instance\n- **`handle_image_paste`** - Optional handler for image paste events\n- **`view_classes`** - Viewport visibility classes. Every node element gets `.in-view`, `.seen`, `.fully-in-view`, `.visible-top`, `.visible-bottom` toggled as it scrolls through the viewport. Useful for scrollytelling and reveal animations driven from CSS. **On by default**. Set `view_classes: false` to opt out — saves ~5 classList ops per IO entry (measurable scroll-FPS win at 200+ nodes) and narrows the IntersectionObserver thresholds from `[0, 0.98]` to `[0]` (halves callback frequency during scroll). Disable if your app doesn't read these classes from CSS.\n\nThe config is accessible throughout your app via `session.config`.\n\n## Session\n\nThe `Session` class manages your content graph, selection state, and history. See [`src/lib/Session.svelte.js`](src/lib/Session.svelte.js) for the full API.\n\n### Immutable state\n\nDocument content (`session.doc`) and selection (`session.selection`) are **immutable** with **copy-on-write** semantics. When a change is made, only the modified parts are copied — unchanged nodes keep their original references. This avoids the overhead of reactive proxies (using Svelte's `$state.raw`) since state is reassigned rather than mutated. Also, `console.log(session.get(some_node_id))` gives you a readable raw object, not a proxy.\n\n### Creating a session\n\n```js\nimport { Session } from 'svedit';\n\nconst session = new Session(schema, doc, config);\n```\n\n### Reading the graph\n\n```js\nsession.get(['page_1', 'body']); // => { nodes: ['nav_1', 'paragraph_1', 'list_1'], marks: [], annotations: [] }\nsession.get(['nav_1']); // => { id: 'nav_1', type: 'nav', ... }\nsession.get('nav_1'); // => shorthand for above (single node ID)\nsession.inspect(['page_1', 'body']); // => { kind: 'property', type: 'node_array', node_types: [...] }\nsession.kind(node); // => 'text', 'block', 'mark', or 'annotation'\n```\n\n### Selection and state\n\n```js\nsession.selection; // Current selection (text, node, or property)\nsession.selected_node; // The currently selected node (derived)\nsession.selected_marks; // Mark records touched by the current selection (derived)\nsession.active_mark; // The selected mark record when exactly one mark is touched, otherwise null\nsession.selected_annotations; // Annotation records touched by the current selection (derived)\nsession.active_annotation; // The selected annotation record when exactly one annotation is touched, otherwise null\nsession.can_insert('paragraph'); // Check if node type can be inserted\nsession.available_mark_types; // Mark types allowed at current selection (derived)\nsession.available_annotation_types; // Annotation types allowed at current selection (derived)\n```\n\n#### Native selection as source of truth\n\nEvery selection that can exist in Svedit must have a DOM representation. This is a core design decision: the browser's native selection determines where the selection is, and `session.selection` is derived from it — not the other way around. If the DOM selection stops being the source of truth, selection mapping becomes ambiguous and Svedit loses the simplicity and determinism that the editor is built on.\n\nThis applies to all selection types:\n\n- Text selections are native selections inside a `text` property.\n- Node selections are native selections whose anchor and focus land inside the selected node range.\n- Property selections are native selections inside the DOM representation of that property.\n\nFor example, a multi-node selection is still represented as a native browser selection that starts somewhere inside one node and ends somewhere inside another. Svedit maps that DOM selection to an internal node selection, hides the browser's visual selection, and renders `NodeSelectionMarkers` instead. The native selection remains in the DOM behind the scenes because `contenteditable` still needs it to do its job: pressing an arrow key should move the cursor from the current focus point to the closest valid next position, and Shift+Arrow should expand the selection from that same focus point. Without a native selection rendered at all times, the browser has no reliable starting point for cursor movement, selection expansion, or other editing behavior.\n\n### Making changes\n\n```js\nconst tr = session.tr; // Create a transaction\ntr.set(['nav_1', 'label'], 'Home');\ntr.insert_nodes(['new_node_id']);\nsession.apply(tr); // Apply the transaction\n```\n\n#### Batching history entries\n\nBy default, every `session.apply(tr)` creates a new undo/redo entry. Pass `{ batch: true }` to merge the transaction into the previous history entry instead — useful for continuous interactions like dragging, where you want the entire gesture to undo as one step.\n\n```js\nsession.apply(tr, { batch: true });\n```\n\nBatched transactions merge into the current history entry as long as they arrive within a 2-second window of the batch start. After 2 seconds of inactivity, the next `apply` starts a fresh entry. To force a new entry immediately (e.g. on pointer up), reset the batch timer:\n\n```js\nsession.last_batch_started = undefined;\n```\n\n### History\n\n```js\nsession.can_undo; // Boolean (derived)\nsession.can_redo; // Boolean (derived)\nsession.undo();\nsession.redo();\n```\n\n### Detecting unsaved changes\n\nBecause document state is immutable, you can detect unsaved changes by comparing references. When a change is made, `session.doc` gets a new reference — unchanged documents keep the same reference.\n\n```js\nlet last_saved_doc = $state(null);\nlet has_unsaved_changes = $derived.by(() => {\n\tif (!last_saved_doc) {\n\t\t// No save yet — use undo history as indicator\n\t\treturn session.can_undo;\n\t} else {\n\t\t// Compare current doc reference against last saved\n\t\treturn last_saved_doc !== session.doc;\n\t}\n});\n\nfunction save() {\n\t// ... save to server ...\n\tlast_saved_doc = session.doc;\n}\n```\n\nThis works because of Svedit's copy-on-write strategy: only modified parts of the document are copied, so reference equality is a reliable and efficient way to detect changes. You can use `has_unsaved_changes` to show/hide a save button, display a dirty indicator, or warn before navigating away.\n\n### Utilities\n\n```js\nsession.doc.document_id; // The document's root ID\nsession.generate_id(); // Generate a new unique ID\nsession.config; // Access the config object\nsession.validate_doc(); // Validate all nodes against schema\nsession.traverse(node_id); // Get all nodes reachable from a node\nsession.select_parent(); // Select parent of current selection\n```\n\n## Transforms\n\nTransforms are pure functions that modify a transaction. They encapsulate common editing operations like breaking text nodes, joining nodes, or inserting new content.\n\nTransforms take a transaction (`tr`) as their parameter and return `true` if successful or `false` if the transform cannot be applied (e.g., wrong selection type or invalid state).\n\n```js\n// Example: break a text node at the caret\nimport { break_text_node } from 'svedit';\n\nconst tr = session.tr;\nconst success = break_text_node(tr);\nif (success) {\n\tsession.apply(tr);\n}\n```\n\n### Built-in transforms\n\nSvedit provides several core transforms in [`src/lib/transforms.svelte.js`](src/lib/transforms.svelte.js):\n\n- `break_text_node(tr)` - Split a text node at the caret position\n- `join_text_node(tr)` - Join current text node with the previous one\n- `insert_default_node(tr)` - Insert a new node at the current selection\n\n### Composability\n\nTransforms are composable. You can build higher-level transforms from lower-level ones:\n\n```js\nfunction custom_transform(tr) {\n\t// Compose multiple transforms\n\tif (!break_text_node(tr)) return false;\n\tif (!insert_default_node(tr)) return false;\n\treturn true;\n}\n```\n\n### Writing your own transforms\n\nYou're encouraged to write custom transforms for your application's specific needs. Keep them pure functions that operate on the transaction object:\n\n```js\nfunction insert_heading(tr) {\n\tconst selection = tr.selection;\n\n\tif (selection?.type !== 'node') return false;\n\n\t// Create and insert a heading node\n\tconst heading_id = tr.generate_id();\n\ttr.create({\n\t\tid: heading_id,\n\t\ttype: 'heading',\n\t\tcontent: { content: '', marks: [], annotations: [] }\n\t});\n\ttr.insert_nodes([heading_id]);\n\n\treturn true;\n}\n```\n\n## Transaction\n\nTransactions group multiple operations into atomic units that can be applied and undone as one. They provide the same read API as sessions (`tr.get()`, `tr.inspect()`, `tr.kind()`, `tr.generate_id()`), so transforms can query document state directly. See [`src/lib/Transaction.svelte.js`](src/lib/Transaction.svelte.js) for the full API.\n\n### Basic usage\n\n```js\nconst tr = session.tr; // Create a new transaction\ntr.set(['node_1', 'title'], 'New Title'); // Modify properties\nsession.apply(tr); // Apply atomically\n```\n\n### Node operations\n\n```js\n// Create a new node (must include all required properties from schema)\ntr.create({\n\tid: 'paragraph_1',\n\ttype: 'paragraph',\n\tcontent: { content: '', marks: [], annotations: [] }\n});\n\n// Delete a node (cascades to unreferenced child nodes)\ntr.delete('paragraph_26');\n\n// Insert nodes at current node selection\ntr.insert_nodes(['paragraph_1', 'list_1']);\n\n// Build a subgraph from existing nodes (generates new IDs)\nconst new_node_id = tr.build('the_list', {\n\tfirst_item: {\n\t\tid: 'first_item',\n\t\ttype: 'list_item',\n\t\tcontent: node.content\n\t},\n\tthe_list: {\n\t\tid: 'the_list',\n\t\ttype: 'list',\n\t\tlist_items: { nodes: ['first_item'], marks: [], annotations: [] }\n\t}\n});\n```\n\n### Text operations\n\n```js\n// Insert text at caret (replaces selection if expanded)\ntr.insert_text('Hello');\n\n// Toggle mark on selected text (marks are mutually exclusive)\ntr.toggle_mark('strong');\ntr.toggle_mark('link', { href: 'https://example.com' });\n\n// Toggle annotation on selected text (competes only with same-type annotations)\ntr.toggle_annotation('comment');\n\n// Delete selected text or nodes\ntr.delete_selection();\n```\n\n### Selection\n\n```js\n// Set the selection after operations\ntr.set_selection({\n\ttype: 'text',\n\tpath: ['node_1', 'content'],\n\tanchor_offset: 0,\n\tfocus_offset: 5\n});\n```\n\nAll transaction methods return `this` for chaining:\n\n```js\ntr.create(node).insert_nodes([node.id]).set_selection(new_selection);\n```\n\n## Commands\n\nCommands provide a structured way to implement user actions. Commands are stateful and UI-aware, unlike transforms which are pure functions.\n\nThere are two types of commands in Svedit:\n\n- **Document-scoped commands** - Bound to a specific Svedit instance/document and only active when that editor has focus\n- **App-level commands** - Operate at the application level, independent of any specific document\n\nLet's start with document-scoped commands, which are the foundation of the editing experience.\n\n### Document-scoped commands\n\nDocument-scoped commands operate on a specific document and have access to its selection, content, and editing state through a context object.\n\n#### Creating a document-scoped command\n\nExtend the `Command` base class and implement the `is_enabled()` and `execute()` methods:\n\n```js\nimport { Command } from 'svedit';\n\nclass ToggleStrongCommand extends Command {\n\tis_enabled() {\n\t\treturn this.context.editable && this.context.session.selection?.type === 'text';\n\t}\n\n\texecute() {\n\t\tthis.context.session.apply(this.context.session.tr.toggle_mark('strong'));\n\t}\n}\n```\n\n#### Document command context\n\nDocument-scoped commands receive a `context` object with access to the Svedit instance's state:\n\n- `context.session` - The current session instance\n- `context.editable` - Whether the editor is in edit mode\n- `context.canvas_el` - The DOM element of the Svedit editor canvas\n- `context.is_composing` - Whether IME composition is currently taking place\n\n### Command lifecycle methods\n\n**`is_enabled(): boolean`**\n\nDetermines if the command can currently be executed. This is automatically evaluated and exposed as the `disabled` derived property, which can be used to disable UI elements.\n\n```js\nis_enabled() {\n  return this.context.editable && this.context.session.selection?.type === 'text';\n}\n```\n\n**`execute(): void | Promise<void>`**\n\nExecutes the command's action. Can be synchronous or asynchronous.\n\n```js\nexecute() {\n  const tr = this.context.session.tr;\n  tr.insert_text('Hello');\n  this.context.session.apply(tr);\n}\n```\n\n#### Built-in document commands\n\nSvedit provides several [core commands](src/lib/Command.svelte.js) out of the box:\n\n- `UndoCommand` - Undo the last change\n- `RedoCommand` - Redo the last undone change\n- `SelectParentCommand` - Select the parent of the current selection\n- `ToggleMarkCommand` - Toggle marks on text or node-array selections\n- `ToggleAnnotationCommand` - Toggle annotations on text or node-array selections\n- `AddNewLineCommand` - Insert newline character in text\n- `BreakTextNodeCommand` - Split text node at caret\n- `SelectAllCommand` - Progressively expand selection\n- `InsertDefaultNodeCommand` - Insert a new node at caret\n\n#### Using document commands\n\nCommands are created by passing them a context object from the Svedit component. See a complete example in [`src/routes/demo_config.ts`](src/routes/demo_config.ts) in the `create_commands_and_keymap` configuration function:\n\n```js\ncreate_commands_and_keymap: (context) => {\n\tconst commands = {\n\t\tundo: new UndoCommand(context),\n\t\tredo: new RedoCommand(context),\n\t\ttoggle_strong: new ToggleMarkCommand('strong', context),\n\t\ttoggle_emphasis: new ToggleMarkCommand('emphasis', context)\n\t\t// ... more commands\n\t};\n\n\tconst keymap = define_keymap({\n\t\t'meta+z,ctrl+z': [commands.undo],\n\t\t'meta+b,ctrl+b': [commands.toggle_strong]\n\t\t// ... more keybindings\n\t});\n\n\treturn { commands, keymap };\n};\n```\n\nBind commands to UI elements in your components:\n\n```svelte\n<button\n\tdisabled={document_commands.toggle_strong.disabled}\n\tclass:active={document_commands.toggle_strong.active}\n\tonclick={() => document_commands.toggle_strong.execute()}\n>\n\tBold\n</button>\n```\n\n#### Derived state in commands\n\nCommands can have derived state for reactive UI binding. The `active` property in toggle commands is a common pattern:\n\n```js\nclass ToggleEmphasisCommand extends Command {\n\t// Automatically recomputes when mark state changes\n\tactive = $derived(this.context.session.active_mark?.node.type === 'emphasis');\n\n\tis_enabled() {\n\t\treturn this.context.editable && this.context.session.selection?.type === 'text';\n\t}\n\n\texecute() {\n\t\tthis.context.session.apply(this.context.session.tr.toggle_mark('emphasis'));\n\t}\n}\n```\n\nThe `disabled` property is automatically derived from `is_enabled()` on all commands.\n\n#### DOM access in commands\n\nCommands can access the DOM through the context or global APIs:\n\n```js\nclass FocusNextSelectableCommand extends Command {\n\texecute() {\n\t\tconst selectables = this.context.canvas_el.querySelectorAll('.svedit-selectable');\n\t\tconst next = selectables[0]; // Find next based on current selection\n\t\tconst path = next.closest('[data-path]').dataset.path.split('.');\n\t\tthis.context.session.selection = { type: 'text', path, anchor_offset: 0, focus_offset: 0 };\n\t}\n}\n```\n\n### App-level commands and scope hierarchy\n\nWhile document-scoped commands operate on a specific Svedit instance, app-level commands operate at the application level and handle concerns like saving, loading, switching between edit/view modes, or managing multiple documents.\n\n#### Understanding the scope stack\n\nSvedit uses a scope hierarchy (scope stack) to manage which commands are active at any given time:\n\n1. **App-level scope** (top level) - Commands that are always available, independent of document focus\n2. **Document-level scope** (per Svedit instance) - Commands bound to a specific document/editor\n\nWhen a Svedit instance gains focus:\n\n- The previous document's scope is **popped** from the stack (its commands become inactive)\n- The newly focused document's scope is **pushed** onto the stack (its commands become active)\n\nThis means commands automatically work with the correct document based on focus.\n\n#### Creating app-level commands\n\nApp-level commands have their own context, separate from any specific document:\n\n```js\nimport { Command } from 'svedit';\n\nclass SaveCommand extends Command {\n\tis_enabled() {\n\t\treturn this.context.editable;\n\t}\n\n\tasync execute() {\n\t\tawait this.context.save_all_documents();\n\t\tthis.context.show_notification('All changes saved');\n\t}\n}\n\nclass ToggleEditModeCommand extends Command {\n\tis_enabled() {\n\t\treturn !this.context.editable;\n\t}\n\n\texecute() {\n\t\tthis.context.editable = true;\n\t}\n}\n```\n\n#### App-level context\n\nThe app-level context contains application-wide state and methods:\n\n```js\nconst app_context = {\n\tget editable() {\n\t\treturn editable; // App-level editable state\n\t},\n\tset editable(value) {\n\t\teditable = value;\n\t},\n\tget session() {\n\t\treturn session;\n\t},\n\tget app_el() {\n\t\treturn app_el;\n\t}\n};\n\nconst app_commands = {\n\tsave: new SaveCommand(app_context),\n\ttoggle_edit: new ToggleEditCommand(app_context)\n};\n```\n\n## Scope-aware keyboard shortcuts\n\nThe KeyMapper manages keyboard shortcuts using a scope-based stack system. Scopes are tried from top to bottom (most recent to least recent), allowing more specific keymaps to override general ones.\n\n### Basic usage\n\n```js\nimport { KeyMapper, define_keymap } from 'svedit';\n\nconst key_mapper = new KeyMapper();\n\n// Define a keymap\nconst keymap = define_keymap({\n\t'meta+z,ctrl+z': [document_commands.undo],\n\t'meta+b,ctrl+b': [document_commands.bold],\n\tenter: [document_commands.break_text_node]\n});\n\n// Push the keymap onto the scope stack\nkey_mapper.push_scope(keymap);\n\n// Handle keydown events\nwindow.addEventListener('keydown', (event) => {\n\tkey_mapper.handle_keydown(event);\n});\n```\n\n### Key syntax\n\n- **Multiple modifiers**: `meta+shift+z`, `ctrl+alt+k`\n- **Cross-platform**: `meta+z,ctrl+z` (tries Meta+Z first, then Ctrl+Z)\n- **Modifiers**: `meta`, `ctrl`, `alt`, `shift`\n- **Keys**: Any key name (e.g., `a`, `enter`, `escape`, `arrowup`)\n\n### Command arrays\n\nCommands are wrapped in arrays to support fallback behavior:\n\n```js\ndefine_keymap({\n\t'meta+b,ctrl+b': [\n\t\tdocument_commands.bold, // Try this first\n\t\tdocument_commands.fallback // Use this if first is disabled\n\t]\n});\n```\n\n### Scope stack\n\nUse `push_scope()` and `pop_scope()` to manage different keyboard contexts:\n\n```js\n// App-level keymap (always active)\nconst app_keymap = define_keymap({\n\t'meta+s,ctrl+s': [app_commands.save],\n\t'meta+n,ctrl+n': [app_commands.new_document]\n});\nkey_mapper.push_scope(app_keymap);\n\n// Document-level keymap (active when editor has focus)\nconst doc_keymap = define_keymap({\n\t'meta+z,ctrl+z': [document_commands.undo],\n\t'meta+b,ctrl+b': [document_commands.bold]\n});\n\n// When editor gains focus:\nkey_mapper.push_scope(doc_keymap);\n\n// When editor loses focus:\nkey_mapper.pop_scope();\n```\n\nThe KeyMapper tries scopes from top to bottom, so push more specific keymaps last.\n\n## Selection\n\nSelections are at the heart of Svedit. There are just three types of selections:\n\n### Terminology note\n\n- Use \"node\" as the domain term.\n- Use \"node caret\" for a collapsed node selection.\n- Use \"node gap\" for the DOM landing zone between nodes.\n\n1. **Text Selection**: A text selection spans across a range of characters in a string. E.g. the below example has a collapsed caret at position 1 in a text property 'content'.\n\n```js\n{\n  type: 'text',\n  path: ['page_1234', 'body', 0, 'content'],\n  anchor_offset: 1,\n  focus_offset: 1\n}\n```\n\n2. **Node Selection**: A node selection spans across a range of nodes inside a node_array. The below example selects the nodes at index 3 and 4.\n\n```js\n{\n  type: 'node',\n  path: ['page_1234', 'body'],\n  anchor_offset: 2,\n  focus_offset: 4\n}\n```\n\n3. **Property Selection**: A property selection addresses one particular property of a node.\n\n```js\n{\n  type: \"property\",\n  path: [\n    \"page_1\",\n    \"body\",\n    11,\n    \"image\"\n  ]\n}\n```\n\nYou can access the current selection through `session.selection` anytime. And you can programmatically set the selection using `session.selection = new_selection`.\n\n## Rendering\n\nNow you can start making your Svelte pages in-place editable by wrapping your design inside the `<Svedit>` component.\n\n```svelte\n<Svedit {session} path={[session.doc.document_id]} editable={true} />\n```\n\n## Node components\n\nNode components are Svelte components that render specific node types in your document. Each node component receives a `path` prop and uses the `<Node>` wrapper component along with property components to render the node's content.\n\n### Basic structure\n\nA typical node component follows this pattern:\n\n```svelte\n<script>\n\timport { Node, TextProperty } from 'svedit';\n\tlet { path } = $props();\n</script>\n\n<Node {path}>\n\t<div class=\"my-node\">\n\t\t<TextProperty path={[...path, 'content']} />\n\t</div>\n</Node>\n```\n\n### The `<Node>` wrapper\n\nEvery node component must wrap its content in the `<Node>` component. This wrapper:\n\n- Registers the node with the editor\n- Handles selection and caret behavior\n- Provides the foundation for editing interactions\n\n### Property components\n\nSvedit provides specialized components for rendering different property types:\n\n**`<TextProperty>`** - For editable text content with inline marks:\n\n```svelte\n<TextProperty tag=\"p\" class=\"body\" path={[...path, 'content']} placeholder=\"Enter text here\" />\n```\n\n**`<NodeArrayProperty>`** - For container properties that hold multiple nodes:\n\n```svelte\n<NodeArrayProperty class=\"list-items\" path={[...path, 'list_items']} />\n```\n\nNode arrays can also carry marks and annotations when their schema includes `mark_types`/`annotation_types`. The value still has one `nodes` array, plus `marks` and `annotations` arrays whose offsets refer to node indexes:\n\n```js\nbuttons: {\n\tnodes: ['button_1', 'button_2', 'button_3'],\n\tmarks: [{ start_offset: 0, end_offset: 2, node_id: 'section_1' }],\n\tannotations: []\n}\n```\n\n`<NodeArrayProperty>` wraps marked ranges with the matching mark component, just like `<TextProperty>` does for text ranges. Child node components also receive range context:\n\n```svelte\n<script>\n\tlet { path, mark: section = null, annotations = [] } = $props();\n</script>\n\n<div class:section-start={section?.is_start}>\n\t<!-- render node content -->\n</div>\n```\n\n`mark` is the mark wrapping the child node, or `null` when none does — mark exclusivity guarantees there is at most one. `annotations` contains all annotations covering the child node, including overlapping ones. Annotations are data-only: they never wrap and are never rendered as components. Each entry contains the flattened attachment plus context for the current child node: `start_offset`, `end_offset`, `node_id`, `index`, `node`, `is_start`, `is_middle`, `is_end`. `node` is the resolved mark or annotation node, `index` is the position in the parent `marks`/`annotations` array, and the `is_*` flags describe the current child node's position in the attached range.\n\nFor styling, you usually don't need the props at all: `<Node>` adds range classes to the node wrapper automatically. A node covered by a `section` mark gets `mark-section`, a node covered by a `marker` annotation gets `anno-marker` — plus `-start`/`-end` variants on the run's first/last covered node. So styling a mark or annotation type — including types without a component — is pure CSS:\n\n```css\n.anno-marker {\n\tbackground: color-mix(in oklch, var(--accent) 13%, transparent);\n}\n```\n\nUse the `annotations` prop only when a component should render something richer than styles (badges, margin indicators, etc.).\n\n> **One path = one DOM mount.** Within a single Svedit document, each node path\n> may be mounted exactly once. Mounting the same path twice (e.g. rendering the\n> same `nav_items` array in both header and footer) breaks anchor positioning,\n> intersection tracking, id uniqueness, and selection mapping.\n> Svedit logs an error if it detects a duplicate mount.\n>\n> If you need to render shared content in multiple places, model it as distinct\n> node_arrays (`header_nav`, `footer_nav`) in the same document, or render it\n> from a separate Svedit instance.\n\n**`<CustomProperty>`** - For custom properties like images or other non-text content:\n\n```svelte\n<CustomProperty class=\"image-wrapper\" path={[...path, 'image']}>\n\t<div contenteditable=\"false\">\n\t\t<img src={node.image} alt={node.title.content} />\n\t</div>\n</CustomProperty>\n```\n\n### Accessing node data\n\nUse the Svedit context to access node data:\n\n```svelte\n<script>\n\timport { getContext } from 'svelte';\n\tconst svedit = getContext('svedit');\n\n\tlet { path } = $props();\n\tlet node = $derived(svedit.session.get(path));\n\tlet layout = $derived(node.layout || 1);\n</script>\n```\n\n### Example: Text component\n\nHere's a complete example of a text node component that supports multiple layouts:\n\n```svelte\n<script>\n\timport { getContext } from 'svelte';\n\timport { Node, TextProperty } from 'svedit';\n\n\tconst svedit = getContext('svedit');\n\tlet { path } = $props();\n\tlet node = $derived(svedit.session.get(path));\n\tlet layout = $derived(node.layout || 1);\n\tlet tag = $derived(layout === 1 ? 'p' : `h${layout - 1}`);\n</script>\n\n<Node {path}>\n\t<div class=\"text layout-{layout}\">\n\t\t<TextProperty {tag} class=\"body\" path={[...path, 'content']} placeholder=\"Enter text\" />\n\t</div>\n</Node>\n```\n\n### Example: List component\n\nA simple list component that renders child items:\n\n```svelte\n<script>\n\timport { Node, NodeArrayProperty } from 'svedit';\n\tlet { path } = $props();\n</script>\n\n<Node {path}>\n\t<div class=\"list\">\n\t\t<NodeArrayProperty path={[...path, 'list_items']} />\n\t</div>\n</Node>\n```\n\n### Registering node components\n\nNode components are registered in the document config's `node_components` map:\n\n```js\nconst session_config = {\n\tnode_components: {\n\t\ttext: Text,\n\t\tstory: Story,\n\t\tlist: List,\n\t\tlist_item: ListItem\n\t\t// ... other components\n\t}\n};\n```\n\nThe keys must be the snake_case node types in your schema. Svedit looks up node components directly by node type, so a node with `type: \"list_item\"` will look for a component registered as `list_item`.\n\n## Mastering contenteditable\n\nSvedit relies on the contenteditable attribute to make elements editable. The below example shows you\na simplified version of the markup of `<NodeGap>` and why it is implemented the way it is.\n\n```html\n<div contenteditable=\"true\">\n\t<div class=\"some-wrapper\">\n\t\t<!--\n      Putting a <br> tag into a div gives you a single addressable caret position.\n\n      Adding a &ZeroWidthSpace; (or any character) here will lead to 2 caret\n      positions (one before, and one after the character)\n\n      Using <wbr> will make it only addressable for ArrowLeft and ArrowRight, but not ArrowUp and ArrowDown.\n      And using <span></span> will not make it addressable at all.\n\n      Svedit uses this behavior for node gaps, and when an\n      <TextProperty> is empty.\n    -->\n\t\t<div class=\"node-gap\"><br /></div>\n\t\t<!--\n      If you create a contenteditable=\"false\" island, there needs to be some content in it,\n      otherwise it will create two additional caret positions. One before, and another one\n      after the island.\n\n      The Svedit demo uses this technique in `<NodeGap>` to create a node-caret\n      visualization, that doesn't mess with the contenteditable caret positions.\n    -->\n\t\t<div contenteditable=\"false\" class=\"node-caret\">&ZeroWidthSpace;</div>\n\t</div>\n</div>\n```\n\nFurther things to consider:\n\n- If you make a sub-tree `contenteditable=\"false\"`, be aware that you can't create a `contenteditable=\"true\"` segment somewhere inside it. Svedit can only work reliably when there's one contenteditable=\"true\" at root (it's set by `<Svedit`>)\n- `<TextProperty>` and `<CustomProperty>` must not be wrapped in `contenteditable=\"false\"` to work properly.\n- Never apply `position: relative` to the direct parent of `<TextProperty>`, it will cause a [weird Safari bug](https://bsky.app/profile/michaelaufreiter.com/post/3lxvdqyxc622s) to destroy the DOM.\n- Never apply `position: relative`, `position: absolute`, `position: fixed` to `<Node.svelte>` (data-type=\"node\") in edit mode. Only `position: static` is permitted to allow css anchor positioning queries to resolve correctly.\n- Never use an `<a>` tag inside a `contenteditable=\"true\"` element, as it will cause unexpected behavior. Make it a `<div>` while editing, and an `<a>` in read-only mode (when `svedit.editable` is `false` ).\n- Avoid adding css `margin` to nodes inside node arrays when using flex or grid layouts. Use `gap` on the container instead. This ensures `NodeGap` and `NodeGapMarkers` render consistently. If you need to add a margin, add it to a child element of Node.\n\n### Consistent DOM structure across modes\n\n`<NodeArrayProperty>` always renders a `.node-gap` element before each node, regardless of whether the editor is in edit or read-only mode. In edit mode, this is the full `<NodeGap>` component with anchor positioning, hit areas, and caret support. In read-only mode, it's a plain `<div class=\"node-gap\"></div>`.\n\nThis means the DOM structure is identical in both modes:\n\n```html\n<!-- Same structure in edit and read-only mode -->\n<div data-type=\"node_array\">\n\t<div class=\"node-gap\">...</div>\n\t<!-- First element is a node-gap -->\n\t<div data-type=\"node\"><!-- first node --></div>\n\t<div class=\"node-gap\">...</div>\n\t<div data-type=\"node\"><!-- second node --></div>\n\t...\n\t<div class=\"node-gap\">...</div>\n\t<!-- Last element is a node-gap -->\n</div>\n```\n\nThe `.node-gap` element uses `display: contents`, so it has no layout impact — it doesn't generate a box, doesn't affect flex/grid item counts, and doesn't consume space. It only serves as a DOM placeholder for consistent selector targeting.\n\nBecause `.node-gap` is always the actual first and last child, do not use `:first-child` or `:last-child` on nodes — they will never match. Use `:nth-child(1 of [data-type=\"node\"])` and `:nth-last-child(1 of [data-type=\"node\"])` instead.\n\nCSS sibling selectors that target node adjacency only need one form:\n\n```css\n/* Works in both edit and read-only mode */\n.node-text + .node-gap + .node-media {\n\tmargin-block-start: 1rem;\n}\n```\n\nFor `:nth-child` selectors, use the `of <selector>` syntax to skip over `.node-gap` elements and count only nodes:\n\n```css\n/* Even nodes */\n.list-node-array > :nth-child(2n of [data-type='node']) {\n\tbackground: var(--zebra-stripe);\n}\n\n/* Odd nodes */\n.list-node-array > :nth-child(2n-1 of [data-type='node']) {\n\tbackground: var(--zebra-stripe);\n}\n\n/* Last node */\n.list-node-array > :nth-last-child(1 of [data-type='node']) {\n\tborder-bottom: none;\n}\n```\n\nWithout the `of` filter, plain `:nth-child(even)` would count `.node-gap` divs and produce incorrect results. The [`of <selector>`](https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Selectors/:nth-child#syntax) form is Baseline 2023 and supported in all modern browsers.\n\n### Node array CSS tokens\n\n`<NodeArrayProperty>` renders a `[data-type=\"node_array\"]` element. Two CSS custom properties control how gaps and carets behave inside it:\n\n**`--row`** — Tell Svedit whether a node array flows horizontally (`1`) or vertically (`0`). Gaps, carets, and markers all switch orientation accordingly. Defaults to `0` on the Svedit canvas. Set it on the node array or an ancestor:\n\n```css\n.my-horizontal-layout :global(.grid-items) {\n\t--row: 1;\n\tdisplay: flex;\n\tflex-wrap: wrap;\n}\n```\n\n**`--node-caret-boundary`** — Edge gaps (before the first node, after the last) extend outward beyond the container to enlarge their click target. If the node array has neighboring elements (e.g. a preceding node or surrounding UI), the outward extension can overlap them:\n\n```\nWithout --node-caret-boundary                  With --node-caret-boundary\n\n: . . . . . . . . . . . . . . . :\n: +---------------------------+ :             +-----------------------------+\n: | Toolbar / UI              | : ← overlap   | Toolbar / UI                |\n: +---------------------------+ :             +-----------------------------+\n:                               :\n:     edge gap (unbounded)      :   boudary → +-----------------------------+\n: . . . . . . . . . . . . . . . :             | : . . . . . . . . . . . . : |\n                                              | :   edge gap (clamped)    : |\n  +---------------------------+               | : . . . . . . . . . . . . : |\n  |  +---------------------+  |               |  +-----------------------+  |\n  |  | First node          |  |               |  | First node            |  |\n  |  +---------------------+  |               |  +-----------------------+  |\n  |    gap between nodes      |               |    gap between nodes        |\n  |  +---------------------+  |               |  +-----------------------+  |\n  |  | Last node           |  |               |  | Last node             |  |\n  |  +---------------------+  |               |  +-----------------------+  |\n  +---------------------------+               | : . . . . . . . . . . . . : |\n                                              | :   edge gap (clamped)    : |\n: . . . . . . . . . . . . . . . :             | : . . . . . . . . . . . . : |\n:     edge gap (unbounded)      :   boudary → +-----------------------------+\n:                               :\n: +---------------------------+ :             +-----------------------------+\n: | Footer / UI               | : ← overlap   | Footer / UI                 |\n: +---------------------------+ :             +-----------------------------+\n: . . . . . . . . . . . . . . . :\n```\n\nSet `--node-caret-boundary` to the `anchor-name` of a parent element to clamp edge gaps to that element's edges:\n\n```css\n.editor-wrapper {\n\tanchor-name: --editor-boundary;\n\tpadding: 24px;\n}\n.editor-wrapper [data-type='node_array'] {\n\t--node-caret-boundary: --editor-boundary;\n}\n```\n\nWhen set, edge gaps clamp to the boundary element's edges instead. When not set, gaps extend to the containing block edge (default).\n\nSince `--node-caret-boundary` inherits to nested node arrays, you may need to unset it on inner containers that should not be clamped:\n\n```css\n.inner-container [data-type='node_array'] {\n\t--node-caret-boundary: initial;\n}\n```\n\nFor per-axis control, use `--node-caret-boundary-x` (left/right) and `--node-caret-boundary-y` (top/bottom). They take precedence over `--node-caret-boundary` when set:\n\n```css\n.editor-wrapper [data-type='node_array'] {\n\t--node-caret-boundary-x: --editor-boundary;\n}\n```\n\n## Beyond the README\n\nThe source code is compact and readable — less than 3000 LOC across a handful of files. We encourage you to explore it. The files in [`src/lib`](./src/lib) are the library code, while the files in [`src/routes`](./src/routes) are example code you can copy and adapt to your needs.\n\n## Developing Svedit\n\nDevelopment requires Node.js 22.17 or newer.\n\nOnce you've cloned the Svedit repository and installed dependencies with `pnpm install`, start a development server:\n\n```bash\npnpm dev\n```\n\n### Running tests\n\nRun the browser tests in Vitest's interactive watch mode:\n\n```bash\npnpm test:unit\n```\n\nFor a single run, use `pnpm test`.\n\nThe tests exercise real browser focus, selection, and keyboard behavior. Keep the\nbrowser runner tab and window focused and let each run finish uninterrupted.\nDo not switch tabs or applications, open DevTools, or click inside the runner\nwhile tests are running. Losing focus can cause selection and keyboard tests to\nfail even when the editor works correctly.\n\nIf a run was interrupted, return focus to the runner and rerun the tests. Failures\nthat persist during a focused, uninterrupted run should be investigated.\n\n## Building\n\nTo create a production version of your app:\n\n```bash\npnpm build\n```\n\nYou can preview the production build with `pnpm preview`.\n\n## Contributing\n\nContributions are very welcome! Bug reports, bug fixes, and small PRs (a couple of lines of code) don't need any ceremony — just go for it. What follows applies to larger changes and new features.\n\nI take long-term maintainability very seriously. Much like the SQLite project, I prioritize minimalism and code quality over features. This means I may decline pull requests — even good ones — if they don't fit my vision for Svedit at a given point in time. I'll always try to articulate my reasons, but sometimes it comes down to intuition more than logic. Please don't take it personally — it doesn't mean your idea is bad, just that I don't see it belonging in core right now.\n\n### How to contribute a feature\n\n1. **Start with your requirements.** Open an issue describing what you need and why. Wait for a green light that this is something that belongs in core before writing code.\n2. **Explore approaches.** For non-trivial features, there may be multiple ways to solve the problem. Discuss trade-offs before committing to one direction — I may ask you to explore alternatives first.\n3. **Prove feasibility.** Make a small PR that solves the root of the problem — no optimizations, no UX polish. This lets me evaluate the impact on the library and give you early feedback if your approach conflicts with Svedit's design decisions.\n4. **Plan the finish.** Once feasibility is approved, outline the remaining steps and wait for another green light. Then we iterate together until it's ready to merge.\n\n**Please don't work on a feature for a long time without checking in regularly.** I have a much lower tolerance for complexity than most developers, and I need to be able to digest changes in small pieces. Going off and making a large rewrite without involving me will likely be frustrating for both of us.\n\n### Sponsorship\n\nAnother great way to help is to donate or sponsor the project, so I can buy more dedicated development time. Email me at michael@letsken.com.\n\n## Beta version\n\nIt's still early. Expect bugs. Expect missing features. Expect the need for more work on your part to make this fit for your use case.\n\n## Credits\n\nSvedit is made by [Michael Aufreiter](https://michaelaufreiter.com) (maintainer) and [Johannes Mutter](https://mutter.co) (design and engineering). Illustrations and icons are hand-crafted by Johannes.\n","readmeFilename":"","description":"Structured, full-canvas editing for Svelte","repository":{"url":"git+https://github.com/michael/svedit.git","type":"git"}}