{"_id":"@artidev/odontogram-core","_rev":"4-6a6f177a1cdbc263843fc18ee79b7f87","name":"@artidev/odontogram-core","dist-tags":{"latest":"0.2.1"},"versions":{"0.1.0":{"name":"@artidev/odontogram-core","version":"0.1.0","keywords":["odontogram","dentistry","dental-chart","svg"],"license":"MIT","_id":"@artidev/odontogram-core@0.1.0","maintainers":[{"name":"artidev","email":"olanda188@gmail.com"}],"homepage":"https://github.com/ArnoldOlanda/odontogram-packages/tree/main/packages/core","bugs":{"url":"https://github.com/ArnoldOlanda/odontogram-packages/issues"},"dist":{"shasum":"dfd278619599d58886326fd6476d41a5ccf640af","tarball":"https://registry.npmjs.org/@artidev/odontogram-core/-/odontogram-core-0.1.0.tgz","fileCount":16,"integrity":"sha512-yS79Kh1b3UHgMgH6pczFnTSS0rLFKG9GxU68VCFre6a2/HB6qPYXMtpP42xZ3VM6plcEjUkb8zH9zn7K/xr0AA==","signatures":[{"sig":"MEYCIQCEaRDMFZ5Uqn8gk3qyrZFU64j3FXdqymqLDT5vVhsOKwIhAMN8G/CJTiCRE3d/a5yr1Rwbnom760O/YPPwE1jqSEbJ","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":342313},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"gitHead":"d4e82fabfa57a23aa0ff41c6a4173fff28fd64cd","scripts":{"build":"vite build && node ./scripts/copy-types.mjs"},"_npmUser":{"name":"artidev","email":"olanda188@gmail.com"},"repository":{"url":"git+https://github.com/ArnoldOlanda/odontogram-packages.git","type":"git","directory":"packages/core"},"_npmVersion":"10.8.2","description":"Framework-agnostic odontogram runtime with bundled SVG assets and a small state-driven API for mounting interactive dental charts in web applications.","directories":{},"_nodeVersion":"20.19.6","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/odontogram-core_0.1.0_1781716397521_0.617182674332359","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"name":"@artidev/odontogram-core","version":"0.1.1","keywords":["odontogram","dentistry","dental-chart","svg"],"license":"MIT","_id":"@artidev/odontogram-core@0.1.1","maintainers":[{"name":"artidev","email":"olanda188@gmail.com"}],"homepage":"https://github.com/ArnoldOlanda/odontogram-packages/tree/main/packages/core","bugs":{"url":"https://github.com/ArnoldOlanda/odontogram-packages/issues"},"dist":{"shasum":"0061d1127127aa9ba1ad412ecf9542213008a9c7","tarball":"https://registry.npmjs.org/@artidev/odontogram-core/-/odontogram-core-0.1.1.tgz","fileCount":16,"integrity":"sha512-hpZ5iPH3Zo4jeTNaieZe9YQ0dWcYQ0hA4Q2KZkODFZNpQDORXOVWs7uHa2OQKEUlyKi6gNPNLtgbldqEJ6XC+Q==","signatures":[{"sig":"MEUCIQC/bKsv0xA6BkD50trQy5SOLc2w9i5rBEBKBHHRSUScmAIgI69d7pT3h3NUhzV3MEpH7+ZM+PTkz4nOYOmE+1cuGa0=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":346075},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"gitHead":"af0d6d0f7ea1813038304789fbfb9ff707d29cc6","scripts":{"build":"vite build && node ./scripts/copy-types.mjs"},"_npmUser":{"name":"artidev","email":"olanda188@gmail.com"},"repository":{"url":"git+https://github.com/ArnoldOlanda/odontogram-packages.git","type":"git","directory":"packages/core"},"_npmVersion":"10.8.2","description":"Low-level odontogram runtime for custom integrations.","directories":{},"_nodeVersion":"20.19.6","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/odontogram-core_0.1.1_1782184129954_0.3510899641811127","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@artidev/odontogram-core","version":"0.2.0","keywords":["odontogram","dentistry","dental-chart","svg"],"license":"MIT","_id":"@artidev/odontogram-core@0.2.0","maintainers":[{"name":"artidev","email":"olanda188@gmail.com"}],"homepage":"https://github.com/ArnoldOlanda/odontogram-packages/tree/main/packages/core","bugs":{"url":"https://github.com/ArnoldOlanda/odontogram-packages/issues"},"dist":{"shasum":"e8d0b05089dc447f6fcc4a21733362e0928a6a11","tarball":"https://registry.npmjs.org/@artidev/odontogram-core/-/odontogram-core-0.2.0.tgz","fileCount":18,"integrity":"sha512-Bbdx/tiz1/kx90r4PMV3Ko+tPAJXKpN3101UQ5drL5zh+ICrcjrOsOo1yQUCSkNaoK4zJqL9Lrjd8K5UC9ukWQ==","signatures":[{"sig":"MEQCIED3t331m6Wpodanp/1TUYnGYh/qi5A1+X3XsC6etTbzAiAFtp9nvphQsvPmYQ8/BCbkojegnrZ3/+GK4vydwuk1Bg==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":365026},"main":"./src/index.js","type":"module","types":"./src/index.d.ts","module":"./src/index.js","exports":{".":{"types":"./src/index.d.ts","import":"./src/index.js"},"./assets/*":"./assets/*"},"gitHead":"fa9c7b4ed9019985f7f11516e7ff170c16bc19a8","scripts":{"build":"tsc"},"_npmUser":{"name":"artidev","email":"olanda188@gmail.com"},"repository":{"url":"git+https://github.com/ArnoldOlanda/odontogram-packages.git","type":"git","directory":"packages/core"},"_npmVersion":"10.8.2","description":"Low-level odontogram runtime for custom integrations.","directories":{},"sideEffects":false,"_nodeVersion":"20.19.6","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/odontogram-core_0.2.0_1784742139788_0.21049042881036417","host":"s3://npm-registry-packages-npm-production"}},"0.2.1":{"name":"@artidev/odontogram-core","version":"0.2.1","license":"MIT","type":"module","main":"./src/index.js","module":"./src/index.js","types":"./src/index.d.ts","sideEffects":false,"exports":{".":{"types":"./src/index.d.ts","import":"./src/index.js"},"./assets/*":"./assets/*"},"keywords":["odontogram","dentistry","dental-chart","svg"],"repository":{"type":"git","url":"git+https://github.com/ArnoldOlanda/odontogram-packages.git","directory":"packages/core"},"homepage":"https://github.com/ArnoldOlanda/odontogram-packages/tree/main/packages/core","bugs":{"url":"https://github.com/ArnoldOlanda/odontogram-packages/issues"},"publishConfig":{"access":"public"},"scripts":{"build":"tsc"},"_id":"@artidev/odontogram-core@0.2.1","gitHead":"c506c2ad548ccc70861e5bd870691637cb011677","description":"Low-level, framework-agnostic odontogram runtime for custom integrations. The runtime manages an interactive dental chart: per-tooth state, occlusal overlays, a built-in control panel, and SVG asset resolution.","_nodeVersion":"20.19.6","_npmVersion":"10.8.2","dist":{"integrity":"sha512-HY2/dqAk7UoLVO00gpsbBaM18XOgXQ1fwZnq2TwhtnVaEvkkozvYkRJD58TmkH6vK9mBCKOd7XM9x/bpl/469Q==","shasum":"6239a0b82db649a679c35569f70958ab4b4d0027","tarball":"https://registry.npmjs.org/@artidev/odontogram-core/-/odontogram-core-0.2.1.tgz","fileCount":18,"unpackedSize":374720,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIHlhdQbJxCNrg29niZtIWzEs21WSLv6irHhcIjzNUbdXAiEA44Z81Jfwttl3GC708h3d1ft/o2bOcDGXnqX/J+FU/iY="}]},"_npmUser":{"name":"artidev","email":"olanda188@gmail.com"},"directories":{},"maintainers":[{"name":"artidev","email":"olanda188@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/odontogram-core_0.2.1_1784906252694_0.9496554945474531"},"_hasShrinkwrap":false}},"time":{"created":"2026-06-17T17:13:17.419Z","modified":"2026-07-24T15:17:33.043Z","0.1.0":"2026-06-17T17:13:17.685Z","0.1.1":"2026-06-23T03:08:50.097Z","0.2.0":"2026-07-22T17:42:19.945Z","0.2.1":"2026-07-24T15:17:32.878Z"},"bugs":{"url":"https://github.com/ArnoldOlanda/odontogram-packages/issues"},"license":"MIT","homepage":"https://github.com/ArnoldOlanda/odontogram-packages/tree/main/packages/core","keywords":["odontogram","dentistry","dental-chart","svg"],"repository":{"type":"git","url":"git+https://github.com/ArnoldOlanda/odontogram-packages.git","directory":"packages/core"},"description":"Low-level, framework-agnostic odontogram runtime for custom integrations. The runtime manages an interactive dental chart: per-tooth state, occlusal overlays, a built-in control panel, and SVG asset resolution.","maintainers":[{"name":"artidev","email":"olanda188@gmail.com"}],"readme":"# @artidev/odontogram-core\r\n\r\nLow-level, framework-agnostic odontogram runtime for custom integrations. The runtime manages an interactive dental chart: per-tooth state, occlusal overlays, a built-in control panel, and SVG asset resolution.\r\n\r\nIf you use Vue 3 and want a working chart in minutes, use [`@artidev/vue-odontogram`](../vue/README.md) instead. Use this package when you need direct runtime control, custom asset overrides, or a non-Vue wrapper.\r\n\r\n## Quick path\r\n\r\n1. Install the package.\r\n2. Provide a root element with the runtime hooks your wrapper needs.\r\n3. Create an instance, call `init()`, and listen to `onChange`.\r\n\r\n```bash\r\nnpm install @artidev/odontogram-core\r\n```\r\n\r\n```ts\r\nimport { createOdontogramInstance } from '@artidev/odontogram-core'\r\n\r\nconst root = document.getElementById('odontogram-root')\r\nif (!root) throw new Error('Missing #odontogram-root')\r\n\r\nconst instance = createOdontogramInstance(root, {\r\n  onChange: (state) => console.log('chart changed', state),\r\n})\r\n\r\nawait instance.init()\r\n```\r\n\r\n## When to use this package\r\n\r\n| Use it when | Don't use it when |\r\n|---|---|\r\n| You are building a custom wrapper around the runtime. | You want a drop-in component — use `@artidev/vue-odontogram`. |\r\n| You need to override the bundled SVG assets with your own. | You're integrating from Vue without unusual needs. |\r\n| You are integrating from a non-Vue framework (React, Svelte, vanilla). | You only need the public API — use the Vue wrapper. |\r\n| You need raw access to `init()` / `importState()` / `exportState()`. | |\r\n\r\n## Distribution model\r\n\r\nThis package ships ES module source code (`src/`) plus the bundled SVG assets (`assets/`). It does **not** ship a pre-built bundle.\r\n\r\n### Why no pre-built bundle\r\n\r\nThe runtime resolves bundled SVG files with `new URL('../assets/...', import.meta.url)`. Bundlers like Vite, Rollup, and Webpack transform that pattern only when they process the source file — i.e. inside your app's source graph, not inside a pre-built `dist/`.\r\n\r\nThe previous 0.1.x line shipped a pre-built `dist/index.js`. Consumer bundlers skipped the URL transform and emitted no SVG files, causing silent 404 errors in production builds. 0.2.0 fixes this by removing `dist/` and shipping the source directly.\r\n\r\n### Practical implications\r\n\r\n| Scenario | Works? |\r\n|---|---|\r\n| `import { ... } from '@artidev/odontogram-core'` in a Vite / Rollup / Webpack / Parcel app | ✅ |\r\n| `<script src=\"/node_modules/@artidev/odontogram-core/dist/index.js\">` direct usage | ❌ No `dist/` is published |\r\n| CDN with a bundler-built artifact of your app | ✅ (use your consumer-built output) |\r\n| Plain `<script>` in a static HTML page | ❌ Not supported |\r\n\r\n### `package.json` shape\r\n\r\nThe package now exposes:\r\n\r\n- `main` / `module` / `types` → `./src/...`\r\n- `exports[\"./assets/*\"]` → `./assets/*` (consumers can resolve individual SVG files)\r\n- `sideEffects: false` (better tree-shaking)\r\n- `files` → `[\"src\", \"assets\", \"!**/*.test.ts\"]` (no tests, no scripts ship)\r\n\r\n### Verifying the build\r\n\r\nAfter `npm run build` in your app, check the emitted URLs:\r\n\r\n```ts\r\nimport { defaultAssets } from '@artidev/odontogram-core'\r\n\r\nconsole.log(defaultAssets.teeth[11])\r\n// Vite dev:    /node_modules/@artidev/odontogram-core/assets/teeth-svgs/11.svg\r\n// Vite build:  /assets/teeth-svgs/11-[hash].svg\r\n```\r\n\r\nIf the URL is still a relative `../assets/teeth-svgs/11.svg` after build, your bundler is not processing the package source — see [Troubleshooting](#troubleshooting).\r\n\r\n### A note on `new URL()` placement\r\n\r\n`new URL(..., import.meta.url)` calls in `defaultAssets` are intentionally inlined at the call site rather than wrapped in a helper like `assetUrl(path)`. Vite and Rollup only transform the pattern when both arguments are statically analyzable string literals at the call site. Wrapping the path in a helper hides it from the static analyzer and silently breaks asset emission. Do not refactor `defaultAssets` into a helper.\r\n\r\n## API\r\n\r\n### Exports\r\n\r\n| Export | Kind | Description |\r\n|---|---|---|\r\n| `createOdontogramInstance` | function | Creates an isolated runtime instance bound to a root element |\r\n| `defaultAssets` | object | Bundled SVG asset URLs (teeth templates, occlusal overlays, icons) |\r\n\r\n### TypeScript types\r\n\r\n```ts\r\nimport type {\r\n  OdontogramState,\r\n  SerializedToothState,\r\n  OdontogramAssets,\r\n  CreateOdontogramOptions,\r\n  OdontogramInstance,\r\n} from '@artidev/odontogram-core'\r\n\r\n// Record<number, SerializedToothState> — keyed by FDI tooth number\r\ntype OdontogramState = Record<number, SerializedToothState>\r\n\r\n// Free-form per-tooth data; the runtime normalizes invalid values\r\ntype SerializedToothState = Record<string, unknown>\r\n\r\n// Asset overrides; each slot merges on top of defaultAssets\r\ninterface OdontogramAssets {\r\n  teeth?: Partial<Record<ToothTemplateKey, string>>            // 11 | 13 | 14 | 16\r\n  teethOcclusal?: Partial<Record<OcclusalTemplateKey, string>> // 14 | 16\r\n  icons?: Partial<{\r\n    occlusal: string\r\n    wisdom: string\r\n    bone: string\r\n    pulp: string\r\n    clearSelection: string\r\n  }>\r\n}\r\n\r\n// Instance options\r\ninterface CreateOdontogramOptions {\r\n  onChange?: (state: OdontogramState) => void\r\n  readOnly?: boolean\r\n  assets?: OdontogramAssets\r\n}\r\n```\r\n\r\n### `createOdontogramInstance(rootEl, options?)`\r\n\r\nCreates one isolated runtime instance bound to `rootEl`.\r\n\r\n| Argument | Type | Description |\r\n|---|---|---|\r\n| `rootEl` | `HTMLElement` | The container the runtime mounts into |\r\n| `options` | `CreateOdontogramOptions \\| (state) => void` | Configuration object, or just an `onChange` callback shorthand |\r\n\r\nReturns an `OdontogramInstance`.\r\n\r\n### Instance methods\r\n\r\n| Method | Returns | Description |\r\n|---|---|---|\r\n| `init()` | `Promise<void>` | Builds the chart UI inside `rootEl`. Await before calling `importState()` |\r\n| `destroy()` | `void` | Removes listeners, clears grid state, detaches the DOM. Safe to call once |\r\n| `importState(data)` | `void` | Replaces the full chart state. Missing teeth fall back to defaults. Invalid enum values are normalized, not rejected |\r\n| `exportState()` | `OdontogramState` | Returns the current full serialized chart state |\r\n| `setReadOnly(value)` | `void` | Toggles readonly mode after mount. `true` blocks editing interactions |\r\n\r\n## State schema\r\n\r\n`OdontogramState` is `Record<number, SerializedToothState>` keyed by **FDI tooth number** — the World Health Organization notation for permanent dentition: 32 teeth numbered `11..18`, `21..28`, `31..38`, `41..48`. Quadrant 1 is upper right, quadrant 4 is lower left.\r\n\r\n### Per-tooth shape\r\n\r\nEach `SerializedToothState` is a free-form record the runtime normalizes on import. The runtime recognizes the following keys:\r\n\r\n| Key | Type | Accepted values | Notes |\r\n|---|---|---|---|\r\n| `toothSelection` | string | `none`, `tooth-base`, `milktooth`, `implant`, `tooth-crownprep`, `tooth-under-gum`, `no-tooth-after-extraction` | `milktooth` is rejected on molars (16..18, 26..28, 36..38, 46..48) |\r\n| `crownMaterial` | string | `natural`, `broken`, `radix`, `emax`, `zircon`, `metal`, `temporary`, `telescope`, `healing-abutment`, `locator`, `locator-prosthesis`, `bar`, `bar-prosthesis` | Constrained per `toothSelection` (implants use a subset) |\r\n| `caries` | string[] | `caries-subcrown`, `caries-buccal`, `caries-lingual`, `caries-mesial`, `caries-distal`, `caries-occlusal` | Affected surfaces |\r\n| `fillingMaterial` | string | `none`, `amalgam`, `composite`, `gic`, `temporary` | |\r\n| `fillingSurfaces` | string[] | `buccal`, `lingual`, `mesial`, `distal`, `occlusal` | |\r\n| `fissureSealing` | boolean | | Only valid on permanent molars (16, 17, 26, 27, 36, 37, 46, 47) |\r\n| `endo` | string | `none`, `endo-medical-filling`, `endo-filling`, `endo-filling-incomplete`, `endo-glass-pin`, `endo-metal-pin` | |\r\n| `pulpInflam` | boolean | | |\r\n| `endoResection` | boolean | | |\r\n| `parapulpalPin` | boolean | | |\r\n| `mobility` | string | `none`, `m1`, `m2`, `m3` | |\r\n| `mods` | string[] | `inflammation`, `parodontal`, `mobility` | |\r\n| `contactMesial` | boolean | | |\r\n| `contactDistal` | boolean | | |\r\n| `bruxismWear` | boolean | | Incisal wear |\r\n| `bruxismNeckWear` | boolean | | Cervical wear |\r\n| `brokenMesial` | boolean | | Broken-crown facet flags |\r\n| `brokenIncisal` | boolean | | |\r\n| `brokenDistal` | boolean | | |\r\n| `extractionWound` | boolean | | |\r\n| `extractionPlan` | boolean | | |\r\n| `missingClosed` | boolean | | Space closed after extraction |\r\n| `bridgePillar` | boolean | | |\r\n| `bridgeUnit` | string | `none`, `removable`, `zircon`, `metal`, `temporary`, `bar`, `bar-prosthesis` | Pontic material |\r\n| `crownReplace` | boolean | | |\r\n| `crownNeeded` | boolean | | |\r\n| `customStates` | object | | Free-form escape hatch for app-specific data |\r\n| `note` | string | | |\r\n\r\n### Import semantics\r\n\r\n- `importState(data)` does a **full replacement**, not a patch.\r\n- Missing teeth in `data` fall back to `defaultState()` (tooth-base + natural crown).\r\n- Invalid enum values are silently normalized to the closest valid value. No errors are thrown.\r\n- The runtime coerces boolean-like strings (`\"true\"`, `\"yes\"`, `\"si\"`, `\"sí\"`, `\"on\"`) for legacy data.\r\n\r\n## Asset catalog\r\n\r\n`defaultAssets` exposes:\r\n\r\n| Key | Available values | Description |\r\n|---|---|---|\r\n| `defaultAssets.teeth` | `11`, `13`, `14`, `16` | Tooth template SVG URLs (4 templates; the engine mirrors and rotates them to render the other 28 teeth) |\r\n| `defaultAssets.teethOcclusal` | `14`, `16` | Occlusal surface overlay URLs for the two occlusal templates |\r\n| `defaultAssets.icons.occlusal` | string | Occlusal view toggle icon |\r\n| `defaultAssets.icons.wisdom` | string | Wisdom teeth visibility toggle icon |\r\n| `defaultAssets.icons.bone` | string | Bone/gum visibility toggle icon |\r\n| `defaultAssets.icons.pulp` | string | Pulp visibility toggle icon |\r\n| `defaultAssets.icons.clearSelection` | string | Clear selection icon |\r\n\r\n### Overriding assets\r\n\r\nPass a partial `OdontogramAssets` to `createOdontogramInstance`. Your override merges on top of `defaultAssets`, slot by slot:\r\n\r\n```ts\r\nimport { createOdontogramInstance } from '@artidev/odontogram-core'\r\n\r\nconst instance = createOdontogramInstance(root, {\r\n  assets: {\r\n    teeth: {\r\n      16: '/my-assets/custom-molar.svg',\r\n    },\r\n    icons: {\r\n      wisdom: '/brand/icons/wisdom.svg',\r\n      bone: '/brand/icons/bone.svg',\r\n    },\r\n  },\r\n})\r\n```\r\n\r\nFor full replacement of a slot, supply every key the runtime expects (see the table above).\r\n\r\nYou can also resolve individual SVG files directly via the package's `exports` map:\r\n\r\n```ts\r\nimport tooth16 from '@artidev/odontogram-core/assets/teeth-svgs/16.svg'\r\n```\r\n\r\nThis is the recommended way to ship asset overrides through a CDN or a static asset folder.\r\n\r\n## Examples\r\n\r\n### Create an instance\r\n\r\n```ts\r\nimport { createOdontogramInstance } from '@artidev/odontogram-core'\r\n\r\nconst root = document.getElementById('odontogram-root')\r\nif (!root) throw new Error('Missing #odontogram-root')\r\n\r\nconst instance = createOdontogramInstance(root, {\r\n  readOnly: false,\r\n  onChange(state) {\r\n    console.log('chart changed', state)\r\n  },\r\n})\r\n\r\nawait instance.init()\r\n```\r\n\r\n### Persist and restore\r\n\r\n```ts\r\n// Save\r\nlocalStorage.setItem('odontogram', JSON.stringify(instance.exportState()))\r\n\r\n// Restore\r\nconst saved = JSON.parse(localStorage.getItem('odontogram') ?? '{}')\r\ninstance.importState(saved)\r\n```\r\n\r\n### Read-only review mode\r\n\r\n```ts\r\nconst review = createOdontogramInstance(root, {\r\n  readOnly: true,\r\n  onChange(state) {\r\n    // read-only — onChange still fires when state is imported programmatically\r\n    auditTrail.log(state)\r\n  },\r\n})\r\n\r\nawait review.init()\r\nreview.importState({\r\n  11: { toothSelection: 'implant', crownMaterial: 'zircon' },\r\n  21: { toothSelection: 'tooth-base', crownMaterial: 'broken' },\r\n})\r\n```\r\n\r\n### Asset override\r\n\r\n```ts\r\nconst themed = createOdontogramInstance(root, {\r\n  assets: {\r\n    icons: {\r\n      wisdom: '/brand/icons/wisdom.svg',\r\n    },\r\n  },\r\n})\r\n```\r\n\r\n### Load a saved chart asynchronously\r\n\r\n```ts\r\nasync function loadChart(instance: OdontogramInstance, id: string) {\r\n  const res = await fetch(`/api/charts/${id}`)\r\n  const state: OdontogramState = await res.json()\r\n  instance.importState(state)\r\n}\r\n```\r\n\r\n## Troubleshooting\r\n\r\n### SVGs return 404 in production\r\n\r\n`defaultAssets.teeth[11]` resolves to a relative `../assets/...` URL after build. Your bundler is not processing the package source.\r\n\r\nFix:\r\n\r\n- Confirm you are not pinning `dist/index.js` anywhere (the file no longer ships).\r\n- If you use Vite, make sure `@artidev/odontogram-core` is not in `optimizeDeps.exclude`.\r\n- Check that the asset URLs appear in your build manifest with hashes (`assets/teeth-svgs/11-[hash].svg`).\r\n- If you are using a custom build pipeline, make sure the package is part of the source graph and not treated as pre-built.\r\n\r\n### Tree-shaking strips runtime functions\r\n\r\nMake sure your bundler respects `sideEffects: false`. Webpack 5 does this by default when reading `package.json#sideEffects`.\r\n\r\n### TypeScript types missing\r\n\r\nIf `tsc` cannot find the types, ensure `moduleResolution` is `node16`, `nodenext`, or `bundler`. The package exposes `.d.ts` next to the `.js` source.\r\n\r\n### Refactor broke SVG emission\r\n\r\nIf you wrap the `new URL()` calls in `default-assets.js` in a helper function, asset emission will break silently. The path argument must be a string literal at the call site. See [Distribution model](#distribution-model).\r\n\r\n## Migration from 0.1.x\r\n\r\n| Before (0.1.x) | After (0.2.0) |\r\n|---|---|\r\n| `import { ... } from '@artidev/odontogram-core/dist/index.js'` | `import { ... } from '@artidev/odontogram-core'` |\r\n| `<script src=\".../dist/index.js\">` (UMD-ish direct usage) | Not supported. Build with a bundler. |\r\n| `dist/` present in the published tarball | Gone — only `src/` and `assets/` ship. |\r\n\r\nThere are **no API changes**. If your bundler processed `node_modules` correctly in 0.1.x, you may have had broken SVG URLs without knowing it — the fix is automatic with 0.2.0.\r\n\r\n## Related packages\r\n\r\n- [`@artidev/vue-odontogram`](../vue/README.md) — ready-to-use Vue 3 wrapper around this runtime.\r\n\r\n## License\r\n\r\nMIT\r\n","readmeFilename":"README.md"}