@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
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tooth-finding-palette-vqC-D2Ye.js","sources":["../../src/components/tooth-finding-palette/tooth-finding-palette.agent.ts","../../src/components/tooth-finding-palette/tooth-finding-palette.tsx"],"sourcesContent":["/* -------------------------------------------------------------------- */\n/* Agent adapter — ToothFindingPalette. */\n/* */\n/* The charting TOOL bar, not the chart. Everything here is transient */\n/* arming state — which finding the next click on a `ToothScheme` records, */\n/* and whether it records as planned or executed. No clinical record is */\n/* reachable through this adapter, so nothing is PHI and nothing is */\n/* `destructive`: clearing the selection discards an armed tool, never a */\n/* charted finding (that lives on `tooth-scheme`). */\n/* */\n/* Pure module: no runtime import of the `tooth-data` unions. The catalog */\n/* build (`scripts/build-agent-catalog.mjs`) imports every `*.agent.ts` */\n/* through Node's native type-stripping loader, which resolves relative */\n/* specifiers WITHOUT TS path/extension inference — so a value import of a */\n/* sibling module would break the build. Type-only imports are stripped */\n/* and safe. */\n/* -------------------------------------------------------------------- */\n\nimport type { AgentAdapter } from '../../agent/types';\nimport type { ConditionId, SymbolStatus } from '../tooth-scheme/tooth-data';\nimport type { ToothFindingPaletteHandle } from './tooth-finding-palette';\n\n/**\n * Deliberately NOT a closed `enum`, unlike `tooth-scheme.agent.ts`'s\n * `CONDITION_SCHEMA`. The palette renders practice-defined findings from\n * `conditionRegistry` alongside the built-ins, so an enum would reject ids the\n * component legitimately accepts. That also means there is no literal here to\n * drift from `ToothCondition` — the open shape is drift-proof by construction.\n */\nconst CONDITION_SCHEMA = {\n type: 'string',\n description:\n \"Finding id — either a built-in condition (the enum on tooth-scheme's set_finding: caries, filled, crowned, rootCanal, implant, …) or a practice-defined id supplied through the conditionRegistry prop. Read the live list from the `tools` state slot rather than guessing.\",\n} as const;\n\n/**\n * Compile-time exhaustiveness for the one CLOSED vocabulary this adapter\n * carries: adding a member to `SymbolStatus` without listing it here fails the\n * `Record` assignment, so the schema cannot silently fall behind the union.\n */\nconst STATUS_PRESENT: Record<SymbolStatus, true> = {\n planned: true,\n executed: true,\n};\n\nconst STATUS_SCHEMA = {\n enum: Object.keys(STATUS_PRESENT),\n description:\n \"Execution status the next charted finding takes — 'planned' (proposed treatment) or 'executed' (work already carried out).\",\n} as const;\n\nexport const toothFindingPaletteAgent: AgentAdapter<ToothFindingPaletteHandle> =\n {\n id: 'tooth-finding-palette',\n // select_single — exactly one finding tool is armed at a time (or none);\n // set_value — the planned/executed status is a second, separate choice.\n // `pick` covers `focus_tool`, which moves the roving tab stop WITHOUT\n // arming — the same shape as tooth-scheme's `focus_tooth` and\n // face-scheme's `focus_zone`, both of which sit under `pick`.\n capabilities: ['pick', 'select_single', 'set_value'],\n state: {\n activeFinding: {\n type: 'string | null',\n descriptionKey: 'ui.agent.toothFindingPalette.state.activeFinding',\n description:\n 'Id of the armed finding tool, or null when no tool is selected. Identifies a finding TYPE — never a patient, and never a charted finding.',\n read: (handle) => handle.getValue(),\n },\n status: {\n type: 'string',\n descriptionKey: 'ui.agent.toothFindingPalette.state.status',\n description:\n \"Execution status the next charted finding takes — 'planned' or 'executed'.\",\n read: (handle) => handle.getStatus(),\n },\n tools: {\n type: 'string[]',\n descriptionKey: 'ui.agent.toothFindingPalette.state.tools',\n description:\n 'Every finding id the palette currently offers, in render order — built-ins plus any practice-defined ids. The authoritative vocabulary for select_finding.',\n read: (handle) => handle.getTools(),\n },\n },\n actions: {\n select_finding: {\n // Arming a tool persists nothing — PRS 26 §3 puts selection and view\n // state under `read`, and 20 of the 23 shipped `select_*` actions\n // agree. `write` would make an agent prompt before picking up a\n // paintbrush.\n safety: 'read',\n argsType: '{ condition: ConditionId }',\n argsSchema: {\n type: 'object',\n properties: { condition: CONDITION_SCHEMA },\n required: ['condition'],\n },\n idempotent: true,\n descriptionKey: 'ui.agent.toothFindingPalette.actions.selectFinding',\n description:\n 'Arm a finding tool, so the next tooth (or surface, or root) picked on the paired dental chart records that finding.',\n examples: [\n {\n args: { condition: 'caries' },\n note: 'Arm the caries tool before marking a decayed surface.',\n },\n ],\n errors: [\n 'The condition is not among the palette\\'s tools — read the \"tools\" state slot for the live list.',\n ],\n invoke: (handle, args: { condition: ConditionId }) => {\n handle.setValue(args.condition);\n },\n },\n clear_selection: {\n // Disarms the palette. Nothing charted is lost and nothing persists —\n // `read`, matching `clear_selection` on data-table, file-manager\n // and list.\n safety: 'read',\n idempotent: true,\n descriptionKey: 'ui.agent.toothFindingPalette.actions.clearSelection',\n description:\n 'Disarm the palette so clicks on the chart stop recording a finding. Charted findings are untouched.',\n invoke: (handle) => {\n handle.clear();\n },\n },\n set_status: {\n safety: 'write',\n argsType: '{ status: SymbolStatus }',\n argsSchema: {\n type: 'object',\n properties: { status: STATUS_SCHEMA },\n required: ['status'],\n },\n idempotent: true,\n descriptionKey: 'ui.agent.toothFindingPalette.actions.setStatus',\n description:\n 'Choose whether the next charted finding is recorded as planned or executed.',\n examples: [\n {\n args: { status: 'executed' },\n note: 'Record the next finding as work already carried out.',\n },\n ],\n invoke: (handle, args: { status: SymbolStatus }) => {\n handle.setStatus(args.status);\n },\n },\n focus_tool: {\n safety: 'read',\n argsType: '{ condition: ConditionId }',\n argsSchema: {\n type: 'object',\n properties: { condition: CONDITION_SCHEMA },\n required: ['condition'],\n },\n idempotent: true,\n descriptionKey: 'ui.agent.toothFindingPalette.actions.focusTool',\n description:\n \"Move keyboard focus to a tool without arming it — relocates the toolbar's single roving tab stop.\",\n invoke: (handle, args: { condition: ConditionId }) => {\n handle.focusTool(args.condition);\n },\n },\n },\n domHooks: {\n root: {\n attr: 'data-component',\n value: 'tooth-finding-palette',\n description: 'Marks the palette toolbar wrapper.',\n },\n instanceId: {\n attr: 'data-component-id',\n sourceProp: 'id',\n description:\n 'Sourced from the id prop. Required to address a specific palette from the agent.',\n },\n item: {\n attr: 'data-condition',\n description:\n 'Each tool button emits its finding id as data-condition, and the armed tool also carries data-active=\"true\". Group wrappers carry data-group with the group id; the planned/executed pills carry data-status.',\n },\n },\n };\n","/* ------------------------------------------------------------------ */\n/* ToothFindingPalette — \"pick a finding, click a tooth\". */\n/* */\n/* The tool bar half of the charting pair. It arms a finding; the */\n/* paired `ToothScheme` records it. Six years of customer feedback */\n/* ask for the competitor's immediacy here: recognise the finding by */\n/* its picture, click it, click the tooth. So every tool renders the */\n/* condition's REAL artwork — the same photoreal render the chart */\n/* paints, resolved through the same `tooth-data` helpers — rather */\n/* than a colour swatch or a text-only button. */\n/* */\n/* - One tab stop. `role=\"toolbar\"` with a roving tabindex: Tab lands */\n/* on the toolbar once, arrow keys walk the tools (RTL-aware), and */\n/* Home / End jump to the ends. Disabled tools are stepped over. */\n/* */\n/* - Toggle-off is first class. Re-activating the armed tool clears */\n/* the selection (`onChange(null)`), matching the platform's */\n/* FindingsPanel — a clinician disarms the palette to go back to */\n/* reading the chart. */\n/* */\n/* - Open vocabulary. `conditionRegistry` entries render as tools */\n/* alongside the built-ins, falling back to the same glyph badge */\n/* the chart draws when a practice-defined finding has no artwork. */\n/* */\n/* - Selection is never colour alone: the armed tool changes border */\n/* COLOUR, background fill and font WEIGHT, and carries */\n/* `aria-pressed`. */\n/* ------------------------------------------------------------------ */\n\nimport {\n forwardRef,\n useCallback,\n useImperativeHandle,\n useMemo,\n useRef,\n useState,\n type HTMLAttributes,\n type KeyboardEvent as ReactKeyboardEvent,\n type ReactElement,\n} from 'react';\nimport { cva } from 'class-variance-authority';\nimport { useTranslation } from 'react-i18next';\nimport { useControllableState } from '../../hooks/use-controllable-state';\nimport { useAgentRegistration } from '../../agent/registry';\nimport { toothFindingPaletteAgent } from './tooth-finding-palette.agent';\nimport {\n CONDITION_COLORS,\n STATUS_COLORS,\n conditionColor,\n conditionGlyph,\n conditionHasArtwork,\n conditionLabel,\n conditionSideSymbol,\n conditionSymbolUrl,\n isBuiltInCondition,\n statusLabelKey,\n toothImageUrl,\n FDI_TO_META,\n type ConditionDefinition,\n type ConditionId,\n type ConditionRegistry,\n type SymbolStatus,\n} from '../tooth-scheme/tooth-data';\n\n/* ------------------------------------------------------------------ */\n/* Public types */\n/* ------------------------------------------------------------------ */\n\n/** One clinically-meaningful run of tools inside the palette. */\nexport interface ToothFindingPaletteGroup {\n /** Stable id — emitted as `data-group` and used for the default heading key. */\n id: string;\n /**\n * Already-translated heading. Omit for a built-in group and the palette\n * resolves `toothFindingPalette.group.<id>` instead; a custom group with no\n * label and no matching key falls back to its own id.\n */\n label?: string;\n /** Findings in this group, in render order. */\n conditions: ConditionId[];\n}\n\n/**\n * Curated imperative handle. Exposed as the `forwardRef` target so a future\n * agent / MCP UI bridge can arm a tool without touching the DOM — see\n * `tooth-finding-palette.agent.ts`. Everything here is transient tool state;\n * no patient record is reachable through it.\n */\nexport interface ToothFindingPaletteHandle {\n /** The armed finding id, or null when the palette is disarmed. */\n getValue: () => ConditionId | null;\n /** Arm a finding tool (or pass null to disarm). */\n setValue: (condition: ConditionId | null) => void;\n /** Disarm the palette. Charted findings are untouched. */\n clear: () => void;\n /** The status the next charted finding takes. */\n getStatus: () => SymbolStatus;\n setStatus: (status: SymbolStatus) => void;\n /** Every finding id the palette currently offers, in render order. */\n getTools: () => ConditionId[];\n /** Move the roving tab stop (and focus) to a tool without arming it. */\n focusTool: (condition: ConditionId) => void;\n}\n\nexport interface ToothFindingPaletteProps extends Omit<\n HTMLAttributes<HTMLDivElement>,\n 'onChange' | 'defaultValue' | 'role' | 'color'\n> {\n /** Controlled armed finding. `null` means \"no tool armed\". */\n value?: ConditionId | null;\n /** Initial armed finding when uncontrolled. */\n defaultValue?: ConditionId | null;\n /** Fires on every arm / disarm, including the toggle-off to `null`. */\n onChange?: (condition: ConditionId | null) => void;\n /**\n * Which tools to show. Ignored when `groups` is supplied (order the groups\n * instead). Omit both and the palette ships the default grouping of all\n * built-in findings.\n */\n conditions?: ConditionId[];\n /** Explicit grouping. Wins over `conditions`. */\n groups?: ToothFindingPaletteGroup[];\n /**\n * Practice-defined findings. They resolve exactly as they do on the chart\n * (registry → built-in → glyph fallback) and, under the default grouping,\n * any id that is not a built-in is appended as a \"Practice findings\" group.\n */\n conditionRegistry?: ConditionDefinition[];\n /** Show the planned / executed selector. Default `false`. */\n showStatus?: boolean;\n /** Controlled execution status. */\n status?: SymbolStatus;\n /** Initial execution status when uncontrolled. Default `'planned'`. */\n defaultStatus?: SymbolStatus;\n onStatusChange?: (status: SymbolStatus) => void;\n /** Tile scale. Default `'md'`. */\n size?: 'sm' | 'md' | 'lg';\n /** Wide bar (`'horizontal'`, default) or sidebar rail (`'vertical'`). */\n orientation?: 'horizontal' | 'vertical';\n /** Root the tooth renders are served from. Matches `ToothScheme`. */\n assetBaseUrl?: string;\n /** Disable every tool. */\n disabled?: boolean;\n /** Disable individual tools (e.g. findings this practice cannot chart). */\n disabledConditions?: ConditionId[];\n /** Accessible name for the toolbar. Falls back to a translated default. */\n ariaLabel?: string;\n /** Render the group headings. Default `true`. */\n showGroupLabels?: boolean;\n className?: string;\n}\n\n/* ------------------------------------------------------------------ */\n/* Default grouping */\n/* */\n/* 21 built-in findings in one flat wall is unusable at the chairside, */\n/* so they arrive in the runs a clinician already thinks in. The split */\n/* is by CLINICAL DISCIPLINE (what you are doing), not by the kit's */\n/* internal `CONDITION_PART` (which layer the symbol paints on) — a */\n/* dentist reaching for \"post\" is thinking endodontics, not \"root\". */\n/* Every built-in appears exactly once; `tooth-finding-palette.test` */\n/* asserts that against `TOOTH_CONDITIONS`, so a new condition cannot */\n/* be added to the union without landing in a group here. */\n/* ------------------------------------------------------------------ */\nexport const DEFAULT_FINDING_GROUPS: ToothFindingPaletteGroup[] = [\n // What a check-up records and what fills it back in.\n { id: 'decay', conditions: ['caries', 'filled', 'medication'] },\n // Everything that replaces or covers the crown.\n {\n id: 'prosthetics',\n conditions: [\n 'crowned',\n 'temporaryCrown',\n 'bridge',\n 'stub',\n 'implant',\n 'implantExtraction',\n ],\n },\n // Work inside the root.\n { id: 'endodontics', conditions: ['rootCanal', 'pin', 'rootAmputation'] },\n // Work on the tissue around the tooth.\n {\n id: 'periodontics',\n conditions: ['periodontalSurgery', 'mucogingivalSurgery', 'curettage'],\n },\n // Flank-mounted appliance fittings.\n { id: 'orthodontics', conditions: ['anchorage', 'hook', 'attachment'] },\n // The tooth's own status, plus the diagnostic marker.\n { id: 'diagnosis', conditions: ['missing', 'destroyed', 'xray'] },\n];\n\n/** Group id used for registry findings under the default grouping. */\nexport const CUSTOM_FINDING_GROUP_ID = 'custom';\n\n/** Status rendering order — planned first, mirroring the care-plan flow. */\nconst STATUS_ORDER: SymbolStatus[] = ['planned', 'executed'];\n\n/** Molar 16 is the legend's reference tooth; every side spec resolves for it. */\nconst REFERENCE_FDI = '16';\n\n/* ------------------------------------------------------------------ */\n/* CVA */\n/* ------------------------------------------------------------------ */\n\nexport const toothFindingPaletteVariants = cva(\n [\n 'ds:flex ds:text-[var(--foreground)]',\n 'ds:focus:outline-none',\n 'ds:aria-disabled:opacity-60',\n ].join(' '),\n {\n variants: {\n orientation: {\n horizontal: 'ds:flex-row ds:flex-wrap ds:items-start',\n vertical: 'ds:flex-col ds:items-stretch',\n },\n size: {\n sm: 'ds:gap-[var(--spacing-sm)]',\n md: 'ds:gap-[var(--spacing-md)]',\n lg: 'ds:gap-[var(--spacing-lg)]',\n },\n },\n defaultVariants: { orientation: 'horizontal', size: 'md' },\n },\n);\n\nconst groupVariants = cva(\n ['ds:flex ds:flex-col ds:gap-[var(--spacing-xs)] ds:min-w-0'].join(' '),\n);\n\nconst groupLabelVariants = cva(\n ['type-eyebrow ds:text-[color:var(--muted-foreground)]'].join(' '),\n);\n\nconst toolListVariants = cva(['ds:flex'].join(' '), {\n variants: {\n orientation: {\n horizontal: 'ds:flex-row ds:flex-wrap ds:items-stretch',\n vertical: 'ds:flex-col ds:items-stretch',\n },\n size: {\n sm: 'ds:gap-[var(--spacing-xs)]',\n md: 'ds:gap-[var(--spacing-xs)]',\n lg: 'ds:gap-[var(--spacing-sm)]',\n },\n },\n defaultVariants: { orientation: 'horizontal', size: 'md' },\n});\n\n// Selection is carried by THREE channels at once — border colour, background\n// fill and font weight — so the armed tool is never colour-alone. The border\n// is 2px in both states so arming a tool causes no layout shift.\nconst toolVariants = cva(\n [\n // `min-w-0` (and NOT `shrink-0`): a wrapped row wraps first and only then\n // shrinks the tiles on a line, so a long CJK label truncates inside its own\n // tile instead of pushing the toolbar wider than its frame.\n 'ds:relative ds:inline-flex ds:items-center ds:min-w-0',\n 'ds:gap-[var(--spacing-xs)] ds:text-start',\n 'ds:rounded-[var(--radius-md)] ds:border-2 ds:cursor-pointer',\n 'ds:min-h-[var(--min-target-size)]',\n 'ds:transition-colors ds:duration-[var(--animation-duration)]',\n 'ds:motion-reduce:transition-none',\n 'ds:focus:outline-none',\n 'ds:focus-visible:outline-[length:var(--focus-ring-width)]',\n 'ds:focus-visible:outline-solid ds:focus-visible:outline-[var(--ring)]',\n 'ds:focus-visible:outline-offset-[length:var(--focus-ring-offset)]',\n 'ds:forced-colors:border-[ButtonBorder]',\n 'ds:forced-colors:focus-visible:outline-[CanvasText]',\n 'ds:disabled:opacity-50 ds:disabled:cursor-not-allowed',\n ].join(' '),\n {\n variants: {\n selected: {\n true: [\n 'ds:border-[var(--primary)]',\n 'ds:bg-[color-mix(in_srgb,var(--primary)_12%,var(--card))]',\n 'ds:font-semibold',\n 'ds:forced-colors:border-[Highlight]',\n ].join(' '),\n false: [\n 'ds:border-[var(--border)] ds:bg-[var(--card)] ds:font-normal',\n 'ds:hover:bg-[color-mix(in_srgb,var(--accent)_10%,var(--card))]',\n ].join(' '),\n },\n size: {\n sm: 'type-meta ds:ps-[var(--spacing-xs)] ds:pe-[var(--spacing-sm)] ds:py-[var(--spacing-xs)]',\n md: 'type-body-sm ds:ps-[var(--spacing-xs)] ds:pe-[var(--spacing-sm)] ds:py-[var(--spacing-xs)]',\n lg: 'type-body ds:ps-[var(--spacing-sm)] ds:pe-[var(--spacing-md)] ds:py-[var(--spacing-sm)]',\n },\n orientation: {\n horizontal: '',\n vertical: 'ds:w-full ds:justify-start',\n },\n },\n defaultVariants: { selected: false, size: 'md', orientation: 'horizontal' },\n },\n);\n\n// Same three-channel treatment as a tool tile, in a compact pill.\nconst statusToolVariants = cva(\n [\n 'ds:inline-flex ds:items-center ds:shrink-0',\n 'ds:gap-[var(--spacing-xs)] ds:text-start ds:whitespace-nowrap',\n 'ds:rounded-[var(--radius-md)] ds:border-2 ds:cursor-pointer',\n 'ds:min-h-[var(--min-target-size)]',\n 'ds:ps-[var(--spacing-sm)] ds:pe-[var(--spacing-sm)]',\n 'ds:transition-colors ds:duration-[var(--animation-duration)]',\n 'ds:motion-reduce:transition-none',\n 'ds:focus:outline-none',\n 'ds:focus-visible:outline-[length:var(--focus-ring-width)]',\n 'ds:focus-visible:outline-solid ds:focus-visible:outline-[var(--ring)]',\n 'ds:focus-visible:outline-offset-[length:var(--focus-ring-offset)]',\n 'ds:forced-colors:border-[ButtonBorder]',\n 'ds:forced-colors:focus-visible:outline-[CanvasText]',\n 'ds:disabled:opacity-50 ds:disabled:cursor-not-allowed',\n ].join(' '),\n {\n variants: {\n selected: {\n true: [\n 'ds:border-[var(--primary)]',\n 'ds:bg-[color-mix(in_srgb,var(--primary)_12%,var(--card))]',\n 'ds:font-semibold',\n 'ds:forced-colors:border-[Highlight]',\n ].join(' '),\n false: [\n 'ds:border-[var(--border)] ds:bg-[var(--card)] ds:font-normal',\n 'ds:hover:bg-[color-mix(in_srgb,var(--accent)_10%,var(--card))]',\n ].join(' '),\n },\n size: {\n sm: 'type-meta ds:py-[var(--spacing-xs)]',\n md: 'type-body-sm ds:py-[var(--spacing-xs)]',\n lg: 'type-body ds:py-[var(--spacing-sm)]',\n },\n },\n defaultVariants: { selected: false, size: 'md' },\n },\n);\n\n/* ------------------------------------------------------------------ */\n/* Symbol rendering */\n/* */\n/* Deliberately mirrors `ToothScheme`'s legend mini-tooth: the same */\n/* reference molar, the same 0.9 inset, the same solid-ring / dashed- */\n/* ring / missing-X / glyph-badge fallbacks. A tool that did not look */\n/* like the thing it charts would defeat the whole point. */\n/* ------------------------------------------------------------------ */\n\n/** Mini-tooth pixel size per palette size (viewBox stays 36×62). */\nconst SYMBOL_SIZE: Record<'sm' | 'md' | 'lg', { w: number; h: number }> = {\n sm: { w: 18, h: 31 },\n md: { w: 22, h: 38 },\n lg: { w: 28, h: 48 },\n};\n\n/**\n * Fallback mark for a finding with no artwork — a `--card`-filled badge\n * outlined in the condition's colour carrying its glyph. Decorative: the\n * condition's label is the button's visible text and accessible name.\n */\nfunction GlyphBadge({\n glyph,\n colour,\n cx,\n cy,\n testId,\n}: {\n glyph: string;\n colour: string;\n cx: number;\n cy: number;\n testId?: string;\n}): ReactElement {\n const w = 9 + glyph.length * 5;\n return (\n <g\n transform={`translate(${cx - w / 2}, ${cy - 7})`}\n data-testid={testId}\n pointerEvents=\"none\"\n aria-hidden=\"true\"\n >\n <rect\n width={w}\n height={14}\n rx=\"3.5\"\n fill=\"var(--card)\"\n stroke={colour}\n strokeWidth=\"1.5\"\n />\n <text\n x={w / 2}\n y={10}\n textAnchor=\"middle\"\n fontSize=\"9\"\n fontWeight=\"600\"\n fill=\"var(--foreground)\"\n className=\"ds:select-none\"\n >\n {glyph}\n </text>\n </g>\n );\n}\n\nfunction FindingSymbol({\n condition,\n assetBaseUrl,\n registry,\n size,\n}: {\n condition: ConditionId;\n assetBaseUrl: string;\n registry?: ConditionRegistry;\n size: 'sm' | 'md' | 'lg';\n}): ReactElement {\n const meta = FDI_TO_META[REFERENCE_FDI];\n const spec = conditionSideSymbol(condition, registry);\n const isMissing = condition === 'missing';\n const colour = conditionColor(condition, registry);\n // caries / filled carry no side artwork — the chart rings them instead\n // (solid = caries, dashed = filled), so the tool shows the same ring.\n const isSurface =\n !spec && !isMissing && conditionHasArtwork(condition, 'side', registry);\n const inset = 36 * 0.9;\n const insetH = 62 * 0.9;\n const x = (36 - inset) / 2;\n const y = (62 - insetH) / 2;\n const { w, h } = SYMBOL_SIZE[size];\n return (\n <svg\n width={w}\n height={h}\n viewBox=\"0 0 36 62\"\n aria-hidden=\"true\"\n className=\"ds:block ds:shrink-0\"\n data-testid={`tooth-finding-palette-symbol-${condition}`}\n >\n <image\n href={toothImageUrl(meta, assetBaseUrl)}\n x={x}\n y={y}\n width={inset}\n height={insetH}\n preserveAspectRatio=\"xMidYMid meet\"\n opacity={isMissing ? 0.25 : 1}\n />\n {spec ? (\n <image\n href={conditionSymbolUrl(meta, assetBaseUrl, spec)}\n x={x}\n y={y}\n width={inset}\n height={insetH}\n preserveAspectRatio=\"xMidYMid meet\"\n />\n ) : null}\n {isSurface ? (\n <rect\n x={3}\n y={2}\n width={30}\n height={58}\n fill=\"none\"\n stroke={colour}\n strokeWidth={2}\n strokeDasharray={condition === 'filled' ? '4 2' : undefined}\n rx={6}\n />\n ) : null}\n {isMissing ? (\n <path\n d=\"M7 9 L29 53 M29 9 L7 53\"\n stroke={CONDITION_COLORS.missing}\n strokeWidth={2}\n fill=\"none\"\n />\n ) : null}\n {!spec && !isSurface && !isMissing ? (\n <GlyphBadge\n glyph={conditionGlyph(condition, registry)}\n colour={colour}\n cx={18}\n cy={31}\n testId={`tooth-finding-palette-glyph-${condition}`}\n />\n ) : null}\n </svg>\n );\n}\n\n/**\n * Execution-status marker: a filled disc for executed, a hollow ring for\n * planned. The SHAPE is the second channel, so status never rides on colour\n * alone. Identical treatment to the chart's own status pip.\n */\nfunction StatusDot({ status }: { status: SymbolStatus }): ReactElement {\n return (\n <svg\n width=\"14\"\n height=\"14\"\n viewBox=\"0 0 14 14\"\n aria-hidden=\"true\"\n className=\"ds:block ds:shrink-0\"\n >\n <circle\n cx={7}\n cy={7}\n r={5}\n fill={status === 'executed' ? STATUS_COLORS.executed : 'var(--card)'}\n stroke={STATUS_COLORS[status]}\n strokeWidth={1.75}\n />\n </svg>\n );\n}\n\n/* ------------------------------------------------------------------ */\n/* Helpers */\n/* ------------------------------------------------------------------ */\n\n/**\n * One entry in the roving-tabindex run. Tools and the status pills share a\n * single tab stop — the WAI-ARIA toolbar pattern explicitly allows a toolbar\n * to own a group of toggle buttons, and two extra Tab stops at the chairside\n * is exactly the friction the palette exists to remove.\n */\ntype RovingItem =\n | { kind: 'tool'; key: string; condition: ConditionId; disabled: boolean }\n | { kind: 'status'; key: string; status: SymbolStatus; disabled: boolean };\n\n/** Resolved group, after dedupe and registry folding. */\ninterface ResolvedGroup {\n id: string;\n label?: string;\n conditions: ConditionId[];\n /**\n * True for the wrapper the palette invents around a flat `conditions` list.\n * A synthetic group is layout only — it takes no `role=\"group\"` and no\n * heading, because \"All\" is not a clinical grouping and naming a group after\n * nothing just adds one more thing for a screen-reader user to step past.\n */\n synthetic?: boolean;\n}\n\nfunction dedupeGroups(\n groups: ToothFindingPaletteGroup[],\n synthetic = false,\n): ResolvedGroup[] {\n const seen = new Set<string>();\n return groups\n .map((group) => ({\n id: group.id,\n label: group.label,\n synthetic,\n conditions: group.conditions.filter((condition) => {\n if (seen.has(condition)) return false;\n seen.add(condition);\n return true;\n }),\n }))\n .filter((group) => group.conditions.length > 0);\n}\n\n/* ------------------------------------------------------------------ */\n/* Component */\n/* ------------------------------------------------------------------ */\n\nexport const ToothFindingPalette = forwardRef<\n ToothFindingPaletteHandle,\n ToothFindingPaletteProps\n>(function ToothFindingPalette(\n {\n id,\n value,\n defaultValue,\n onChange,\n conditions,\n groups,\n conditionRegistry,\n showStatus = false,\n status,\n defaultStatus = 'planned',\n onStatusChange,\n size = 'md',\n orientation = 'horizontal',\n assetBaseUrl = '/toothscheme/teeth',\n disabled = false,\n disabledConditions,\n ariaLabel,\n showGroupLabels = true,\n className,\n 'aria-label': ariaLabelAttr,\n 'aria-labelledby': ariaLabelledBy,\n ...rest\n },\n ref,\n): ReactElement {\n const { t } = useTranslation();\n\n const [rawValue, setRawValue] = useControllableState<ConditionId | null>({\n value,\n defaultValue: defaultValue ?? null,\n onChange,\n });\n const active = rawValue ?? null;\n\n const [rawStatus, setRawStatus] = useControllableState<SymbolStatus>({\n value: status,\n defaultValue: defaultStatus,\n onChange: onStatusChange,\n });\n const activeStatus = rawStatus ?? defaultStatus;\n\n /* ---- Which tools, in which groups ----------------------------- */\n const resolvedGroups = useMemo<ResolvedGroup[]>(() => {\n if (groups) return dedupeGroups(groups);\n if (conditions) return dedupeGroups([{ id: 'all', conditions }], true);\n const customIds = (conditionRegistry ?? [])\n .map((entry) => entry.id)\n .filter((entryId) => !isBuiltInCondition(entryId));\n const base =\n customIds.length > 0\n ? [\n ...DEFAULT_FINDING_GROUPS,\n { id: CUSTOM_FINDING_GROUP_ID, conditions: customIds },\n ]\n : DEFAULT_FINDING_GROUPS;\n return dedupeGroups(base);\n }, [groups, conditions, conditionRegistry]);\n\n const disabledSet = useMemo(\n () => new Set<string>(disabledConditions ?? []),\n [disabledConditions],\n );\n\n const toolIds = useMemo(\n () => resolvedGroups.flatMap((group) => group.conditions),\n [resolvedGroups],\n );\n\n /* ---- The roving run, in DOM order ------------------------------ */\n const items = useMemo<RovingItem[]>(() => {\n const tools = resolvedGroups.flatMap((group) =>\n group.conditions.map<RovingItem>((condition) => ({\n kind: 'tool',\n key: `tool:${condition}`,\n condition,\n disabled: disabled || disabledSet.has(condition),\n })),\n );\n if (!showStatus) return tools;\n return [\n ...tools,\n ...STATUS_ORDER.map<RovingItem>((s) => ({\n kind: 'status',\n key: `status:${s}`,\n status: s,\n disabled,\n })),\n ];\n }, [resolvedGroups, disabledSet, disabled, showStatus]);\n\n const itemsRef = useRef<(HTMLButtonElement | null)[]>([]);\n itemsRef.current.length = items.length;\n\n // `null` until the user (or the handle) moves focus — so the initial tab\n // stop can prefer the ARMED tool, matching the APG guidance that the roving\n // stop sits on the selected item.\n const [focusIndex, setFocusIndex] = useState<number | null>(null);\n\n const rovingIndex = useMemo(() => {\n if (items.length === 0) return -1;\n const firstEnabled = items.findIndex((item) => !item.disabled);\n // Nothing is operable — hand out NO tab stop at all. Parking `tabIndex=0`\n // on a `disabled` button would be a lie either way (the browser skips it),\n // and -1 keeps the DOM honest about a fully disabled toolbar.\n if (firstEnabled === -1) return -1;\n if (focusIndex !== null) {\n const clamped = Math.min(Math.max(focusIndex, 0), items.length - 1);\n return items[clamped].disabled ? firstEnabled : clamped;\n }\n // APG: the roving stop starts on the SELECTED item, not blindly on the\n // first one, so Tab lands a keyboard user on the tool already in hand.\n if (active !== null) {\n const armed = items.findIndex(\n (item) => item.kind === 'tool' && item.condition === active,\n );\n if (armed !== -1 && !items[armed].disabled) return armed;\n }\n return firstEnabled;\n }, [items, focusIndex, active]);\n\n /* ---- RTL: arrow keys follow reading order ---------------------- */\n const rootRef = useRef<HTMLDivElement | null>(null);\n // Read direction AT KEYDOWN rather than caching it at mount: a locale switch\n // that flips `dir` without remounting would otherwise leave the arrow mapping\n // running backwards against reading order. `getComputedStyle` walks up, so a\n // scoped `dir` works as well as one on the document.\n const readRTL = useCallback(\n (): boolean =>\n typeof window !== 'undefined' &&\n rootRef.current !== null &&\n window.getComputedStyle(rootRef.current).direction === 'rtl',\n [],\n );\n\n const focusAt = useCallback((index: number) => {\n setFocusIndex(index);\n itemsRef.current[index]?.focus();\n }, []);\n\n const handleKeyDown = useCallback(\n (event: ReactKeyboardEvent<HTMLDivElement>) => {\n const count = items.length;\n if (count === 0) return;\n const isRTL = readRTL();\n const forwardKey =\n orientation === 'vertical'\n ? 'ArrowDown'\n : isRTL\n ? 'ArrowLeft'\n : 'ArrowRight';\n const backwardKey =\n orientation === 'vertical'\n ? 'ArrowUp'\n : isRTL\n ? 'ArrowRight'\n : 'ArrowLeft';\n\n const from = rovingIndex === -1 ? 0 : rovingIndex;\n let next: number | null = null;\n let step = 1;\n if (event.key === forwardKey) {\n next = (from + 1) % count;\n } else if (event.key === backwardKey) {\n next = (from - 1 + count) % count;\n step = -1;\n } else if (event.key === 'Home') {\n next = 0;\n } else if (event.key === 'End') {\n next = count - 1;\n step = -1;\n }\n if (next === null) return;\n event.preventDefault();\n\n // Step over disabled tools rather than parking the tab stop on one.\n let candidate = next;\n for (let attempts = 0; attempts < count; attempts += 1) {\n if (!items[candidate].disabled) break;\n candidate = (candidate + step + count) % count;\n }\n focusAt(candidate);\n },\n [items, orientation, readRTL, rovingIndex, focusAt],\n );\n\n /* ---- Commit ---------------------------------------------------- */\n // Re-activating the armed tool disarms the palette — the platform's\n // FindingsPanel toggles off the same way, and a clinician needs to get back\n // to reading the chart without hunting for an \"off\" button.\n const toggleTool = useCallback(\n (condition: ConditionId) => {\n setRawValue(active === condition ? null : condition);\n },\n [active, setRawValue],\n );\n\n /* ---- Imperative handle ----------------------------------------- */\n const handle = useMemo<ToothFindingPaletteHandle>(\n () => ({\n getValue: () => active,\n setValue: (next) => setRawValue(next),\n clear: () => setRawValue(null),\n getStatus: () => activeStatus,\n setStatus: (next) => setRawStatus(next),\n getTools: () => [...toolIds],\n focusTool: (condition) => {\n const index = items.findIndex(\n (item) => item.kind === 'tool' && item.condition === condition,\n );\n if (index !== -1) focusAt(index);\n },\n }),\n [active, activeStatus, items, toolIds, setRawValue, setRawStatus, focusAt],\n );\n\n useImperativeHandle(ref, () => handle, [handle]);\n useAgentRegistration(toothFindingPaletteAgent, handle, id);\n\n /* ---- Render ---------------------------------------------------- */\n const toolbarLabel =\n ariaLabel ?? ariaLabelAttr ?? t('toothFindingPalette.ariaLabel');\n\n const groupHeading = (group: ResolvedGroup): string =>\n group.label ??\n t(`toothFindingPalette.group.${group.id}`, { defaultValue: group.id });\n\n let cursor = 0;\n\n return (\n <div\n {...rest}\n ref={rootRef}\n role=\"toolbar\"\n aria-orientation={orientation}\n aria-label={ariaLabelledBy ? undefined : toolbarLabel}\n aria-labelledby={ariaLabelledBy}\n aria-disabled={disabled || undefined}\n data-component=\"tooth-finding-palette\"\n data-component-id={id}\n data-orientation={orientation}\n data-size={size}\n data-active={active ?? undefined}\n data-status={showStatus ? activeStatus : undefined}\n data-testid=\"tooth-finding-palette\"\n id={id}\n onKeyDown={handleKeyDown}\n className={[toothFindingPaletteVariants({ orientation, size }), className]\n .filter(Boolean)\n .join(' ')}\n >\n {items.length === 0 ? (\n <p className=\"type-body-sm ds:text-[color:var(--muted-foreground)] ds:m-0\">\n {t('toothFindingPalette.empty')}\n </p>\n ) : null}\n\n {resolvedGroups.map((group) => {\n const heading = groupHeading(group);\n return (\n <div\n key={group.id}\n // A real group keeps its ARIA name even with the visible heading\n // hidden — a screen-reader user still benefits from the structure.\n role={group.synthetic ? undefined : 'group'}\n aria-label={group.synthetic ? undefined : heading}\n data-group={group.id}\n data-testid={`tooth-finding-palette-group-${group.id}`}\n className={groupVariants()}\n >\n {showGroupLabels && !group.synthetic ? (\n // Sighted-only: the group already carries `heading` as its\n // accessible name, so exposing the text again would announce\n // the group twice.\n <span aria-hidden=\"true\" className={groupLabelVariants()}>\n {heading}\n </span>\n ) : null}\n <div className={toolListVariants({ orientation, size })}>\n {group.conditions.map((condition) => {\n const index = cursor;\n cursor += 1;\n const item = items[index];\n const isDisabled = item?.disabled ?? disabled;\n const label = conditionLabel(condition, conditionRegistry);\n const text = label.kind === 'key' ? t(label.key) : label.text;\n const isActive = active === condition;\n return (\n <button\n key={condition}\n type=\"button\"\n ref={(node) => {\n itemsRef.current[index] = node;\n }}\n disabled={isDisabled}\n aria-pressed={isActive}\n tabIndex={index === rovingIndex ? 0 : -1}\n data-condition={condition}\n data-active={isActive ? 'true' : undefined}\n data-testid={`tooth-finding-palette-tool-${condition}`}\n // A long label (CJK, or a practice-defined finding) truncates\n // to an ellipsis; the palette trades on recognition, so a\n // sighted user needs a way to read it back.\n title={text}\n onFocus={() => setFocusIndex(index)}\n onClick={() => toggleTool(condition)}\n className={toolVariants({\n selected: isActive,\n size,\n orientation,\n })}\n >\n <FindingSymbol\n condition={condition}\n assetBaseUrl={assetBaseUrl}\n registry={conditionRegistry}\n size={size}\n />\n <span className=\"ds:truncate ds:min-w-0\">{text}</span>\n </button>\n );\n })}\n </div>\n </div>\n );\n })}\n\n {showStatus ? (\n <div\n role=\"group\"\n aria-label={t('toothFindingPalette.statusLabel')}\n data-group=\"status\"\n data-testid=\"tooth-finding-palette-status-group\"\n className={groupVariants()}\n >\n {showGroupLabels ? (\n <span aria-hidden=\"true\" className={groupLabelVariants()}>\n {t('toothFindingPalette.statusLabel')}\n </span>\n ) : null}\n <div className={toolListVariants({ orientation, size })}>\n {STATUS_ORDER.map((option) => {\n const index = cursor;\n cursor += 1;\n const isActive = activeStatus === option;\n return (\n <button\n key={option}\n type=\"button\"\n ref={(node) => {\n itemsRef.current[index] = node;\n }}\n disabled={disabled}\n aria-pressed={isActive}\n tabIndex={index === rovingIndex ? 0 : -1}\n data-status={option}\n data-active={isActive ? 'true' : undefined}\n data-testid={`tooth-finding-palette-status-${option}`}\n onFocus={() => setFocusIndex(index)}\n // Status always has a value — unlike a tool, re-activating\n // the current status is a no-op rather than a toggle-off.\n onClick={() => setRawStatus(option)}\n className={statusToolVariants({ selected: isActive, size })}\n >\n <StatusDot status={option} />\n <span>{t(statusLabelKey(option))}</span>\n </button>\n );\n })}\n </div>\n </div>\n ) : null}\n </div>\n );\n});\n\nToothFindingPalette.displayName = 'ToothFindingPalette';\n"],"names":["CONDITION_SCHEMA","STATUS_PRESENT","STATUS_SCHEMA","toothFindingPaletteAgent","handle","args","DEFAULT_FINDING_GROUPS","CUSTOM_FINDING_GROUP_ID","STATUS_ORDER","REFERENCE_FDI","toothFindingPaletteVariants","cva","groupVariants","groupLabelVariants","toolListVariants","toolVariants","statusToolVariants","SYMBOL_SIZE","GlyphBadge","glyph","colour","cx","cy","testId","w","jsxs","jsx","FindingSymbol","condition","assetBaseUrl","registry","size","meta","FDI_TO_META","spec","conditionSideSymbol","isMissing","conditionColor","isSurface","conditionHasArtwork","inset","insetH","x","y","h","toothImageUrl","conditionSymbolUrl","CONDITION_COLORS","conditionGlyph","StatusDot","status","STATUS_COLORS","dedupeGroups","groups","synthetic","seen","group","ToothFindingPalette","forwardRef","id","value","defaultValue","onChange","conditions","conditionRegistry","showStatus","defaultStatus","onStatusChange","orientation","disabled","disabledConditions","ariaLabel","showGroupLabels","className","ariaLabelAttr","ariaLabelledBy","rest","ref","t","useTranslation","rawValue","setRawValue","useControllableState","active","rawStatus","setRawStatus","activeStatus","resolvedGroups","useMemo","customIds","entry","entryId","isBuiltInCondition","base","disabledSet","toolIds","items","tools","s","itemsRef","useRef","focusIndex","setFocusIndex","useState","rovingIndex","firstEnabled","item","clamped","armed","rootRef","readRTL","useCallback","focusAt","index","_a","handleKeyDown","event","count","isRTL","forwardKey","backwardKey","from","next","step","candidate","attempts","toggleTool","useImperativeHandle","useAgentRegistration","toolbarLabel","groupHeading","cursor","heading","isDisabled","label","conditionLabel","text","isActive","node","option","statusLabelKey"],"mappings":";;;;;;;AA6BA,MAAMA,KAAmB;AAAA,EACvB,MAAM;AAAA,EACN,aACE;AACJ,GAOMC,KAA6C;AAAA,EACjD,SAAS;AAAA,EACT,UAAU;AACZ,GAEMC,KAAgB;AAAA,EACpB,MAAM,OAAO,KAAKD,EAAc;AAAA,EAChC,aACE;AACJ,GAEaE,KACX;AAAA,EACE,IAAI;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMJ,cAAc,CAAC,QAAQ,iBAAiB,WAAW;AAAA,EACnD,OAAO;AAAA,IACL,eAAe;AAAA,MACb,MAAM;AAAA,MACN,gBAAgB;AAAA,MAChB,aACE;AAAA,MACF,MAAM,CAACC,MAAWA,EAAO,SAAA;AAAA,IAAS;AAAA,IAEpC,QAAQ;AAAA,MACN,MAAM;AAAA,MACN,gBAAgB;AAAA,MAChB,aACE;AAAA,MACF,MAAM,CAACA,MAAWA,EAAO,UAAA;AAAA,IAAU;AAAA,IAErC,OAAO;AAAA,MACL,MAAM;AAAA,MACN,gBAAgB;AAAA,MAChB,aACE;AAAA,MACF,MAAM,CAACA,MAAWA,EAAO,SAAA;AAAA,IAAS;AAAA,EACpC;AAAA,EAEF,SAAS;AAAA,IACP,gBAAgB;AAAA;AAAA;AAAA;AAAA;AAAA,MAKd,QAAQ;AAAA,MACR,UAAU;AAAA,MACV,YAAY;AAAA,QACV,MAAM;AAAA,QACN,YAAY,EAAE,WAAWJ,GAAA;AAAA,QACzB,UAAU,CAAC,WAAW;AAAA,MAAA;AAAA,MAExB,YAAY;AAAA,MACZ,gBAAgB;AAAA,MAChB,aACE;AAAA,MACF,UAAU;AAAA,QACR;AAAA,UACE,MAAM,EAAE,WAAW,SAAA;AAAA,UACnB,MAAM;AAAA,QAAA;AAAA,MACR;AAAA,MAEF,QAAQ;AAAA,QACN;AAAA,MAAA;AAAA,MAEF,QAAQ,CAACI,GAAQC,MAAqC;AACpD,QAAAD,EAAO,SAASC,EAAK,SAAS;AAAA,MAChC;AAAA,IAAA;AAAA,IAEF,iBAAiB;AAAA;AAAA;AAAA;AAAA,MAIf,QAAQ;AAAA,MACR,YAAY;AAAA,MACZ,gBAAgB;AAAA,MAChB,aACE;AAAA,MACF,QAAQ,CAACD,MAAW;AAClB,QAAAA,EAAO,MAAA;AAAA,MACT;AAAA,IAAA;AAAA,IAEF,YAAY;AAAA,MACV,QAAQ;AAAA,MACR,UAAU;AAAA,MACV,YAAY;AAAA,QACV,MAAM;AAAA,QACN,YAAY,EAAE,QAAQF,GAAA;AAAA,QACtB,UAAU,CAAC,QAAQ;AAAA,MAAA;AAAA,MAErB,YAAY;AAAA,MACZ,gBAAgB;AAAA,MAChB,aACE;AAAA,MACF,UAAU;AAAA,QACR;AAAA,UACE,MAAM,EAAE,QAAQ,WAAA;AAAA,UAChB,MAAM;AAAA,QAAA;AAAA,MACR;AAAA,MAEF,QAAQ,CAACE,GAAQC,MAAmC;AAClD,QAAAD,EAAO,UAAUC,EAAK,MAAM;AAAA,MAC9B;AAAA,IAAA;AAAA,IAEF,YAAY;AAAA,MACV,QAAQ;AAAA,MACR,UAAU;AAAA,MACV,YAAY;AAAA,QACV,MAAM;AAAA,QACN,YAAY,EAAE,WAAWL,GAAA;AAAA,QACzB,UAAU,CAAC,WAAW;AAAA,MAAA;AAAA,MAExB,YAAY;AAAA,MACZ,gBAAgB;AAAA,MAChB,aACE;AAAA,MACF,QAAQ,CAACI,GAAQC,MAAqC;AACpD,QAAAD,EAAO,UAAUC,EAAK,SAAS;AAAA,MACjC;AAAA,IAAA;AAAA,EACF;AAAA,EAEF,UAAU;AAAA,IACR,MAAM;AAAA,MACJ,MAAM;AAAA,MACN,OAAO;AAAA,MACP,aAAa;AAAA,IAAA;AAAA,IAEf,YAAY;AAAA,MACV,MAAM;AAAA,MACN,YAAY;AAAA,MACZ,aACE;AAAA,IAAA;AAAA,IAEJ,MAAM;AAAA,MACJ,MAAM;AAAA,MACN,aACE;AAAA,IAAA;AAAA,EACJ;AAEJ,GCnBWC,KAAqD;AAAA;AAAA,EAEhE,EAAE,IAAI,SAAS,YAAY,CAAC,UAAU,UAAU,YAAY,EAAA;AAAA;AAAA,EAE5D;AAAA,IACE,IAAI;AAAA,IACJ,YAAY;AAAA,MACV;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IAAA;AAAA,EACF;AAAA;AAAA,EAGF,EAAE,IAAI,eAAe,YAAY,CAAC,aAAa,OAAO,gBAAgB,EAAA;AAAA;AAAA,EAEtE;AAAA,IACE,IAAI;AAAA,IACJ,YAAY,CAAC,sBAAsB,uBAAuB,WAAW;AAAA,EAAA;AAAA;AAAA,EAGvE,EAAE,IAAI,gBAAgB,YAAY,CAAC,aAAa,QAAQ,YAAY,EAAA;AAAA;AAAA,EAEpE,EAAE,IAAI,aAAa,YAAY,CAAC,WAAW,aAAa,MAAM,EAAA;AAChE,GAGaC,KAA0B,UAGjCC,KAA+B,CAAC,WAAW,UAAU,GAGrDC,KAAgB,MAMTC,KAA8BC;AAAA,EACzC;AAAA,IACE;AAAA,IACA;AAAA,IACA;AAAA,EAAA,EACA,KAAK,GAAG;AAAA,EACV;AAAA,IACE,UAAU;AAAA,MACR,aAAa;AAAA,QACX,YAAY;AAAA,QACZ,UAAU;AAAA,MAAA;AAAA,MAEZ,MAAM;AAAA,QACJ,IAAI;AAAA,QACJ,IAAI;AAAA,QACJ,IAAI;AAAA,MAAA;AAAA,IACN;AAAA,IAEF,iBAAiB,EAAE,aAAa,cAAc,MAAM,KAAA;AAAA,EAAK;AAE7D,GAEMC,KAAgBD;AAAA,EACpB,CAAC,2DAA2D,EAAE,KAAK,GAAG;AACxE,GAEME,KAAqBF;AAAA,EACzB,CAAC,sDAAsD,EAAE,KAAK,GAAG;AACnE,GAEMG,KAAmBH,EAAI,CAAC,SAAS,EAAE,KAAK,GAAG,GAAG;AAAA,EAClD,UAAU;AAAA,IACR,aAAa;AAAA,MACX,YAAY;AAAA,MACZ,UAAU;AAAA,IAAA;AAAA,IAEZ,MAAM;AAAA,MACJ,IAAI;AAAA,MACJ,IAAI;AAAA,MACJ,IAAI;AAAA,IAAA;AAAA,EACN;AAAA,EAEF,iBAAiB,EAAE,aAAa,cAAc,MAAM,KAAA;AACtD,CAAC,GAKKI,KAAeJ;AAAA,EACnB;AAAA;AAAA;AAAA;AAAA,IAIE;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EAAA,EACA,KAAK,GAAG;AAAA,EACV;AAAA,IACE,UAAU;AAAA,MACR,UAAU;AAAA,QACR,MAAM;AAAA,UACJ;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,QAAA,EACA,KAAK,GAAG;AAAA,QACV,OAAO;AAAA,UACL;AAAA,UACA;AAAA,QAAA,EACA,KAAK,GAAG;AAAA,MAAA;AAAA,MAEZ,MAAM;AAAA,QACJ,IAAI;AAAA,QACJ,IAAI;AAAA,QACJ,IAAI;AAAA,MAAA;AAAA,MAEN,aAAa;AAAA,QACX,YAAY;AAAA,QACZ,UAAU;AAAA,MAAA;AAAA,IACZ;AAAA,IAEF,iBAAiB,EAAE,UAAU,IAAO,MAAM,MAAM,aAAa,aAAA;AAAA,EAAa;AAE9E,GAGMK,KAAqBL;AAAA,EACzB;AAAA,IACE;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EAAA,EACA,KAAK,GAAG;AAAA,EACV;AAAA,IACE,UAAU;AAAA,MACR,UAAU;AAAA,QACR,MAAM;AAAA,UACJ;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,QAAA,EACA,KAAK,GAAG;AAAA,QACV,OAAO;AAAA,UACL;AAAA,UACA;AAAA,QAAA,EACA,KAAK,GAAG;AAAA,MAAA;AAAA,MAEZ,MAAM;AAAA,QACJ,IAAI;AAAA,QACJ,IAAI;AAAA,QACJ,IAAI;AAAA,MAAA;AAAA,IACN;AAAA,IAEF,iBAAiB,EAAE,UAAU,IAAO,MAAM,KAAA;AAAA,EAAK;AAEnD,GAYMM,KAAoE;AAAA,EACxE,IAAI,EAAE,GAAG,IAAI,GAAG,GAAA;AAAA,EAChB,IAAI,EAAE,GAAG,IAAI,GAAG,GAAA;AAAA,EAChB,IAAI,EAAE,GAAG,IAAI,GAAG,GAAA;AAClB;AAOA,SAASC,GAAW;AAAA,EAClB,OAAAC;AAAA,EACA,QAAAC;AAAA,EACA,IAAAC;AAAA,EACA,IAAAC;AAAA,EACA,QAAAC;AACF,GAMiB;AACf,QAAMC,IAAI,IAAIL,EAAM,SAAS;AAC7B,SACE,gBAAAM;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,WAAW,aAAaJ,IAAKG,IAAI,CAAC,KAAKF,IAAK,CAAC;AAAA,MAC7C,eAAaC;AAAA,MACb,eAAc;AAAA,MACd,eAAY;AAAA,MAEZ,UAAA;AAAA,QAAA,gBAAAG;AAAA,UAAC;AAAA,UAAA;AAAA,YACC,OAAOF;AAAA,YACP,QAAQ;AAAA,YACR,IAAG;AAAA,YACH,MAAK;AAAA,YACL,QAAQJ;AAAA,YACR,aAAY;AAAA,UAAA;AAAA,QAAA;AAAA,QAEd,gBAAAM;AAAA,UAAC;AAAA,UAAA;AAAA,YACC,GAAGF,IAAI;AAAA,YACP,GAAG;AAAA,YACH,YAAW;AAAA,YACX,UAAS;AAAA,YACT,YAAW;AAAA,YACX,MAAK;AAAA,YACL,WAAU;AAAA,YAET,UAAAL;AAAA,UAAA;AAAA,QAAA;AAAA,MACH;AAAA,IAAA;AAAA,EAAA;AAGN;AAEA,SAASQ,GAAc;AAAA,EACrB,WAAAC;AAAA,EACA,cAAAC;AAAA,EACA,UAAAC;AAAA,EACA,MAAAC;AACF,GAKiB;AACf,QAAMC,IAAOC,GAAYxB,EAAa,GAChCyB,IAAOC,GAAoBP,GAAWE,CAAQ,GAC9CM,IAAYR,MAAc,WAC1BR,IAASiB,GAAeT,GAAWE,CAAQ,GAG3CQ,IACJ,CAACJ,KAAQ,CAACE,KAAaG,GAAoBX,GAAW,QAAQE,CAAQ,GAClEU,IAAQ,KAAK,KACbC,IAAS,KAAK,KACdC,KAAK,KAAKF,KAAS,GACnBG,KAAK,KAAKF,KAAU,GACpB,EAAE,GAAAjB,GAAG,GAAAoB,MAAM3B,GAAYc,CAAI;AACjC,SACE,gBAAAN;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,OAAOD;AAAA,MACP,QAAQoB;AAAA,MACR,SAAQ;AAAA,MACR,eAAY;AAAA,MACZ,WAAU;AAAA,MACV,eAAa,gCAAgChB,CAAS;AAAA,MAEtD,UAAA;AAAA,QAAA,gBAAAF;AAAA,UAAC;AAAA,UAAA;AAAA,YACC,MAAMmB,GAAcb,GAAMH,CAAY;AAAA,YACtC,GAAAa;AAAA,YACA,GAAAC;AAAA,YACA,OAAOH;AAAA,YACP,QAAQC;AAAA,YACR,qBAAoB;AAAA,YACpB,SAASL,IAAY,OAAO;AAAA,UAAA;AAAA,QAAA;AAAA,QAE7BF,IACC,gBAAAR;AAAA,UAAC;AAAA,UAAA;AAAA,YACC,MAAMoB,GAAmBd,GAAMH,GAAcK,CAAI;AAAA,YACjD,GAAAQ;AAAA,YACA,GAAAC;AAAA,YACA,OAAOH;AAAA,YACP,QAAQC;AAAA,YACR,qBAAoB;AAAA,UAAA;AAAA,QAAA,IAEpB;AAAA,QACHH,IACC,gBAAAZ;AAAA,UAAC;AAAA,UAAA;AAAA,YACC,GAAG;AAAA,YACH,GAAG;AAAA,YACH,OAAO;AAAA,YACP,QAAQ;AAAA,YACR,MAAK;AAAA,YACL,QAAQN;AAAA,YACR,aAAa;AAAA,YACb,iBAAiBQ,MAAc,WAAW,QAAQ;AAAA,YAClD,IAAI;AAAA,UAAA;AAAA,QAAA,IAEJ;AAAA,QACHQ,IACC,gBAAAV;AAAA,UAAC;AAAA,UAAA;AAAA,YACC,GAAE;AAAA,YACF,QAAQqB,GAAiB;AAAA,YACzB,aAAa;AAAA,YACb,MAAK;AAAA,UAAA;AAAA,QAAA,IAEL;AAAA,QACH,CAACb,KAAQ,CAACI,KAAa,CAACF,IACvB,gBAAAV;AAAA,UAACR;AAAA,UAAA;AAAA,YACC,OAAO8B,GAAepB,GAAWE,CAAQ;AAAA,YACzC,QAAAV;AAAA,YACA,IAAI;AAAA,YACJ,IAAI;AAAA,YACJ,QAAQ,+BAA+BQ,CAAS;AAAA,UAAA;AAAA,QAAA,IAEhD;AAAA,MAAA;AAAA,IAAA;AAAA,EAAA;AAGV;AAOA,SAASqB,GAAU,EAAE,QAAAC,KAAkD;AACrE,SACE,gBAAAxB;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,OAAM;AAAA,MACN,QAAO;AAAA,MACP,SAAQ;AAAA,MACR,eAAY;AAAA,MACZ,WAAU;AAAA,MAEV,UAAA,gBAAAA;AAAA,QAAC;AAAA,QAAA;AAAA,UACC,IAAI;AAAA,UACJ,IAAI;AAAA,UACJ,GAAG;AAAA,UACH,MAAMwB,MAAW,aAAaC,GAAc,WAAW;AAAA,UACvD,QAAQA,GAAcD,CAAM;AAAA,UAC5B,aAAa;AAAA,QAAA;AAAA,MAAA;AAAA,IACf;AAAA,EAAA;AAGN;AA8BA,SAASE,EACPC,GACAC,IAAY,IACK;AACjB,QAAMC,wBAAW,IAAA;AACjB,SAAOF,EACJ,IAAI,CAACG,OAAW;AAAA,IACf,IAAIA,EAAM;AAAA,IACV,OAAOA,EAAM;AAAA,IACb,WAAAF;AAAA,IACA,YAAYE,EAAM,WAAW,OAAO,CAAC5B,MAC/B2B,EAAK,IAAI3B,CAAS,IAAU,MAChC2B,EAAK,IAAI3B,CAAS,GACX,GACR;AAAA,EAAA,EACD,EACD,OAAO,CAAC4B,MAAUA,EAAM,WAAW,SAAS,CAAC;AAClD;AAMO,MAAMC,KAAsBC,GAGjC,SACA;AAAA,EACE,IAAAC;AAAA,EACA,OAAAC;AAAA,EACA,cAAAC;AAAA,EACA,UAAAC;AAAA,EACA,YAAAC;AAAA,EACA,QAAAV;AAAA,EACA,mBAAAW;AAAA,EACA,YAAAC,IAAa;AAAA,EACb,QAAAf;AAAA,EACA,eAAAgB,IAAgB;AAAA,EAChB,gBAAAC;AAAA,EACA,MAAApC,IAAO;AAAA,EACP,aAAAqC,IAAc;AAAA,EACd,cAAAvC,IAAe;AAAA,EACf,UAAAwC,IAAW;AAAA,EACX,oBAAAC;AAAA,EACA,WAAAC;AAAA,EACA,iBAAAC,IAAkB;AAAA,EAClB,WAAAC;AAAA,EACA,cAAcC;AAAA,EACd,mBAAmBC;AAAA,EACnB,GAAGC;AACL,GACAC,IACc;AACd,QAAM,EAAE,GAAAC,EAAA,IAAMC,GAAA,GAER,CAACC,IAAUC,CAAW,IAAIC,GAAyC;AAAA,IACvE,OAAAtB;AAAA,IACA,cAAcC,KAAgB;AAAA,IAC9B,UAAAC;AAAA,EAAA,CACD,GACKqB,IAASH,MAAY,MAErB,CAACI,IAAWC,CAAY,IAAIH,GAAmC;AAAA,IACnE,OAAOhC;AAAA,IACP,cAAcgB;AAAA,IACd,UAAUC;AAAA,EAAA,CACX,GACKmB,IAAeF,MAAalB,GAG5BqB,IAAiBC,EAAyB,MAAM;AACpD,QAAInC,EAAQ,QAAOD,EAAaC,CAAM;AACtC,QAAIU,EAAY,QAAOX,EAAa,CAAC,EAAE,IAAI,OAAO,YAAAW,GAAY,GAAG,EAAI;AACrE,UAAM0B,KAAazB,KAAqB,CAAA,GACrC,IAAI,CAAC0B,MAAUA,EAAM,EAAE,EACvB,OAAO,CAACC,MAAY,CAACC,GAAmBD,CAAO,CAAC,GAC7CE,IACJJ,EAAU,SAAS,IACf;AAAA,MACE,GAAGnF;AAAA,MACH,EAAE,IAAIC,IAAyB,YAAYkF,EAAA;AAAA,IAAU,IAEvDnF;AACN,WAAO8C,EAAayC,CAAI;AAAA,EAC1B,GAAG,CAACxC,GAAQU,GAAYC,CAAiB,CAAC,GAEpC8B,IAAcN;AAAA,IAClB,MAAM,IAAI,IAAYlB,KAAsB,EAAE;AAAA,IAC9C,CAACA,CAAkB;AAAA,EAAA,GAGfyB,IAAUP;AAAA,IACd,MAAMD,EAAe,QAAQ,CAAC/B,MAAUA,EAAM,UAAU;AAAA,IACxD,CAAC+B,CAAc;AAAA,EAAA,GAIXS,IAAQR,EAAsB,MAAM;AACxC,UAAMS,IAAQV,EAAe;AAAA,MAAQ,CAAC/B,MACpCA,EAAM,WAAW,IAAgB,CAAC5B,OAAe;AAAA,QAC/C,MAAM;AAAA,QACN,KAAK,QAAQA,CAAS;AAAA,QACtB,WAAAA;AAAA,QACA,UAAUyC,KAAYyB,EAAY,IAAIlE,CAAS;AAAA,MAAA,EAC/C;AAAA,IAAA;AAEJ,WAAKqC,IACE;AAAA,MACL,GAAGgC;AAAA,MACH,GAAGzF,GAAa,IAAgB,CAAC0F,OAAO;AAAA,QACtC,MAAM;AAAA,QACN,KAAK,UAAUA,CAAC;AAAA,QAChB,QAAQA;AAAA,QACR,UAAA7B;AAAA,MAAA,EACA;AAAA,IAAA,IARoB4B;AAAA,EAU1B,GAAG,CAACV,GAAgBO,GAAazB,GAAUJ,CAAU,CAAC,GAEhDkC,IAAWC,EAAqC,EAAE;AACxD,EAAAD,EAAS,QAAQ,SAASH,EAAM;AAKhC,QAAM,CAACK,GAAYC,CAAa,IAAIC,GAAwB,IAAI,GAE1DC,IAAchB,EAAQ,MAAM;AAChC,QAAIQ,EAAM,WAAW,EAAG,QAAO;AAC/B,UAAMS,IAAeT,EAAM,UAAU,CAACU,MAAS,CAACA,EAAK,QAAQ;AAI7D,QAAID,MAAiB,GAAI,QAAO;AAChC,QAAIJ,MAAe,MAAM;AACvB,YAAMM,IAAU,KAAK,IAAI,KAAK,IAAIN,GAAY,CAAC,GAAGL,EAAM,SAAS,CAAC;AAClE,aAAOA,EAAMW,CAAO,EAAE,WAAWF,IAAeE;AAAA,IAClD;AAGA,QAAIxB,MAAW,MAAM;AACnB,YAAMyB,IAAQZ,EAAM;AAAA,QAClB,CAACU,MAASA,EAAK,SAAS,UAAUA,EAAK,cAAcvB;AAAA,MAAA;AAEvD,UAAIyB,MAAU,MAAM,CAACZ,EAAMY,CAAK,EAAE,SAAU,QAAOA;AAAA,IACrD;AACA,WAAOH;AAAA,EACT,GAAG,CAACT,GAAOK,GAAYlB,CAAM,CAAC,GAGxB0B,IAAUT,EAA8B,IAAI,GAK5CU,IAAUC;AAAA,IACd,MACE,OAAO,SAAW,OAClBF,EAAQ,YAAY,QACpB,OAAO,iBAAiBA,EAAQ,OAAO,EAAE,cAAc;AAAA,IACzD,CAAA;AAAA,EAAC,GAGGG,IAAUD,EAAY,CAACE,MAAkB;;AAC7C,IAAAX,EAAcW,CAAK,IACnBC,IAAAf,EAAS,QAAQc,CAAK,MAAtB,QAAAC,EAAyB;AAAA,EAC3B,GAAG,CAAA,CAAE,GAECC,KAAgBJ;AAAA,IACpB,CAACK,MAA8C;AAC7C,YAAMC,IAAQrB,EAAM;AACpB,UAAIqB,MAAU,EAAG;AACjB,YAAMC,IAAQR,EAAA,GACRS,IACJnD,MAAgB,aACZ,cACAkD,IACE,cACA,cACFE,IACJpD,MAAgB,aACZ,YACAkD,IACE,eACA,aAEFG,IAAOjB,MAAgB,KAAK,IAAIA;AACtC,UAAIkB,IAAsB,MACtBC,IAAO;AAYX,UAXIP,EAAM,QAAQG,IAChBG,KAAQD,IAAO,KAAKJ,IACXD,EAAM,QAAQI,KACvBE,KAAQD,IAAO,IAAIJ,KAASA,GAC5BM,IAAO,MACEP,EAAM,QAAQ,SACvBM,IAAO,IACEN,EAAM,QAAQ,UACvBM,IAAOL,IAAQ,GACfM,IAAO,KAELD,MAAS,KAAM;AACnB,MAAAN,EAAM,eAAA;AAGN,UAAIQ,IAAYF;AAChB,eAASG,IAAW,GAAGA,IAAWR,KAC3BrB,EAAM4B,CAAS,EAAE,UADiBC,KAAY;AAEnD,QAAAD,KAAaA,IAAYD,IAAON,KAASA;AAE3C,MAAAL,EAAQY,CAAS;AAAA,IACnB;AAAA,IACA,CAAC5B,GAAO5B,GAAa0C,GAASN,GAAaQ,CAAO;AAAA,EAAA,GAO9Cc,KAAaf;AAAA,IACjB,CAACnF,MAA2B;AAC1B,MAAAqD,EAAYE,MAAWvD,IAAY,OAAOA,CAAS;AAAA,IACrD;AAAA,IACA,CAACuD,GAAQF,CAAW;AAAA,EAAA,GAIhB7E,IAASoF;AAAA,IACb,OAAO;AAAA,MACL,UAAU,MAAML;AAAA,MAChB,UAAU,CAACuC,MAASzC,EAAYyC,CAAI;AAAA,MACpC,OAAO,MAAMzC,EAAY,IAAI;AAAA,MAC7B,WAAW,MAAMK;AAAA,MACjB,WAAW,CAACoC,MAASrC,EAAaqC,CAAI;AAAA,MACtC,UAAU,MAAM,CAAC,GAAG3B,CAAO;AAAA,MAC3B,WAAW,CAACnE,MAAc;AACxB,cAAMqF,IAAQjB,EAAM;AAAA,UAClB,CAACU,MAASA,EAAK,SAAS,UAAUA,EAAK,cAAc9E;AAAA,QAAA;AAEvD,QAAIqF,MAAU,MAAID,EAAQC,CAAK;AAAA,MACjC;AAAA,IAAA;AAAA,IAEF,CAAC9B,GAAQG,GAAcU,GAAOD,GAASd,GAAaI,GAAc2B,CAAO;AAAA,EAAA;AAG3E,EAAAe,GAAoBlD,IAAK,MAAMzE,GAAQ,CAACA,CAAM,CAAC,GAC/C4H,GAAqB7H,IAA0BC,GAAQuD,CAAE;AAGzD,QAAMsE,KACJ1D,MAAaG,MAAiBI,EAAE,+BAA+B,GAE3DoD,KAAe,CAAC1E,MACpBA,EAAM,SACNsB,EAAE,6BAA6BtB,EAAM,EAAE,IAAI,EAAE,cAAcA,EAAM,IAAI;AAEvE,MAAI2E,IAAS;AAEb,SACE,gBAAA1G;AAAA,IAAC;AAAA,IAAA;AAAA,MACE,GAAGmD;AAAA,MACJ,KAAKiC;AAAA,MACL,MAAK;AAAA,MACL,oBAAkBzC;AAAA,MAClB,cAAYO,IAAiB,SAAYsD;AAAA,MACzC,mBAAiBtD;AAAA,MACjB,iBAAeN,KAAY;AAAA,MAC3B,kBAAe;AAAA,MACf,qBAAmBV;AAAA,MACnB,oBAAkBS;AAAA,MAClB,aAAWrC;AAAA,MACX,eAAaoD,KAAU;AAAA,MACvB,eAAalB,IAAaqB,IAAe;AAAA,MACzC,eAAY;AAAA,MACZ,IAAA3B;AAAA,MACA,WAAWwD;AAAA,MACX,WAAW,CAACzG,GAA4B,EAAE,aAAA0D,GAAa,MAAArC,EAAA,CAAM,GAAG0C,EAAS,EACtE,OAAO,OAAO,EACd,KAAK,GAAG;AAAA,MAEV,UAAA;AAAA,QAAAuB,EAAM,WAAW,IAChB,gBAAAtE,EAAC,KAAA,EAAE,WAAU,+DACV,UAAAoD,EAAE,2BAA2B,EAAA,CAChC,IACE;AAAA,QAEHS,EAAe,IAAI,CAAC/B,MAAU;AAC7B,gBAAM4E,IAAUF,GAAa1E,CAAK;AAClC,iBACE,gBAAA/B;AAAA,YAAC;AAAA,YAAA;AAAA,cAIC,MAAM+B,EAAM,YAAY,SAAY;AAAA,cACpC,cAAYA,EAAM,YAAY,SAAY4E;AAAA,cAC1C,cAAY5E,EAAM;AAAA,cAClB,eAAa,+BAA+BA,EAAM,EAAE;AAAA,cACpD,WAAW5C,GAAA;AAAA,cAEV,UAAA;AAAA,gBAAA4D,KAAmB,CAAChB,EAAM;AAAA;AAAA;AAAA;AAAA,oCAIxB,QAAA,EAAK,eAAY,QAAO,WAAW3C,GAAA,GACjC,UAAAuH,EAAA,CACH;AAAA,oBACE;AAAA,gBACJ,gBAAA1G,EAAC,OAAA,EAAI,WAAWZ,GAAiB,EAAE,aAAAsD,GAAa,MAAArC,GAAM,GACnD,UAAAyB,EAAM,WAAW,IAAI,CAAC5B,MAAc;AACnC,wBAAMqF,IAAQkB;AACd,kBAAAA,KAAU;AACV,wBAAMzB,IAAOV,EAAMiB,CAAK,GAClBoB,KAAa3B,KAAA,gBAAAA,EAAM,aAAYrC,GAC/BiE,IAAQC,GAAe3G,GAAWoC,CAAiB,GACnDwE,IAAOF,EAAM,SAAS,QAAQxD,EAAEwD,EAAM,GAAG,IAAIA,EAAM,MACnDG,IAAWtD,MAAWvD;AAC5B,yBACE,gBAAAH;AAAA,oBAAC;AAAA,oBAAA;AAAA,sBAEC,MAAK;AAAA,sBACL,KAAK,CAACiH,MAAS;AACb,wBAAAvC,EAAS,QAAQc,CAAK,IAAIyB;AAAA,sBAC5B;AAAA,sBACA,UAAUL;AAAA,sBACV,gBAAcI;AAAA,sBACd,UAAUxB,MAAUT,IAAc,IAAI;AAAA,sBACtC,kBAAgB5E;AAAA,sBAChB,eAAa6G,IAAW,SAAS;AAAA,sBACjC,eAAa,8BAA8B7G,CAAS;AAAA,sBAIpD,OAAO4G;AAAA,sBACP,SAAS,MAAMlC,EAAcW,CAAK;AAAA,sBAClC,SAAS,MAAMa,GAAWlG,CAAS;AAAA,sBACnC,WAAWb,GAAa;AAAA,wBACtB,UAAU0H;AAAA,wBACV,MAAA1G;AAAA,wBACA,aAAAqC;AAAA,sBAAA,CACD;AAAA,sBAED,UAAA;AAAA,wBAAA,gBAAA1C;AAAA,0BAACC;AAAA,0BAAA;AAAA,4BACC,WAAAC;AAAA,4BACA,cAAAC;AAAA,4BACA,UAAUmC;AAAA,4BACV,MAAAjC;AAAA,0BAAA;AAAA,wBAAA;AAAA,wBAEF,gBAAAL,EAAC,QAAA,EAAK,WAAU,0BAA0B,UAAA8G,EAAA,CAAK;AAAA,sBAAA;AAAA,oBAAA;AAAA,oBA7B1C5G;AAAA,kBAAA;AAAA,gBAgCX,CAAC,EAAA,CACH;AAAA,cAAA;AAAA,YAAA;AAAA,YA7DK4B,EAAM;AAAA,UAAA;AAAA,QAgEjB,CAAC;AAAA,QAEAS,IACC,gBAAAxC;AAAA,UAAC;AAAA,UAAA;AAAA,YACC,MAAK;AAAA,YACL,cAAYqD,EAAE,iCAAiC;AAAA,YAC/C,cAAW;AAAA,YACX,eAAY;AAAA,YACZ,WAAWlE,GAAA;AAAA,YAEV,UAAA;AAAA,cAAA4D,IACC,gBAAA9C,EAAC,QAAA,EAAK,eAAY,QAAO,WAAWb,MACjC,UAAAiE,EAAE,iCAAiC,EAAA,CACtC,IACE;AAAA,cACJ,gBAAApD,EAAC,OAAA,EAAI,WAAWZ,GAAiB,EAAE,aAAAsD,GAAa,MAAArC,EAAA,CAAM,GACnD,UAAAvB,GAAa,IAAI,CAACmI,MAAW;AAC5B,sBAAM1B,IAAQkB;AACd,gBAAAA,KAAU;AACV,sBAAMM,IAAWnD,MAAiBqD;AAClC,uBACE,gBAAAlH;AAAA,kBAAC;AAAA,kBAAA;AAAA,oBAEC,MAAK;AAAA,oBACL,KAAK,CAACiH,MAAS;AACb,sBAAAvC,EAAS,QAAQc,CAAK,IAAIyB;AAAA,oBAC5B;AAAA,oBACA,UAAArE;AAAA,oBACA,gBAAcoE;AAAA,oBACd,UAAUxB,MAAUT,IAAc,IAAI;AAAA,oBACtC,eAAamC;AAAA,oBACb,eAAaF,IAAW,SAAS;AAAA,oBACjC,eAAa,gCAAgCE,CAAM;AAAA,oBACnD,SAAS,MAAMrC,EAAcW,CAAK;AAAA,oBAGlC,SAAS,MAAM5B,EAAasD,CAAM;AAAA,oBAClC,WAAW3H,GAAmB,EAAE,UAAUyH,GAAU,MAAA1G,GAAM;AAAA,oBAE1D,UAAA;AAAA,sBAAA,gBAAAL,EAACuB,IAAA,EAAU,QAAQ0F,EAAA,CAAQ;AAAA,wCAC1B,QAAA,EAAM,UAAA7D,EAAE8D,GAAeD,CAAM,CAAC,EAAA,CAAE;AAAA,oBAAA;AAAA,kBAAA;AAAA,kBAlB5BA;AAAA,gBAAA;AAAA,cAqBX,CAAC,EAAA,CACH;AAAA,YAAA;AAAA,UAAA;AAAA,QAAA,IAEA;AAAA,MAAA;AAAA,IAAA;AAAA,EAAA;AAGV,CAAC;AAEDlF,GAAoB,cAAc;"}
|