@alfadocs/ui-kit 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.
Files changed (82) hide show
  1. package/dist/_chunks/{anamnesis-summary-rows-CVzxC2X3.js → anamnesis-summary-rows-BpYztFUv.js} +20 -4
  2. package/dist/_chunks/{care-plan-card-C83Hb9EE.js → care-plan-card-CehAU7F_.js} +2 -2
  3. package/dist/_chunks/{care-plan-entry-card-CHMPSTCz.js → care-plan-entry-card-ChWRMfMw.js} +2 -2
  4. package/dist/_chunks/{periodontal-chart-brakjj0s.js → periodontal-chart-C0nNQKKK.js} +121 -121
  5. package/dist/_chunks/tooth-data-DR8Kdnup.js +903 -0
  6. package/dist/_chunks/tooth-finding-palette-vqC-D2Ye.js +708 -0
  7. package/dist/_chunks/tooth-scheme-BPXE1_3N.js +2051 -0
  8. package/dist/agent-catalog.json +465 -21
  9. package/dist/agent-i18n/en.json +23 -6
  10. package/dist/components/care-plan-card/index.js +1 -1
  11. package/dist/components/care-plan-entry-card/index.js +1 -1
  12. package/dist/components/index.d.ts +1 -0
  13. package/dist/components/periodontal-chart/index.js +1 -1
  14. package/dist/components/tooth-finding-palette/index.d.ts +4 -0
  15. package/dist/components/tooth-finding-palette/index.js +9 -0
  16. package/dist/components/tooth-finding-palette/tooth-finding-palette.agent.d.ts +4 -0
  17. package/dist/components/tooth-finding-palette/tooth-finding-palette.d.ts +89 -0
  18. package/dist/components/tooth-scheme/index.d.ts +2 -2
  19. package/dist/components/tooth-scheme/index.js +57 -30
  20. package/dist/components/tooth-scheme/tooth-data.d.ts +279 -10
  21. package/dist/components/tooth-scheme/tooth-scheme.agent.d.ts +2 -2
  22. package/dist/components/tooth-scheme/tooth-scheme.d.ts +30 -3
  23. package/dist/i18n/locales/ar.d.ts +40 -1
  24. package/dist/i18n/locales/ar.js +41 -2
  25. package/dist/i18n/locales/de.d.ts +40 -1
  26. package/dist/i18n/locales/de.js +41 -2
  27. package/dist/i18n/locales/el.d.ts +40 -1
  28. package/dist/i18n/locales/el.js +41 -2
  29. package/dist/i18n/locales/en.d.ts +40 -1
  30. package/dist/i18n/locales/en.js +41 -2
  31. package/dist/i18n/locales/es.d.ts +40 -1
  32. package/dist/i18n/locales/es.js +41 -2
  33. package/dist/i18n/locales/fr.d.ts +40 -1
  34. package/dist/i18n/locales/fr.js +41 -2
  35. package/dist/i18n/locales/hi.d.ts +40 -1
  36. package/dist/i18n/locales/hi.js +41 -2
  37. package/dist/i18n/locales/it.d.ts +40 -1
  38. package/dist/i18n/locales/it.js +41 -2
  39. package/dist/i18n/locales/ja.d.ts +40 -1
  40. package/dist/i18n/locales/ja.js +41 -2
  41. package/dist/i18n/locales/nl.d.ts +40 -1
  42. package/dist/i18n/locales/nl.js +41 -2
  43. package/dist/i18n/locales/pl.d.ts +40 -1
  44. package/dist/i18n/locales/pl.js +41 -2
  45. package/dist/i18n/locales/pt.d.ts +40 -1
  46. package/dist/i18n/locales/pt.js +41 -2
  47. package/dist/i18n/locales/ro.d.ts +40 -1
  48. package/dist/i18n/locales/ro.js +41 -2
  49. package/dist/i18n/locales/ru.d.ts +40 -1
  50. package/dist/i18n/locales/ru.js +41 -2
  51. package/dist/i18n/locales/sq.d.ts +40 -1
  52. package/dist/i18n/locales/sq.js +41 -2
  53. package/dist/i18n/locales/sv.d.ts +40 -1
  54. package/dist/i18n/locales/sv.js +41 -2
  55. package/dist/i18n/locales/tr.d.ts +40 -1
  56. package/dist/i18n/locales/tr.js +41 -2
  57. package/dist/i18n/locales/zh.d.ts +40 -1
  58. package/dist/i18n/locales/zh.js +41 -2
  59. package/dist/index.js +448 -415
  60. package/dist/locales/ar.json +41 -2
  61. package/dist/locales/de.json +41 -2
  62. package/dist/locales/el.json +41 -2
  63. package/dist/locales/en.json +41 -2
  64. package/dist/locales/es.json +41 -2
  65. package/dist/locales/fr.json +41 -2
  66. package/dist/locales/hi.json +41 -2
  67. package/dist/locales/it.json +41 -2
  68. package/dist/locales/ja.json +41 -2
  69. package/dist/locales/nl.json +41 -2
  70. package/dist/locales/pl.json +41 -2
  71. package/dist/locales/pt.json +41 -2
  72. package/dist/locales/ro.json +41 -2
  73. package/dist/locales/ru.json +41 -2
  74. package/dist/locales/sq.json +41 -2
  75. package/dist/locales/sv.json +41 -2
  76. package/dist/locales/tr.json +41 -2
  77. package/dist/locales/zh.json +41 -2
  78. package/dist/patterns/anamnesis/index.js +1 -1
  79. package/dist/tokens.css +1 -1
  80. package/package.json +5 -1
  81. package/dist/_chunks/tooth-data-BwuoKFf4.js +0 -660
  82. package/dist/_chunks/tooth-scheme-s0ipC0-Z.js +0 -1483
