@alfadocs/ui-kit-debug 1.4.16 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_chunks/{anamnesis-summary-rows-CVzxC2X3.js → anamnesis-summary-rows-BpYztFUv.js} +20 -4
- package/dist/_chunks/{anamnesis-summary-rows-CVzxC2X3.js.map → anamnesis-summary-rows-BpYztFUv.js.map} +1 -1
- package/dist/_chunks/{care-plan-card-C83Hb9EE.js → care-plan-card-CehAU7F_.js} +2 -2
- package/dist/_chunks/{care-plan-card-C83Hb9EE.js.map → care-plan-card-CehAU7F_.js.map} +1 -1
- package/dist/_chunks/{care-plan-entry-card-CHMPSTCz.js → care-plan-entry-card-ChWRMfMw.js} +2 -2
- package/dist/_chunks/{care-plan-entry-card-CHMPSTCz.js.map → care-plan-entry-card-ChWRMfMw.js.map} +1 -1
- package/dist/_chunks/{periodontal-chart-brakjj0s.js → periodontal-chart-C0nNQKKK.js} +121 -121
- package/dist/_chunks/{periodontal-chart-brakjj0s.js.map → periodontal-chart-C0nNQKKK.js.map} +1 -1
- package/dist/_chunks/tooth-data-DR8Kdnup.js +903 -0
- package/dist/_chunks/tooth-data-DR8Kdnup.js.map +1 -0
- package/dist/_chunks/tooth-finding-palette-vqC-D2Ye.js +708 -0
- package/dist/_chunks/tooth-finding-palette-vqC-D2Ye.js.map +1 -0
- package/dist/_chunks/tooth-scheme-BPXE1_3N.js +2051 -0
- package/dist/_chunks/tooth-scheme-BPXE1_3N.js.map +1 -0
- package/dist/agent-catalog.json +465 -21
- package/dist/agent-i18n/en.json +23 -6
- package/dist/components/care-plan-card/index.js +1 -1
- package/dist/components/care-plan-entry-card/index.js +1 -1
- package/dist/components/index.d.ts +1 -0
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/periodontal-chart/index.js +1 -1
- package/dist/components/tooth-finding-palette/index.d.ts +4 -0
- package/dist/components/tooth-finding-palette/index.d.ts.map +1 -0
- package/dist/components/tooth-finding-palette/index.js +9 -0
- package/dist/components/tooth-finding-palette/index.js.map +1 -0
- package/dist/components/tooth-finding-palette/tooth-finding-palette.agent.d.ts +4 -0
- package/dist/components/tooth-finding-palette/tooth-finding-palette.agent.d.ts.map +1 -0
- package/dist/components/tooth-finding-palette/tooth-finding-palette.d.ts +89 -0
- package/dist/components/tooth-finding-palette/tooth-finding-palette.d.ts.map +1 -0
- package/dist/components/tooth-scheme/index.d.ts +2 -2
- package/dist/components/tooth-scheme/index.d.ts.map +1 -1
- package/dist/components/tooth-scheme/index.js +57 -30
- package/dist/components/tooth-scheme/tooth-data.d.ts +279 -10
- package/dist/components/tooth-scheme/tooth-data.d.ts.map +1 -1
- package/dist/components/tooth-scheme/tooth-scheme.agent.d.ts +2 -2
- package/dist/components/tooth-scheme/tooth-scheme.agent.d.ts.map +1 -1
- package/dist/components/tooth-scheme/tooth-scheme.d.ts +30 -3
- package/dist/components/tooth-scheme/tooth-scheme.d.ts.map +1 -1
- package/dist/i18n/locales/ar.d.ts +40 -1
- package/dist/i18n/locales/ar.d.ts.map +1 -1
- package/dist/i18n/locales/ar.js +41 -2
- package/dist/i18n/locales/ar.js.map +1 -1
- package/dist/i18n/locales/de.d.ts +40 -1
- package/dist/i18n/locales/de.d.ts.map +1 -1
- package/dist/i18n/locales/de.js +41 -2
- package/dist/i18n/locales/de.js.map +1 -1
- package/dist/i18n/locales/el.d.ts +40 -1
- package/dist/i18n/locales/el.d.ts.map +1 -1
- package/dist/i18n/locales/el.js +41 -2
- package/dist/i18n/locales/el.js.map +1 -1
- package/dist/i18n/locales/en.d.ts +40 -1
- package/dist/i18n/locales/en.d.ts.map +1 -1
- package/dist/i18n/locales/en.js +41 -2
- package/dist/i18n/locales/en.js.map +1 -1
- package/dist/i18n/locales/es.d.ts +40 -1
- package/dist/i18n/locales/es.d.ts.map +1 -1
- package/dist/i18n/locales/es.js +41 -2
- package/dist/i18n/locales/es.js.map +1 -1
- package/dist/i18n/locales/fr.d.ts +40 -1
- package/dist/i18n/locales/fr.d.ts.map +1 -1
- package/dist/i18n/locales/fr.js +41 -2
- package/dist/i18n/locales/fr.js.map +1 -1
- package/dist/i18n/locales/hi.d.ts +40 -1
- package/dist/i18n/locales/hi.d.ts.map +1 -1
- package/dist/i18n/locales/hi.js +41 -2
- package/dist/i18n/locales/hi.js.map +1 -1
- package/dist/i18n/locales/it.d.ts +40 -1
- package/dist/i18n/locales/it.d.ts.map +1 -1
- package/dist/i18n/locales/it.js +41 -2
- package/dist/i18n/locales/it.js.map +1 -1
- package/dist/i18n/locales/ja.d.ts +40 -1
- package/dist/i18n/locales/ja.d.ts.map +1 -1
- package/dist/i18n/locales/ja.js +41 -2
- package/dist/i18n/locales/ja.js.map +1 -1
- package/dist/i18n/locales/nl.d.ts +40 -1
- package/dist/i18n/locales/nl.d.ts.map +1 -1
- package/dist/i18n/locales/nl.js +41 -2
- package/dist/i18n/locales/nl.js.map +1 -1
- package/dist/i18n/locales/pl.d.ts +40 -1
- package/dist/i18n/locales/pl.d.ts.map +1 -1
- package/dist/i18n/locales/pl.js +41 -2
- package/dist/i18n/locales/pl.js.map +1 -1
- package/dist/i18n/locales/pt.d.ts +40 -1
- package/dist/i18n/locales/pt.d.ts.map +1 -1
- package/dist/i18n/locales/pt.js +41 -2
- package/dist/i18n/locales/pt.js.map +1 -1
- package/dist/i18n/locales/ro.d.ts +40 -1
- package/dist/i18n/locales/ro.d.ts.map +1 -1
- package/dist/i18n/locales/ro.js +41 -2
- package/dist/i18n/locales/ro.js.map +1 -1
- package/dist/i18n/locales/ru.d.ts +40 -1
- package/dist/i18n/locales/ru.d.ts.map +1 -1
- package/dist/i18n/locales/ru.js +41 -2
- package/dist/i18n/locales/ru.js.map +1 -1
- package/dist/i18n/locales/sq.d.ts +40 -1
- package/dist/i18n/locales/sq.d.ts.map +1 -1
- package/dist/i18n/locales/sq.js +41 -2
- package/dist/i18n/locales/sq.js.map +1 -1
- package/dist/i18n/locales/sv.d.ts +40 -1
- package/dist/i18n/locales/sv.d.ts.map +1 -1
- package/dist/i18n/locales/sv.js +41 -2
- package/dist/i18n/locales/sv.js.map +1 -1
- package/dist/i18n/locales/tr.d.ts +40 -1
- package/dist/i18n/locales/tr.d.ts.map +1 -1
- package/dist/i18n/locales/tr.js +41 -2
- package/dist/i18n/locales/tr.js.map +1 -1
- package/dist/i18n/locales/zh.d.ts +40 -1
- package/dist/i18n/locales/zh.d.ts.map +1 -1
- package/dist/i18n/locales/zh.js +41 -2
- package/dist/i18n/locales/zh.js.map +1 -1
- package/dist/index.js +448 -415
- package/dist/index.js.map +1 -1
- package/dist/locales/ar.json +41 -2
- package/dist/locales/de.json +41 -2
- package/dist/locales/el.json +41 -2
- package/dist/locales/en.json +41 -2
- package/dist/locales/es.json +41 -2
- package/dist/locales/fr.json +41 -2
- package/dist/locales/hi.json +41 -2
- package/dist/locales/it.json +41 -2
- package/dist/locales/ja.json +41 -2
- package/dist/locales/nl.json +41 -2
- package/dist/locales/pl.json +41 -2
- package/dist/locales/pt.json +41 -2
- package/dist/locales/ro.json +41 -2
- package/dist/locales/ru.json +41 -2
- package/dist/locales/sq.json +41 -2
- package/dist/locales/sv.json +41 -2
- package/dist/locales/tr.json +41 -2
- package/dist/locales/zh.json +41 -2
- package/dist/patterns/anamnesis/anamnesis-i18n.d.ts.map +1 -1
- package/dist/patterns/anamnesis/index.js +1 -1
- package/dist/tokens.css +1 -1
- package/package.json +5 -1
- package/dist/_chunks/tooth-data-BwuoKFf4.js +0 -660
- package/dist/_chunks/tooth-data-BwuoKFf4.js.map +0 -1
- package/dist/_chunks/tooth-scheme-s0ipC0-Z.js +0 -1483
- package/dist/_chunks/tooth-scheme-s0ipC0-Z.js.map +0 -1
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"tooth-data-BwuoKFf4.js","sources":["../../src/components/tooth-scheme/tooth-data.ts"],"sourcesContent":["/* ------------------------------------------------------------------ */\n/* ToothScheme — data model + lookup tables. */\n/* */\n/* FDI ISO 3950 is the canonical internal id. This module exposes */\n/* conversion tables to Universal (ADA) and a simplified Palmer-style */\n/* quadrant+position string so consuming UIs can toggle numbering. */\n/* */\n/* Why simplified Palmer? True Palmer notation uses quadrant bracket */\n/* glyphs (\"⌐\", \"¬\", \"L\", \"J\") that do not render reliably in every */\n/* browser/font combination. We emit a readable `UR3` / `LL6` form */\n/* instead and document the deviation in the component MDX. */\n/* ------------------------------------------------------------------ */\n\n/* ------------------------------------------------------------------ */\n/* Core types */\n/* ------------------------------------------------------------------ */\n\nexport type FdiId = string; // '11'–'48' permanent, '51'–'85' primary\n\nexport type Dentition = 'permanent' | 'primary' | 'mixed';\nexport type Numbering = 'fdi' | 'universal' | 'palmer';\nexport type ToothMode = 'interactive' | 'display';\nexport type Surface = 'mesial' | 'distal' | 'occlusal' | 'buccal' | 'lingual';\nexport type ToothCondition =\n | 'caries'\n | 'filled'\n | 'crowned'\n | 'temporaryCrown'\n | 'bridge'\n | 'missing'\n | 'implant'\n | 'implantExtraction'\n | 'stub'\n | 'destroyed'\n | 'rootCanal';\n\nexport interface ToothState {\n conditions: ToothCondition[];\n surfaces: Surface[];\n notes?: string;\n}\n\nexport type ToothChart = Record<FdiId, ToothState>;\n\n/** Radiograph (x-ray) type. `'periapical'` is the intraoral *endorale*. */\nexport type RadiographKind = 'periapical' | 'bitewing' | 'panoramic' | 'other';\n\n/**\n * A single radiograph attached to a tooth. Image urls are opaque,\n * consumer-hosted strings, interpolated into an SVG `<image href>` / `<img src>`\n * (which cannot execute scripts) — never bundled and never runtime-validated.\n * Real radiographs are PHI: stories and demos must use synthetic placeholders.\n */\nexport interface Radiograph {\n /** Stable id — keys the archive list and the imperative open call. */\n id: string;\n /** Full-resolution image url. */\n url: string;\n /** Optional smaller preview url (falls back to `url`). */\n thumbnailUrl?: string;\n /** ISO acquisition date (`YYYY-MM-DD`). */\n date?: string;\n /** Free-text clinical note. */\n note?: string;\n /** Radiograph type. Default `'periapical'` (an *endorale*). */\n kind?: RadiographKind;\n /**\n * Accessible description for the FULL-SIZE image, consumed by a downstream\n * viewer (e.g. the Dental Imaging Hub archive `<img>`). The chart's slot\n * render is decorative (`aria-hidden` — the tooth's accessible name carries\n * the radiograph count), so `alt` is intentionally not read by the chart.\n */\n alt?: string;\n}\n\nexport type Anatomy = 'incisor' | 'canine' | 'premolar' | 'molar';\nexport type Arch = 'upper' | 'lower';\nexport type Side = 'right' | 'left'; // patient's side (NOT viewer's)\nexport type Quadrant = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8;\n\nexport interface ToothMeta {\n fdi: FdiId;\n /** '1'–'32' for permanent, 'A'–'T' for primary. */\n universal: string;\n /** Simplified Palmer quadrant-and-position string, e.g. 'UR3', 'LL6'. */\n palmer: string;\n quadrant: Quadrant;\n /** 1–8 (permanent) or 1–5 (primary), counted from the midline outward. */\n positionInQuadrant: number;\n anatomy: Anatomy;\n arch: Arch;\n side: Side;\n dentition: 'permanent' | 'primary';\n}\n\n/* ------------------------------------------------------------------ */\n/* Layout orders */\n/* */\n/* Layout reads from the viewer's perspective, top-left to bottom- */\n/* right. The chart is FIXED in clinical convention: the patient's */\n/* right side sits on the viewer's left, regardless of document `dir`. */\n/* ------------------------------------------------------------------ */\n\n/**\n * 32 permanent teeth in visual layout order:\n * upper row: 18..11 (patient upper-right, outermost → midline), then 21..28 (upper-left, midline → outermost)\n * lower row: 48..41 (patient lower-right, outermost → midline), then 31..38 (lower-left, midline → outermost)\n */\nexport const PERMANENT_TEETH: FdiId[] = [\n // upper\n '18',\n '17',\n '16',\n '15',\n '14',\n '13',\n '12',\n '11',\n '21',\n '22',\n '23',\n '24',\n '25',\n '26',\n '27',\n '28',\n // lower\n '48',\n '47',\n '46',\n '45',\n '44',\n '43',\n '42',\n '41',\n '31',\n '32',\n '33',\n '34',\n '35',\n '36',\n '37',\n '38',\n];\n\n/**\n * 20 primary teeth in visual layout order:\n * upper row: 55..51 (upper-right outermost → midline), then 61..65\n * lower row: 85..81, then 71..75\n */\nexport const PRIMARY_TEETH: FdiId[] = [\n // upper\n '55',\n '54',\n '53',\n '52',\n '51',\n '61',\n '62',\n '63',\n '64',\n '65',\n // lower\n '85',\n '84',\n '83',\n '82',\n '81',\n '71',\n '72',\n '73',\n '74',\n '75',\n];\n\n/* ------------------------------------------------------------------ */\n/* Anatomy by position */\n/* ------------------------------------------------------------------ */\n\nfunction permanentAnatomy(positionInQuadrant: number): Anatomy {\n if (positionInQuadrant <= 2) return 'incisor';\n if (positionInQuadrant === 3) return 'canine';\n if (positionInQuadrant <= 5) return 'premolar';\n return 'molar';\n}\n\nfunction primaryAnatomy(positionInQuadrant: number): Anatomy {\n if (positionInQuadrant <= 2) return 'incisor';\n if (positionInQuadrant === 3) return 'canine';\n // Primary dentition has no premolars — positions 4 & 5 are molars.\n return 'molar';\n}\n\n/* ------------------------------------------------------------------ */\n/* Palmer helper */\n/* ------------------------------------------------------------------ */\n\nfunction palmerCode(quadrant: Quadrant, position: number): string {\n // Permanent: 1=UR, 2=UL, 3=LL, 4=LR. Primary: 5=UR, 6=UL, 7=LL, 8=LR.\n // Mapping is identical within each dentition ring; the number is what\n // changes, so we fold 5→1, 6→2, 7→3, 8→4 to derive the quadrant code.\n const normalised = ((quadrant - 1) % 4) + 1;\n const code =\n normalised === 1\n ? 'UR'\n : normalised === 2\n ? 'UL'\n : normalised === 3\n ? 'LL'\n : 'LR';\n return `${code}${position}`;\n}\n\n/* ------------------------------------------------------------------ */\n/* Universal (ADA) conversion */\n/* */\n/* Permanent — Universal numbers 1..32 run clockwise from the */\n/* patient's upper-right 3rd molar (FDI 18 = Universal 1), across */\n/* the upper arch (18→11 = 1→8, 21→28 = 9→16), then down the left */\n/* lower arch (38→31 = 17→24) and across the right lower (41→48 = */\n/* 25→32). */\n/* */\n/* Primary — Universal letters A..T walk the same path: */\n/* 55→51 = A→E, 61→65 = F→J, 75→71 = K→O, 81→85 = P→T. */\n/* ------------------------------------------------------------------ */\n\nfunction permanentUniversal(quadrant: Quadrant, position: number): string {\n switch (quadrant) {\n case 1:\n // 18→1, 17→2, …, 11→8\n return String(1 + (8 - position));\n case 2:\n // 21→9, 22→10, …, 28→16\n return String(8 + position);\n case 3:\n // 38→17, 37→18, …, 31→24\n return String(17 + (8 - position));\n case 4:\n // 41→25, 42→26, …, 48→32\n return String(24 + position);\n default:\n return '';\n }\n}\n\nfunction primaryUniversal(quadrant: Quadrant, position: number): string {\n const letters = 'ABCDEFGHIJKLMNOPQRST';\n let index = -1;\n switch (quadrant) {\n case 5:\n // 55→A, 54→B, 53→C, 52→D, 51→E\n index = 0 + (5 - position);\n break;\n case 6:\n // 61→F, 62→G, 63→H, 64→I, 65→J\n index = 5 + (position - 1);\n break;\n case 7:\n // 75→K, 74→L, 73→M, 72→N, 71→O\n index = 10 + (5 - position);\n break;\n case 8:\n // 81→P, 82→Q, 83→R, 84→S, 85→T\n index = 15 + (position - 1);\n break;\n default:\n index = -1;\n }\n return index >= 0 ? letters[index] : '';\n}\n\n/* ------------------------------------------------------------------ */\n/* Meta builder */\n/* ------------------------------------------------------------------ */\n\nfunction buildMeta(fdi: FdiId): ToothMeta {\n const quadrant = Number(fdi[0]) as Quadrant;\n const position = Number(fdi[1]);\n const isPrimary = quadrant >= 5;\n const anatomy = isPrimary\n ? primaryAnatomy(position)\n : permanentAnatomy(position);\n const arch: Arch =\n quadrant === 1 || quadrant === 2 || quadrant === 5 || quadrant === 6\n ? 'upper'\n : 'lower';\n const side: Side =\n quadrant === 1 || quadrant === 4 || quadrant === 5 || quadrant === 8\n ? 'right'\n : 'left';\n const universal = isPrimary\n ? primaryUniversal(quadrant, position)\n : permanentUniversal(quadrant, position);\n const palmer = palmerCode(quadrant, position);\n return {\n fdi,\n universal,\n palmer,\n quadrant,\n positionInQuadrant: position,\n anatomy,\n arch,\n side,\n dentition: isPrimary ? 'primary' : 'permanent',\n };\n}\n\nexport const FDI_TO_META: Record<FdiId, ToothMeta> = [\n ...PERMANENT_TEETH,\n ...PRIMARY_TEETH,\n].reduce<Record<FdiId, ToothMeta>>((acc, fdi) => {\n acc[fdi] = buildMeta(fdi);\n return acc;\n}, {});\n\nexport const FDI_TO_UNIVERSAL: Record<FdiId, string> = Object.fromEntries(\n Object.entries(FDI_TO_META).map(([fdi, meta]) => [fdi, meta.universal]),\n);\n\nexport const FDI_TO_PALMER: Record<FdiId, string> = Object.fromEntries(\n Object.entries(FDI_TO_META).map(([fdi, meta]) => [fdi, meta.palmer]),\n);\n\n/**\n * FDI used to resolve a tooth's RENDER. There are no separate deciduous\n * renders — primary teeth reuse the permanent image of the same position\n * (quadrant 5→1, 6→2, 7→3, 8→4; position digit unchanged), scaled down by\n * the renderer. Permanent teeth resolve to themselves.\n */\nexport function assetFdi(meta: ToothMeta): FdiId {\n if (meta.dentition !== 'primary') return meta.fdi;\n return `${meta.quadrant - 4}${meta.positionInQuadrant}`;\n}\n\n/** Render scale applied to a tooth image (primary teeth are smaller). */\nexport const PRIMARY_TOOTH_SCALE = 0.72;\n\n/* ------------------------------------------------------------------ */\n/* Condition → token + pattern */\n/* */\n/* Every condition carries BOTH a colour AND a fill pattern so */\n/* colour-blind clinicians can distinguish states in the SVG (and in */\n/* black-and-white prints) — colour is never the sole channel. */\n/* ------------------------------------------------------------------ */\n\n/** CSS variable name (without the leading `var(...)`) that drives a condition's colour. */\nexport const CONDITION_TOKENS: Record<ToothCondition, string> = {\n caries: '--destructive',\n filled: '--info',\n crowned: '--warning',\n temporaryCrown: '--warning',\n bridge: '--primary',\n missing: '--muted-foreground',\n implant: '--accent',\n implantExtraction: '--accent',\n stub: '--muted-foreground',\n destroyed: '--destructive',\n rootCanal: '--primary',\n};\n\n/**\n * Convenience: the `fill=\"var(--destructive)\"` string ready to paste onto\n * an SVG attribute. Kept in sync with `CONDITION_TOKENS`. Used for the legend\n * swatch + the surface-condition ring; whole-tooth conditions render a\n * distinct symbol image (the glyph is the colour-independent channel).\n */\nexport const CONDITION_COLORS: Record<ToothCondition, string> = {\n caries: 'var(--destructive)',\n filled: 'var(--info)',\n crowned: 'var(--warning)',\n temporaryCrown: 'var(--warning)',\n bridge: 'var(--primary)',\n missing: 'var(--muted-foreground)',\n implant: 'var(--accent)',\n implantExtraction: 'var(--accent)',\n stub: 'var(--muted-foreground)',\n destroyed: 'var(--destructive)',\n rootCanal: 'var(--primary)',\n};\n\n/* ------------------------------------------------------------------ */\n/* Condition → side-view symbol overlay */\n/* */\n/* Whole-tooth findings overlay a photoreal symbol render on the */\n/* facial tooth. `caries` / `filled` are per-surface findings shown */\n/* in the plan (occlusal) view and carry no side symbol here. The */\n/* folder layout is `{symbol}/{top|bottom}/...` under `side/`. */\n/* ------------------------------------------------------------------ */\n\nexport interface SideSymbolSpec {\n /** Asset folder under `side/`. */\n folder: string;\n /** How the file resolves inside `{folder}/{view}/`. */\n resolve: 'per-fdi' | 'general' | 'rct';\n}\n\nexport const CONDITION_SIDE_SYMBOL: Partial<\n Record<ToothCondition, SideSymbolSpec>\n> = {\n crowned: { folder: 'crown', resolve: 'per-fdi' },\n temporaryCrown: { folder: 'temporaryCrown', resolve: 'per-fdi' },\n bridge: { folder: 'replacement', resolve: 'per-fdi' },\n destroyed: { folder: 'destroyed', resolve: 'per-fdi' },\n implant: { folder: 'implant', resolve: 'general' },\n implantExtraction: { folder: 'implantExtraction', resolve: 'general' },\n stub: { folder: 'stub', resolve: 'general' },\n rootCanal: { folder: 'rootTreatment', resolve: 'rct' },\n};\n\n/** Resolve the side-view symbol overlay URL for a condition on a tooth. */\nexport function conditionSymbolUrl(\n meta: ToothMeta,\n assetBaseUrl: string,\n spec: SideSymbolSpec,\n): string {\n const view = meta.arch === 'upper' ? 'top' : 'bottom';\n const base = `${assetBaseUrl}/side/${spec.folder}/${view}`;\n if (spec.resolve === 'general') return `${base}/general.webp`;\n const fdi = assetFdi(meta);\n if (spec.resolve === 'rct') return `${base}/${fdi}/full.webp`;\n return `${base}/${fdi}.webp`;\n}\n\n/* ------------------------------------------------------------------ */\n/* Plan (occlusal) view — per-surface findings */\n/* */\n/* The \"Stato dentale\" / Findings surface marks caries & fillings on */\n/* individual tooth surfaces in the occlusal (plan) view. The asset */\n/* surface names differ from the clinical `Surface` enum: buccal → */\n/* vestibular (cheek/lip side), lingual → oral (tongue side). */\n/* ------------------------------------------------------------------ */\n\nexport type Projection = 'side' | 'plan';\n/** The user-selectable view: a single projection, or both stacked together. */\nexport type ViewMode = Projection | 'both';\n\n/** Map the clinical `Surface` enum to the asset surface filename token. */\nexport const SURFACE_ASSET_NAME: Record<Surface, string> = {\n mesial: 'mesial',\n distal: 'distal',\n occlusal: 'occlusal',\n buccal: 'vestibular',\n lingual: 'oral',\n};\n\n/** Conditions that render as per-surface fills in the plan view. */\nexport const CONDITION_PLAN_FOLDER: Partial<Record<ToothCondition, string>> = {\n caries: 'caries',\n filled: 'filling',\n};\n\n/** Resolve a per-surface plan-view overlay URL (e.g. caries on the distal). */\nexport function surfaceSymbolUrl(\n meta: ToothMeta,\n assetBaseUrl: string,\n folder: string,\n surface: Surface,\n): string {\n const view = meta.arch === 'upper' ? 'top' : 'bottom';\n return `${assetBaseUrl}/plan/${folder}/${view}/${assetFdi(meta)}-${SURFACE_ASSET_NAME[surface]}.webp`;\n}\n\n/* ------------------------------------------------------------------ */\n/* Surface zones — interactive per-surface picking (plan view) */\n/* ------------------------------------------------------------------ */\n\n/**\n * Clickable surface regions of a plan-view tooth, named by SCREEN position.\n * `zoneSurface()` resolves each to the anatomical `Surface` per quadrant.\n */\nexport type SurfaceZone = 'top' | 'bottom' | 'left' | 'right' | 'center';\n\n/**\n * Which zones a tooth exposes. Molars & premolars have a central occlusal\n * table (5 zones); incisors & canines have no occlusal surface to mark, so\n * their crown splits into 4 directional zones.\n */\nexport function zonesForTooth(meta: ToothMeta): SurfaceZone[] {\n const fiveZone = meta.anatomy === 'molar' || meta.anatomy === 'premolar';\n return fiveZone\n ? ['top', 'bottom', 'left', 'right', 'center']\n : ['top', 'bottom', 'left', 'right'];\n}\n\n/**\n * Map a screen zone to the anatomical surface, QUADRANT-AWARE. Two flips make\n * this non-trivial (ported from the platform odontogram's `_getAreaBySide`, so\n * a finding always lands on the correct surface wherever the tooth sits):\n *\n * - mesial / distal (left & right zones) flip between the patient's right\n * quadrants {1,4} and left quadrants {2,3} — \"toward the midline\" reverses.\n * - buccal(vestibular) / lingual(oral) (top & bottom zones) flip between the\n * upper and lower arch.\n * - centre is always occlusal.\n *\n * Primary quadrants (5-8) normalise to their permanent counterpart (1-4).\n */\nexport function zoneSurface(meta: ToothMeta, zone: SurfaceZone): Surface {\n if (zone === 'center') return 'occlusal';\n if (zone === 'top') return meta.arch === 'upper' ? 'buccal' : 'lingual';\n if (zone === 'bottom') return meta.arch === 'upper' ? 'lingual' : 'buccal';\n // left / right → mesial / distal, by quadrant.\n const q = meta.quadrant <= 4 ? meta.quadrant : meta.quadrant - 4;\n const leftIsDistal = q === 1 || q === 4; // Q1 (upper-right) & Q4 (lower-left)\n if (zone === 'left') return leftIsDistal ? 'distal' : 'mesial';\n return leftIsDistal ? 'mesial' : 'distal';\n}\n\nconst r2 = (n: number) => Math.round(n * 100) / 100;\n\n/**\n * SVG `d` path for a clickable surface zone within a tooth's local box.\n * 5-zone teeth frame a central occlusal rect with four edge trapezoids;\n * 4-zone teeth split the box along both diagonals into four triangles meeting\n * at the centre.\n */\nexport function zonePath(\n zone: SurfaceZone,\n rect: { x: number; y: number; w: number; h: number },\n hasCenter: boolean,\n): string {\n const { x, y, w, h } = rect;\n const x1 = x + w;\n const y1 = y + h;\n const p = (px: number, py: number) => `${r2(px)} ${r2(py)}`;\n if (!hasCenter) {\n const cx = x + w / 2;\n const cy = y + h / 2;\n switch (zone) {\n case 'top':\n return `M ${p(x, y)} L ${p(x1, y)} L ${p(cx, cy)} Z`;\n case 'bottom':\n return `M ${p(x, y1)} L ${p(x1, y1)} L ${p(cx, cy)} Z`;\n case 'left':\n return `M ${p(x, y)} L ${p(cx, cy)} L ${p(x, y1)} Z`;\n case 'right':\n return `M ${p(x1, y)} L ${p(cx, cy)} L ${p(x1, y1)} Z`;\n default:\n return '';\n }\n }\n // 5-zone: central occlusal rect inset 30% on each axis, four trapezoids.\n const ix = x + w * 0.3;\n const iy = y + h * 0.3;\n const ix1 = x + w * 0.7;\n const iy1 = y + h * 0.7;\n switch (zone) {\n case 'center':\n return `M ${p(ix, iy)} L ${p(ix1, iy)} L ${p(ix1, iy1)} L ${p(ix, iy1)} Z`;\n case 'top':\n return `M ${p(x, y)} L ${p(x1, y)} L ${p(ix1, iy)} L ${p(ix, iy)} Z`;\n case 'bottom':\n return `M ${p(x, y1)} L ${p(x1, y1)} L ${p(ix1, iy1)} L ${p(ix, iy1)} Z`;\n case 'left':\n return `M ${p(x, y)} L ${p(ix, iy)} L ${p(ix, iy1)} L ${p(x, y1)} Z`;\n case 'right':\n return `M ${p(x1, y)} L ${p(ix1, iy)} L ${p(ix1, iy1)} L ${p(x1, y1)} Z`;\n default:\n return '';\n }\n}\n\n/* ------------------------------------------------------------------ */\n/* Viewport layout helpers */\n/* */\n/* Image-based rendering: every photoreal render shares a fixed canvas */\n/* (375×808 facial, 328×328 occlusal), so relative tooth size is baked */\n/* into the artwork. The chart lays teeth out in UNIFORM slots and lets */\n/* each image carry its own anatomy. */\n/* ------------------------------------------------------------------ */\n\nexport const TOOTH_GAP = 5;\nexport const TOOTH_ROW_GAP = 40;\nexport const TOOTH_HIT_PAD = 4;\nexport const TOOTH_SLOT_WIDTH = 36;\n/** Facial-view slot block-size; the plan/occlusal view uses a shorter slot. */\nexport const TOOTH_SLOT_HEIGHT = 62;\n\n/**\n * Resolve a tooth render URL. `view` follows the arch (`top` = upper,\n * `bottom` = lower); `symbol` selects the artwork layer (`default` is the\n * healthy tooth; condition/finding overlays use their symbol folder).\n */\nexport function toothImageUrl(\n meta: ToothMeta,\n assetBaseUrl: string,\n symbol = 'default',\n projection: 'side' | 'plan' = 'side',\n): string {\n const view = meta.arch === 'upper' ? 'top' : 'bottom';\n return `${assetBaseUrl}/${projection}/${symbol}/${view}/${assetFdi(meta)}.webp`;\n}\n\nexport interface PositionedTooth {\n fdi: FdiId;\n meta: ToothMeta;\n x: number;\n y: number;\n}\n\n/**\n * Produce the positioned list of teeth for a given dentition. The layout\n * is a two-row grid reading viewer-left-to-right across the upper arch,\n * then viewer-left-to-right across the lower arch. Clinical convention\n * is preserved: the patient's right sits on the viewer's left in both\n * arches, regardless of document direction.\n */\nexport function layoutTeeth(\n dentition: Dentition,\n slotHeight: number = TOOTH_SLOT_HEIGHT,\n options: { arch?: Arch } = {},\n): {\n teeth: PositionedTooth[];\n width: number;\n height: number;\n} {\n const ids: FdiId[] =\n dentition === 'primary'\n ? PRIMARY_TEETH\n : dentition === 'mixed'\n ? [...PERMANENT_TEETH, ...PRIMARY_TEETH]\n : PERMANENT_TEETH;\n\n // Upper / lower partition is inferred from meta.arch.\n const upper: FdiId[] = [];\n const lower: FdiId[] = [];\n for (const id of ids) {\n const meta = FDI_TO_META[id];\n if (meta.arch === 'upper') upper.push(id);\n else lower.push(id);\n }\n\n // Uniform slots — relative tooth size is baked into each render, so every\n // tooth occupies the same slot and the artwork carries the anatomy.\n const slot = TOOTH_SLOT_WIDTH + TOOTH_GAP;\n // Both arches carry the same count in every dentition (permanent 16/16,\n // primary 10/10, mixed 26/26), so the row width is symmetric — a single-arch\n // layout therefore aligns column-for-column with its opposite arch, which the\n // `both` view relies on to stack the four rows.\n const maxCount = Math.max(upper.length, lower.length);\n const maxRowWidth = maxCount > 0 ? maxCount * slot - TOOTH_GAP : 0;\n\n function positionRow(row: FdiId[], yOffset: number): PositionedTooth[] {\n const rowWidth = row.length > 0 ? row.length * slot - TOOTH_GAP : 0;\n const startX = (maxRowWidth - rowWidth) / 2;\n return row.map((id, i) => {\n const meta = FDI_TO_META[id];\n return {\n fdi: id,\n meta,\n x: startX + i * slot,\n y: yOffset,\n };\n });\n }\n\n // Single-arch layout (the `both` view renders one arch per SVG so a tooth's\n // FDI id stays unique within each SVG): a single row at y = 0.\n if (options.arch) {\n const row = options.arch === 'upper' ? upper : lower;\n const teeth = positionRow(row, 0);\n return { teeth, width: maxRowWidth, height: slotHeight };\n }\n\n const upperRow = positionRow(upper, 0);\n const lowerRow = positionRow(lower, slotHeight + TOOTH_ROW_GAP);\n\n const width = maxRowWidth > 0 ? maxRowWidth : maxCount * slot;\n const height = slotHeight * 2 + TOOTH_ROW_GAP;\n\n return { teeth: [...upperRow, ...lowerRow], width, height };\n}\n\n/* ------------------------------------------------------------------ */\n/* Special elements — quadrants, sextants, arches, whole-mouth. */\n/* */\n/* The platform odontogram appends selectable \"abstract elements\" to */\n/* the inline-end of each arch row (see platform */\n/* `AlfaDocs/Domain/ToothScheme/Elements.php`): quadrants (Q1–Q4), */\n/* arches (AS / AI), a whole-mouth General, and sextants (S1–S6). */\n/* They let a clinician record a finding / treatment that spans a */\n/* region rather than a single tooth (e.g. a denture on the upper */\n/* arch, scaling on a sextant). Quadrant / arch / general sit on the */\n/* facial (side) rows; sextants sit on the occlusal (plan) rows, */\n/* column-aligned beneath / above their quadrant — matching the */\n/* platform's QuarterWithSextant / JawWithSextant pairing. */\n/* ------------------------------------------------------------------ */\n\nexport type SpecialElementKind = 'quadrant' | 'sextant' | 'arch' | 'general';\n\nexport interface SpecialElement {\n /**\n * Stable id — a chart key and the value emitted by `onSelect`. Permanent:\n * `Q1`–`Q4`, `S1`–`S6`, `AS`, `AI`, `general`. Primary mirrors with an `m`\n * prefix: `mQ1`…, `mS1`…, `mAS`, `mAI`, `mGeneral`.\n */\n id: string;\n kind: SpecialElementKind;\n arch: Arch;\n dentition: 'permanent' | 'primary';\n /** i18n token under `toothScheme.special.*` for the accessible name. */\n labelKey: 'quadrant' | 'sextant' | 'archUpper' | 'archLower' | 'general';\n /** Interpolated into the `{{n}}` of quadrant / sextant labels (e.g. `1`). */\n labelIndex?: number;\n /** Short code shown in the slot, e.g. `Q1`, `S3`, `AS`, `Gen.`. */\n code: string;\n /**\n * Member teeth (FDI) — informational for consumers that highlight or expand a\n * region. Selection itself keys on `id`; the platform expands membership\n * server-side. Sextants follow the WHO/CPITN convention.\n */\n teeth: FdiId[];\n /**\n * Column within the special-element block. Quadrant and its paired sextant\n * share a column so they stack (Q1↕S1, Q2↕S2, AS↕S3 / S4↕Q4, S5↕Q3, S6↕AI).\n */\n column: number;\n /** Row the element attaches to: side (quadrant/arch/general) or plan (sextant). */\n row: Projection;\n}\n\nconst teethInQuadrant = (q: Quadrant): FdiId[] =>\n [...PERMANENT_TEETH, ...PRIMARY_TEETH].filter(\n (id) => FDI_TO_META[id].quadrant === q,\n );\n\nconst PERMANENT_SPECIALS: SpecialElement[] = [\n // Upper facial (side) row — Q1, Q2, AS, Gen.\n {\n id: 'Q1',\n kind: 'quadrant',\n arch: 'upper',\n dentition: 'permanent',\n labelKey: 'quadrant',\n labelIndex: 1,\n code: 'Q1',\n teeth: teethInQuadrant(1),\n column: 0,\n row: 'side',\n },\n {\n id: 'Q2',\n kind: 'quadrant',\n arch: 'upper',\n dentition: 'permanent',\n labelKey: 'quadrant',\n labelIndex: 2,\n code: 'Q2',\n teeth: teethInQuadrant(2),\n column: 1,\n row: 'side',\n },\n {\n id: 'AS',\n kind: 'arch',\n arch: 'upper',\n dentition: 'permanent',\n labelKey: 'archUpper',\n code: 'AS',\n teeth: [...teethInQuadrant(1), ...teethInQuadrant(2)],\n column: 2,\n row: 'side',\n },\n {\n id: 'general',\n kind: 'general',\n arch: 'upper',\n dentition: 'permanent',\n labelKey: 'general',\n code: 'Gen.',\n teeth: PERMANENT_TEETH,\n column: 3,\n row: 'side',\n },\n // Upper occlusal (plan) row — S1, S2, S3 (WHO sextants).\n {\n id: 'S1',\n kind: 'sextant',\n arch: 'upper',\n dentition: 'permanent',\n labelKey: 'sextant',\n labelIndex: 1,\n code: 'S1',\n teeth: ['18', '17', '16', '15', '14'],\n column: 0,\n row: 'plan',\n },\n {\n id: 'S2',\n kind: 'sextant',\n arch: 'upper',\n dentition: 'permanent',\n labelKey: 'sextant',\n labelIndex: 2,\n code: 'S2',\n teeth: ['13', '12', '11', '21', '22', '23'],\n column: 1,\n row: 'plan',\n },\n {\n id: 'S3',\n kind: 'sextant',\n arch: 'upper',\n dentition: 'permanent',\n labelKey: 'sextant',\n labelIndex: 3,\n code: 'S3',\n teeth: ['24', '25', '26', '27', '28'],\n column: 2,\n row: 'plan',\n },\n // Lower occlusal (plan) row — S4, S5, S6.\n {\n id: 'S4',\n kind: 'sextant',\n arch: 'lower',\n dentition: 'permanent',\n labelKey: 'sextant',\n labelIndex: 4,\n code: 'S4',\n teeth: ['34', '35', '36', '37', '38'],\n column: 0,\n row: 'plan',\n },\n {\n id: 'S5',\n kind: 'sextant',\n arch: 'lower',\n dentition: 'permanent',\n labelKey: 'sextant',\n labelIndex: 5,\n code: 'S5',\n teeth: ['43', '42', '41', '31', '32', '33'],\n column: 1,\n row: 'plan',\n },\n {\n id: 'S6',\n kind: 'sextant',\n arch: 'lower',\n dentition: 'permanent',\n labelKey: 'sextant',\n labelIndex: 6,\n code: 'S6',\n teeth: ['44', '45', '46', '47', '48'],\n column: 2,\n row: 'plan',\n },\n // Lower facial (side) row — Q4, Q3, AI.\n {\n id: 'Q4',\n kind: 'quadrant',\n arch: 'lower',\n dentition: 'permanent',\n labelKey: 'quadrant',\n labelIndex: 4,\n code: 'Q4',\n teeth: teethInQuadrant(4),\n column: 0,\n row: 'side',\n },\n {\n id: 'Q3',\n kind: 'quadrant',\n arch: 'lower',\n dentition: 'permanent',\n labelKey: 'quadrant',\n labelIndex: 3,\n code: 'Q3',\n teeth: teethInQuadrant(3),\n column: 1,\n row: 'side',\n },\n {\n id: 'AI',\n kind: 'arch',\n arch: 'lower',\n dentition: 'permanent',\n labelKey: 'archLower',\n code: 'AI',\n teeth: [...teethInQuadrant(3), ...teethInQuadrant(4)],\n column: 2,\n row: 'side',\n },\n];\n\nconst PRIMARY_SPECIALS: SpecialElement[] = [\n // Upper facial (side) row.\n {\n id: 'mQ1',\n kind: 'quadrant',\n arch: 'upper',\n dentition: 'primary',\n labelKey: 'quadrant',\n labelIndex: 1,\n code: 'Q1',\n teeth: teethInQuadrant(5),\n column: 0,\n row: 'side',\n },\n {\n id: 'mQ2',\n kind: 'quadrant',\n arch: 'upper',\n dentition: 'primary',\n labelKey: 'quadrant',\n labelIndex: 2,\n code: 'Q2',\n teeth: teethInQuadrant(6),\n column: 1,\n row: 'side',\n },\n {\n id: 'mAS',\n kind: 'arch',\n arch: 'upper',\n dentition: 'primary',\n labelKey: 'archUpper',\n code: 'AS',\n teeth: [...teethInQuadrant(5), ...teethInQuadrant(6)],\n column: 2,\n row: 'side',\n },\n {\n id: 'mGeneral',\n kind: 'general',\n arch: 'upper',\n dentition: 'primary',\n labelKey: 'general',\n code: 'Gen.',\n teeth: PRIMARY_TEETH,\n column: 3,\n row: 'side',\n },\n // Upper occlusal (plan) row.\n {\n id: 'mS1',\n kind: 'sextant',\n arch: 'upper',\n dentition: 'primary',\n labelKey: 'sextant',\n labelIndex: 1,\n code: 'S1',\n teeth: ['55', '54'],\n column: 0,\n row: 'plan',\n },\n {\n id: 'mS2',\n kind: 'sextant',\n arch: 'upper',\n dentition: 'primary',\n labelKey: 'sextant',\n labelIndex: 2,\n code: 'S2',\n teeth: ['53', '52', '51', '61', '62', '63'],\n column: 1,\n row: 'plan',\n },\n {\n id: 'mS3',\n kind: 'sextant',\n arch: 'upper',\n dentition: 'primary',\n labelKey: 'sextant',\n labelIndex: 3,\n code: 'S3',\n teeth: ['64', '65'],\n column: 2,\n row: 'plan',\n },\n // Lower occlusal (plan) row.\n {\n id: 'mS4',\n kind: 'sextant',\n arch: 'lower',\n dentition: 'primary',\n labelKey: 'sextant',\n labelIndex: 4,\n code: 'S4',\n teeth: ['74', '75'],\n column: 0,\n row: 'plan',\n },\n {\n id: 'mS5',\n kind: 'sextant',\n arch: 'lower',\n dentition: 'primary',\n labelKey: 'sextant',\n labelIndex: 5,\n code: 'S5',\n teeth: ['73', '72', '71', '81', '82', '83'],\n column: 1,\n row: 'plan',\n },\n {\n id: 'mS6',\n kind: 'sextant',\n arch: 'lower',\n dentition: 'primary',\n labelKey: 'sextant',\n labelIndex: 6,\n code: 'S6',\n teeth: ['84', '85'],\n column: 2,\n row: 'plan',\n },\n // Lower facial (side) row.\n {\n id: 'mQ4',\n kind: 'quadrant',\n arch: 'lower',\n dentition: 'primary',\n labelKey: 'quadrant',\n labelIndex: 4,\n code: 'Q4',\n teeth: teethInQuadrant(8),\n column: 0,\n row: 'side',\n },\n {\n id: 'mQ3',\n kind: 'quadrant',\n arch: 'lower',\n dentition: 'primary',\n labelKey: 'quadrant',\n labelIndex: 3,\n code: 'Q3',\n teeth: teethInQuadrant(7),\n column: 1,\n row: 'side',\n },\n {\n id: 'mAI',\n kind: 'arch',\n arch: 'lower',\n dentition: 'primary',\n labelKey: 'archLower',\n code: 'AI',\n teeth: [...teethInQuadrant(7), ...teethInQuadrant(8)],\n column: 2,\n row: 'side',\n },\n];\n\n/** Every special element, flat — permanent then primary. */\nexport const SPECIAL_ELEMENTS: SpecialElement[] = [\n ...PERMANENT_SPECIALS,\n ...PRIMARY_SPECIALS,\n];\n\n/** Lookup a special element by its id (`Q1`, `mS3`, `AS`, `general`…). */\nexport const SPECIAL_BY_ID: Record<string, SpecialElement> =\n SPECIAL_ELEMENTS.reduce<Record<string, SpecialElement>>((acc, el) => {\n acc[el.id] = el;\n return acc;\n }, {});\n\n/** True when an id names a special element rather than a tooth. */\nexport function isSpecialElement(id: string): boolean {\n return id in SPECIAL_BY_ID;\n}\n\n/**\n * The special-element SLOTS that attach to a given row, ordered by column.\n * Quadrant / arch live on the `side` row; sextants on the `plan` row. The\n * whole-mouth `general` element is excluded — it renders as a switch beneath the\n * chart (see `generalElement`), not as a column. Mixed dentition reuses the\n * permanent set (the platform has no mixed view).\n */\nexport function specialsForRow(\n dentition: Dentition,\n arch: Arch,\n projection: Projection,\n): SpecialElement[] {\n const set = dentition === 'primary' ? PRIMARY_SPECIALS : PERMANENT_SPECIALS;\n return set\n .filter(\n (el) =>\n el.arch === arch && el.row === projection && el.kind !== 'general',\n )\n .sort((a, b) => a.column - b.column);\n}\n\n/**\n * The whole-mouth `general` element for a dentition. It is rendered as a switch\n * beneath the chart rather than as a cramped icon column (the platform's vertical\n * \"GENERALE\" glyph is illegible at column width).\n */\nexport function generalElement(dentition: Dentition): SpecialElement {\n const set = dentition === 'primary' ? PRIMARY_SPECIALS : PERMANENT_SPECIALS;\n // Every dentition set defines exactly one general element.\n return set.find((el) => el.kind === 'general') as SpecialElement;\n}\n\n/** A special-element slot placed in an SVG, to the inline-end of the teeth. */\nexport interface PositionedSpecial {\n element: SpecialElement;\n x: number;\n y: number;\n}\n\n/** Special-element slot block-size — matches a tooth slot so icons read clearly. */\nexport const SPECIAL_SLOT_WIDTH = 36;\n\n/**\n * Resolve the photoreal icon URL for a special element, or `null` when it has\n * none (sextants are label-only — the platform ships no sextant icon). Icons\n * live in the `global/` sibling of the `teeth/` render folder; quadrants and\n * arches reuse their code as the file stem (`Q1`, `AS`, …), general resolves to\n * `general`, and primary elements reuse the permanent art.\n */\nexport function specialIconUrl(\n element: SpecialElement,\n assetBaseUrl: string,\n): string | null {\n const stem =\n element.kind === 'quadrant' || element.kind === 'arch'\n ? element.code\n : element.kind === 'general'\n ? 'general'\n : null;\n if (!stem) return null;\n // Icons sit in the `global/` sibling of the `teeth/` render folder; tolerate a\n // trailing slash on the configured base.\n const base = /\\/teeth\\/?$/.test(assetBaseUrl)\n ? assetBaseUrl.replace(/\\/teeth\\/?$/, '/global')\n : `${assetBaseUrl}/global`;\n return `${base}/${stem}.webp`;\n}\n/** Gap between the last tooth and the first special-element column. */\nexport const SPECIAL_GAP = 8;\n/** Column pitch within the special-element block. */\nexport const SPECIAL_PITCH = SPECIAL_SLOT_WIDTH + TOOTH_GAP;\n\n/**\n * Place a row's special elements after the teeth (inline-end) and report the\n * total row width. Columns are derived from `element.column`, so a quadrant and\n * its paired sextant share an x and stack across the two rows.\n */\nexport function layoutSpecials(\n specials: SpecialElement[],\n teethWidth: number,\n): { specials: PositionedSpecial[]; width: number } {\n if (specials.length === 0) return { specials: [], width: teethWidth };\n const startX = teethWidth + SPECIAL_GAP;\n const positioned = specials.map((element) => ({\n element,\n x: startX + element.column * SPECIAL_PITCH,\n y: 0,\n }));\n const maxColumn = Math.max(...specials.map((el) => el.column));\n const width = startX + (maxColumn + 1) * SPECIAL_PITCH - TOOTH_GAP;\n return { specials: positioned, width };\n}\n\n/* ------------------------------------------------------------------ */\n/* Labels by numbering */\n/* ------------------------------------------------------------------ */\n\nexport function labelFor(id: FdiId, numbering: Numbering): string {\n switch (numbering) {\n case 'universal':\n return FDI_TO_UNIVERSAL[id] ?? id;\n case 'palmer':\n return FDI_TO_PALMER[id] ?? id;\n case 'fdi':\n default:\n return id;\n }\n}\n\n/* ------------------------------------------------------------------ */\n/* Chart helpers */\n/* ------------------------------------------------------------------ */\n\nexport function emptyChart(): ToothChart {\n return {};\n}\n\nexport function chartFromConditions(\n conditions: Partial<Record<FdiId, ToothCondition[]>>,\n): ToothChart {\n const chart: ToothChart = {};\n for (const [id, list] of Object.entries(conditions)) {\n if (!list || list.length === 0) continue;\n chart[id] = { conditions: list, surfaces: [] };\n }\n return chart;\n}\n"],"names":["PERMANENT_TEETH","PRIMARY_TEETH","permanentAnatomy","positionInQuadrant","primaryAnatomy","palmerCode","quadrant","position","normalised","permanentUniversal","primaryUniversal","letters","index","buildMeta","fdi","isPrimary","anatomy","arch","side","universal","palmer","FDI_TO_META","acc","FDI_TO_UNIVERSAL","meta","FDI_TO_PALMER","assetFdi","PRIMARY_TOOTH_SCALE","CONDITION_TOKENS","CONDITION_COLORS","CONDITION_SIDE_SYMBOL","conditionSymbolUrl","assetBaseUrl","spec","view","base","SURFACE_ASSET_NAME","CONDITION_PLAN_FOLDER","surfaceSymbolUrl","folder","surface","zonesForTooth","zoneSurface","zone","q","leftIsDistal","r2","n","zonePath","rect","hasCenter","x","y","w","h","x1","y1","p","px","py","cx","cy","ix","iy","ix1","iy1","TOOTH_GAP","TOOTH_ROW_GAP","TOOTH_HIT_PAD","TOOTH_SLOT_WIDTH","TOOTH_SLOT_HEIGHT","toothImageUrl","symbol","projection","layoutTeeth","dentition","slotHeight","options","ids","upper","lower","id","slot","maxCount","maxRowWidth","positionRow","row","yOffset","rowWidth","startX","i","upperRow","lowerRow","width","height","teethInQuadrant","PERMANENT_SPECIALS","PRIMARY_SPECIALS","SPECIAL_ELEMENTS","SPECIAL_BY_ID","el","isSpecialElement","specialsForRow","a","b","generalElement","SPECIAL_SLOT_WIDTH","specialIconUrl","element","stem","SPECIAL_GAP","SPECIAL_PITCH","layoutSpecials","specials","teethWidth","positioned","maxColumn","labelFor","numbering","emptyChart","chartFromConditions","conditions","chart","list"],"mappings":"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"}
|