@@ -196,6 +196,7 @@ export * from './sign-document';
196
196
  export * from './slot-grid';
197
197
  export * from './face-scheme';
198
198
  export * from './tooth-scheme';
199
+ export * from './tooth-finding-palette';
199
200
  export * from './transaction-chip';
200
201
  export * from './whatsapp-button';
201
202
  export * from './workflow';
@@ -1,4 +1,4 @@
1
- import { B as t, M as s, P as e, a as i, b as E, c as r, d as P, S as _, e as n, f as A, g as d, h as l, p as T, i as p, j as C, k as L, s as N, t as O, l as R } from "../../_chunks/periodontal-chart-brakjj0s.js";
1
+ import { B as t, M as s, P as e, a as i, b as E, c as r, d as P, S as _, e as n, f as A, g as d, h as l, p as T, i as p, j as C, k as L, s as N, t as O, l as R } from "../../_chunks/periodontal-chart-C0nNQKKK.js";
2
2
  export {
3
3
  t as BLEEDING_WARN_PERCENT,
4
4
  s as MODERATE_POCKET_MM,
@@ -0,0 +1,4 @@
1
+ export { ToothFindingPalette, toothFindingPaletteVariants, DEFAULT_FINDING_GROUPS, CUSTOM_FINDING_GROUP_ID, } from './tooth-finding-palette';
2
+ export type { ToothFindingPaletteProps, ToothFindingPaletteHandle, ToothFindingPaletteGroup, } from './tooth-finding-palette';
3
+ export { toothFindingPaletteAgent } from './tooth-finding-palette.agent';
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,9 @@
1
+ import { C as e, D as n, T as o, t as i, a as s } from "../../_chunks/tooth-finding-palette-vqC-D2Ye.js";
2
+ export {
3
+ e as CUSTOM_FINDING_GROUP_ID,
4
+ n as DEFAULT_FINDING_GROUPS,
5
+ o as ToothFindingPalette,
6
+ i as toothFindingPaletteAgent,
7
+ s as toothFindingPaletteVariants
8
+ };
9
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,4 @@
1
+ import type { AgentAdapter } from '../../agent/types';
2
+ import type { ToothFindingPaletteHandle } from './tooth-finding-palette';
3
+ export declare const toothFindingPaletteAgent: AgentAdapter<ToothFindingPaletteHandle>;
4
+ //# sourceMappingURL=tooth-finding-palette.agent.d.ts.map
@@ -0,0 +1,89 @@
1
+ import { type HTMLAttributes } from 'react';
2
+ import { type ConditionDefinition, type ConditionId, type SymbolStatus } from '../tooth-scheme/tooth-data';
3
+ /** One clinically-meaningful run of tools inside the palette. */
4
+ export interface ToothFindingPaletteGroup {
5
+ /** Stable id — emitted as `data-group` and used for the default heading key. */
6
+ id: string;
7
+ /**
8
+ * Already-translated heading. Omit for a built-in group and the palette
9
+ * resolves `toothFindingPalette.group.<id>` instead; a custom group with no
10
+ * label and no matching key falls back to its own id.
11
+ */
12
+ label?: string;
13
+ /** Findings in this group, in render order. */
14
+ conditions: ConditionId[];
15
+ }
16
+ /**
17
+ * Curated imperative handle. Exposed as the `forwardRef` target so a future
18
+ * agent / MCP UI bridge can arm a tool without touching the DOM — see
19
+ * `tooth-finding-palette.agent.ts`. Everything here is transient tool state;
20
+ * no patient record is reachable through it.
21
+ */
22
+ export interface ToothFindingPaletteHandle {
23
+ /** The armed finding id, or null when the palette is disarmed. */
24
+ getValue: () => ConditionId | null;
25
+ /** Arm a finding tool (or pass null to disarm). */
26
+ setValue: (condition: ConditionId | null) => void;
27
+ /** Disarm the palette. Charted findings are untouched. */
28
+ clear: () => void;
29
+ /** The status the next charted finding takes. */
30
+ getStatus: () => SymbolStatus;
31
+ setStatus: (status: SymbolStatus) => void;
32
+ /** Every finding id the palette currently offers, in render order. */
33
+ getTools: () => ConditionId[];
34
+ /** Move the roving tab stop (and focus) to a tool without arming it. */
35
+ focusTool: (condition: ConditionId) => void;
36
+ }
37
+ export interface ToothFindingPaletteProps extends Omit<HTMLAttributes<HTMLDivElement>, 'onChange' | 'defaultValue' | 'role' | 'color'> {
38
+ /** Controlled armed finding. `null` means "no tool armed". */
39
+ value?: ConditionId | null;
40
+ /** Initial armed finding when uncontrolled. */
41
+ defaultValue?: ConditionId | null;
42
+ /** Fires on every arm / disarm, including the toggle-off to `null`. */
43
+ onChange?: (condition: ConditionId | null) => void;
44
+ /**
45
+ * Which tools to show. Ignored when `groups` is supplied (order the groups
46
+ * instead). Omit both and the palette ships the default grouping of all
47
+ * built-in findings.
48
+ */
49
+ conditions?: ConditionId[];
50
+ /** Explicit grouping. Wins over `conditions`. */
51
+ groups?: ToothFindingPaletteGroup[];
52
+ /**
53
+ * Practice-defined findings. They resolve exactly as they do on the chart
54
+ * (registry → built-in → glyph fallback) and, under the default grouping,
55
+ * any id that is not a built-in is appended as a "Practice findings" group.
56
+ */
57
+ conditionRegistry?: ConditionDefinition[];
58
+ /** Show the planned / executed selector. Default `false`. */
59
+ showStatus?: boolean;
60
+ /** Controlled execution status. */
61
+ status?: SymbolStatus;
62
+ /** Initial execution status when uncontrolled. Default `'planned'`. */
63
+ defaultStatus?: SymbolStatus;
64
+ onStatusChange?: (status: SymbolStatus) => void;
65
+ /** Tile scale. Default `'md'`. */
66
+ size?: 'sm' | 'md' | 'lg';
67
+ /** Wide bar (`'horizontal'`, default) or sidebar rail (`'vertical'`). */
68
+ orientation?: 'horizontal' | 'vertical';
69
+ /** Root the tooth renders are served from. Matches `ToothScheme`. */
70
+ assetBaseUrl?: string;
71
+ /** Disable every tool. */
72
+ disabled?: boolean;
73
+ /** Disable individual tools (e.g. findings this practice cannot chart). */
74
+ disabledConditions?: ConditionId[];
75
+ /** Accessible name for the toolbar. Falls back to a translated default. */
76
+ ariaLabel?: string;
77
+ /** Render the group headings. Default `true`. */
78
+ showGroupLabels?: boolean;
79
+ className?: string;
80
+ }
81
+ export declare const DEFAULT_FINDING_GROUPS: ToothFindingPaletteGroup[];
82
+ /** Group id used for registry findings under the default grouping. */
83
+ export declare const CUSTOM_FINDING_GROUP_ID = "custom";
84
+ export declare const toothFindingPaletteVariants: (props?: ({
85
+ orientation?: "vertical" | "horizontal" | null | undefined;
86
+ size?: "sm" | "md" | "lg" | null | undefined;
87
+ } & import("class-variance-authority/types").ClassProp) | undefined) => string;
88
+ export declare const ToothFindingPalette: import("react").ForwardRefExoticComponent<ToothFindingPaletteProps & import("react").RefAttributes<ToothFindingPaletteHandle>>;
89
+ //# sourceMappingURL=tooth-finding-palette.d.ts.map
@@ -1,5 +1,5 @@
1
1
  export * from './tooth-scheme';
2
- export { FDI_TO_META, FDI_TO_UNIVERSAL, FDI_TO_PALMER, CONDITION_COLORS, CONDITION_TOKENS, CONDITION_SIDE_SYMBOL, CONDITION_PLAN_FOLDER, SURFACE_ASSET_NAME, toothImageUrl, conditionSymbolUrl, surfaceSymbolUrl, zonesForTooth, zoneSurface, zonePath, chartFromConditions, emptyChart, labelFor, layoutTeeth, layoutSpecials, specialsForRow, specialIconUrl, generalElement, isSpecialElement, SPECIAL_ELEMENTS, SPECIAL_BY_ID, } from './tooth-data';
3
- export type { Anatomy, Arch, Quadrant, Side, Surface, SurfaceZone, Projection, Radiograph, RadiographKind, SideSymbolSpec, SpecialElement, SpecialElementKind, PositionedSpecial, ToothMeta, PositionedTooth, } from './tooth-data';
2
+ export { FDI_TO_META, FDI_TO_UNIVERSAL, FDI_TO_PALMER, CONDITION_COLORS, CONDITION_TOKENS, CONDITION_PART, CONDITION_SIDE_SYMBOL, CONDITION_PLAN_SYMBOL, CONDITION_PLAN_FOLDER, SURFACE_ASSET_NAME, TOOTH_CONDITIONS, INTRINSIC_CONDITIONS, STATUS_TOKENS, STATUS_COLORS, STATUS_OPACITY, FALLBACK_CONDITION_TOKEN, isBuiltInCondition, statusLabelKey, conditionLabelKey, conditionDefinition, conditionLabel, conditionColor, conditionPart, conditionGlyph, conditionHasArtwork, conditionSideSymbol, conditionPlanSymbol, conditionPlanFolder, symbolColor, symbolsForTooth, rootsForTooth, rootScreenSide, rootFileStem, toothImageUrl, conditionSymbolUrl, planSymbolUrl, surfaceSymbolUrl, zonesForTooth, zoneSurface, zonePath, chartFromConditions, chartFromSymbols, emptyChart, labelFor, layoutTeeth, layoutSpecials, specialsForRow, specialIconUrl, generalElement, isSpecialElement, SPECIAL_ELEMENTS, SPECIAL_BY_ID, } from './tooth-data';
3
+ export type { Anatomy, Arch, Quadrant, Side, Surface, SurfaceZone, Projection, ViewMode, Radiograph, RadiographKind, SideSymbolSpec, SymbolTarget, SymbolRegion, SymbolLetter, SymbolPart, SymbolStatus, Root, RootFileStem, ConditionId, ConditionDefinition, ConditionRegistry, ConditionLabel, ToothSymbol, SpecialElement, SpecialElementKind, PositionedSpecial, ToothMeta, PositionedTooth, } from './tooth-data';
4
4
  export { toothSchemeAgent } from './tooth-scheme.agent';
5
5
  //# sourceMappingURL=index.d.ts.map
@@ -1,35 +1,62 @@
1
- import { T as o, t as e, r as t } from "../../_chunks/tooth-scheme-s0ipC0-Z.js";
2
- import { C as r, a as S, b as T, c as I, F as _, d as l, e as O, P as N, f as n, S as c, g as h, h as m, i as A, j as i, k as C, l as D, m as F, n as L, o as R, p, s as P, q as M, r as U, t as f, z as u, u as y, v as b } from "../../_chunks/tooth-data-BwuoKFf4.js";
1
+ import { T as s, t, r as e } from "../../_chunks/tooth-scheme-BPXE1_3N.js";
2
+ import { C as i, a as T, b as S, c as l, d as r, e as I, F as O, f as N, g as _, h as c, I as E, P as C, i as A, S as m, j as L, k as d, l as h, m as D, n as F, T as y, o as P, p as b, q as R, r as U, s as p, t as M, u, v as K, w as f, x as B, y as Y, z as H, A as g, B as z, D as w, E as x, G as V, H as k, J as G, K as j, L as q, M as v, N as J, O as Q, Q as W, R as X, U as Z, V as $, W as aa, X as oa, Y as sa, Z as ta, _ as ea, $ as na } from "../../_chunks/tooth-data-DR8Kdnup.js";
3
3
  export {
4
- r as CONDITION_COLORS,
4
+ i as CONDITION_COLORS,
5
+ T as CONDITION_PART,
5
6
  S as CONDITION_PLAN_FOLDER,
6
- T as CONDITION_SIDE_SYMBOL,
7
+ l as CONDITION_PLAN_SYMBOL,
8
+ r as CONDITION_SIDE_SYMBOL,
7
9
  I as CONDITION_TOKENS,
8
- _ as FDI_TO_META,
9
- l as FDI_TO_PALMER,
10
- O as FDI_TO_UNIVERSAL,
11
- N as PERMANENT_TEETH,
12
- n as PRIMARY_TEETH,
13
- c as SPECIAL_BY_ID,
14
- h as SPECIAL_ELEMENTS,
15
- m as SURFACE_ASSET_NAME,
16
- o as ToothScheme,
17
- A as chartFromConditions,
18
- i as conditionSymbolUrl,
19
- C as emptyChart,
20
- D as generalElement,
21
- F as isSpecialElement,
22
- L as labelFor,
23
- R as layoutSpecials,
24
- p as layoutTeeth,
25
- P as specialIconUrl,
26
- M as specialsForRow,
27
- U as surfaceSymbolUrl,
28
- f as toothImageUrl,
29
- e as toothSchemeAgent,
30
- t as toothSchemeVariants,
31
- u as zonePath,
32
- y as zoneSurface,
33
- b as zonesForTooth
10
+ O as FALLBACK_CONDITION_TOKEN,
11
+ N as FDI_TO_META,
12
+ _ as FDI_TO_PALMER,
13
+ c as FDI_TO_UNIVERSAL,
14
+ E as INTRINSIC_CONDITIONS,
15
+ C as PERMANENT_TEETH,
16
+ A as PRIMARY_TEETH,
17
+ m as SPECIAL_BY_ID,
18
+ L as SPECIAL_ELEMENTS,
19
+ d as STATUS_COLORS,
20
+ h as STATUS_OPACITY,
21
+ D as STATUS_TOKENS,
22
+ F as SURFACE_ASSET_NAME,
23
+ y as TOOTH_CONDITIONS,
24
+ s as ToothScheme,
25
+ P as chartFromConditions,
26
+ b as chartFromSymbols,
27
+ R as conditionColor,
28
+ U as conditionDefinition,
29
+ p as conditionGlyph,
30
+ M as conditionHasArtwork,
31
+ u as conditionLabel,
32
+ K as conditionLabelKey,
33
+ f as conditionPart,
34
+ B as conditionPlanFolder,
35
+ Y as conditionPlanSymbol,
36
+ H as conditionSideSymbol,
37
+ g as conditionSymbolUrl,
38
+ z as emptyChart,
39
+ w as generalElement,
40
+ x as isBuiltInCondition,
41
+ V as isSpecialElement,
42
+ k as labelFor,
43
+ G as layoutSpecials,
44
+ j as layoutTeeth,
45
+ q as planSymbolUrl,
46
+ v as rootFileStem,
47
+ J as rootScreenSide,
48
+ Q as rootsForTooth,
49
+ W as specialIconUrl,
50
+ X as specialsForRow,
51
+ Z as statusLabelKey,
52
+ $ as surfaceSymbolUrl,
53
+ aa as symbolColor,
54
+ oa as symbolsForTooth,
55
+ sa as toothImageUrl,
56
+ t as toothSchemeAgent,
57
+ e as toothSchemeVariants,
58
+ ta as zonePath,
59
+ ea as zoneSurface,
60
+ na as zonesForTooth
34
61
  };
35
62
  //# sourceMappingURL=index.js.map
@@ -3,10 +3,67 @@ export type Dentition = 'permanent' | 'primary' | 'mixed';
3
3
  export type Numbering = 'fdi' | 'universal' | 'palmer';
4
4
  export type ToothMode = 'interactive' | 'display';
5
5
  export type Surface = 'mesial' | 'distal' | 'occlusal' | 'buccal' | 'lingual';
6
- export type ToothCondition = 'caries' | 'filled' | 'crowned' | 'temporaryCrown' | 'bridge' | 'missing' | 'implant' | 'implantExtraction' | 'stub' | 'destroyed' | 'rootCanal';
6
+ /**
7
+ * Built-in findings. `destroyed` is the platform's *radice persistente*
8
+ * (retained root); the id is frozen for backward compatibility but the label
9
+ * and docs read "Retained root" so the two systems describe the same thing.
10
+ * `xray` is the chartable radiograph SYMBOL — distinct from the `radiographs`
11
+ * image archive a tooth can also carry.
12
+ */
13
+ export type ToothCondition = 'caries' | 'filled' | 'crowned' | 'temporaryCrown' | 'bridge' | 'missing' | 'implant' | 'implantExtraction' | 'stub' | 'destroyed' | 'rootCanal' | 'pin' | 'rootAmputation' | 'anchorage' | 'hook' | 'attachment' | 'mucogingivalSurgery' | 'periodontalSurgery' | 'curettage' | 'medication' | 'xray';
14
+ /**
15
+ * Open condition id: a built-in, or an id from a consumer's condition registry.
16
+ * The `(string & {})` keeps editor autocomplete on the built-ins while still
17
+ * accepting a practice-defined finding.
18
+ */
19
+ export type ConditionId = ToothCondition | (string & {});
20
+ /** Which anatomical layer a symbol marks — mirrors the platform's `part`. */
21
+ export type SymbolPart = 'element' | 'surface' | 'root';
22
+ /** Planned vs already executed. Matches the Care Plan's blue/green convention. */
23
+ export type SymbolStatus = 'planned' | 'executed';
24
+ /**
25
+ * A tooth's roots, named anatomically; `'single'` covers every one-rooted
26
+ * tooth. For flank-mounted markers (anchorage, hook) `mesial` / `distal` select
27
+ * which SIDE of the tooth the marker sits on rather than a root proper.
28
+ */
29
+ export type Root = 'mesial' | 'distal' | 'palatal' | 'buccal' | 'single';
30
+ /**
31
+ * One charted finding. Unlike the legacy `conditions` + `surfaces` pair — where
32
+ * a single flat surface list is shared by every condition on the tooth — a
33
+ * symbol scopes its own surfaces and roots, so two fillings on one tooth (one
34
+ * mesial, one distal) are finally representable.
35
+ */
36
+ export interface ToothSymbol {
37
+ /**
38
+ * Provenance — the platform's carePlanEntryId / findingEntryId. Lets a
39
+ * consumer route a click on the symbol back to the record that produced it.
40
+ */
41
+ id?: string;
42
+ condition: ConditionId;
43
+ /** Defaults to the condition's natural part (see `conditionPart`). */
44
+ part?: SymbolPart;
45
+ surfaces?: Surface[];
46
+ roots?: Root[];
47
+ /**
48
+ * Where a flank-mounted marker sits — the gingival surgeries ship
49
+ * `left` / `right` / `total` / `repeat` renders rather than per-root ones.
50
+ * Omitted, they fall back to `repeat`.
51
+ */
52
+ region?: SymbolRegion;
53
+ status?: SymbolStatus;
54
+ /** Per-practice colour override (a CSS colour or `var(--token)`). */
55
+ color?: string;
56
+ note?: string;
57
+ }
7
58
  export interface ToothState {
8
59
  conditions: ToothCondition[];
9
60
  surfaces: Surface[];
61
+ /**
62
+ * Per-symbol findings. When present these WIN over `conditions`/`surfaces`;
63
+ * when absent `symbolsForTooth()` derives the equivalent list from them, so
64
+ * the renderer only ever walks one shape.
65
+ */
66
+ symbols?: ToothSymbol[];
10
67
  notes?: string;
11
68
  }
12
69
  export type ToothChart = Record<FdiId, ToothState>;
@@ -81,24 +138,150 @@ export declare const FDI_TO_PALMER: Record<FdiId, string>;
81
138
  export declare function assetFdi(meta: ToothMeta): FdiId;
82
139
  /** Render scale applied to a tooth image (primary teeth are smaller). */
83
140
  export declare const PRIMARY_TOOTH_SCALE = 0.72;
141
+ /** Positional file stem used by the per-root `rootTreatment` renders. */
142
+ export type RootFileStem = 'full' | 'left' | 'right' | 'center';
143
+ /**
144
+ * The roots a tooth's artwork distinguishes: upper molars are three-rooted
145
+ * (mesial, distal, palatal), lower molars two-rooted, everything else renders
146
+ * as one root. Derived from `assetFdi` rather than `meta.anatomy` because a
147
+ * primary molar reuses PREMOLAR art (54 draws 14) and so has one root's worth
148
+ * of renders. The upper first premolars 14/24 are treated as single-rooted for
149
+ * the same reason — clinically they are often bifurcated, but no second render
150
+ * ships, and a wrong answer here resolves to a file that does not exist.
151
+ */
152
+ export declare function rootsForTooth(meta: ToothMeta): Root[];
153
+ /**
154
+ * Which SCREEN side a tooth's mesial or distal root faces. The chart is FIXED
155
+ * to clinical orientation — the patient's right sits on the viewer's left in
156
+ * BOTH arches — so the midline falls on the viewer's RIGHT for the patient's
157
+ * right quadrants (1 & 4) and on the viewer's LEFT for the patient's left
158
+ * quadrants (2 & 3). Mesial means "toward the midline", so it flips with it;
159
+ * distal is always its opposite. This is the same flip `zoneSurface` applies
160
+ * to the plan view's left/right zones. Primary quadrants normalise (5-8 → 1-4).
161
+ */
162
+ export declare function rootScreenSide(meta: ToothMeta, root: 'mesial' | 'distal'): 'left' | 'right';
163
+ /**
164
+ * Map an anatomical root to the `rootTreatment` file stem. `center` is the
165
+ * palatal root and ships for upper molars only; a root the tooth does not
166
+ * distinguish falls back to the whole-tooth render rather than a dead URL.
167
+ */
168
+ export declare function rootFileStem(meta: ToothMeta, root: Root): RootFileStem;
84
169
  /** CSS variable name (without the leading `var(...)`) that drives a condition's colour. */
85
170
  export declare const CONDITION_TOKENS: Record<ToothCondition, string>;
86
171
  /**
87
172
  * Convenience: the `fill="var(--destructive)"` string ready to paste onto
88
- * an SVG attribute. Kept in sync with `CONDITION_TOKENS`. Used for the legend
89
- * swatch + the surface-condition ring; whole-tooth conditions render a
90
- * distinct symbol image (the glyph is the colour-independent channel).
173
+ * an SVG attribute. DERIVED from `CONDITION_TOKENS` so the two can never drift.
174
+ * Used for the legend swatch + the surface-condition ring; whole-tooth
175
+ * conditions render a distinct symbol image (the glyph is the
176
+ * colour-independent channel).
91
177
  */
92
178
  export declare const CONDITION_COLORS: Record<ToothCondition, string>;
93
- export interface SideSymbolSpec {
94
- /** Asset folder under `side/`. */
179
+ /**
180
+ * Every built-in condition id, in declaration order. Derived from
181
+ * `CONDITION_TOKENS`, which the compiler forces to be exhaustive — so a new
182
+ * condition can never silently fall out of a hand-maintained list.
183
+ */
184
+ export declare const TOOTH_CONDITIONS: ToothCondition[];
185
+ /** True when an id names a built-in condition rather than a registry entry. */
186
+ export declare function isBuiltInCondition(id: ConditionId): id is ToothCondition;
187
+ /** The layer each built-in finding marks, used when a symbol omits `part`. */
188
+ export declare const CONDITION_PART: Record<ToothCondition, SymbolPart>;
189
+ /**
190
+ * Built-ins the chart draws ITSELF rather than from artwork — `missing` is an
191
+ * SVG cross, `caries` / `filled` fall back to the surface ring in the side
192
+ * view. They must never take the registry's glyph badge.
193
+ */
194
+ export declare const INTRINSIC_CONDITIONS: ToothCondition[];
195
+ export declare const STATUS_TOKENS: Record<SymbolStatus, string>;
196
+ /** The `var(...)` form of `STATUS_TOKENS`, ready for an SVG attribute. */
197
+ export declare const STATUS_COLORS: Record<SymbolStatus, string>;
198
+ /** The second, non-colour channel: a planned symbol renders lighter. */
199
+ export declare const STATUS_OPACITY: Record<SymbolStatus, number>;
200
+ /** i18n key for a status label (`ui.toothScheme.status.*`). */
201
+ export declare function statusLabelKey(status: SymbolStatus): string;
202
+ /**
203
+ * Screen-relative marker slot. `repeat` is what the BARE gingival-surgery type
204
+ * resolves to — it is the default marker, not a "repeat treatment" variant.
205
+ */
206
+ export type SymbolRegion = 'left' | 'right' | 'total' | 'repeat';
207
+ /** Corner glyph stem in `side/other/` — curettage, medication, x-ray. */
208
+ export type SymbolLetter = 'c' | 'm' | 'x';
209
+ /**
210
+ * How a symbol's file resolves inside `{folder}/{view}/`. A discriminated union
211
+ * rather than a bare enum because two shapes carry their own data: `per-fdi-root`
212
+ * has to know whether a multi-rooted tooth also ships a whole-tooth render, and
213
+ * `letter` pins the single glyph its condition owns.
214
+ */
215
+ export type SideSymbolSpec =
216
+ /** `{fdi}.webp` — one render per tooth. */
217
+ {
95
218
  folder: string;
96
- /** How the file resolves inside `{folder}/{view}/`. */
97
- resolve: 'per-fdi' | 'general' | 'rct';
219
+ resolve: 'per-fdi';
220
+ }
221
+ /** `general.webp` — one render per arch. */
222
+ | {
223
+ folder: string;
224
+ resolve: 'general';
225
+ }
226
+ /** `{fdi}/{full|left|right|center}.webp` — per-FDI subdirectory. */
227
+ | {
228
+ folder: string;
229
+ resolve: 'rct';
230
+ }
231
+ /**
232
+ * `{fdi}[-{mesial|distal|palatal}].webp`. `wholeTooth` distinguishes two
233
+ * folders that look symmetric and are not: `pin` ships a bare file for every
234
+ * FDI, `rootAmputation` only for single-rooted teeth.
235
+ */
236
+ | {
237
+ folder: string;
238
+ resolve: 'per-fdi-root';
239
+ wholeTooth: 'always' | 'single-rooted-only';
240
+ }
241
+ /** `{fdi}-{mesial|distal}.webp` — always suffixed, no bare file. */
242
+ | {
243
+ folder: string;
244
+ resolve: 'per-fdi-side';
245
+ }
246
+ /** `{fdi}_{left|right}.webp` — the only folder using an UNDERSCORE. */
247
+ | {
248
+ folder: string;
249
+ resolve: 'per-fdi-lr';
250
+ }
251
+ /** `{left|right|total|repeat}.webp` — no FDI in the name. */
252
+ | {
253
+ folder: string;
254
+ resolve: 'region';
255
+ }
256
+ /** `{left|right}.webp` — no FDI, and only the two sides ship. */
257
+ | {
258
+ folder: string;
259
+ resolve: 'screen-side';
260
+ }
261
+ /** `{c|m|x}.webp`. */
262
+ | {
263
+ folder: string;
264
+ resolve: 'letter';
265
+ letter: SymbolLetter;
266
+ };
267
+ /**
268
+ * Which instance of a symbol to resolve. The two fields are independent
269
+ * discriminators read by different `resolve` strategies (roots by the per-root
270
+ * shapes, regions by the screen-side ones) — passed in, never module state.
271
+ */
272
+ export interface SymbolTarget {
273
+ /** Root, or tooth side for a flank-mounted marker. */
274
+ root?: Root;
275
+ /** Explicit screen-relative slot; wins over `root` where both apply. */
276
+ region?: SymbolRegion;
98
277
  }
99
278
  export declare const CONDITION_SIDE_SYMBOL: Partial<Record<ToothCondition, SideSymbolSpec>>;
100
- /** Resolve the side-view symbol overlay URL for a condition on a tooth. */
101
- export declare function conditionSymbolUrl(meta: ToothMeta, assetBaseUrl: string, spec: SideSymbolSpec): string;
279
+ /**
280
+ * Resolve the side-view symbol overlay URL for a condition on a tooth. `target`
281
+ * picks a specific root / side; omitted, every strategy resolves to the
282
+ * whole-tooth or default marker, which is what the legacy call sites want.
283
+ */
284
+ export declare function conditionSymbolUrl(meta: ToothMeta, assetBaseUrl: string, spec: SideSymbolSpec, target?: SymbolTarget): string;
102
285
  export type Projection = 'side' | 'plan';
103
286
  /** The user-selectable view: a single projection, or both stacked together. */
104
287
  export type ViewMode = Projection | 'both';
@@ -108,6 +291,84 @@ export declare const SURFACE_ASSET_NAME: Record<Surface, string>;
108
291
  export declare const CONDITION_PLAN_FOLDER: Partial<Record<ToothCondition, string>>;
109
292
  /** Resolve a per-surface plan-view overlay URL (e.g. caries on the distal). */
110
293
  export declare function surfaceSymbolUrl(meta: ToothMeta, assetBaseUrl: string, folder: string, surface: Surface): string;
294
+ /**
295
+ * Whole-tooth symbols in the PLAN projection. Without these a crowned tooth
296
+ * reads as a plain tooth in the occlusal view, which also made `projection`
297
+ * `'both'` disagree with itself between its own rows.
298
+ *
299
+ * `anchorage` is the odd one: the plan art is two generic screen-side markers
300
+ * (no FDI in the name) composited over the default render, so its mesial/distal
301
+ * root has to be flipped through the quadrant — `symbolUrl` does that.
302
+ */
303
+ export declare const CONDITION_PLAN_SYMBOL: Partial<Record<ToothCondition, SideSymbolSpec>>;
304
+ /** Resolve the plan-view whole-tooth symbol overlay URL for a condition. */
305
+ export declare function planSymbolUrl(meta: ToothMeta, assetBaseUrl: string, spec: SideSymbolSpec, target?: SymbolTarget): string;
306
+ /**
307
+ * How much to scale a plan overlay so its tooth registers with the base render.
308
+ * `1` for everything on the standard canvas.
309
+ */
310
+ export declare function planOverlayScale(url: string): number;
311
+ export interface ConditionDefinition {
312
+ id: string;
313
+ /** Already-translated label — the CONSUMER owns custom-condition copy. */
314
+ label: string;
315
+ color?: string;
316
+ part?: SymbolPart;
317
+ sideSymbol?: SideSymbolSpec;
318
+ planSymbol?: SideSymbolSpec;
319
+ planFolder?: string;
320
+ /** 1–3 char mark rendered when the condition has no artwork. */
321
+ glyph?: string;
322
+ }
323
+ export type ConditionRegistry = readonly ConditionDefinition[];
324
+ /**
325
+ * A condition's display name. Built-ins resolve to an i18n key; registry
326
+ * entries carry literal text, because their copy lives in the consuming app.
327
+ */
328
+ export type ConditionLabel = {
329
+ kind: 'key';
330
+ key: string;
331
+ } | {
332
+ kind: 'text';
333
+ text: string;
334
+ };
335
+ /** Colour used when a condition is neither registered nor built in. */
336
+ export declare const FALLBACK_CONDITION_TOKEN = "--muted-foreground";
337
+ /** i18n key for a built-in condition label (`ui.toothScheme.condition.*`). */
338
+ export declare function conditionLabelKey(condition: ToothCondition): string;
339
+ /**
340
+ * Look up a registry entry. Registry ids shadow built-ins field by field, so a
341
+ * practice can re-tint or re-label a shipped finding without losing its art.
342
+ */
343
+ export declare function conditionDefinition(condition: ConditionId, registry?: ConditionRegistry): ConditionDefinition | undefined;
344
+ export declare function conditionLabel(condition: ConditionId, registry?: ConditionRegistry): ConditionLabel;
345
+ /** Paintable colour for a condition — never empty, so nothing renders unseen. */
346
+ export declare function conditionColor(condition: ConditionId, registry?: ConditionRegistry): string;
347
+ /** Paint colour for ONE charted symbol — its own override wins. */
348
+ export declare function symbolColor(symbol: ToothSymbol, registry?: ConditionRegistry): string;
349
+ export declare function conditionSideSymbol(condition: ConditionId, registry?: ConditionRegistry): SideSymbolSpec | undefined;
350
+ export declare function conditionPlanSymbol(condition: ConditionId, registry?: ConditionRegistry): SideSymbolSpec | undefined;
351
+ export declare function conditionPlanFolder(condition: ConditionId, registry?: ConditionRegistry): string | undefined;
352
+ export declare function conditionPart(condition: ConditionId, registry?: ConditionRegistry): SymbolPart;
353
+ /**
354
+ * The 1–3 character mark that stands in when a condition ships no artwork.
355
+ * Falls back to the id's initials so a practice-defined finding can be charted
356
+ * the day it is registered, rather than rendering as a silent blank.
357
+ */
358
+ export declare function conditionGlyph(condition: ConditionId, registry?: ConditionRegistry): string;
359
+ /**
360
+ * Whether a condition has artwork in a projection. When false the renderer
361
+ * must fall back to the glyph badge — that seam is what lets a practice chart
362
+ * a finding before a designer draws it.
363
+ */
364
+ export declare function conditionHasArtwork(condition: ConditionId, projection: Projection, registry?: ConditionRegistry): boolean;
365
+ /**
366
+ * The findings on a tooth as a symbol list. `state.symbols` wins outright when
367
+ * present (an explicit empty array therefore means "no findings"); otherwise
368
+ * the legacy pair is derived faithfully — the shared `surfaces` list attaches
369
+ * only to surface-part conditions, exactly as the flat renderer treated it.
370
+ */
371
+ export declare function symbolsForTooth(state: ToothState | undefined, registry?: ConditionRegistry): ToothSymbol[];
111
372
  /**
112
373
  * Clickable surface regions of a plan-view tooth, named by SCREEN position.
113
374
  * `zoneSurface()` resolves each to the anatomical `Surface` per quadrant.
@@ -260,4 +521,12 @@ export declare function layoutSpecials(specials: SpecialElement[], teethWidth: n
260
521
  export declare function labelFor(id: FdiId, numbering: Numbering): string;
261
522
  export declare function emptyChart(): ToothChart;
262
523
  export declare function chartFromConditions(conditions: Partial<Record<FdiId, ToothCondition[]>>): ToothChart;
524
+ /**
525
+ * Build a chart from the richer per-symbol shape. Each entry also projects its
526
+ * built-in conditions and surfaces back onto the legacy pair so readers that
527
+ * predate `symbols` (the first-condition `data-condition`, the periodontal
528
+ * chart, the care-plan card) still see the tooth. Registry ids are omitted from
529
+ * that projection — a legacy reader does not know the custom vocabulary.
530
+ */
531
+ export declare function chartFromSymbols(symbols: Partial<Record<FdiId, ToothSymbol[]>>): ToothChart;
263
532
  //# sourceMappingURL=tooth-data.d.ts.map
@@ -1,6 +1,6 @@
1
1
  import type { AgentAdapter } from '../../agent/types';
2
2
  import type { ToothSchemeHandle } from './tooth-scheme';
3
- import type { Radiograph, Surface, ToothCondition } from './tooth-data';
3
+ import type { ConditionId, Radiograph, RadiographKind, Root, Surface, SymbolPart, SymbolStatus, ToothCondition, ToothSymbol } from './tooth-data';
4
4
  export declare const toothSchemeAgent: AgentAdapter<ToothSchemeHandle>;
5
- export type { Radiograph, Surface, ToothCondition };
5
+ export type { ConditionId, Radiograph, RadiographKind, Root, Surface, SymbolPart, SymbolStatus, ToothCondition, ToothSymbol, };
6
6
  //# sourceMappingURL=tooth-scheme.agent.d.ts.map
@@ -1,4 +1,4 @@
1
- import { PERMANENT_TEETH, PRIMARY_TEETH, type Arch, type ViewMode, type Dentition, type FdiId, type Numbering, type Radiograph, type Surface, type ToothChart, type ToothCondition, type ToothMode } from './tooth-data';
1
+ import { PERMANENT_TEETH, PRIMARY_TEETH, type Arch, type ConditionDefinition, type ConditionId, type ToothSymbol, type ViewMode, type Dentition, type FdiId, type Numbering, type Radiograph, type Surface, type ToothChart, type ToothCondition, type ToothMode } from './tooth-data';
2
2
  export interface ToothSchemeProps {
3
3
  /** Opaque instance id — emitted as `data-component-id` for the agent registry. */
4
4
  id?: string;
@@ -67,8 +67,22 @@ export interface ToothSchemeProps {
67
67
  * The finding a tooth-click applies in interactive mode (the selected
68
68
  * tool in a Findings / "Stato dentale" panel). Clicking a tooth toggles
69
69
  * this condition. When unset, clicking cycles caries on/off.
70
+ *
71
+ * A surface-part tool (caries / filled) drills into the tooth's surfaces in
72
+ * the occlusal view; a root-part tool (root canal, post, apicectomy) drills
73
+ * into the roots of a multi-rooted tooth in the facial view. Accepts a
74
+ * `conditionRegistry` id as well as a built-in.
75
+ */
76
+ activeCondition?: ConditionId;
77
+ /**
78
+ * Practice-defined findings, resolved AHEAD of the built-in vocabulary field
79
+ * by field — so an entry may re-tint or re-label a shipped finding without
80
+ * losing its artwork, or add a wholly new one. An entry with no artwork
81
+ * renders its `glyph` badge, which is the seam that lets a practice chart a
82
+ * finding before a designer draws it. Labels are already-translated text:
83
+ * the consuming app owns custom-condition copy, not the kit.
70
84
  */
71
- activeCondition?: ToothCondition;
85
+ conditionRegistry?: ConditionDefinition[];
72
86
  /**
73
87
  * Base URL the photoreal tooth renders are served from. Resolves to
74
88
  * `${assetBaseUrl}/{side|plan}/{symbol}/{top|bottom}/{fdi}.webp`. Defaults
@@ -132,12 +146,25 @@ export interface ToothSchemeHandle {
132
146
  focusTooth: (id: FdiId) => void;
133
147
  /** Read the current chart state. */
134
148
  getChart: () => ToothChart;
135
- /** Read the findings recorded on one tooth (empty array if none). */
149
+ /**
150
+ * Read the built-in findings recorded on one tooth (empty array if none).
151
+ * Registry ids are omitted — use `getSymbols` for the full list.
152
+ */
136
153
  getTooth: (id: FdiId) => ToothCondition[];
154
+ /** Read the findings on one tooth as symbols, with surfaces, roots and status. */
155
+ getSymbols: (id: FdiId) => ToothSymbol[];
137
156
  /** Read the FDI ids of every tooth carrying a given finding. */
138
157
  teethWith: (condition: ToothCondition) => FdiId[];
158
+ /** As `teethWith`, but open to a `conditionRegistry` id. */
159
+ teethWithSymbol: (condition: ConditionId) => FdiId[];
139
160
  /** Add a finding to a tooth (no-op if already present). Optionally mark surfaces. */
140
161
  setFinding: (id: FdiId, condition: ToothCondition, surfaces?: Surface[]) => void;
162
+ /**
163
+ * Add or replace one symbol on a tooth — the surface-, root- and
164
+ * status-aware form of `setFinding`. Matched by `symbol.id` when the symbol
165
+ * carries provenance, else by its condition.
166
+ */
167
+ setSymbol: (id: FdiId, symbol: ToothSymbol) => void;
141
168
  /** Remove a finding from a tooth (no-op if absent). */
142
169
  removeFinding: (id: FdiId, condition: ToothCondition) => void;
143
170
  /** Clear every finding from a tooth. */