nuvox 0.1.4 → 0.1.6
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/BUILDING-COMPONENTS.md +555 -551
- package/CHANGELOG.md +66 -147
- package/dist/chunk-HAMPWL7I.js +2457 -0
- package/dist/chunk-HAMPWL7I.js.map +1 -0
- package/dist/index.cjs +967 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +65 -1
- package/dist/pdf-viewer-CF7o-73J.d.cts +1092 -0
- package/dist/pdf-viewer-CF7o-73J.d.ts +1092 -0
- package/dist/react/index.cjs +11754 -5768
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.css +3446 -918
- package/dist/react/index.css.map +1 -1
- package/dist/react/index.d.cts +521 -5
- package/dist/react/index.d.ts +521 -5
- package/dist/react/index.js +11273 -6430
- package/dist/react/index.js.map +1 -1
- package/dist/themes/generated.css +55 -55
- package/package.json +2 -2
- package/dist/chunk-KKRVJF3Y.js +0 -1159
- package/dist/chunk-KKRVJF3Y.js.map +0 -1
- package/dist/context-menu-CcYi_Dc_.d.cts +0 -635
- package/dist/context-menu-CcYi_Dc_.d.ts +0 -635
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/tokens/contract.ts","../src/tokens/themes/light.ts","../src/tokens/themes/dark.ts","../src/tokens/themes/glass.ts","../src/tokens/themes/dark-glass.ts","../src/tokens/themes/minimalism.ts","../src/tokens/themes/brutalism.ts","../src/tokens/themes/neobrutalism.ts","../src/tokens/themes/cyberpunk.ts","../src/tokens/themes/gradient.ts","../src/tokens/themes/material.ts","../src/tokens/themes/skeuomorphism.ts","../src/tokens/registry.ts","../src/tokens/accents/indigo.ts","../src/tokens/accents/violet.ts","../src/tokens/accents/teal.ts","../src/tokens/accents/rose.ts","../src/tokens/accents/amber.ts","../src/tokens/accents/emerald.ts","../src/tokens/accents/copper.ts","../src/tokens/accents/graphite.ts","../src/tokens/accents/magenta.ts","../src/tokens/accents.ts","../src/core/field.ts","../src/core/colorpicker.ts","../src/core/table.ts","../src/core/kanban.ts","../src/core/code-highlight.ts","../src/core/rich-text.ts","../src/core/gallery.ts","../src/core/slider.ts","../src/core/media.ts","../src/core/pdf-viewer.ts"],"sourcesContent":["// ============================================================\n// TOKEN CONTRACT\n//\n// This is the single most important file in the foundation.\n//\n// Every theme (light, dark, glass, cyberpunk, whatever comes\n// next) must produce an object that satisfies ThemeTokens.\n// TypeScript enforces this at compile time via `defineTheme()`\n// below — leave out a single key and the build fails with a\n// clear error naming the missing key.\n//\n// In the old codebase, themes were hand-written CSS files.\n// Nothing checked that cyberpunk.css defined every variable\n// brutalism.css did. A missing token didn't error — it just\n// silently fell back to nothing, and you only found out by\n// looking at the rendered page.\n//\n// Here, themes are TypeScript objects. CSS is *generated* from\n// them (see generate-css.ts), never hand-authored. There is\n// exactly one source of truth per theme, and it cannot be\n// incomplete.\n// ============================================================\n\n// ============================================================\n// A theme controls SHAPE, not brand color: radius, shadow style,\n// motion curves, border treatment, glass/gradient/glow effects,\n// and the neutral/semantic palette needed for legibility (a dark\n// theme's background/text MUST differ from light's — that's a\n// contrast requirement, not a brand choice). It does NOT define\n// `color.brand` — that's a separate, orthogonal axis (see\n// accents.ts) that a consumer picks independently of which theme\n// is active, and can override per-component exactly like `theme`\n// can. success/danger/warning/info stay theme-defined (their\n// exact shade can differ per theme for contrast, but their\n// meaning — green reads as success, red as danger — must not be\n// user-customizable away from that).\n// ============================================================\n\nexport interface ColorScale {\n 50: string; 100: string; 200: string; 300: string; 400: string;\n 500: string; 600: string; 700: string; 800: string; 900: string;\n}\n\nexport interface SemanticColor {\n light: string;\n subtle: string;\n base: string;\n dark: string;\n deeper: string;\n}\n\nexport interface ThemeTokens {\n meta: {\n name: string;\n label: string;\n /** Short description of the visual identity — kept out of CSS, used in docs/registry UI. */\n description: string;\n };\n\n color: {\n neutral: ColorScale & { 0: string; 950: string };\n success: SemanticColor;\n danger: SemanticColor;\n warning: SemanticColor;\n info: SemanticColor;\n /** Page/app background, not component surface. */\n background: string;\n /** Default component surface (card, input, modal body). */\n surface: string;\n /** Elevated surface (popover, dropdown, tooltip). */\n surfaceRaised: string;\n border: string;\n borderFocus: string;\n text: string;\n textMuted: string;\n textInverse: string;\n };\n\n radius: {\n sm: string; md: string; lg: string; xl: string; \"2xl\": string; full: string;\n };\n\n spacing: {\n xs: string; sm: string; md: string; lg: string; xl: string; \"2xl\": string;\n };\n\n typography: {\n fontSans: string;\n fontMono: string;\n sizeXs: string; sizeSm: string; sizeMd: string; sizeLg: string; sizeXl: string;\n weightNormal: string; weightMedium: string; weightBold: string;\n lineHeightTight: string; lineHeightNormal: string;\n };\n\n shadow: {\n xs: string; sm: string; md: string; lg: string; xl: string; \"2xl\": string;\n inner: string;\n };\n\n elevation: {\n /**\n * z-index steps — must be consistent across every portal-based\n * component. Deliberately NOT alphabetical-by-concept order:\n * `modal` is the LOWEST tier, not the highest. Every portal\n * target shares one stacking context (usually the end of\n * `<body>`), so a Select/Combobox/DropdownMenu/Popover opened\n * from *inside* an open Modal must out-rank the Modal itself\n * to render above its backdrop instead of being swallowed\n * behind it. Real order, low to high:\n * modal < dropdown < popover < tooltip < toast.\n */\n dropdown: number;\n popover: number;\n modal: number;\n toast: number;\n tooltip: number;\n };\n\n motion: {\n durationFast: string;\n durationNormal: string;\n durationSlow: string;\n easeStandard: string;\n easeEmphasized: string;\n /** Set to '0.01ms' by a reduced-motion-aware theme variant if needed. */\n durationInstant: string;\n };\n\n border: {\n widthThin: string;\n widthMedium: string;\n widthThick: string;\n style: string;\n };\n\n effects: {\n /**\n * CSS `backdrop-filter: blur()` amount, e.g. \"16px\" or \"0px\".\n * Every non-glass theme sets this to \"0px\" — an explicit zero,\n * not an absent field, so `defineTheme`'s completeness check\n * still catches an accidentally-forgotten theme. Translucent\n * `color.surface`/`color.border` values (glass, dark-glass) are\n * only actually visible as \"glass\" when something behind them\n * is blurred; without this, a translucent white surface over a\n * plain page background is indistinguishable from opaque white.\n */\n backdropBlur: string;\n };\n}\n\n/**\n * Identity function with a strict parameter type.\n * Its only job is to make an incomplete theme a compile error\n * at the call site, with TypeScript naming the exact missing key.\n */\nexport function defineTheme(tokens: ThemeTokens): ThemeTokens {\n return tokens;\n}\n\n/** Every top-level and nested key path, used by the completeness test. */\nexport const TOKEN_CATEGORIES = [\n \"meta\", \"color\", \"radius\", \"spacing\", \"typography\", \"shadow\", \"elevation\", \"motion\", \"border\", \"effects\",\n] as const;\n\n/**\n * Every required leaf path, hardcoded independently of any\n * theme instance. This is deliberately NOT derived from\n * whichever theme happens to register first — that would make\n * the very first theme registered the unverified \"trust me\"\n * reference. Every path listed here corresponds 1:1 to a field\n * in ThemeTokens; if you add a field to the contract, add its\n * path here too (a mismatch here is caught by\n * registry.test.ts's cross-check against `light`).\n */\nexport const REQUIRED_TOKEN_PATHS = [\n \"meta.name\", \"meta.label\", \"meta.description\",\n ...[\"0\", \"50\", \"100\", \"200\", \"300\", \"400\", \"500\", \"600\", \"700\", \"800\", \"900\", \"950\"].map((k) => `color.neutral.${k}`),\n ...([\"success\", \"danger\", \"warning\", \"info\"] as const).flatMap((c) =>\n [\"light\", \"subtle\", \"base\", \"dark\", \"deeper\"].map((k) => `color.${c}.${k}`)\n ),\n \"color.background\", \"color.surface\", \"color.surfaceRaised\", \"color.border\", \"color.borderFocus\",\n \"color.text\", \"color.textMuted\", \"color.textInverse\",\n \"radius.sm\", \"radius.md\", \"radius.lg\", \"radius.xl\", \"radius.2xl\", \"radius.full\",\n \"spacing.xs\", \"spacing.sm\", \"spacing.md\", \"spacing.lg\", \"spacing.xl\", \"spacing.2xl\",\n \"typography.fontSans\", \"typography.fontMono\",\n \"typography.sizeXs\", \"typography.sizeSm\", \"typography.sizeMd\", \"typography.sizeLg\", \"typography.sizeXl\",\n \"typography.weightNormal\", \"typography.weightMedium\", \"typography.weightBold\",\n \"typography.lineHeightTight\", \"typography.lineHeightNormal\",\n \"shadow.xs\", \"shadow.sm\", \"shadow.md\", \"shadow.lg\", \"shadow.xl\", \"shadow.2xl\", \"shadow.inner\",\n \"elevation.dropdown\", \"elevation.popover\", \"elevation.modal\", \"elevation.toast\", \"elevation.tooltip\",\n \"motion.durationFast\", \"motion.durationNormal\", \"motion.durationSlow\",\n \"motion.easeStandard\", \"motion.easeEmphasized\", \"motion.durationInstant\",\n \"border.widthThin\", \"border.widthMedium\", \"border.widthThick\", \"border.style\",\n \"effects.backdropBlur\",\n] as const;\n\nfunction getAtPath(obj: unknown, path: string): unknown {\n return path.split(\".\").reduce<unknown>((acc, key) => {\n if (acc === null || typeof acc !== \"object\") return undefined;\n return (acc as Record<string, unknown>)[key];\n }, obj);\n}\n\n/** Returns every required path that is missing or empty on the given theme-like object. */\nexport function findMissingTokenPaths(theme: unknown): string[] {\n return REQUIRED_TOKEN_PATHS.filter((path) => {\n const value = getAtPath(theme, path);\n return value === null || value === undefined || value === \"\";\n });\n}","import { defineTheme } from \"../contract\";\n\nexport const light = defineTheme({\n meta: {\n name: \"light\",\n label: \"Light\",\n description: \"Default, neutral, high-legibility surface.\",\n },\n color: {\n neutral: {\n 0: \"#ffffff\", 50: \"#f9fafb\", 100: \"#f3f4f6\", 200: \"#e5e7eb\", 300: \"#d1d5db\",\n 400: \"#9ca3af\", 500: \"#6b7280\", 600: \"#4b5563\", 700: \"#374151\", 800: \"#1f2937\",\n 900: \"#111827\", 950: \"#030712\",\n },\n success: { light: \"#d1fae5\", subtle: \"#a7f3d0\", base: \"#10b981\", dark: \"#059669\", deeper: \"#064e3b\" },\n danger: { light: \"#fee2e2\", subtle: \"#fecaca\", base: \"#ef4444\", dark: \"#dc2626\", deeper: \"#7f1d1d\" },\n warning: { light: \"#fef3c7\", subtle: \"#fde68a\", base: \"#f59e0b\", dark: \"#d97706\", deeper: \"#78350f\" },\n info: { light: \"#dbeafe\", subtle: \"#bfdbfe\", base: \"#3b82f6\", dark: \"#2563eb\", deeper: \"#1e3a5f\" },\n background: \"#ffffff\",\n surface: \"#ffffff\",\n surfaceRaised: \"#ffffff\",\n border: \"#e5e7eb\",\n // Neutral, not brand-tied — the actual focus-ring color\n // interactive components use is the picked accent\n // (--color-brand-500/600), not this. This exists for\n // non-branded emphasis borders.\n borderFocus: \"#9ca3af\",\n text: \"#111827\",\n textMuted: \"#6b7280\",\n textInverse: \"#ffffff\",\n },\n radius: { sm: \"6px\", md: \"10px\", lg: \"14px\", xl: \"18px\", \"2xl\": \"24px\", full: \"9999px\" },\n spacing: { xs: \"4px\", sm: \"8px\", md: \"12px\", lg: \"16px\", xl: \"24px\", \"2xl\": \"32px\" },\n typography: {\n fontSans: \"'Inter', system-ui, sans-serif\",\n fontMono: \"'JetBrains Mono', monospace\",\n sizeXs: \"12px\", sizeSm: \"14px\", sizeMd: \"16px\", sizeLg: \"18px\", sizeXl: \"22px\",\n weightNormal: \"400\", weightMedium: \"500\", weightBold: \"700\",\n lineHeightTight: \"1.2\", lineHeightNormal: \"1.5\",\n },\n shadow: {\n xs: \"0 1px 2px rgba(0,0,0,0.05)\",\n sm: \"0 2px 6px rgba(0,0,0,0.06)\",\n md: \"0 4px 12px rgba(0,0,0,0.08)\",\n lg: \"0 10px 24px rgba(0,0,0,0.10)\",\n xl: \"0 20px 40px rgba(0,0,0,0.12)\",\n \"2xl\": \"0 28px 56px rgba(0,0,0,0.16)\",\n inner: \"inset 0 1px 2px rgba(0,0,0,0.05)\",\n },\n elevation: { dropdown: 1050, popover: 1060, modal: 1000, toast: 1200, tooltip: 1100 },\n motion: {\n durationFast: \"120ms\", durationNormal: \"200ms\", durationSlow: \"320ms\",\n easeStandard: \"cubic-bezier(0.4, 0, 0.2, 1)\", easeEmphasized: \"cubic-bezier(0.2, 0, 0, 1)\",\n durationInstant: \"0ms\",\n },\n border: { widthThin: \"1px\", widthMedium: \"1.5px\", widthThick: \"2px\", style: \"solid\" },\n effects: { backdropBlur: \"0px\" },\n});","import { defineTheme } from \"../contract\";\n\nexport const dark = defineTheme({\n meta: {\n name: \"dark\",\n label: \"Dark\",\n description: \"Low-glare surface for extended use.\",\n },\n color: {\n neutral: {\n 0: \"#030712\", 50: \"#111827\", 100: \"#1f2937\", 200: \"#374151\", 300: \"#4b5563\",\n 400: \"#6b7280\", 500: \"#9ca3af\", 600: \"#d1d5db\", 700: \"#e5e7eb\", 800: \"#f3f4f6\",\n 900: \"#f9fafb\", 950: \"#ffffff\",\n },\n success: { light: \"#064e3b\", subtle: \"#065f46\", base: \"#34d399\", dark: \"#6ee7b7\", deeper: \"#d1fae5\" },\n danger: { light: \"#7f1d1d\", subtle: \"#991b1b\", base: \"#f87171\", dark: \"#fca5a5\", deeper: \"#fee2e2\" },\n warning: { light: \"#78350f\", subtle: \"#92400e\", base: \"#fbbf24\", dark: \"#fcd34d\", deeper: \"#fef3c7\" },\n info: { light: \"#1e3a5f\", subtle: \"#1e40af\", base: \"#60a5fa\", dark: \"#93c5fd\", deeper: \"#dbeafe\" },\n background: \"#0b0f19\",\n surface: \"#131826\",\n surfaceRaised: \"#1c2333\",\n border: \"#2a3143\",\n borderFocus: \"#6b7280\",\n text: \"#f3f4f6\",\n textMuted: \"#9ca3af\",\n textInverse: \"#111827\",\n },\n radius: { sm: \"6px\", md: \"10px\", lg: \"14px\", xl: \"18px\", \"2xl\": \"24px\", full: \"9999px\" },\n spacing: { xs: \"4px\", sm: \"8px\", md: \"12px\", lg: \"16px\", xl: \"24px\", \"2xl\": \"32px\" },\n typography: {\n fontSans: \"'Inter', system-ui, sans-serif\",\n fontMono: \"'JetBrains Mono', monospace\",\n sizeXs: \"12px\", sizeSm: \"14px\", sizeMd: \"16px\", sizeLg: \"18px\", sizeXl: \"22px\",\n weightNormal: \"400\", weightMedium: \"500\", weightBold: \"700\",\n lineHeightTight: \"1.2\", lineHeightNormal: \"1.5\",\n },\n shadow: {\n xs: \"0 1px 2px rgba(0,0,0,0.4)\",\n sm: \"0 2px 6px rgba(0,0,0,0.45)\",\n md: \"0 4px 12px rgba(0,0,0,0.5)\",\n lg: \"0 10px 24px rgba(0,0,0,0.55)\",\n xl: \"0 20px 40px rgba(0,0,0,0.6)\",\n \"2xl\": \"0 28px 56px rgba(0,0,0,0.65)\",\n inner: \"inset 0 1px 2px rgba(0,0,0,0.4)\",\n },\n elevation: { dropdown: 1050, popover: 1060, modal: 1000, toast: 1200, tooltip: 1100 },\n motion: {\n durationFast: \"120ms\", durationNormal: \"200ms\", durationSlow: \"320ms\",\n easeStandard: \"cubic-bezier(0.4, 0, 0.2, 1)\", easeEmphasized: \"cubic-bezier(0.2, 0, 0, 1)\",\n durationInstant: \"0ms\",\n },\n border: { widthThin: \"1px\", widthMedium: \"1.5px\", widthThick: \"2px\", style: \"solid\" },\n effects: { backdropBlur: \"0px\" },\n});","import { defineTheme } from \"../contract\";\n\nexport const glass = defineTheme({\n meta: {\n name: \"glass\",\n label: \"Glass\",\n description: \"Translucent, softly rounded, floating surfaces. Needs a rich background behind it.\",\n },\n color: {\n neutral: {\n 0: \"#ffffff\", 50: \"#f9fafb\", 100: \"#f3f4f6\", 200: \"#e5e7eb\", 300: \"#d1d5db\",\n 400: \"#9ca3af\", 500: \"#6b7280\", 600: \"#4b5563\", 700: \"#374151\", 800: \"#1f2937\",\n 900: \"#111827\", 950: \"#030712\",\n },\n success: { light: \"#d1fae5\", subtle: \"#a7f3d0\", base: \"#10b981\", dark: \"#059669\", deeper: \"#064e3b\" },\n danger: { light: \"#fee2e2\", subtle: \"#fecaca\", base: \"#ef4444\", dark: \"#dc2626\", deeper: \"#7f1d1d\" },\n warning: { light: \"#fef3c7\", subtle: \"#fde68a\", base: \"#f59e0b\", dark: \"#d97706\", deeper: \"#78350f\" },\n info: { light: \"#dbeafe\", subtle: \"#bfdbfe\", base: \"#3b82f6\", dark: \"#2563eb\", deeper: \"#1e3a5f\" },\n background: \"transparent\",\n surface: \"rgba(255,255,255,0.55)\",\n surfaceRaised: \"rgba(255,255,255,0.75)\",\n // Was rgba(255,255,255,0.35) — translucent WHITE. Composited on\n // a plain white page (the theme's own meta.description admits it\n // \"needs a rich background behind it,\" but most consumers won't\n // add one), that resolves to literal #ffffff — a 1.0:1 contrast\n // ratio, i.e. actually invisible, which is the reported vanished\n // Progress-track border. A neutral slate tint stays visible\n // against both a plain white AND a plain black fallback backdrop\n // (measured ~2.1-2.3:1 either way) — short of full 3:1 UI-contrast\n // on both extremes at once, but a large improvement over 1.0, and\n // still reads as a soft glass edge over an actual rich backdrop.\n border: \"rgba(100,116,139,0.6)\",\n borderFocus: \"#9ca3af\",\n text: \"#111827\",\n textMuted: \"rgba(17,24,39,0.65)\",\n textInverse: \"#ffffff\",\n },\n // Notably rounder than light/dark — this is a shape difference, not just color.\n radius: { sm: \"10px\", md: \"16px\", lg: \"22px\", xl: \"28px\", \"2xl\": \"36px\", full: \"9999px\" },\n spacing: { xs: \"4px\", sm: \"8px\", md: \"12px\", lg: \"16px\", xl: \"24px\", \"2xl\": \"32px\" },\n typography: {\n fontSans: \"'Inter', system-ui, sans-serif\",\n fontMono: \"'JetBrains Mono', monospace\",\n sizeXs: \"12px\", sizeSm: \"14px\", sizeMd: \"16px\", sizeLg: \"18px\", sizeXl: \"22px\",\n weightNormal: \"400\", weightMedium: \"500\", weightBold: \"700\",\n lineHeightTight: \"1.2\", lineHeightNormal: \"1.5\",\n },\n shadow: {\n xs: \"0 2px 8px rgba(0,0,0,0.06)\",\n sm: \"0 4px 16px rgba(0,0,0,0.08)\",\n md: \"0 8px 32px rgba(0,0,0,0.10)\",\n lg: \"0 16px 48px rgba(0,0,0,0.12)\",\n xl: \"0 24px 64px rgba(0,0,0,0.14)\",\n \"2xl\": \"0 32px 80px rgba(0,0,0,0.18)\",\n inner: \"inset 0 1px 0 rgba(255,255,255,0.4)\",\n },\n elevation: { dropdown: 1050, popover: 1060, modal: 1000, toast: 1200, tooltip: 1100 },\n motion: {\n // Slightly slower/softer than light/dark — matches \"everything floats\".\n durationFast: \"160ms\", durationNormal: \"260ms\", durationSlow: \"400ms\",\n easeStandard: \"cubic-bezier(0.25, 0.1, 0.25, 1)\", easeEmphasized: \"cubic-bezier(0.16, 1, 0.3, 1)\",\n durationInstant: \"0ms\",\n },\n border: { widthThin: \"1px\", widthMedium: \"1px\", widthThick: \"1.5px\", style: \"solid\" },\n effects: { backdropBlur: \"16px\" },\n});","import { defineTheme } from \"../contract\";\nimport { dark } from \"./dark\";\n\nexport const darkGlass = defineTheme({\n ...dark,\n meta: {\n name: \"dark-glass\",\n label: \"Dark Glass\",\n description: \"Translucent glass panels floating over a dark background.\",\n },\n color: {\n ...dark.color,\n background: \"#05070d\",\n surface: \"rgba(255,255,255,0.06)\",\n surfaceRaised: \"rgba(255,255,255,0.10)\",\n border: \"rgba(255,255,255,0.12)\",\n },\n radius: { sm: \"10px\", md: \"16px\", lg: \"22px\", xl: \"28px\", \"2xl\": \"36px\", full: \"9999px\" },\n shadow: {\n xs: \"0 2px 8px rgba(0,0,0,0.5)\", sm: \"0 4px 16px rgba(0,0,0,0.55)\", md: \"0 8px 32px rgba(0,0,0,0.6)\",\n lg: \"0 16px 48px rgba(0,0,0,0.65)\", xl: \"0 24px 64px rgba(0,0,0,0.7)\", \"2xl\": \"0 32px 80px rgba(0,0,0,0.75)\",\n inner: \"inset 0 1px 0 rgba(255,255,255,0.08)\",\n },\n motion: { ...dark.motion, durationFast: \"160ms\", durationNormal: \"260ms\", durationSlow: \"400ms\" },\n effects: { backdropBlur: \"18px\" },\n});\n","import { defineTheme } from \"../contract\";\nimport { light } from \"./light\";\n\nexport const minimalism = defineTheme({\n ...light,\n meta: {\n name: \"minimalism\",\n label: \"Minimalism\",\n description: \"Reduced to essentials — thin borders, flat surfaces, no shadow, tight radius.\",\n },\n color: {\n ...light.color,\n border: \"#e2e2e2\", surface: \"#ffffff\", surfaceRaised: \"#ffffff\",\n },\n radius: { sm: \"2px\", md: \"3px\", lg: \"4px\", xl: \"6px\", \"2xl\": \"8px\", full: \"9999px\" },\n shadow: { xs: \"none\", sm: \"none\", md: \"none\", lg: \"none\", xl: \"none\", \"2xl\": \"none\", inner: \"none\" },\n border: { widthThin: \"1px\", widthMedium: \"1px\", widthThick: \"1px\", style: \"solid\" },\n effects: { backdropBlur: \"0px\" },\n});\n","import { defineTheme } from \"../contract\";\nimport { light } from \"./light\";\n\nexport const brutalism = defineTheme({\n ...light,\n meta: {\n name: \"brutalism\",\n label: \"Brutalism\",\n description: \"Raw, unstyled web aesthetic — square corners, zero shadow, monospace type.\",\n },\n color: {\n ...light.color,\n background: \"#ffffff\", surface: \"#ffffff\", surfaceRaised: \"#ffffff\",\n border: \"#000000\", borderFocus: \"#ff0000\", text: \"#000000\",\n },\n radius: { sm: \"0px\", md: \"0px\", lg: \"0px\", xl: \"0px\", \"2xl\": \"0px\", full: \"0px\" },\n typography: { ...light.typography, fontSans: \"'Courier New', monospace\", fontMono: \"'Courier New', monospace\" },\n shadow: { xs: \"none\", sm: \"none\", md: \"none\", lg: \"none\", xl: \"none\", \"2xl\": \"none\", inner: \"none\" },\n border: { widthThin: \"1px\", widthMedium: \"2px\", widthThick: \"3px\", style: \"solid\" },\n motion: { ...light.motion, durationFast: \"0ms\", durationNormal: \"0ms\", durationSlow: \"0ms\" },\n effects: { backdropBlur: \"0px\" },\n});\n","import { defineTheme } from \"../contract\";\nimport { light } from \"./light\";\n\nexport const neobrutalism = defineTheme({\n ...light,\n meta: {\n name: \"neobrutalism\",\n label: \"Neo Brutalism\",\n description: \"Bold black borders, flat offset shadows, saturated color blocks.\",\n },\n color: {\n ...light.color,\n background: \"#fef3c7\", surface: \"#ffffff\", surfaceRaised: \"#ffffff\",\n border: \"#000000\", borderFocus: \"#000000\",\n // light.color.warning.light (#fef3c7) is identical to this\n // theme's own background — every warning Alert/Badge/etc. was\n // rendering invisibly on the page. Bumped to the next step in\n // the same warm-amber family (matches light.color.warning.subtle)\n // so it stays on-theme but is actually visible against the page.\n warning: { ...light.color.warning, light: \"#fde68a\" },\n },\n radius: { sm: \"4px\", md: \"6px\", lg: \"8px\", xl: \"10px\", \"2xl\": \"12px\", full: \"9999px\" },\n shadow: {\n xs: \"2px 2px 0 #000000\", sm: \"3px 3px 0 #000000\", md: \"4px 4px 0 #000000\",\n lg: \"6px 6px 0 #000000\", xl: \"8px 8px 0 #000000\", \"2xl\": \"10px 10px 0 #000000\",\n inner: \"inset 2px 2px 0 #000000\",\n },\n border: { widthThin: \"2px\", widthMedium: \"3px\", widthThick: \"4px\", style: \"solid\" },\n motion: { ...light.motion, durationFast: \"80ms\", durationNormal: \"140ms\", durationSlow: \"200ms\", easeStandard: \"linear\", easeEmphasized: \"linear\" },\n effects: { backdropBlur: \"0px\" },\n});","import { defineTheme } from \"../contract\";\nimport { dark } from \"./dark\";\n\nexport const cyberpunk = defineTheme({\n ...dark,\n meta: {\n name: \"cyberpunk\",\n label: \"Cyberpunk\",\n description: \"Neon-on-black, glowing edges, high-saturation magenta/cyan accents.\",\n },\n color: {\n ...dark.color,\n background: \"#0a0014\", surface: \"#12001f\", surfaceRaised: \"#1a0230\",\n border: \"#ff2bd6\", borderFocus: \"#00f0ff\", text: \"#e8faff\", textMuted: \"#9a8cc9\",\n },\n radius: { sm: \"2px\", md: \"4px\", lg: \"6px\", xl: \"8px\", \"2xl\": \"10px\", full: \"9999px\" },\n shadow: {\n xs: \"0 0 6px rgba(255,43,214,0.5)\", sm: \"0 0 12px rgba(255,43,214,0.55)\",\n md: \"0 0 20px rgba(0,240,255,0.5)\", lg: \"0 0 32px rgba(0,240,255,0.55)\",\n xl: \"0 0 48px rgba(255,43,214,0.6)\", \"2xl\": \"0 0 64px rgba(0,240,255,0.65)\",\n inner: \"inset 0 0 8px rgba(255,43,214,0.4)\",\n },\n border: { widthThin: \"1px\", widthMedium: \"1.5px\", widthThick: \"2px\", style: \"solid\" },\n motion: { ...dark.motion, easeStandard: \"cubic-bezier(0.83,0,0.17,1)\", easeEmphasized: \"cubic-bezier(0.9,0,0.1,1)\" },\n effects: { backdropBlur: \"0px\" },\n});\n","import { defineTheme } from \"../contract\";\nimport { light } from \"./light\";\n\nexport const gradient = defineTheme({\n ...light,\n meta: {\n name: \"gradient\",\n label: \"Gradient\",\n description: \"Vibrant, colorful transitions across surfaces and accents.\",\n },\n color: {\n ...light.color,\n background: \"linear-gradient(135deg, #667eea 0%, #764ba2 100%)\",\n surface: \"linear-gradient(135deg, rgba(255,255,255,0.92), rgba(255,255,255,0.75))\",\n surfaceRaised: \"#ffffff\",\n border: \"rgba(118,75,162,0.25)\",\n borderFocus: \"#764ba2\",\n // text/textMuted were still light theme's values (#111827 /\n // #6b7280) — fine on the near-white `surface`, but components\n // that render straight on `background` (Timeline, Pagination,\n // Tabs — all used with no wrapping surface in this library's own\n // docs examples) sat directly on the purple gradient instead.\n // Measured contrast against the gradient's darker stop\n // (#764ba2): text 2.79:1, textMuted a barely-there 1.32:1 — the\n // reported \"nearly invisible\" text. Darkened both; textMuted\n // still can't clear 4.5:1 against the darkest point of this\n // specific gradient with a single flat color (nothing dark\n // enough to do that stays legible as \"muted\" against the near-\n // white surface too), so this is the best available compromise\n // within a single global text/textMuted pair, not a full fix —\n // worth knowing if it still reads as low-contrast on the\n // darkest corner of the gradient.\n text: \"#000000\",\n textMuted: \"#2e2440\",\n },\n radius: { sm: \"12px\", md: \"18px\", lg: \"24px\", xl: \"32px\", \"2xl\": \"40px\", full: \"9999px\" },\n shadow: {\n xs: \"0 2px 10px rgba(118,75,162,0.15)\", sm: \"0 4px 20px rgba(118,75,162,0.18)\",\n md: \"0 8px 30px rgba(118,75,162,0.22)\", lg: \"0 16px 40px rgba(118,75,162,0.26)\",\n xl: \"0 24px 56px rgba(118,75,162,0.3)\", \"2xl\": \"0 32px 72px rgba(118,75,162,0.35)\",\n inner: \"inset 0 1px 0 rgba(255,255,255,0.5)\",\n },\n motion: { ...light.motion, durationNormal: \"240ms\", durationSlow: \"380ms\" },\n effects: { backdropBlur: \"0px\" },\n});","import { defineTheme } from \"../contract\";\nimport { light } from \"./light\";\n\nexport const material = defineTheme({\n ...light,\n meta: {\n name: \"material\",\n label: \"Material\",\n description: \"Google Material Design-inspired elevation shadows and motion curves.\",\n },\n color: {\n ...light.color,\n surface: \"#ffffff\", background: \"#fafafa\",\n },\n radius: { sm: \"4px\", md: \"8px\", lg: \"12px\", xl: \"16px\", \"2xl\": \"28px\", full: \"9999px\" },\n typography: { ...light.typography, fontSans: \"'Roboto', system-ui, sans-serif\" },\n shadow: {\n xs: \"0 1px 2px rgba(0,0,0,0.24)\",\n sm: \"0 1px 4px rgba(0,0,0,0.2), 0 1px 2px rgba(0,0,0,0.12)\",\n md: \"0 3px 6px rgba(0,0,0,0.16), 0 3px 6px rgba(0,0,0,0.23)\",\n lg: \"0 10px 20px rgba(0,0,0,0.19), 0 6px 6px rgba(0,0,0,0.23)\",\n xl: \"0 14px 28px rgba(0,0,0,0.25), 0 10px 10px rgba(0,0,0,0.22)\",\n \"2xl\": \"0 19px 38px rgba(0,0,0,0.3), 0 15px 12px rgba(0,0,0,0.22)\",\n inner: \"inset 0 1px 2px rgba(0,0,0,0.12)\",\n },\n motion: { ...light.motion, easeStandard: \"cubic-bezier(0.4,0,0.2,1)\", easeEmphasized: \"cubic-bezier(0.0,0,0.2,1)\" },\n effects: { backdropBlur: \"0px\" },\n});\n","import { defineTheme } from \"../contract\";\nimport { light } from \"./light\";\n\nexport const skeuomorphism = defineTheme({\n ...light,\n meta: {\n name: \"skeuomorphism\",\n label: \"Skeuomorphism\",\n description: \"Realistic depth — soft embossed surfaces mimicking physical material.\",\n },\n color: {\n ...light.color,\n background: \"#e8e4da\", surface: \"#f0ece2\", surfaceRaised: \"#f7f4ec\",\n border: \"#c9c2b0\", text: \"#3a3226\",\n },\n radius: { sm: \"8px\", md: \"12px\", lg: \"18px\", xl: \"24px\", \"2xl\": \"32px\", full: \"9999px\" },\n shadow: {\n xs: \"inset 0 1px 1px rgba(255,255,255,0.8), 0 1px 3px rgba(58,50,38,0.25)\",\n sm: \"inset 0 1px 1px rgba(255,255,255,0.8), 0 2px 6px rgba(58,50,38,0.28)\",\n md: \"inset 0 1px 1px rgba(255,255,255,0.8), 0 4px 10px rgba(58,50,38,0.3)\",\n lg: \"inset 0 1px 1px rgba(255,255,255,0.8), 0 8px 18px rgba(58,50,38,0.32)\",\n xl: \"inset 0 1px 1px rgba(255,255,255,0.8), 0 14px 28px rgba(58,50,38,0.35)\",\n \"2xl\": \"inset 0 1px 1px rgba(255,255,255,0.8), 0 20px 40px rgba(58,50,38,0.38)\",\n inner: \"inset 0 2px 4px rgba(58,50,38,0.3)\",\n },\n motion: { ...light.motion, durationNormal: \"220ms\", easeStandard: \"cubic-bezier(0.34,1.56,0.64,1)\" },\n effects: { backdropBlur: \"0px\" },\n});\n","import type { ThemeTokens } from \"./contract\";\nimport { findMissingTokenPaths } from \"./contract\";\nimport { light } from \"./themes/light\";\nimport { dark } from \"./themes/dark\";\nimport { glass } from \"./themes/glass\";\nimport { darkGlass } from \"./themes/dark-glass\";\nimport { minimalism } from \"./themes/minimalism\";\nimport { brutalism } from \"./themes/brutalism\";\nimport { neobrutalism } from \"./themes/neobrutalism\";\nimport { cyberpunk } from \"./themes/cyberpunk\";\nimport { gradient } from \"./themes/gradient\";\nimport { material } from \"./themes/material\";\nimport { skeuomorphism } from \"./themes/skeuomorphism\";\n\nconst registry = new Map<string, ThemeTokens>();\n\n/**\n * Checks the theme against REQUIRED_TOKEN_PATHS — a fixed list\n * hardcoded in contract.ts, not derived from whichever theme\n * happens to register first. This is the runtime safety net for\n * themes that bypass the TypeScript-level defineTheme() check\n * (e.g. loaded from JSON at runtime rather than authored as a\n * .ts file in this repo). A theme that fails this throws\n * immediately on registration, not silently at render time on\n * whichever component hits the missing key first.\n */\nfunction assertComplete(theme: ThemeTokens): void {\n const missing = findMissingTokenPaths(theme);\n if (missing.length > 0) {\n throw new Error(\n `Theme \"${theme.meta?.name ?? \"unknown\"}\" is missing/empty required token(s): ${missing.join(\", \")}`\n );\n }\n}\n\nexport function registerTheme(theme: ThemeTokens): void {\n assertComplete(theme);\n registry.set(theme.meta.name, theme);\n}\n\nexport function getTheme(name: string): ThemeTokens | undefined {\n return registry.get(name);\n}\n\nexport function listThemes(): ThemeTokens[] {\n return Array.from(registry.values());\n}\n\n// Built-in themes register on import. Third-party/future themes\n// (Cyberpunk, Neo Brutal, Aurora, ...) call registerTheme() the\n// same way from wherever they're defined — no other file needs\n// to change.\nregisterTheme(light);\nregisterTheme(dark);\nregisterTheme(glass);\nregisterTheme(darkGlass);\nregisterTheme(minimalism);\nregisterTheme(brutalism);\nregisterTheme(neobrutalism);\nregisterTheme(cyberpunk);\nregisterTheme(gradient);\nregisterTheme(material);\nregisterTheme(skeuomorphism);\n","import type { AccentDefinition } from \"../accents\";\n\nexport const indigo: AccentDefinition = {\n name: \"indigo\",\n label: \"Indigo\",\n scale: {\n 50: \"#f0f4ff\", 100: \"#e0e8ff\", 200: \"#c7d5fe\", 300: \"#a5b4fc\", 400: \"#818cf8\",\n 500: \"#6366f1\", 600: \"#4f46e5\", 700: \"#4338ca\", 800: \"#3730a3\", 900: \"#312e81\",\n },\n};\n","import type { AccentDefinition } from \"../accents\";\n\nexport const violet: AccentDefinition = {\n name: \"violet\",\n label: \"Violet\",\n scale: {\n 50: \"#faf5ff\", 100: \"#f3e8ff\", 200: \"#e9d5ff\", 300: \"#d8b4fe\", 400: \"#c084fc\",\n 500: \"#a855f7\", 600: \"#9333ea\", 700: \"#7e22ce\", 800: \"#6b21a8\", 900: \"#581c87\",\n },\n};\n","import type { AccentDefinition } from \"../accents\";\n\nexport const teal: AccentDefinition = {\n name: \"teal\",\n label: \"Teal\",\n scale: {\n 50: \"#f0fdfa\", 100: \"#ccfbf1\", 200: \"#99f6e4\", 300: \"#5eead4\", 400: \"#2dd4bf\",\n 500: \"#14b8a6\", 600: \"#0d9488\", 700: \"#0f766e\", 800: \"#115e59\", 900: \"#134e4a\",\n },\n};\n","import type { AccentDefinition } from \"../accents\";\n\nexport const rose: AccentDefinition = {\n name: \"rose\",\n label: \"Rose\",\n scale: {\n 50: \"#fff1f2\", 100: \"#ffe4e6\", 200: \"#fecdd3\", 300: \"#fda4af\", 400: \"#fb7185\",\n 500: \"#f43f5e\", 600: \"#e11d48\", 700: \"#be123c\", 800: \"#9f1239\", 900: \"#881337\",\n },\n};\n","import type { AccentDefinition } from \"../accents\";\n\nexport const amber: AccentDefinition = {\n name: \"amber\",\n label: \"Amber\",\n scale: {\n 50: \"#fffbeb\", 100: \"#fef3c7\", 200: \"#fde68a\", 300: \"#fcd34d\", 400: \"#fbbf24\",\n 500: \"#f59e0b\", 600: \"#d97706\", 700: \"#b45309\", 800: \"#92400e\", 900: \"#78350f\",\n },\n};\n","import type { AccentDefinition } from \"../accents\";\n\nexport const emerald: AccentDefinition = {\n name: \"emerald\",\n label: \"Emerald\",\n scale: {\n 50: \"#ecfdf5\", 100: \"#d1fae5\", 200: \"#a7f3d0\", 300: \"#6ee7b7\", 400: \"#34d399\",\n 500: \"#10b981\", 600: \"#059669\", 700: \"#047857\", 800: \"#065f46\", 900: \"#064e3b\",\n },\n};\n","import type { AccentDefinition } from \"../accents\";\n\nexport const copper: AccentDefinition = {\n name: \"copper\",\n label: \"Copper\",\n scale: {\n 50: \"#fdf6ec\", 100: \"#faebd2\", 200: \"#f3d3a0\", 300: \"#e9b56b\",\n 400: \"#d99a45\", 500: \"#b9752c\", 600: \"#9c5d24\", 700: \"#7d4a1f\", 800: \"#623a1c\", 900: \"#4e2f19\",\n },\n};\n","import type { AccentDefinition } from \"../accents\";\n\nexport const graphite: AccentDefinition = {\n name: \"graphite\",\n label: \"Graphite\",\n scale: {\n 50: \"#fafafa\", 100: \"#f0f0f0\", 200: \"#e0e0e0\", 300: \"#c7c7c7\", 400: \"#a3a3a3\",\n 500: \"#525252\", 600: \"#404040\", 700: \"#262626\", 800: \"#171717\", 900: \"#0a0a0a\",\n },\n};\n","import type { AccentDefinition } from \"../accents\";\n\nexport const magenta: AccentDefinition = {\n name: \"magenta\",\n label: \"Magenta\",\n scale: {\n 50: \"#ffe0fb\", 100: \"#ffb3f5\", 200: \"#ff80ee\", 300: \"#ff4de6\", 400: \"#ff26e0\",\n 500: \"#ff00d9\", 600: \"#e600c4\", 700: \"#cc00af\", 800: \"#b3009a\", 900: \"#800070\",\n },\n};\n","import type { ColorScale } from \"./contract\";\n\n// ============================================================\n// ACCENT COLORS\n//\n// This is the axis a consumer actually picks their brand color\n// from — completely independent of which theme (shape/structure)\n// is active. \"Our brand is teal\" should work whether the app is\n// using the light theme, the cyberpunk theme, or any other —\n// the theme still controls radius/shadow/motion/glass-or-not,\n// the accent controls hue.\n//\n// Mirrors tokens/registry.ts's shape deliberately (registerX /\n// getX / listX) so the two systems are learned once, not twice.\n// ============================================================\n\nexport interface AccentDefinition {\n name: string;\n label: string;\n scale: ColorScale;\n}\n\nconst registry = new Map<string, AccentDefinition>();\n\nfunction assertCompleteScale(def: AccentDefinition): void {\n const missing = ([\"50\", \"100\", \"200\", \"300\", \"400\", \"500\", \"600\", \"700\", \"800\", \"900\"] as const).filter(\n (k) => !def.scale[k as unknown as keyof ColorScale]\n );\n if (missing.length > 0) {\n throw new Error(`Accent \"${def.name}\" is missing scale step(s): ${missing.join(\", \")}`);\n }\n}\n\nexport function registerAccent(def: AccentDefinition): void {\n assertCompleteScale(def);\n registry.set(def.name, def);\n}\n\nexport function getAccent(name: string): AccentDefinition | undefined {\n return registry.get(name);\n}\n\nexport function listAccents(): AccentDefinition[] {\n return Array.from(registry.values());\n}\n\n// Built-in accents register on import. A consumer's own custom\n// brand accent registers the same way — registerAccent() with a\n// name/label/scale, called from wherever they define it.\nimport { indigo } from \"./accents/indigo\";\nimport { violet } from \"./accents/violet\";\nimport { teal } from \"./accents/teal\";\nimport { rose } from \"./accents/rose\";\nimport { amber } from \"./accents/amber\";\nimport { emerald } from \"./accents/emerald\";\nimport { copper } from \"./accents/copper\";\nimport { graphite } from \"./accents/graphite\";\nimport { magenta } from \"./accents/magenta\";\n\nregisterAccent(indigo);\nregisterAccent(violet);\nregisterAccent(teal);\nregisterAccent(rose);\nregisterAccent(amber);\nregisterAccent(emerald);\nregisterAccent(copper);\nregisterAccent(graphite);\nregisterAccent(magenta);\n\nexport const DEFAULT_ACCENT = \"indigo\";\n","import type { Size } from \"./types\";\n\n// ============================================================\n// Category B (form/input primitives) shared shape.\n//\n// BUILDING-COMPONENTS.md's Category B note: \"Standard props every\n// component in this category should share: value/defaultValue,\n// onChange, disabled, error (or invalid), placeholder where\n// applicable. Decide this shape once, here, rather than letting\n// Input and NumberInput drift (the old color/radius drift, one\n// category up).\" This module is that one decision, applied by\n// every field-based component (Input, Textarea, Checkbox, Radio,\n// Toggle, and any future one) instead of each re-deriving it.\n// ============================================================\n\nexport interface FieldProps {\n disabled?: boolean;\n required?: boolean;\n /** Whether the field is in an invalid/error state. */\n error?: boolean;\n size?: Size;\n}\n\nexport interface FieldState {\n isDisabled: boolean;\n isRequired: boolean;\n isInvalid: boolean;\n size: Size;\n}\n\nexport function resolveFieldState(props: FieldProps): FieldState {\n return {\n // ?? throughout, never || (CONTRACT.md Rule 3) — `error={false}`\n // or `disabled={false}` must not be silently discarded.\n isDisabled: props.disabled ?? false,\n isRequired: props.required ?? false,\n isInvalid: props.error ?? false,\n size: props.size ?? \"md\",\n };\n}\n\n/**\n * Category B's accessibility baseline (BUILDING-COMPONENTS.md):\n * aria-invalid and aria-describedby (for an error/helper message)\n * on every field component, not just the ones an author happened\n * to remember.\n */\nexport function getFieldAriaProps(state: FieldState, describedBy?: string) {\n return {\n \"aria-invalid\": state.isInvalid ? (true as const) : undefined,\n \"aria-required\": state.isRequired ? (true as const) : undefined,\n \"aria-describedby\": describedBy,\n };\n}\n","import { resolveFieldState, getFieldAriaProps, type FieldProps, type FieldState } from \"./field\";\n\n// ============================================================\n// COLOR PICKER — pure core (Category D: Input's field shape +\n// Popover, with the popover panel composing a preset swatch grid and\n// a custom saturation/hue panel — see react/ColorPicker.tsx's own\n// header for why both, rather than the native <input type=\"color\">\n// an earlier version of this file used). This module owns the field\n// concerns: hex normalization/parsing (now including `rgb(...)`/\n// `hsl(...)` function-notation strings, since the field can display\n// either depending on which format tab is active) and swatch/dial\n// classes and ARIA — the same split datepicker.ts draws between\n// \"field concerns\" and the grid widget's own math (there, Calendar's;\n// here, the swatch grid's and the custom panel's).\n// ============================================================\n\nexport type ColorPickerVariant = \"both\" | \"presets\" | \"custom\";\n\nexport interface ColorPickerCoreProps extends FieldProps {\n /** Preset swatches offered in the popover, as hex strings. Defaults to DEFAULT_PRESETS. */\n presets?: string[];\n /**\n * Which part(s) of the popover to show. `\"both\"` (default) is the\n * preset grid plus the custom saturation/hue panel underneath it.\n * `\"presets\"` shows only the grid — a fixed palette with no\n * arbitrary-color escape hatch. `\"custom\"` shows only the\n * saturation/hue panel — no preset shortcuts at all.\n */\n variant?: ColorPickerVariant;\n}\n\nexport interface ColorPickerState extends FieldState {\n presets: string[];\n variant: ColorPickerVariant;\n}\n\nexport const DEFAULT_PRESETS: string[] = [\n \"#ef4444\",\n \"#f97316\",\n \"#f59e0b\",\n \"#eab308\",\n \"#84cc16\",\n \"#22c55e\",\n \"#10b981\",\n \"#14b8a6\",\n \"#06b6d4\",\n \"#3b82f6\",\n \"#6366f1\",\n \"#8b5cf6\",\n \"#a855f7\",\n \"#d946ef\",\n \"#ec4899\",\n \"#6b7280\",\n \"#171717\",\n \"#ffffff\",\n];\n\nexport function resolveColorPickerState(props: ColorPickerCoreProps): ColorPickerState {\n return {\n ...resolveFieldState(props),\n presets: props.presets ?? DEFAULT_PRESETS,\n variant: props.variant ?? \"both\",\n };\n}\n\n// --- hex parsing / normalization -----------------------------------\n\nconst HEX_RE = /^#?([0-9a-f]{3}|[0-9a-f]{6})$/i;\n\n/** Whether `text` is a recognizable 3- or 6-digit hex color, with or without a leading `#`. */\nexport function isValidHex(text: string): boolean {\n return HEX_RE.test(text.trim());\n}\n\n/**\n * Normalizes any accepted hex input (`\"f00\"`, `\"#f00\"`, `\"ff0000\"`,\n * `\"#FF0000\"`) into a canonical lowercase `#rrggbb` string, or `null`\n * if it isn't a valid hex color at all. The single source of truth\n * every call site (typed-text commit, swatch click, the native color\n * input's own onChange) normalizes through, the same role\n * isDateSelectable/isTimeSelectable play for their fields.\n */\nexport function normalizeHex(text: string): string | null {\n const trimmed = text.trim();\n const match = trimmed.match(HEX_RE);\n if (!match) return null;\n let hex = match[1].toLowerCase();\n if (hex.length === 3) {\n hex = hex\n .split(\"\")\n .map((c) => c + c)\n .join(\"\");\n }\n return `#${hex}`;\n}\n\n/** Whether two hex strings (however formatted) represent the same color. */\nexport function isSameColor(a: string | null | undefined, b: string | null | undefined): boolean {\n if (!a || !b) return a === b;\n const na = normalizeHex(a);\n const nb = normalizeHex(b);\n return na !== null && na === nb;\n}\n\n/** Rough perceived-lightness check, used only to decide a checkmark's contrast color on a selected swatch — not a color-science API. */\nexport function isLightColor(hex: string): boolean {\n const normalized = normalizeHex(hex);\n if (!normalized) return true;\n const r = parseInt(normalized.slice(1, 3), 16);\n const g = parseInt(normalized.slice(3, 5), 16);\n const b = parseInt(normalized.slice(5, 7), 16);\n const luma = (0.299 * r + 0.587 * g + 0.114 * b) / 255;\n return luma > 0.6;\n}\n\n// --- classes ------------------------------------------------------\n\nexport function getSwatchClasses(input: { isSelected: boolean; isActive: boolean }): string {\n return [\n \"nx-colorpicker__swatch-option\",\n input.isSelected && \"nx-colorpicker__swatch-option--selected\",\n input.isActive && \"nx-colorpicker__swatch-option--active\",\n ]\n .filter(Boolean)\n .join(\" \");\n}\n\n// --- ARIA -----------------------------------------------------\n\nexport function getSwatchGridAriaProps(label: string) {\n return { role: \"listbox\" as const, \"aria-label\": label };\n}\n\nexport function getSwatchAriaProps(input: { isSelected: boolean; isFocusable: boolean; label: string }) {\n return {\n role: \"option\" as const,\n \"aria-selected\": input.isSelected,\n \"aria-label\": input.label,\n tabIndex: input.isFocusable ? 0 : -1,\n };\n}\n\nexport function getFieldTriggerAriaProps(state: FieldState, describedBy?: string) {\n return getFieldAriaProps(state, describedBy);\n}\n\n// --- custom panel: color space conversions --------------------------\n// The typed hex field and preset grid only ever need hex in/out, but\n// the custom panel (saturation/value area + hue slider + RGB/HSL\n// fields) needs to move between hex, RGB, HSV, and HSL freely. HSV\n// drives the SV area/hue slider (the natural space for a \"pick a hue,\n// then a shade of it\" 2D control); RGB/HSL are only ever produced for\n// display in their own format tab and round-tripped back through hex\n// on edit, so hex stays the single canonical value ColorPicker's\n// public onChange emits (Rule 10 — the public shape doesn't change).\n\nexport interface RGB {\n r: number;\n g: number;\n b: number;\n}\n\nexport interface HSV {\n h: number;\n s: number;\n v: number;\n}\n\nexport interface HSL {\n h: number;\n s: number;\n l: number;\n}\n\nexport type ColorFormat = \"hex\" | \"rgb\" | \"hsl\";\n\nexport const COLOR_FORMATS: ColorFormat[] = [\"hex\", \"rgb\", \"hsl\"];\n\nfunction clampNum(n: number, min: number, max: number): number {\n return Math.min(Math.max(n, min), max);\n}\n\n/** Hex (any accepted shape, see `normalizeHex`) to 0-255 RGB channels, or `null` if not a valid hex color. */\nexport function hexToRgb(hex: string): RGB | null {\n const normalized = normalizeHex(hex);\n if (!normalized) return null;\n return {\n r: parseInt(normalized.slice(1, 3), 16),\n g: parseInt(normalized.slice(3, 5), 16),\n b: parseInt(normalized.slice(5, 7), 16),\n };\n}/** 0-255 RGB channels (rounded, clamped) to canonical lowercase `#rrggbb`. */\nexport function rgbToHex({ r, g, b }: RGB): string {\n const channel = (n: number) => clampNum(Math.round(n), 0, 255).toString(16).padStart(2, \"0\");\n return `#${channel(r)}${channel(g)}${channel(b)}`;\n}\n\n/** 0-255 RGB to HSV (`h` in 0-360, `s`/`v` in 0-100). */\nexport function rgbToHsv({ r, g, b }: RGB): HSV {\n const rn = r / 255,\n gn = g / 255,\n bn = b / 255;\n const max = Math.max(rn, gn, bn),\n min = Math.min(rn, gn, bn);\n const d = max - min;\n let h = 0;\n if (d !== 0) {\n if (max === rn) h = ((gn - bn) / d) % 6;\n else if (max === gn) h = (bn - rn) / d + 2;\n else h = (rn - gn) / d + 4;\n h *= 60;\n if (h < 0) h += 360;\n }\n const s = max === 0 ? 0 : d / max;\n return { h, s: s * 100, v: max * 100 };\n}\n\n/** HSV (`h` in 0-360, `s`/`v` in 0-100) to 0-255 RGB. */\nexport function hsvToRgb({ h, s, v }: HSV): RGB {\n const s1 = clampNum(s, 0, 100) / 100;\n const v1 = clampNum(v, 0, 100) / 100;\n const hh = (((h % 360) + 360) % 360) / 60;\n const c = v1 * s1;\n const x = c * (1 - Math.abs((hh % 2) - 1));\n const m = v1 - c;\n let [r1, g1, b1] = [0, 0, 0];\n if (hh < 1) [r1, g1, b1] = [c, x, 0];\n else if (hh < 2) [r1, g1, b1] = [x, c, 0];\n else if (hh < 3) [r1, g1, b1] = [0, c, x];\n else if (hh < 4) [r1, g1, b1] = [0, x, c];\n else if (hh < 5) [r1, g1, b1] = [x, 0, c];\n else [r1, g1, b1] = [c, 0, x];\n return { r: (r1 + m) * 255, g: (g1 + m) * 255, b: (b1 + m) * 255 };\n}\n\n/** 0-255 RGB to HSL (`h` in 0-360, `s`/`l` in 0-100). */\nexport function rgbToHsl({ r, g, b }: RGB): HSL {\n const rn = r / 255,\n gn = g / 255,\n bn = b / 255;\n const max = Math.max(rn, gn, bn),\n min = Math.min(rn, gn, bn);\n const l = (max + min) / 2;\n const d = max - min;\n let h = 0,\n s = 0;\n if (d !== 0) {\n s = l > 0.5 ? d / (2 - max - min) : d / (max + min);\n if (max === rn) h = ((gn - bn) / d) % 6;\n else if (max === gn) h = (bn - rn) / d + 2;\n else h = (rn - gn) / d + 4;\n h *= 60;\n if (h < 0) h += 360;\n }\n return { h, s: s * 100, l: l * 100 };\n}\n\n/** HSL (`h` in 0-360, `s`/`l` in 0-100) to 0-255 RGB. */\nexport function hslToRgb({ h, s, l }: HSL): RGB {\n const s1 = clampNum(s, 0, 100) / 100;\n const l1 = clampNum(l, 0, 100) / 100;\n const c = (1 - Math.abs(2 * l1 - 1)) * s1;\n const hh = (((h % 360) + 360) % 360) / 60;\n const x = c * (1 - Math.abs((hh % 2) - 1));\n const m = l1 - c / 2;\n let [r1, g1, b1] = [0, 0, 0];\n if (hh < 1) [r1, g1, b1] = [c, x, 0];\n else if (hh < 2) [r1, g1, b1] = [x, c, 0];\n else if (hh < 3) [r1, g1, b1] = [0, c, x];\n else if (hh < 4) [r1, g1, b1] = [0, x, c];\n else if (hh < 5) [r1, g1, b1] = [x, 0, c];\n else [r1, g1, b1] = [c, 0, x];\n return { r: (r1 + m) * 255, g: (g1 + m) * 255, b: (b1 + m) * 255 };\n}\n\n/** Hex to HSV, or `null` if not a valid hex color — the SV-area/hue-slider's read side. */\nexport function hexToHsv(hex: string): HSV | null {\n const rgb = hexToRgb(hex);\n return rgb ? rgbToHsv(rgb) : null;\n}\n\n/** HSV to canonical hex — the SV-area/hue-slider's write side. */\nexport function hsvToHex(hsv: HSV): string {\n return rgbToHex(hsvToRgb(hsv));\n}\n\n/** HSL to canonical hex — the HSL format tab's write side. */\nexport function hslToHex(hsl: HSL): string {\n return rgbToHex(hslToRgb(hsl));\n}\n\n// --- typed-field text: parsing whichever format is on screen, and\n// producing that format back out --------------------------------\n// The field's displayed text now follows whichever format tab is\n// active in the custom panel (hex/rgb/hsl) rather than always\n// showing hex, so it needs to accept typed `rgb(...)`/`hsla(...)`\n// text back, not just hex — otherwise a person editing the field\n// while \"RGB\" is selected would have their own format rejected.\n\nconst RGB_FUNCTION_RE = /^rgba?\\(\\s*(\\d{1,3})\\s*,\\s*(\\d{1,3})\\s*,\\s*(\\d{1,3})\\s*(?:,\\s*[\\d.]+\\s*)?\\)$/i;\nconst HSL_FUNCTION_RE = /^hsla?\\(\\s*(-?\\d{1,3})\\s*,\\s*(\\d{1,3})%\\s*,\\s*(\\d{1,3})%\\s*(?:,\\s*[\\d.]+\\s*)?\\)$/i;\n\n/** Parses hex OR `rgb(a)(...)`/`hsl(a)(...)` function-notation text into normalized hex, or `null` if it's none of those. */\nexport function parseColorInput(text: string): string | null {\n const trimmed = text.trim();\n const hex = normalizeHex(trimmed);\n if (hex) return hex;\n\n const rgbMatch = trimmed.match(RGB_FUNCTION_RE);\n if (rgbMatch) {\n const [, r, g, b] = rgbMatch;\n return rgbToHex({ r: clampNum(Number(r), 0, 255), g: clampNum(Number(g), 0, 255), b: clampNum(Number(b), 0, 255) });\n }\n\n const hslMatch = trimmed.match(HSL_FUNCTION_RE);\n if (hslMatch) {\n const [, h, s, l] = hslMatch;\n return hslToHex({ h: clampNum(Number(h), 0, 360), s: clampNum(Number(s), 0, 100), l: clampNum(Number(l), 0, 100) });\n }\n\n return null;\n}\n\n/** Renders hex as text in the given format — the field's display-side counterpart to `parseColorInput`, so what's shown always matches whichever format tab is currently active. */\nexport function formatColorValue(hex: string, format: ColorFormat): string {\n if (format === \"hex\") return hex;\n const rgb = hexToRgb(hex);\n if (!rgb) return hex;\n if (format === \"rgb\") return `rgb(${Math.round(rgb.r)}, ${Math.round(rgb.g)}, ${Math.round(rgb.b)})`;\n const hsl = rgbToHsl(rgb);\n return `hsl(${Math.round(hsl.h)}, ${Math.round(hsl.s)}%, ${Math.round(hsl.l)}%)`;\n}\n\n/** Arrow-key deltas for the 2D saturation/value area (Shift for a larger step) — Home/End/PageUp/PageDown aren't meaningful on a 2D surface, so only the four arrows are handled, same spirit as Slider's own keyboard set restricted to what the control actually has. */\nexport function getSvKeyboardDelta(key: string, coarse: boolean): { ds: number; dv: number } | null {\n const step = coarse ? 10 : 2;\n switch (key) {\n case \"ArrowLeft\":\n return { ds: -step, dv: 0 };\n case \"ArrowRight\":\n return { ds: step, dv: 0 };\n case \"ArrowUp\":\n return { ds: 0, dv: step };\n case \"ArrowDown\":\n return { ds: 0, dv: -step };\n default:\n return null;\n }\n}\n\n/** Clamps a saturation or value percentage into 0-100. Exported so the SV area's pointer/keyboard handlers share one clamp rather than each re-deriving it. */\nexport function clampPercent(n: number): number {\n return clampNum(n, 0, 100);\n}\n\n// --- custom panel: classes / ARIA -----------------------------------\n\nexport function getFormatTabClasses(isActive: boolean): string {\n return [\"nx-colorpicker__format-tab\", isActive && \"nx-colorpicker__format-tab--active\"].filter(Boolean).join(\" \");\n}\n\nexport function getFormatTablistAriaProps(label: string) {\n return { role: \"tablist\" as const, \"aria-label\": label };\n}\n\nexport function getFormatTabAriaProps(isActive: boolean) {\n return { role: \"tab\" as const, \"aria-selected\": isActive, tabIndex: isActive ? 0 : -1 };\n}\n\nexport function getSvThumbAriaProps(saturation: number, value: number) {\n return {\n role: \"slider\" as const,\n \"aria-label\": \"Color saturation and brightness\",\n \"aria-valuetext\": `Saturation ${Math.round(saturation)}%, brightness ${Math.round(value)}%`,\n tabIndex: 0,\n };\n}","import type { Size } from \"./types\";\n\n// ============================================================\n// TABLE — pure core (Category F: complex/stateful).\n//\n// Per BUILDING-COMPONENTS.md's own note for this component: \"sort\n// state should be lifted to props the consumer can control, not\n// trapped inside the component\" — the same controlled-value shape\n// Input/Slider/NumberInput already use. Table itself holds no sort\n// or selection state; TableHead's `sortDirection` is a prop the\n// consumer supplies, and getNextSortDirection is exported so the\n// consumer's onSort handler doesn't have to reinvent the standard\n// asc → desc → unsorted cycle. Row SELECTION is intentionally not\n// modeled here at all — TableRow just renders whatever `selected`\n// boolean it's given, and a table with checkboxes composes the real\n// Checkbox component (Rule 12) rather than Table reimplementing a\n// second one. getSelectAllState is the one piece of selection logic\n// that's genuinely shared/non-trivial (the tri-state \"select all\"\n// checkbox's own state, computed from a count) — pulled out here so\n// it's unit-tested the same way everything else pure is.\n// ============================================================\n\nexport interface TableCoreProps {\n size?: Size;\n /** Alternating row background. Default false. */\n striped?: boolean;\n /** Borders around every cell, not just horizontal row rules. Default false. */\n bordered?: boolean;\n /** Keeps the header visible while the body scrolls — only has something to stick within if an ancestor bounds the table's height (e.g. a wrapping div with max-height). Default false. */\n stickyHeader?: boolean;\n}\n\nexport interface TableState {\n size: Size;\n striped: boolean;\n bordered: boolean;\n stickyHeader: boolean;\n}\n\nexport function resolveTableState(props: TableCoreProps): TableState {\n return {\n size: props.size ?? \"md\",\n striped: props.striped ?? false,\n bordered: props.bordered ?? false,\n stickyHeader: props.stickyHeader ?? false,\n };\n}\n\nexport function getWrapperClasses(className?: string): string {\n return [\"nx-table-wrapper\", className].filter(Boolean).join(\" \");\n}\n\nexport function getTableClasses(state: TableState, className?: string): string {\n return [\n \"nx-table\",\n `nx-table--${state.size}`,\n state.striped && \"nx-table--striped\",\n state.bordered && \"nx-table--bordered\",\n state.stickyHeader && \"nx-table--sticky-header\",\n className,\n ]\n .filter(Boolean)\n .join(\" \");\n}\n\nexport function getRowClasses(selected: boolean, clickable: boolean, className?: string): string {\n return [\n \"nx-table__row\",\n selected && \"nx-table__row--selected\",\n clickable && \"nx-table__row--clickable\",\n className,\n ]\n .filter(Boolean)\n .join(\" \");\n}\n\nexport function getHeadCellClasses(sortable: boolean, className?: string): string {\n return [\"nx-table__head-cell\", sortable && \"nx-table__head-cell--sortable\", className].filter(Boolean).join(\" \");\n}\n\nexport function getCellClasses(className?: string): string {\n return [\"nx-table__cell\", className].filter(Boolean).join(\" \");\n}\n\n// --- Sorting ----------------------------------------------------\n\nexport type SortDirection = \"asc\" | \"desc\" | null;\n\n/** The conventional 3-state cycle a sortable header click steps through: unsorted → asc → desc → unsorted. */\nexport function getNextSortDirection(current: SortDirection): SortDirection {\n if (current === null) return \"asc\";\n if (current === \"asc\") return \"desc\";\n return null;\n}\n\nexport function getSortAriaProps(direction: SortDirection) {\n return {\n \"aria-sort\":\n direction === \"asc\" ? (\"ascending\" as const) : direction === \"desc\" ? (\"descending\" as const) : (\"none\" as const),\n };\n}\n\n// --- Selection ----------------------------------------------------\n\nexport type SelectAllState = \"none\" | \"some\" | \"all\";\n\n/** The tri-state a \"select all\" header checkbox should show, computed from how many of the total rows are currently selected. */\nexport function getSelectAllState(totalCount: number, selectedCount: number): SelectAllState {\n if (totalCount <= 0 || selectedCount <= 0) return \"none\";\n if (selectedCount >= totalCount) return \"all\";\n return \"some\";\n}","// ============================================================\n// KANBAN — pure core (Category F: complex/stateful).\n//\n// Decomposed on paper first, per BUILDING-COMPONENTS.md's Category F\n// guidance: KanbanBoard (root — board state, drag/keyboard-move\n// orchestration, the drag-layer portal), KanbanColumn (a droppable\n// list, data-ordered not JSX-ordered), KanbanCard (composes the real\n// `Card` for chrome, Rule 12). This file owns none of the DOM/pointer\n// wiring — only the parts that are pure enough to unit-test without\n// a browser: board-state reordering, keyboard-move targeting, and\n// the drop-position geometry math, expressed over plain rects rather\n// than real DOM nodes so it's testable with fabricated data instead\n// of needing jsdom to lay anything out for real.\n//\n// Board state is intentionally minimal: `{ columns: [{ id, cardIds }] }`.\n// Card CONTENT is never modeled here — same reasoning as\n// core/table.ts not owning cell content — the consumer renders\n// arbitrary children per `<KanbanCard cardId=\"...\">`, and this board\n// only ever moves ids around. `findCardLocation`/`moveCard` are the\n// two functions everything else is built from.\n// ============================================================\n\n/** Movement (in px) a pointer-down must travel before it commits to a drag — below this, it's treated as a click/tap instead, so interactive content inside a card's children still works. */\nexport const DRAG_START_THRESHOLD_PX = 4;\n\nexport interface KanbanCoreProps {\n /** Tighter column padding/gaps and smaller card chrome. Default false. */\n compact?: boolean;\n}\n\nexport interface KanbanState {\n compact: boolean;\n}\n\nexport function resolveKanbanState(props: KanbanCoreProps): KanbanState {\n return { compact: props.compact ?? false };\n}\n\nexport function getBoardClasses(state: KanbanState, className?: string): string {\n return [\"nx-kanban\", state.compact && \"nx-kanban--compact\", className].filter(Boolean).join(\" \");\n}\n\nexport function getColumnClasses(isDropTarget: boolean, isDragging: boolean, className?: string): string {\n return [\n \"nx-kanban__column\",\n isDropTarget && \"nx-kanban__column--drop-target\",\n isDragging && \"nx-kanban__column--dragging\",\n className,\n ]\n .filter(Boolean)\n .join(\" \");\n}\n\nexport function getColumnCountClasses(overLimit: boolean): string {\n return [\"nx-kanban__column-count\", overLimit && \"nx-kanban__column-count--over-limit\"].filter(Boolean).join(\" \");\n}\n\nexport interface CardClassOptions {\n isPointerDragging: boolean;\n isPickedUp: boolean;\n disabled: boolean;\n}\n\nexport function getCardWrapperClasses(opts: CardClassOptions, className?: string): string {\n return [\n \"nx-kanban__card\",\n opts.isPointerDragging && \"nx-kanban__card--dragging\",\n opts.isPickedUp && \"nx-kanban__card--picked-up\",\n opts.disabled && \"nx-kanban__card--disabled\",\n className,\n ]\n .filter(Boolean)\n .join(\" \");\n}\n\n// --- Board data ---------------------------------------------------\n\nexport interface KanbanColumnValue {\n id: string;\n cardIds: string[];\n}\n\nexport interface KanbanBoardValue {\n columns: KanbanColumnValue[];\n}\n\nexport interface KanbanCardLocation {\n columnId: string;\n index: number;\n}\n\n/** Where a card currently sits, or null if it isn't in any column's cardIds — either it hasn't been added to the board value yet, or it was just removed. */\nexport function findCardLocation(value: KanbanBoardValue, cardId: string): KanbanCardLocation | null {\n for (const column of value.columns) {\n const index = column.cardIds.indexOf(cardId);\n if (index !== -1) return { columnId: column.id, index };\n }\n return null;\n}\n\n/**\n * Pure, immutable removal of a card from wherever it currently sits.\n * A no-op if the card isn't found. There's no delete UI in KanbanCard\n * itself — a delete affordance is card CONTENT, same as everything\n * else `renderCard` returns — this is just the board-state half: call\n * it, then `onChange` the result the same way a move does.\n */\nexport function removeCard(value: KanbanBoardValue, cardId: string): KanbanBoardValue {\n const location = findCardLocation(value, cardId);\n if (!location) return value;\n return {\n columns: value.columns.map((column) =>\n column.id === location.columnId ? { ...column, cardIds: column.cardIds.filter((id) => id !== cardId) } : column\n ),\n };\n}\n\nexport interface MoveCardTarget {\n cardId: string;\n toColumnId: string;\n /** Insertion index within the destination column's list, AFTER the card has been removed from wherever it was. Clamped to [0, length]. */\n toIndex: number;\n}\n\n/**\n * Pure, immutable move: removes `cardId` from wherever it currently\n * sits (same column or a different one — both are just \"wherever it\n * sits\" to this function) and inserts it at `toIndex` in\n * `toColumnId`. A no-op — returns `value` unchanged — if the card or\n * the destination column can't be found, so a caller doesn't need to\n * guard every call site itself.\n */\nexport function moveCard(value: KanbanBoardValue, target: MoveCardTarget): KanbanBoardValue {\n const location = findCardLocation(value, target.cardId);\n const destExists = value.columns.some((column) => column.id === target.toColumnId);\n if (!location || !destExists) return value;\n\n const withoutCard = value.columns.map((column) =>\n column.cardIds.includes(target.cardId)\n ? { ...column, cardIds: column.cardIds.filter((id) => id !== target.cardId) }\n : column\n );\n\n return {\n columns: withoutCard.map((column) => {\n if (column.id !== target.toColumnId) return column;\n const clampedIndex = Math.max(0, Math.min(target.toIndex, column.cardIds.length));\n const cardIds = [...column.cardIds];\n cardIds.splice(clampedIndex, 0, target.cardId);\n return { ...column, cardIds };\n }),\n };\n}\n\n/** The column immediately before (`-1`) or after (`1`) `columnId`, or null at either end. */\nexport function getAdjacentColumnId(value: KanbanBoardValue, columnId: string, direction: 1 | -1): string | null {\n const index = value.columns.findIndex((column) => column.id === columnId);\n if (index === -1) return null;\n return value.columns[index + direction]?.id ?? null;\n}\n\nexport type KeyboardMoveDirection = \"up\" | \"down\" | \"prev\" | \"next\";\n\n/**\n * Given a picked-up card and a keyboard direction, returns the move\n * it should perform — or null if that direction is a no-op from here\n * (already first/last in the column, or no adjacent column that way).\n * `up`/`down` reorder within the current column; `prev`/`next` move\n * to the adjacent column, keeping the same relative position (clamped\n * to that column's length) rather than always landing at the top or\n * bottom.\n */\nexport function getKeyboardMoveTarget(\n value: KanbanBoardValue,\n cardId: string,\n direction: KeyboardMoveDirection\n): MoveCardTarget | null {\n const location = findCardLocation(value, cardId);\n if (!location) return null;\n const column = value.columns.find((c) => c.id === location.columnId);\n if (!column) return null;\n\n if (direction === \"up\" || direction === \"down\") {\n const toIndex = direction === \"up\" ? location.index - 1 : location.index + 1;\n if (toIndex < 0 || toIndex > column.cardIds.length - 1) return null;\n return { cardId, toColumnId: location.columnId, toIndex };\n }\n\n const adjacentColumnId = getAdjacentColumnId(value, location.columnId, direction === \"prev\" ? -1 : 1);\n if (!adjacentColumnId) return null;\n const destColumn = value.columns.find((c) => c.id === adjacentColumnId);\n if (!destColumn) return null;\n const toIndex = Math.min(location.index, destColumn.cardIds.length);\n return { cardId, toColumnId: adjacentColumnId, toIndex };\n}\n\n// --- Pointer-drag geometry (pure — operates on plain rects, not DOM) ---\n\nexport interface RectLike {\n top: number;\n bottom: number;\n left: number;\n right: number;\n}\n\n/**\n * Given the pointer's Y position and the rects of the cards currently\n * rendered in a column (top-to-bottom render order, with the card\n * being dragged already excluded by the caller), returns the index a\n * dropped card would land at: before the first card whose vertical\n * midpoint the pointer is above, or at the end if the pointer is\n * below all of them.\n */\nexport function computeDropIndex(pointerY: number, cardRects: RectLike[]): number {\n for (let i = 0; i < cardRects.length; i++) {\n const midpoint = (cardRects[i].top + cardRects[i].bottom) / 2;\n if (pointerY < midpoint) return i;\n }\n return cardRects.length;\n}\n\n/** Which column (by id) the pointer is currently over, given each column body's rect — or null if it's outside all of them. */\nexport function computeDropColumn(\n pointerX: number,\n pointerY: number,\n columns: Array<RectLike & { id: string }>\n): string | null {\n for (const column of columns) {\n if (pointerX >= column.left && pointerX <= column.right && pointerY >= column.top && pointerY <= column.bottom) {\n return column.id;\n }\n }\n return null;\n}\n\n// --- Column reordering ---------------------------------------------\n//\n// Unlike cards, columns don't have the \"which parent owns me\" problem\n// — every column lives directly under the same KanbanBoard, so\n// reordering `value.columns` and re-sorting the SAME set of JSX\n// children to match is enough; nothing needs to move across a parent\n// boundary the way a card does between two different KanbanColumns.\n// See Kanban.tsx's header for how that plays out in the React layer.\n\nexport interface MoveColumnTarget {\n columnId: string;\n toIndex: number;\n}\n\n/** Pure, immutable reorder of the columns array itself. A no-op if columnId isn't found. `toIndex` is clamped to [0, columns.length - 1]. */\nexport function moveColumn(value: KanbanBoardValue, target: MoveColumnTarget): KanbanBoardValue {\n const fromIndex = value.columns.findIndex((c) => c.id === target.columnId);\n if (fromIndex === -1) return value;\n const columns = [...value.columns];\n const [moved] = columns.splice(fromIndex, 1);\n const clampedIndex = Math.max(0, Math.min(target.toIndex, columns.length));\n columns.splice(clampedIndex, 0, moved);\n return { columns };\n}\n\n/** Pure, immutable removal of a column (and every card in it). A no-op if columnId isn't found. */\nexport function removeColumn(value: KanbanBoardValue, columnId: string): KanbanBoardValue {\n if (!value.columns.some((c) => c.id === columnId)) return value;\n return { columns: value.columns.filter((c) => c.id !== columnId) };\n}\n\nexport type ColumnKeyboardMoveDirection = \"prev\" | \"next\";\n\n/** Given a picked-up column and a keyboard direction, the move it should perform — or null at either end of the row. */\nexport function getColumnKeyboardMoveTarget(\n value: KanbanBoardValue,\n columnId: string,\n direction: ColumnKeyboardMoveDirection\n): MoveColumnTarget | null {\n const fromIndex = value.columns.findIndex((c) => c.id === columnId);\n if (fromIndex === -1) return null;\n const toIndex = direction === \"prev\" ? fromIndex - 1 : fromIndex + 1;\n if (toIndex < 0 || toIndex > value.columns.length - 1) return null;\n return { columnId, toIndex };\n}\n\n/**\n * The horizontal counterpart of computeDropIndex: given the pointer's\n * X position and the rects of the columns currently in the row (left-\n * to-right render order, with the column being dragged already\n * excluded by the caller), returns the index it would land at.\n */\nexport function computeColumnDropIndex(pointerX: number, columnRects: RectLike[]): number {\n for (let i = 0; i < columnRects.length; i++) {\n const midpoint = (columnRects[i].left + columnRects[i].right) / 2;\n if (pointerX < midpoint) return i;\n }\n return columnRects.length;\n}","// ============================================================\r\n// CODE-HIGHLIGHT — shared primitive (like floating.ts / collection.ts),\r\n// not owned by CodeBlock alone. Two responsibilities:\r\n//\r\n// 1. A tiny rule-based tokenizer (`nuvoxHighlight`) covering a\r\n// CURATED set of common languages. This is deliberately NOT a\r\n// real parser — no AST, no language-server-grade accuracy. It's\r\n// the same \"ordered regex rules, first match wins\" technique\r\n// Prism/highlight.js use, scoped down to what a doc-site/component\r\n// library actually needs — pushed further than a first pass\r\n// (regex-literal disambiguation, template-literal interpolation,\r\n// decorators, CSS pseudo-classes/custom properties, Python\r\n// f-strings) but still not chasing full-grammar accuracy.\r\n// Anything outside the curated list (or any pattern a grammar\r\n// doesn't anticipate) safely degrades to unhighlighted plain\r\n// text — it never throws.\r\n//\r\n// 2. The `HighlightEngine` escape hatch: CodeBlock's `highlighter`\r\n// prop accepts any function matching this exact signature. A\r\n// consumer who needs full-language, TextMate-grade accuracy can\r\n// wire in Shiki/Prism themselves — this file, and Nuvox, never\r\n// import one.\r\n//\r\n// Zero runtime dependencies: plain RegExp only.\r\n// ============================================================\r\n\r\nexport interface CodeToken {\r\n type: string;\r\n content: string;\r\n}\r\n\r\nexport type CodeLine = CodeToken[];\r\n\r\n/**\r\n * The shape any tokenizer must satisfy to plug into CodeBlock via its\r\n * `highlighter` prop — including `nuvoxHighlight` itself. A consumer\r\n * wiring in Shiki/Prism just needs to adapt that engine's output to\r\n * this shape once.\r\n */\r\nexport type HighlightEngine = (code: string, language: string) => CodeLine[];\r\n\r\ninterface GrammarRule {\r\n type: string;\r\n /** Must use the sticky (\"y\") flag, never \"g\" — see tokenizeFlat(). */\r\n pattern?: RegExp;\r\n /**\r\n * Escape hatch for the handful of cases a single regex genuinely\r\n * can't express: template-literal interpolation (needs to\r\n * recursively tokenize an arbitrary-depth `${...}` expression) and\r\n * regex-literal-vs-division disambiguation (needs to look at the\r\n * PREVIOUS token, which a stateless regex can't do). Returns the\r\n * number of source characters consumed and the token(s) to emit,\r\n * or null if this rule doesn't apply at `index`.\r\n */\r\n match?: (source: string, index: number, tokensSoFar: CodeToken[]) => { length: number; tokens: CodeToken[] } | null;\r\n}\r\n\r\ninterface Grammar {\r\n rules: GrammarRule[];\r\n}\r\n\r\n// ---- the tokenizer loop -----------------------------------------\r\n\r\n/**\r\n * Walks `source` left to right. At each position, tries every rule in\r\n * order and takes the first one that matches AT that exact position\r\n * (order = precedence, not \"longest match\" — this is why comments and\r\n * strings are always listed before keywords in every grammar below:\r\n * `// return` must not tokenize \"return\" as a keyword).\r\n *\r\n * Anything no rule matches is consumed one character at a time as a\r\n * \"plain\" token (merged with any preceding plain token) — this is\r\n * what makes the whole thing safe to run on code with syntax the\r\n * grammar never anticipated: it degrades to unstyled text instead of\r\n * throwing or mis-highlighting.\r\n */\r\nfunction tokenizeFlat(source: string, grammar: Grammar): CodeToken[] {\r\n const tokens: CodeToken[] = [];\r\n const n = source.length;\r\n let i = 0;\r\n\r\n outer: while (i < n) {\r\n for (const rule of grammar.rules) {\r\n if (rule.pattern) {\r\n rule.pattern.lastIndex = i;\r\n const m = rule.pattern.exec(source);\r\n if (m && m.index === i && m[0].length > 0) {\r\n tokens.push({ type: rule.type, content: m[0] });\r\n i += m[0].length;\r\n continue outer;\r\n }\r\n } else if (rule.match) {\r\n const result = rule.match(source, i, tokens);\r\n if (result && result.length > 0) {\r\n tokens.push(...result.tokens);\r\n i += result.length;\r\n continue outer;\r\n }\r\n }\r\n }\r\n const ch = source[i];\r\n const last = tokens[tokens.length - 1];\r\n if (last && last.type === \"plain\") last.content += ch;\r\n else tokens.push({ type: \"plain\", content: ch });\r\n i++;\r\n }\r\n\r\n return tokens;\r\n}\r\n\r\n/**\r\n * Splits a flat token stream into per-line arrays, breaking multi-line\r\n * tokens (block comments, template literals, triple-quoted strings)\r\n * at their internal newlines so each line's tokens can be rendered\r\n * independently (line numbers, per-line highlighting).\r\n */\r\nfunction splitIntoLines(tokens: CodeToken[]): CodeLine[] {\r\n const lines: CodeLine[] = [[]];\r\n for (const tok of tokens) {\r\n const parts = tok.content.split(\"\\n\");\r\n parts.forEach((part, idx) => {\r\n if (part.length > 0) lines[lines.length - 1].push({ type: tok.type, content: part });\r\n if (idx < parts.length - 1) lines.push([]);\r\n });\r\n }\r\n return lines;\r\n}\r\n\r\nconst KEYWORD = (words: string) => new RegExp(`\\\\b(?:${words})\\\\b`, \"y\");\r\n\r\n// ---- custom matchers shared by JS/TS/JSX/TSX ------------------------\r\n\r\n/**\r\n * Template literals need special handling a single regex can't do:\r\n * `${...}` interpolations can nest arbitrarily deep and contain\r\n * completely arbitrary JS, which should itself be highlighted (an\r\n * interpolated `${user.name}` should color `user`/`name` normally,\r\n * not render as inert string text). This scans the literal by hand,\r\n * recursing into tokenizeFlat for each interpolation's contents.\r\n */\r\nfunction matchTemplateLiteral(\r\n source: string,\r\n start: number,\r\n jsGrammar: Grammar\r\n): { length: number; tokens: CodeToken[] } | null {\r\n if (source[start] !== \"`\") return null;\r\n\r\n const tokens: CodeToken[] = [{ type: \"string\", content: \"`\" }];\r\n let i = start + 1;\r\n let buf = \"\";\r\n const flush = () => {\r\n if (buf) {\r\n tokens.push({ type: \"string\", content: buf });\r\n buf = \"\";\r\n }\r\n };\r\n\r\n while (i < source.length) {\r\n const ch = source[i];\r\n if (ch === \"\\\\\" && i + 1 < source.length) {\r\n buf += ch + source[i + 1];\r\n i += 2;\r\n continue;\r\n }\r\n if (ch === \"`\") {\r\n flush();\r\n tokens.push({ type: \"string\", content: \"`\" });\r\n i++;\r\n return { length: i - start, tokens };\r\n }\r\n if (ch === \"$\" && source[i + 1] === \"{\") {\r\n flush();\r\n tokens.push({ type: \"punctuation\", content: \"${\" });\r\n i += 2;\r\n const exprStart = i;\r\n let depth = 1;\r\n while (i < source.length && depth > 0) {\r\n if (source[i] === \"{\") depth++;\r\n else if (source[i] === \"}\") depth--;\r\n if (depth > 0) i++;\r\n }\r\n const exprSource = source.slice(exprStart, i);\r\n tokens.push(...tokenizeFlat(exprSource, jsGrammar));\r\n if (i < source.length) {\r\n tokens.push({ type: \"punctuation\", content: \"}\" });\r\n i++;\r\n }\r\n continue;\r\n }\r\n buf += ch;\r\n i++;\r\n }\r\n\r\n // Unterminated template literal (adversarial/malformed input) — flush\r\n // what we have rather than throwing or scanning past the end.\r\n flush();\r\n return { length: i - start, tokens };\r\n}\r\n\r\nconst REGEX_EXPRESSION_KEYWORDS = new Set([\r\n \"return\", \"typeof\", \"instanceof\", \"in\", \"of\", \"new\", \"delete\", \"void\", \"throw\", \"case\", \"do\", \"else\", \"yield\", \"await\",\r\n]);\r\n\r\n/**\r\n * `/` is ambiguous in JS: division (`a / b`) vs. a regex literal\r\n * (`/ab+c/gi`). A stateless regex can't tell these apart — it depends\r\n * on what came before. Heuristic, based on the last non-whitespace\r\n * SOURCE CHARACTER (not the last emitted token — this grammar doesn't\r\n * tokenize every operator into its own type, so `=`, `+`, `<` etc.\r\n * all end up folded into generic \"plain\" tokens alongside identifier\r\n * text, which made a token-type-based version of this check\r\n * indistinguishable between \"after `x`\" and \"after `=`\"): a regex is\r\n * only valid where an EXPRESSION is expected, i.e. NOT immediately\r\n * after an identifier/number, `)`, `]`, or a closing quote — all of\r\n * which mean the previous thing was a complete value, so `/` must be\r\n * division — UNLESS that preceding word is itself an\r\n * expression-position keyword (`return /foo/`, `typeof /foo/`, ...).\r\n */\r\nfunction matchRegexLiteral(source: string, start: number): { length: number; tokens: CodeToken[] } | null {\r\n if (source[start] !== \"/\" || source[start + 1] === \"/\" || source[start + 1] === \"*\") return null;\r\n\r\n let j = start - 1;\r\n while (j >= 0 && /\\s/.test(source[j])) j--;\r\n if (j >= 0) {\r\n const prevChar = source[j];\r\n const looksLikeValueEnd = /[)\\]}\\w$]/.test(prevChar) || prevChar === '\"' || prevChar === \"'\" || prevChar === \"`\";\r\n if (looksLikeValueEnd) {\r\n const wordBefore = source.slice(0, j + 1).match(/([A-Za-z_$][\\w$]*)$/)?.[1];\r\n if (!(wordBefore && REGEX_EXPRESSION_KEYWORDS.has(wordBefore))) return null;\r\n }\r\n }\r\n\r\n let i = start + 1;\r\n let inClass = false;\r\n while (i < source.length) {\r\n const ch = source[i];\r\n if (ch === \"\\\\\") {\r\n i += 2;\r\n continue;\r\n }\r\n if (ch === \"\\n\") return null; // regex literals never span lines — bail to \"division\" reading\r\n if (ch === \"[\") inClass = true;\r\n else if (ch === \"]\") inClass = false;\r\n else if (ch === \"/\" && !inClass) {\r\n i++;\r\n break;\r\n }\r\n i++;\r\n }\r\n if (source[i - 1] !== \"/\") return null; // never found a closing slash\r\n\r\n while (i < source.length && /[a-z]/i.test(source[i])) i++;\r\n return { length: i - start, tokens: [{ type: \"regex\", content: source.slice(start, i) }] };\r\n}\r\n\r\nfunction buildJsGrammar(withTags: boolean): Grammar {\r\n const grammar: Grammar = { rules: [] };\r\n const rules = grammar.rules;\r\n\r\n if (withTags) {\r\n rules.push({ type: \"tag\", pattern: /<\\/?[A-Za-z][\\w.-]*/y });\r\n }\r\n\r\n rules.push(\r\n { type: \"comment\", pattern: /\\/\\/[^\\n]*|\\/\\*[\\s\\S]*?\\*\\//y },\r\n { type: \"string\", pattern: /\"(?:[^\"\\\\\\n]|\\\\.)*\"|'(?:[^'\\\\\\n]|\\\\.)*'/y },\r\n { type: \"custom-template\", match: (src, idx) => matchTemplateLiteral(src, idx, grammar) },\r\n { type: \"custom-regex\", match: (src, idx) => matchRegexLiteral(src, idx) },\r\n { type: \"decorator\", pattern: /@[A-Za-z_$][\\w$]*/y },\r\n { type: \"boolean\", pattern: KEYWORD(\"true|false|null|undefined\") },\r\n {\r\n type: \"keyword\",\r\n pattern: KEYWORD(\r\n \"const|let|var|function|return|if|else|for|while|do|switch|case|default|break|continue|\" +\r\n \"class|extends|super|new|this|typeof|instanceof|in|of|try|catch|finally|throw|async|await|\" +\r\n \"yield|import|export|from|as|void|delete|static|get|set|\" +\r\n \"interface|type|enum|implements|public|private|protected|readonly|namespace|declare|\" +\r\n \"abstract|is|keyof|infer|satisfies\"\r\n ),\r\n },\r\n {\r\n type: \"builtin\",\r\n pattern: KEYWORD(\r\n \"console|Math|JSON|Promise|Array|Object|Map|Set|WeakMap|WeakSet|String|Number|Boolean|Symbol|\" +\r\n \"Error|Date|RegExp|Reflect|Proxy|globalThis|Infinity|NaN|BigInt|fetch|localStorage|sessionStorage\"\r\n ),\r\n },\r\n { type: \"number\", pattern: /\\b0[xX][0-9a-fA-F]+\\b|\\b\\d+\\.?\\d*(?:[eE][+-]?\\d+)?n?\\b/y },\r\n { type: \"function\", pattern: /[A-Za-z_$][\\w$]*(?=\\s*\\()/y },\r\n { type: \"punctuation\", pattern: /[{}()[\\];,.:?]/y }\r\n );\r\n return grammar;\r\n}\r\n\r\nconst jsGrammar = buildJsGrammar(false);\r\nconst jsxGrammar = buildJsGrammar(true);\r\n\r\nconst jsonGrammar: Grammar = {\r\n rules: [\r\n { type: \"string\", pattern: /\"(?:[^\"\\\\\\n]|\\\\.)*\"/y },\r\n { type: \"boolean\", pattern: KEYWORD(\"true|false|null\") },\r\n { type: \"number\", pattern: /-?\\b\\d+\\.?\\d*(?:[eE][+-]?\\d+)?\\b/y },\r\n { type: \"punctuation\", pattern: /[{}[\\]:,]/y },\r\n ],\r\n};\r\n\r\nconst cssGrammar: Grammar = {\r\n rules: [\r\n { type: \"comment\", pattern: /\\/\\*[\\s\\S]*?\\*\\//y },\r\n { type: \"string\", pattern: /\"(?:[^\"\\\\\\n]|\\\\.)*\"|'(?:[^'\\\\\\n]|\\\\.)*'/y },\r\n { type: \"keyword\", pattern: /!important\\b/y },\r\n { type: \"atrule\", pattern: /@[a-zA-Z-]+/y },\r\n // Custom-property declarations/usage (--brand-color, var(--x)) —\r\n // checked before the generic hex-color/selector rules below.\r\n { type: \"variable\", pattern: /--[a-zA-Z_][\\w-]*/y },\r\n { type: \"function\", pattern: /\\b(?:var|calc|rgba?|hsla?|clamp|minmax|repeat|url|translate[XYZ]?|rotate[XYZ]?|scale[XYZ]?|linear-gradient|radial-gradient)(?=\\()/y },\r\n // Pseudo-classes/elements (:hover, ::before) before the plain \":\" punctuation.\r\n { type: \"pseudo\", pattern: /::?[a-zA-Z-]+/y },\r\n // Hex colors before the generic \"#name\" id-selector pattern below.\r\n { type: \"builtin\", pattern: /#[0-9a-fA-F]{3,8}\\b/y },\r\n { type: \"selector\", pattern: /[.#][a-zA-Z_-][\\w-]*/y },\r\n { type: \"property\", pattern: /[a-zA-Z-]+(?=\\s*:)/y },\r\n { type: \"number\", pattern: /-?\\d+\\.?\\d*(?:px|em|rem|%|vh|vw|s|ms|deg|fr)?\\b/y },\r\n { type: \"punctuation\", pattern: /[{}:;,()]/y },\r\n ],\r\n};\r\n\r\nconst htmlGrammar: Grammar = {\r\n rules: [\r\n { type: \"comment\", pattern: /<!--[\\s\\S]*?-->/y },\r\n { type: \"keyword\", pattern: /<!DOCTYPE[^>]*>/iy },\r\n { type: \"tag\", pattern: /<\\/?[a-zA-Z][\\w:-]*/y },\r\n { type: \"attrName\", pattern: /[a-zA-Z-]+(?=\\s*=)/y },\r\n { type: \"string\", pattern: /\"(?:[^\"\\\\\\n]|\\\\.)*\"|'(?:[^'\\\\\\n]|\\\\.)*'/y },\r\n { type: \"builtin\", pattern: /&[a-zA-Z#0-9]+;/y },\r\n { type: \"punctuation\", pattern: /[<>=/]/y },\r\n ],\r\n};\r\n\r\nconst bashGrammar: Grammar = {\r\n rules: [\r\n { type: \"comment\", pattern: /#[^\\n]*/y },\r\n { type: \"string\", pattern: /\"(?:[^\"\\\\\\n]|\\\\.)*\"|'[^'\\n]*'/y },\r\n { type: \"variable\", pattern: /\\$\\{?\\w+\\}?/y },\r\n {\r\n type: \"keyword\",\r\n pattern: KEYWORD(\r\n \"if|then|else|elif|fi|for|do|done|while|case|esac|function|return|exit|export|local|\" +\r\n \"echo|cd|sudo|npm|yarn|pnpm|git|mkdir|rm|cp|mv|cat|grep|sed|awk\"\r\n ),\r\n },\r\n { type: \"number\", pattern: /\\b\\d+\\b/y },\r\n { type: \"function\", pattern: /(?<=^|\\s)--?[a-zA-Z][\\w-]*/y },\r\n { type: \"punctuation\", pattern: /[|&;()]/y },\r\n ],\r\n};\r\n\r\nconst pythonGrammar: Grammar = {\r\n rules: [\r\n { type: \"comment\", pattern: /#[^\\n]*/y },\r\n { type: \"decorator\", pattern: /@[A-Za-z_][\\w.]*/y },\r\n {\r\n // f/r/b/u string prefixes (f\"...\", rb'...', etc.) before plain strings.\r\n type: \"string\",\r\n pattern: /[rRbBfFuU]{0,2}(?:\"\"\"[\\s\\S]*?\"\"\"|'''[\\s\\S]*?'''|\"(?:[^\"\\\\\\n]|\\\\.)*\"|'(?:[^'\\\\\\n]|\\\\.)*')/y,\r\n },\r\n { type: \"boolean\", pattern: KEYWORD(\"True|False|None\") },\r\n {\r\n type: \"keyword\",\r\n pattern: KEYWORD(\r\n \"def|return|if|elif|else|for|while|in|not|and|or|is|import|from|as|class|try|except|\" +\r\n \"finally|raise|with|lambda|yield|pass|break|continue|global|nonlocal|assert|del|\" +\r\n \"async|await|self\"\r\n ),\r\n },\r\n { type: \"builtin\", pattern: KEYWORD(\"print|len|range|str|int|float|list|dict|set|tuple|type|isinstance|super|open|input|enumerate|zip|map|filter|sorted|sum|min|max|any|all\") },\r\n { type: \"number\", pattern: /\\b\\d+\\.?\\d*\\b/y },\r\n { type: \"function\", pattern: /[A-Za-z_]\\w*(?=\\s*\\()/y },\r\n { type: \"punctuation\", pattern: /[:,.()[\\]{}]/y },\r\n ],\r\n};\r\n\r\nconst markdownGrammar: Grammar = {\r\n rules: [\r\n { type: \"comment\", pattern: /```[\\s\\S]*?```/y },\r\n { type: \"keyword\", pattern: /^#{1,6}[^\\n]*/my },\r\n { type: \"punctuation\", pattern: /^(?:-{3,}|\\*{3,}|_{3,})$/my },\r\n { type: \"function\", pattern: /`[^`\\n]+`/y },\r\n { type: \"builtin\", pattern: /\\*\\*[^*\\n]+\\*\\*/y },\r\n { type: \"variable\", pattern: /~~[^~\\n]+~~/y },\r\n { type: \"string\", pattern: /\\*[^*\\n]+\\*/y },\r\n { type: \"tag\", pattern: /!?\\[[^\\]\\n]*\\]\\([^)\\n]*\\)/y },\r\n { type: \"punctuation\", pattern: /^[-*+]\\s|^\\d+\\.\\s/my },\r\n ],\r\n};\r\n\r\nconst yamlGrammar: Grammar = {\r\n rules: [\r\n { type: \"comment\", pattern: /#[^\\n]*/y },\r\n { type: \"property\", pattern: /^[ \\t]*[\\w.-]+(?=\\s*:)/my },\r\n { type: \"string\", pattern: /\"(?:[^\"\\\\\\n]|\\\\.)*\"|'[^'\\n]*'/y },\r\n { type: \"boolean\", pattern: KEYWORD(\"true|false|null|yes|no|on|off|True|False|Null\") },\r\n { type: \"number\", pattern: /-?\\b\\d+\\.?\\d*\\b/y },\r\n { type: \"variable\", pattern: /[&*][\\w-]+/y },\r\n { type: \"punctuation\", pattern: /[:\\-[\\]{},]/y },\r\n ],\r\n};\r\n\r\nconst sqlGrammar: Grammar = {\r\n rules: [\r\n { type: \"comment\", pattern: /--[^\\n]*|\\/\\*[\\s\\S]*?\\*\\//y },\r\n { type: \"string\", pattern: /'(?:[^'\\\\\\n]|\\\\.)*'/y },\r\n {\r\n type: \"keyword\",\r\n pattern: new RegExp(\r\n \"\\\\b(?:SELECT|FROM|WHERE|INSERT|INTO|VALUES|UPDATE|SET|DELETE|CREATE|TABLE|ALTER|DROP|\" +\r\n \"JOIN|INNER|LEFT|RIGHT|OUTER|ON|GROUP|BY|ORDER|HAVING|LIMIT|OFFSET|AS|AND|OR|NOT|NULL|\" +\r\n \"IS|IN|LIKE|BETWEEN|DISTINCT|UNION|ALL|EXISTS|CASE|WHEN|THEN|ELSE|END|PRIMARY|KEY|\" +\r\n \"FOREIGN|REFERENCES|DEFAULT|CONSTRAINT|INDEX|VIEW|WITH)\\\\b\",\r\n \"iy\"\r\n ),\r\n },\r\n { type: \"number\", pattern: /\\b\\d+\\.?\\d*\\b/y },\r\n { type: \"function\", pattern: /\\b[A-Za-z_]\\w*(?=\\s*\\()/y },\r\n { type: \"punctuation\", pattern: /[(),;.*=<>!]/y },\r\n ],\r\n};\r\n\r\nconst GRAMMARS: Record<string, Grammar> = {\r\n javascript: jsGrammar,\r\n typescript: jsGrammar,\r\n jsx: jsxGrammar,\r\n tsx: jsxGrammar,\r\n json: jsonGrammar,\r\n css: cssGrammar,\r\n html: htmlGrammar,\r\n bash: bashGrammar,\r\n python: pythonGrammar,\r\n markdown: markdownGrammar,\r\n yaml: yamlGrammar,\r\n sql: sqlGrammar,\r\n};\r\n\r\n/** Aliases → canonical grammar key. Anything not listed here (and not\r\n * a canonical key itself) falls back to plain, unhighlighted text. */\r\nconst ALIASES: Record<string, string> = {\r\n js: \"javascript\",\r\n mjs: \"javascript\",\r\n cjs: \"javascript\",\r\n ts: \"typescript\",\r\n sh: \"bash\",\r\n shell: \"bash\",\r\n zsh: \"bash\",\r\n py: \"python\",\r\n md: \"markdown\",\r\n htm: \"html\",\r\n yml: \"yaml\",\r\n};\r\n\r\n/** Human-readable labels for CodeBlock's default header — overridable via the `filename` prop. */\r\nexport const LANGUAGE_LABELS: Record<string, string> = {\r\n javascript: \"JavaScript\",\r\n typescript: \"TypeScript\",\r\n jsx: \"JSX\",\r\n tsx: \"TSX\",\r\n json: \"JSON\",\r\n css: \"CSS\",\r\n html: \"HTML\",\r\n bash: \"Shell\",\r\n python: \"Python\",\r\n markdown: \"Markdown\",\r\n yaml: \"YAML\",\r\n sql: \"SQL\",\r\n plaintext: \"Plain text\",\r\n};\r\n\r\nfunction normalizeLanguage(language: string): string {\r\n const lower = language.trim().toLowerCase();\r\n return ALIASES[lower] ?? lower;\r\n}\r\n\r\n/**\r\n * The built-in, zero-dependency highlighter. Unknown languages (and\r\n * any grammar that somehow throws on malformed/adversarial input)\r\n * degrade to a single unhighlighted \"plain\" token per line rather\r\n * than surfacing an error — see this module's header for why that's\r\n * the deliberate scope boundary here rather than expanding grammar\r\n * coverage indefinitely.\r\n */\r\nexport const nuvoxHighlight: HighlightEngine = (code, language) => {\r\n const key = normalizeLanguage(language ?? \"plaintext\");\r\n const grammar = GRAMMARS[key];\r\n if (!grammar) return splitIntoLines([{ type: \"plain\", content: code }]);\r\n try {\r\n return splitIntoLines(tokenizeFlat(code, grammar));\r\n } catch {\r\n return splitIntoLines([{ type: \"plain\", content: code }]);\r\n }\r\n};\r\n\r\nexport { normalizeLanguage };\r\n","// ============================================================\r\n// RICH-TEXT — the largest component in the library by design\r\n// surface, scoped down deliberately (see project chat history this\r\n// was designed in). This is NOT a ProseMirror-style document model:\r\n// no schema, no plugin system, no collaborative editing. It's a\r\n// contentEditable + Selection/Range layer covering the common\r\n// denominator of every popular WYSIWYG toolbar (bold/italic/\r\n// underline/strike/code, headings, blockquote, lists, links) —\r\n// closer to Quill/Mantine-RTE's ambition than to Tiptap/ProseMirror's.\r\n//\r\n// Deliberately NOT using document.execCommand — deprecated and\r\n// inconsistent across browsers. Every mutation below is direct\r\n// Selection/Range/DOM manipulation instead, which is why these\r\n// functions can live here (no React/JSX) rather than in the .tsx\r\n// layer: they only need web-platform DOM APIs, the same class of\r\n// dependency core/calendar.ts already takes on Intl/Date. A future\r\n// Vue/Svelte adapter reuses every function in this file unchanged.\r\n//\r\n// Known, deliberate v1 scope limits (documented rather than silently\r\n// broken):\r\n// - List creation/removal operates one block at a time — selecting\r\n// three paragraphs and listifying them doesn't yet merge them\r\n// into a single <ul>.\r\n// - Nested/overlapping marks in complex selections aren't\r\n// guaranteed to unwrap perfectly; the common case (a selection\r\n// fully inside one mark, or none) is what's tested and relied on.\r\n// - \"Type-then-format\" (toggle a mark with the cursor collapsed, so\r\n// it applies to what you type NEXT) works via an invisible\r\n// zero-width-space caret marker inserted at the cursor — the same\r\n// technique small hand-rolled contentEditable editors commonly\r\n// use in place of the browser's native (execCommand-only) typing\r\n// state. If a mark is toggled on and the user never types\r\n// anything before moving on, an empty marked element with just\r\n// that invisible character can linger in the DOM — harmless\r\n// (invisible, produces no visible artifact) but real markup\r\n// clutter if untouched, which is why RichTextEditor sweeps them\r\n// on blur via `stripEmptyMarkers` before the value is read.\r\n// ============================================================\r\n\r\nimport type { Size } from \"./types\";\r\n\r\n// ---- command model ------------------------------------------------\r\n\r\nexport type RichTextMark = \"bold\" | \"italic\" | \"underline\" | \"strike\" | \"code\";\r\nexport type RichTextBlock = \"paragraph\" | \"h1\" | \"h2\" | \"h3\" | \"blockquote\" | \"bulletList\" | \"orderedList\";\r\nexport type RichTextCommand =\r\n | RichTextMark\r\n | RichTextBlock\r\n | \"link\"\r\n | \"unlink\"\r\n | \"textColor\"\r\n | \"clearTextColor\"\r\n | \"highlight\"\r\n | \"clearHighlight\";\r\n\r\nexport const MARK_TAGS: Record<RichTextMark, string> = {\r\n bold: \"STRONG\",\r\n italic: \"EM\",\r\n underline: \"U\",\r\n strike: \"S\",\r\n code: \"CODE\",\r\n};\r\n\r\nexport const BLOCK_TAGS: Record<RichTextBlock, string> = {\r\n paragraph: \"P\",\r\n h1: \"H1\",\r\n h2: \"H2\",\r\n h3: \"H3\",\r\n blockquote: \"BLOCKQUOTE\",\r\n bulletList: \"UL\",\r\n orderedList: \"OL\",\r\n};\r\n\r\nconst BLOCK_TAG_NAMES = new Set([\"P\", \"H1\", \"H2\", \"H3\", \"BLOCKQUOTE\", \"DIV\"]);\r\n\r\nexport function isMark(command: RichTextCommand): command is RichTextMark {\r\n return command in MARK_TAGS;\r\n}\r\n\r\nexport function isBlock(command: RichTextCommand): command is RichTextBlock {\r\n return command in BLOCK_TAGS;\r\n}\r\n\r\n/**\r\n * The interface a rendering engine must satisfy to plug into\r\n * `<RichTextEditor engine={...}>` in place of the built-in\r\n * contentEditable engine — the escape hatch described in the\r\n * project's design discussion. A consumer needing full-schema\r\n * editing can hand-write a thin adapter over Tiptap/ProseMirror\r\n * satisfying this shape; Nuvox itself never imports either.\r\n */\r\nexport interface EditorEngine {\r\n getHTML(): string;\r\n setHTML(html: string): void;\r\n exec(command: RichTextCommand, value?: string): void;\r\n isActive(command: RichTextCommand): boolean;\r\n canUndo(): boolean;\r\n canRedo(): boolean;\r\n undo(): void;\r\n redo(): void;\r\n}\r\n\r\n/** Given ancestor tag names (any order) around a selection, is `command` currently active? */\r\nexport function isCommandActive(ancestorTags: string[], command: RichTextCommand): boolean {\r\n if (command === \"link\") return ancestorTags.includes(\"A\");\r\n if (command === \"unlink\") return false;\r\n if (isMark(command)) return ancestorTags.includes(MARK_TAGS[command]);\r\n if (isBlock(command)) return ancestorTags.includes(BLOCK_TAGS[command]);\r\n return false;\r\n}\r\n\r\n// ---- DOM command execution -----------------------------------------\r\n// Every function below takes `root` (the contentEditable element) as\r\n// its scope boundary, so ancestor walks never escape into the rest\r\n// of the page.\r\n\r\nfunction closestWithinRoot(node: Node | null, tag: string, root: HTMLElement): HTMLElement | null {\r\n let n: Node | null = node;\r\n while (n && n !== root) {\r\n if (n.nodeType === Node.ELEMENT_NODE && (n as Element).tagName === tag) return n as HTMLElement;\r\n n = n.parentNode;\r\n }\r\n return null;\r\n}\r\n\r\nfunction closestBlock(node: Node | null, root: HTMLElement): HTMLElement | null {\r\n let n: Node | null = node && node.nodeType === Node.ELEMENT_NODE ? node : node?.parentNode ?? null;\r\n while (n && n !== root) {\r\n if (n.nodeType === Node.ELEMENT_NODE && BLOCK_TAG_NAMES.has((n as Element).tagName)) return n as HTMLElement;\r\n n = n.parentNode;\r\n }\r\n return null;\r\n}\r\n\r\nfunction unwrapElement(el: HTMLElement): void {\r\n const parent = el.parentNode;\r\n if (!parent) return;\r\n while (el.firstChild) parent.insertBefore(el.firstChild, el);\r\n parent.removeChild(el);\r\n}\r\n\r\nfunction replaceTag(el: HTMLElement, tag: string): HTMLElement {\r\n const next = document.createElement(tag);\r\n while (el.firstChild) next.appendChild(el.firstChild);\r\n el.parentNode?.replaceChild(next, el);\r\n return next;\r\n}\r\n\r\nfunction selectNodeContents(node: Node, collapseToEnd = false): void {\r\n const sel = window.getSelection();\r\n if (!sel) return;\r\n const range = document.createRange();\r\n range.selectNodeContents(node);\r\n if (collapseToEnd) range.collapse(false);\r\n sel.removeAllRanges();\r\n sel.addRange(range);\r\n}\r\n\r\nconst ZERO_WIDTH_SPACE = \"\\u200B\";\r\n\r\n/** Selects (collapsed) right after position `offset` inside an already-attached marker text node. */\r\nfunction selectAtMarker(sel: Selection, marker: Text, offset: number): void {\r\n const range = document.createRange();\r\n range.setStart(marker, offset);\r\n range.collapse(true);\r\n sel.removeAllRanges();\r\n sel.addRange(range);\r\n}\r\n\r\n/**\r\n * Collapsed-selection version of a mark toggle: with no text\r\n * selected, \"toggling bold\" can't wrap anything — instead it starts\r\n * (or exits) a marked element at the cursor by inserting an invisible\r\n * zero-width-space marker inside (or right after) it and placing the\r\n * caret at that marker, so the NEXT characters the user types land\r\n * inside (or outside) the mark naturally. This is the same technique\r\n * small hand-rolled contentEditable editors use in place of the\r\n * browser's native (execCommand-only) typing state.\r\n */\r\nfunction togglePendingMark(root: HTMLElement, tag: string): void {\r\n const sel = window.getSelection();\r\n if (!sel || sel.rangeCount === 0) return;\r\n const range = sel.getRangeAt(0);\r\n\r\n const existing = closestWithinRoot(range.startContainer, tag, root);\r\n if (existing) {\r\n // Already typing inside this mark — exit it.\r\n const marker = document.createTextNode(ZERO_WIDTH_SPACE);\r\n existing.parentNode?.insertBefore(marker, existing.nextSibling);\r\n selectAtMarker(sel, marker, 1);\r\n return;\r\n }\r\n\r\n const wrapper = document.createElement(tag);\r\n const marker = document.createTextNode(ZERO_WIDTH_SPACE);\r\n wrapper.appendChild(marker);\r\n range.insertNode(wrapper);\r\n selectAtMarker(sel, marker, 1);\r\n}\r\n\r\nfunction toggleMark(root: HTMLElement, tag: string): void {\r\n const sel = window.getSelection();\r\n if (!sel || sel.rangeCount === 0) return;\r\n if (sel.isCollapsed) {\r\n togglePendingMark(root, tag);\r\n return;\r\n }\r\n const range = sel.getRangeAt(0);\r\n\r\n const existing = closestWithinRoot(range.commonAncestorContainer, tag, root);\r\n if (existing) {\r\n unwrapElement(existing);\r\n return;\r\n }\r\n\r\n const wrapper = document.createElement(tag);\r\n wrapper.appendChild(range.extractContents());\r\n range.insertNode(wrapper);\r\n selectNodeContents(wrapper);\r\n}\r\n\r\n/**\r\n * Sweeps every mark/color element under `root` whose entire content\r\n * is just the invisible caret marker character AND that no longer\r\n * contains the live caret — i.e. the user toggled a mark on and then\r\n * moved on without typing into it. Called on blur, before the value\r\n * is read/persisted, so `getHTML()`/`onChange` never surface the\r\n * marker as visible clutter. Returns whether anything was removed\r\n * (so the caller knows whether to re-commit the value).\r\n */\r\n/**\r\n * Re-applies a previously-captured Range to the document selection.\r\n * Needed because opening a Popover (Link, text color, highlight)\r\n * moves focus into its content for accessibility (Rule 12's shared\r\n * `useOverlayBehavior` autoFocus) — which resets `window.getSelection()`\r\n * away from the contentEditable region. Toolbar controls that open a\r\n * popover capture the range on trigger click (before focus moves) and\r\n * restore it here immediately before calling `exec`, so the command\r\n * still applies to what the user actually selected instead of\r\n * whatever collapsed selection is left after the popover took focus.\r\n */\r\nexport function restoreSelection(range: Range): void {\r\n const sel = window.getSelection();\r\n if (!sel) return;\r\n sel.removeAllRanges();\r\n sel.addRange(range);\r\n}\r\n\r\nexport function stripEmptyMarkers(root: HTMLElement): boolean {\r\n // Keyed off real DOM focus, not window.getSelection() — blurring an\r\n // element does not reliably clear/move the document Selection (in\r\n // most browsers it just keeps pointing at the old range), so using\r\n // the stale selection as the \"still being edited\" signal would mean\r\n // a marker never gets swept on an ordinary blur, which is the one\r\n // place this is actually meant to run.\r\n const isRootFocused = typeof document !== \"undefined\" && root.contains(document.activeElement);\r\n const sel = isRootFocused ? window.getSelection() : null;\r\n const caretNode = sel && sel.rangeCount > 0 ? sel.getRangeAt(0).startContainer : null;\r\n const candidateTags = [...Object.values(MARK_TAGS), \"SPAN\"];\r\n let changed = false;\r\n for (const tag of candidateTags) {\r\n for (const el of Array.from(root.getElementsByTagName(tag))) {\r\n if (el.textContent === ZERO_WIDTH_SPACE && !(caretNode && el.contains(caretNode))) {\r\n el.remove();\r\n changed = true;\r\n }\r\n }\r\n }\r\n return changed;\r\n}\r\n\r\nfunction setBlockTag(root: HTMLElement, targetTag: string): void {\r\n const sel = window.getSelection();\r\n if (!sel || sel.rangeCount === 0) return;\r\n const block = closestBlock(sel.getRangeAt(0).startContainer, root);\r\n if (!block) return;\r\n const next = replaceTag(block, block.tagName === targetTag ? \"P\" : targetTag);\r\n selectNodeContents(next, true);\r\n}\r\n\r\nfunction toggleList(root: HTMLElement, listTag: \"UL\" | \"OL\"): void {\r\n const sel = window.getSelection();\r\n if (!sel || sel.rangeCount === 0) return;\r\n const range = sel.getRangeAt(0);\r\n\r\n const li = closestWithinRoot(range.startContainer, \"LI\", root);\r\n if (li && li.parentElement?.tagName === listTag) {\r\n const list = li.parentElement;\r\n const p = document.createElement(\"P\");\r\n while (li.firstChild) p.appendChild(li.firstChild);\r\n list.parentNode?.replaceChild(p, list);\r\n selectNodeContents(p, true);\r\n return;\r\n }\r\n\r\n const block = closestBlock(range.startContainer, root);\r\n if (!block) return;\r\n const list = document.createElement(listTag);\r\n const item = document.createElement(\"LI\");\r\n while (block.firstChild) item.appendChild(block.firstChild);\r\n list.appendChild(item);\r\n block.parentNode?.replaceChild(list, block);\r\n selectNodeContents(item, true);\r\n}\r\n\r\nfunction setLink(root: HTMLElement, url: string): void {\r\n const sel = window.getSelection();\r\n if (!sel || sel.rangeCount === 0 || sel.isCollapsed) return;\r\n const range = sel.getRangeAt(0);\r\n const existing = closestWithinRoot(range.commonAncestorContainer, \"A\", root);\r\n if (existing) {\r\n existing.setAttribute(\"href\", url);\r\n return;\r\n }\r\n const a = document.createElement(\"A\");\r\n a.setAttribute(\"href\", url);\r\n a.setAttribute(\"rel\", \"noopener noreferrer\");\r\n a.appendChild(range.extractContents());\r\n range.insertNode(a);\r\n selectNodeContents(a);\r\n}\r\n\r\nfunction unlink(root: HTMLElement): void {\r\n const sel = window.getSelection();\r\n if (!sel || sel.rangeCount === 0) return;\r\n const a = closestWithinRoot(sel.getRangeAt(0).commonAncestorContainer, \"A\", root);\r\n if (a) unwrapElement(a);\r\n}\r\n\r\n// ---- text color / highlight ---------------------------------------\r\n// Unlike marks (a fixed tag toggled on/off), a color is a value —\r\n// applying it means \"set/replace\", and \"active\" isn't a boolean the\r\n// same way bold is. Implemented as a <span style=\"...\"> rather than a\r\n// dedicated tag; re-coloring an already-colored selection updates the\r\n// existing span's style in place instead of nesting spans endlessly.\r\n\r\nfunction findStyledSpanWithinRoot(node: Node | null, property: string, root: HTMLElement): HTMLElement | null {\r\n let n: Node | null = node;\r\n while (n && n !== root) {\r\n if (n.nodeType === Node.ELEMENT_NODE) {\r\n const el = n as HTMLElement;\r\n if (el.tagName === \"SPAN\" && el.style.getPropertyValue(property)) return el;\r\n }\r\n n = n.parentNode;\r\n }\r\n return null;\r\n}\r\n\r\nfunction setInlineStyle(root: HTMLElement, property: \"color\" | \"background-color\", value: string): void {\r\n const sel = window.getSelection();\r\n if (!sel || sel.rangeCount === 0) return;\r\n const range = sel.getRangeAt(0);\r\n\r\n if (sel.isCollapsed) {\r\n const existing = findStyledSpanWithinRoot(range.startContainer, property, root);\r\n if (existing) {\r\n existing.style.setProperty(property, value);\r\n return;\r\n }\r\n const wrapper = document.createElement(\"span\");\r\n wrapper.style.setProperty(property, value);\r\n const marker = document.createTextNode(ZERO_WIDTH_SPACE);\r\n wrapper.appendChild(marker);\r\n range.insertNode(wrapper);\r\n selectAtMarker(sel, marker, 1);\r\n return;\r\n }\r\n\r\n const existing = findStyledSpanWithinRoot(range.commonAncestorContainer, property, root);\r\n if (existing) {\r\n existing.style.setProperty(property, value);\r\n return;\r\n }\r\n const wrapper = document.createElement(\"span\");\r\n wrapper.style.setProperty(property, value);\r\n wrapper.appendChild(range.extractContents());\r\n range.insertNode(wrapper);\r\n selectNodeContents(wrapper);\r\n}\r\n\r\nfunction clearInlineStyle(root: HTMLElement, property: \"color\" | \"background-color\"): void {\r\n const sel = window.getSelection();\r\n if (!sel || sel.rangeCount === 0) return;\r\n const el = findStyledSpanWithinRoot(sel.getRangeAt(0).commonAncestorContainer, property, root);\r\n if (!el) return;\r\n el.style.removeProperty(property);\r\n if (!el.getAttribute(\"style\")) unwrapElement(el);\r\n}\r\n\r\n/**\r\n * The single dispatch point the React layer's `exec()` calls into.\r\n * Mutates the DOM under `root` directly and leaves selection in a\r\n * sensible place; does NOT itself read the result back out (the\r\n * caller reads `root.innerHTML` afterward) or manage history (the\r\n * caller decides when a checkpoint is worth recording).\r\n */\r\nexport function execRichTextCommand(root: HTMLElement, command: RichTextCommand, value?: string): void {\r\n if (isMark(command)) toggleMark(root, MARK_TAGS[command]);\r\n else if (command === \"bulletList\") toggleList(root, \"UL\");\r\n else if (command === \"orderedList\") toggleList(root, \"OL\");\r\n else if (isBlock(command)) setBlockTag(root, BLOCK_TAGS[command]);\r\n else if (command === \"link\" && value) setLink(root, value);\r\n else if (command === \"unlink\") unlink(root);\r\n else if (command === \"textColor\" && value) setInlineStyle(root, \"color\", value);\r\n else if (command === \"clearTextColor\") clearInlineStyle(root, \"color\");\r\n else if (command === \"highlight\" && value) setInlineStyle(root, \"background-color\", value);\r\n else if (command === \"clearHighlight\") clearInlineStyle(root, \"background-color\");\r\n}\r\n\r\n/**\r\n * Walks the current selection's ancestor chain (bounded by `root`)\r\n * and returns every command that's currently active — what the\r\n * toolbar buttons should render as \"pressed\". Returns an empty set\r\n * outside a browser (SSR) rather than throwing.\r\n */\r\nexport function getActiveCommands(root: HTMLElement): Set<RichTextCommand> {\r\n const result = new Set<RichTextCommand>();\r\n if (typeof window === \"undefined\") return result;\r\n const sel = window.getSelection();\r\n if (!sel || sel.rangeCount === 0) return result;\r\n\r\n const tags: string[] = [];\r\n let node: Node | null = sel.anchorNode;\r\n while (node && node !== root) {\r\n if (node.nodeType === Node.ELEMENT_NODE) tags.push((node as Element).tagName);\r\n node = node.parentNode;\r\n }\r\n\r\n const commands: RichTextCommand[] = [...(Object.keys(MARK_TAGS) as RichTextMark[]), ...(Object.keys(BLOCK_TAGS) as RichTextBlock[]), \"link\"];\r\n for (const command of commands) {\r\n if (isCommandActive(tags, command)) result.add(command);\r\n }\r\n return result;\r\n}\r\n\r\n// ---- sanitize --------------------------------------------------\r\n// Applied to any HTML entering the editor from OUTSIDE user typing:\r\n// pasted content, and an externally-supplied `value`/`defaultValue`.\r\n// Content the user types via the toolbar/keyboard is already\r\n// constructed from this module's own allow-listed tags, so it never\r\n// needs to pass back through this.\r\n\r\nconst ALLOWED_TAGS = new Set([\r\n \"P\", \"BR\", \"STRONG\", \"B\", \"EM\", \"I\", \"U\", \"S\", \"STRIKE\", \"CODE\", \"A\",\r\n \"H1\", \"H2\", \"H3\", \"BLOCKQUOTE\", \"UL\", \"OL\", \"LI\", \"SPAN\",\r\n]);\r\n\r\nconst ALLOWED_ATTRS: Record<string, string[]> = {\r\n A: [\"href\"],\r\n SPAN: [\"style\"],\r\n};\r\n\r\n/** style=\"\" is only ever produced by RichTextEditor's own text-color/highlight\r\n * commands, both of which set exactly one of these two properties to a\r\n * color value — so incoming style attributes (pasted content, an\r\n * externally-supplied value) are held to that same shape rather than\r\n * passed through verbatim, which would otherwise be an open styling\r\n * (and, for older/legacy CSS features, injection) surface. */\r\nconst ALLOWED_STYLE_PROPS = new Set([\"color\", \"background-color\"]);\r\nconst SAFE_COLOR_VALUE = /^(#[0-9a-fA-F]{3,8}|rgb\\(\\s*\\d+\\s*,\\s*\\d+\\s*,\\s*\\d+\\s*\\)|rgba\\(\\s*\\d+\\s*,\\s*\\d+\\s*,\\s*\\d+\\s*,\\s*[\\d.]+\\s*\\)|hsl\\([^();]*\\)|hsla\\([^();]*\\)|[a-zA-Z]+)$/;\r\n\r\nfunction sanitizeStyleAttr(value: string): string {\r\n const out: string[] = [];\r\n for (const decl of value.split(\";\")) {\r\n const colon = decl.indexOf(\":\");\r\n if (colon === -1) continue;\r\n const prop = decl.slice(0, colon).trim().toLowerCase();\r\n const val = decl.slice(colon + 1).trim();\r\n if (ALLOWED_STYLE_PROPS.has(prop) && SAFE_COLOR_VALUE.test(val)) {\r\n out.push(`${prop}: ${val}`);\r\n }\r\n }\r\n return out.join(\"; \");\r\n}\r\n\r\n/** Tags whose entire contents are dropped (not unwrapped) — never worth keeping the text of. */\r\nconst STRIP_ENTIRELY = new Set([\"SCRIPT\", \"STYLE\", \"IFRAME\", \"OBJECT\", \"EMBED\"]);\r\n\r\nfunction sanitizeElement(el: Element): void {\r\n for (const child of Array.from(el.childNodes)) {\r\n if (child.nodeType === Node.TEXT_NODE) continue;\r\n\r\n if (child.nodeType !== Node.ELEMENT_NODE) {\r\n child.parentNode?.removeChild(child);\r\n continue;\r\n }\r\n\r\n const childEl = child as Element;\r\n\r\n if (STRIP_ENTIRELY.has(childEl.tagName)) {\r\n childEl.parentNode?.removeChild(childEl);\r\n continue;\r\n }\r\n\r\n if (!ALLOWED_TAGS.has(childEl.tagName)) {\r\n // Clean its subtree first, THEN unwrap — otherwise disallowed\r\n // tags nested inside a disallowed tag never get visited.\r\n sanitizeElement(childEl);\r\n const parent = childEl.parentNode;\r\n while (childEl.firstChild) parent?.insertBefore(childEl.firstChild, childEl);\r\n parent?.removeChild(childEl);\r\n continue;\r\n }\r\n\r\n const allowedAttrs = ALLOWED_ATTRS[childEl.tagName] ?? [];\r\n for (const attr of Array.from(childEl.attributes)) {\r\n const name = attr.name.toLowerCase();\r\n if (!allowedAttrs.includes(name)) {\r\n childEl.removeAttribute(attr.name);\r\n continue;\r\n }\r\n if (name === \"style\") {\r\n const cleaned = sanitizeStyleAttr(attr.value);\r\n if (cleaned) childEl.setAttribute(\"style\", cleaned);\r\n else childEl.removeAttribute(\"style\");\r\n }\r\n }\r\n if (childEl.tagName === \"A\") {\r\n const href = childEl.getAttribute(\"href\") ?? \"\";\r\n const safe = /^https?:\\/\\//i.test(href) || href.startsWith(\"/\") || href.startsWith(\"#\") || href.startsWith(\"mailto:\");\r\n if (!safe) childEl.removeAttribute(\"href\");\r\n childEl.setAttribute(\"rel\", \"noopener noreferrer\");\r\n childEl.setAttribute(\"target\", \"_blank\");\r\n }\r\n\r\n sanitizeElement(childEl);\r\n }\r\n}\r\n\r\n/**\r\n * Strips any tag/attribute not on the allow-list. Uses DOMParser — a\r\n * web-platform API, not a React dependency; see this module's header\r\n * for why that's still \"framework-agnostic core\" the same way\r\n * core/calendar.ts's use of Intl/Date is. Never throws; returns an\r\n * empty string if DOMParser isn't available rather than passing\r\n * unsanitized markup through.\r\n */\r\nexport function sanitizeHtml(html: string): string {\r\n if (typeof DOMParser === \"undefined\") return \"\";\r\n const doc = new DOMParser().parseFromString(html, \"text/html\");\r\n sanitizeElement(doc.body);\r\n return doc.body.innerHTML;\r\n}\r\n\r\n// ---- undo/redo history ------------------------------------------\r\n\r\nexport interface RichTextHistory {\r\n past: string[];\r\n future: string[];\r\n}\r\n\r\nconst HISTORY_LIMIT = 100;\r\n\r\nexport function createHistory(): RichTextHistory {\r\n return { past: [], future: [] };\r\n}\r\n\r\n/** Records `previousValue` as a checkpoint to return to. Caller decides checkpoint granularity (e.g. word boundaries). */\r\nexport function pushHistory(history: RichTextHistory, previousValue: string): RichTextHistory {\r\n return { past: [...history.past, previousValue].slice(-HISTORY_LIMIT), future: [] };\r\n}\r\n\r\nexport function undoHistory(\r\n history: RichTextHistory,\r\n currentValue: string\r\n): { history: RichTextHistory; value: string } | null {\r\n if (history.past.length === 0) return null;\r\n const value = history.past[history.past.length - 1];\r\n return {\r\n history: { past: history.past.slice(0, -1), future: [currentValue, ...history.future] },\r\n value,\r\n };\r\n}\r\n\r\nexport function redoHistory(\r\n history: RichTextHistory,\r\n currentValue: string\r\n): { history: RichTextHistory; value: string } | null {\r\n if (history.future.length === 0) return null;\r\n const value = history.future[0];\r\n return {\r\n history: { past: [...history.past, currentValue], future: history.future.slice(1) },\r\n value,\r\n };\r\n}\r\n\r\n// ---- props/state (Rule 1/3 conventions) --------------------------\r\n\r\nexport interface RichTextEditorCoreProps {\r\n disabled?: boolean;\r\n readOnly?: boolean;\r\n placeholder?: string;\r\n size?: Size;\r\n}\r\n\r\nexport interface RichTextEditorState {\r\n isDisabled: boolean;\r\n isReadOnly: boolean;\r\n placeholder: string;\r\n size: Size;\r\n}\r\n\r\nexport function resolveRichTextEditorState(props: RichTextEditorCoreProps): RichTextEditorState {\r\n return {\r\n isDisabled: props.disabled ?? false,\r\n isReadOnly: props.readOnly ?? false,\r\n placeholder: props.placeholder ?? \"\",\r\n size: props.size ?? \"md\",\r\n };\r\n}\r\n\r\nexport function getRichTextEditorClasses(state: RichTextEditorState): string {\r\n return [\r\n \"nx-rich-text\",\r\n `nx-rich-text--${state.size}`,\r\n state.isDisabled && \"nx-rich-text--disabled\",\r\n state.isReadOnly && \"nx-rich-text--readonly\",\r\n ]\r\n .filter(Boolean)\r\n .join(\" \");\r\n}\r\n","// ============================================================\n// GALLERY — pure core (Category F: complex/stateful).\n//\n// Decomposed on paper first, per BUILDING-COMPONENTS.md's own\n// guidance for this category: Gallery itself only ever resolves\n// layout state and (for \"justified\") computes row geometry — it\n// does not render an <img> or own loading/error state at all,\n// which belongs to Image (Rule 12: the React layer composes the\n// real Image component per thumbnail rather than a second\n// hand-rolled one).\n//\n// Three layouts, each a genuinely different CSS/JS strategy rather\n// than the same grid with a class-name swap:\n// - \"grid\": CSS Grid, N equal-size columns.\n// - \"masonry\": CSS multi-column layout (`columns`), a\n// zero-JS way to get the classic staggered-height\n// look — each item's own aspect ratio decides its\n// height, the browser packs columns itself.\n// - \"justified\": Flickr-style row packing — every row is scaled to\n// fill the container's full width at a shared row\n// height, computed here from each image's aspect\n// ratio. The one layout that genuinely needs pure,\n// unit-tested math rather than a CSS one-liner, the\n// same reasoning core/floating.ts and\n// core/carousel.ts's index math got pulled out of\n// their React layers.\n// ============================================================\n\nexport type GalleryLayout = \"grid\" | \"masonry\" | \"justified\";\n\nexport interface GalleryCoreProps {\n layout?: GalleryLayout;\n /** Column count for \"grid\" and \"masonry\". Default 3. Ignored by \"justified\", which fills rows by width instead. */\n columns?: number;\n /** CSS gap value between items. Default undefined (falls back to the CSS file's own token default). */\n gap?: string;\n /** Target row height in px for \"justified\". Default 220. */\n targetRowHeight?: number;\n}\n\nexport interface GalleryState {\n layout: GalleryLayout;\n columns: number;\n gap: string | undefined;\n targetRowHeight: number;\n}\n\nexport function resolveGalleryState(props: GalleryCoreProps): GalleryState {\n return {\n layout: props.layout ?? \"grid\",\n columns: props.columns ?? 3,\n gap: props.gap,\n targetRowHeight: props.targetRowHeight ?? 220,\n };\n}\n\nexport function getGalleryClasses(state: GalleryState, className?: string): string {\n return [\"nx-gallery\", `nx-gallery--${state.layout}`, className].filter(Boolean).join(\" \");\n}\n\nexport function getGalleryStyle(state: GalleryState): React.CSSProperties {\n const style: Record<string, string> = {};\n if (state.gap) style.gap = state.gap;\n if (state.layout === \"grid\" || state.layout === \"masonry\") {\n style[\"--nx-gallery-columns\"] = String(state.columns);\n }\n return style as React.CSSProperties;\n}\n\n/** An item with no known dimensions is treated as a 1:1 square — a reasonable default that keeps the row-packing math well-defined rather than throwing on missing data. */\nexport function aspectRatioFromDims(width?: number, height?: number): number {\n if (!width || !height || width <= 0 || height <= 0) return 1;\n return width / height;\n}\n\nexport interface JustifiedRow {\n /** Indices into the original items array, in order. */\n indices: number[];\n /** Shared row height in px, after scaling to fill the container width. */\n height: number;\n /** Each item's rendered width in px, same order/length as `indices`. */\n widths: number[];\n}\n\n/**\n * Classic row-packing: accumulate items into a row at `targetRowHeight`\n * until the row's natural width (plus gaps) reaches the container\n * width, then scale the whole row (height and every item's width) by\n * the ratio needed to make it land exactly on `containerWidth`. The\n * last row is left at `targetRowHeight`, unscaled, unless it would\n * need to be scaled DOWN (i.e. it already overflows) — a short final\n * row of 1-2 images stretched to fill the full width looks broken,\n * which is the standard justified-gallery convention (Flickr does\n * the same).\n */\nexport function computeJustifiedRows(aspectRatios: number[], containerWidth: number, targetRowHeight: number, gap: number): JustifiedRow[] {\n if (aspectRatios.length === 0 || containerWidth <= 0 || targetRowHeight <= 0) return [];\n\n const rows: JustifiedRow[] = [];\n let rowIndices: number[] = [];\n let rowNaturalWidths: number[] = [];\n let rowWidthSum = 0;\n\n function flushRow(scale: boolean) {\n if (rowIndices.length === 0) return;\n const gapsWidth = gap * (rowIndices.length - 1);\n const availableWidth = containerWidth - gapsWidth;\n const naturalTotal = rowWidthSum;\n const scaleFactor = scale ? availableWidth / naturalTotal : 1;\n rows.push({\n indices: rowIndices,\n height: targetRowHeight * scaleFactor,\n widths: rowNaturalWidths.map((w) => w * scaleFactor),\n });\n rowIndices = [];\n rowNaturalWidths = [];\n rowWidthSum = 0;\n }\n\n for (let i = 0; i < aspectRatios.length; i++) {\n const naturalWidth = aspectRatios[i] * targetRowHeight;\n const gapsWidth = gap * rowIndices.length; // one more item -> one more internal gap\n if (rowIndices.length > 0 && rowWidthSum + naturalWidth + gapsWidth > containerWidth) {\n flushRow(true);\n }\n rowIndices.push(i);\n rowNaturalWidths.push(naturalWidth);\n rowWidthSum += naturalWidth;\n }\n\n // Final row: only scale up-to-fill if it already overflows the\n // container (rare, e.g. one very wide panorama) — otherwise leave\n // it at the natural target height rather than stretching a short\n // trailing row to full width.\n const finalGapsWidth = gap * (rowIndices.length - 1);\n const overflows = rowWidthSum + finalGapsWidth > containerWidth;\n flushRow(overflows);\n\n return rows;\n}\n","import type { Size } from \"./types\";\n\n// ============================================================\n// SLIDER — pure core (Category B: form/input primitive).\n//\n// One component handles both single-value and range (two-thumb)\n// sliders rather than being two components or needing a separate\n// `range` boolean prop: the VALUE's shape says which mode it's in\n// — a plain `number` is single-thumb, a `[number, number]` tuple is\n// a range — the same way core/select.ts infers single vs multiple\n// selection from whether `value` is a scalar or an array, rather\n// than a redundant `multiple` flag that could disagree with what\n// was actually passed.\n//\n// The index math for range mode (which thumb is closer to a click,\n// keeping the two thumbs from crossing) is the part most likely to\n// have an off-by-one, so it's pure and unit-tested here, same\n// reasoning as core/floating.ts and core/collection.ts.\n// ============================================================\n\nexport type SliderValue = number | readonly [number, number];\n\nexport function isRangeValue(value: SliderValue): value is readonly [number, number] {\n return Array.isArray(value);\n}\n\n/** Normalizes either shape into an array of thumb positions, in track order (low to high for a range). */\nexport function toThumbs(value: SliderValue): number[] {\n return isRangeValue(value) ? [value[0], value[1]] : [value];\n}\n\n/** The inverse of toThumbs — rebuilds the original shape (scalar or tuple) from a thumbs array, matching the shape of `like`. */\nexport function fromThumbs(thumbs: readonly number[], like: SliderValue): SliderValue {\n return isRangeValue(like) ? [thumbs[0], thumbs[1]] : thumbs[0];\n}\n\nexport interface SliderCoreProps {\n min?: number;\n max?: number;\n step?: number;\n disabled?: boolean;\n size?: Size;\n}\n\nexport interface SliderState {\n min: number;\n max: number;\n step: number;\n isDisabled: boolean;\n size: Size;\n}\n\nexport function resolveSliderState(props: SliderCoreProps): SliderState {\n return {\n // ?? not || (CONTRACT.md Rule 3) — an explicit min={0} must not\n // be discarded (it already is the default, but a future\n // negative-range default would make this the exact bug Rule 3\n // guards against).\n min: props.min ?? 0,\n max: props.max ?? 100,\n step: props.step ?? 1,\n isDisabled: props.disabled ?? false,\n size: props.size ?? \"md\",\n };\n}\n\n/** Clamps to [min, max] AND snaps to the nearest step boundary, measured from `min` so steps always land on min + n*step regardless of where min itself falls. */\nexport function clampAndSnap(value: number, state: Pick<SliderState, \"min\" | \"max\" | \"step\">): number {\n const { min, max, step } = state;\n const clamped = Math.min(Math.max(value, min), max);\n if (step <= 0) return clamped;\n const steps = Math.round((clamped - min) / step);\n const snapped = min + steps * step;\n // Rounding can push a value snapped near the top edge fractionally\n // past `max` (float error, not a logic error) — clamp once more.\n return Math.min(Math.max(snapped, min), max);\n}\n\nexport function valueToPercent(value: number, min: number, max: number): number {\n if (max === min) return 0;\n return ((value - min) / (max - min)) * 100;\n}\n\nexport function percentToValue(percent: number, min: number, max: number): number {\n return min + (percent / 100) * (max - min);\n}\n\n/**\n * Range mode only: applying a new value to one thumb must not let\n * it cross the other. `minGap` (default 0, i.e. thumbs may touch\n * but not pass) is in value units, not percent, so it scales\n * correctly regardless of the min/max range's size.\n */\nexport function clampRangeThumb(thumbs: readonly number[], index: 0 | 1, next: number, minGap = 0): number {\n if (index === 0) return Math.min(next, thumbs[1] - minGap);\n return Math.max(next, thumbs[0] + minGap);\n}\n\n/**\n * Range mode: which thumb (0 = low, 1 = high) a track click or\n * keyboard-on-track interaction nearest `targetValue` should move.\n * Ties go to the low thumb, matching most slider implementations'\n * convention (arbitrary but needs to be consistent, and low-first\n * matches reading order).\n */\nexport function nearestThumbIndex(thumbs: readonly [number, number], targetValue: number): 0 | 1 {\n const distLow = Math.abs(thumbs[0] - targetValue);\n const distHigh = Math.abs(thumbs[1] - targetValue);\n return distHigh < distLow ? 1 : 0;\n}\n\n// --- Keyboard ---------------------------------------------------\n\nconst STEP_KEYS_UP = new Set([\"ArrowRight\", \"ArrowUp\"]);\nconst STEP_KEYS_DOWN = new Set([\"ArrowLeft\", \"ArrowDown\"]);\n\n/**\n * Maps a keydown on a focused thumb to its new value, or null if\n * the key isn't one this widget handles (so the caller knows\n * whether to preventDefault). PageUp/PageDown move by a larger\n * \"big step\" (10 steps, the common slider convention — mirrors how\n * a native <input type=\"range\"> often pairs with a bigger keyboard\n * jump than the arrow-key step) capped so it never exceeds the\n * min/max span itself. Home/End jump to the bounds directly, not\n * relative to the current value — WAI-ARIA slider pattern.\n */\nexport function getKeyboardValue(\n key: string,\n current: number,\n state: Pick<SliderState, \"min\" | \"max\" | \"step\">\n): number | null {\n const bigStep = Math.min(state.step * 10, state.max - state.min);\n if (STEP_KEYS_UP.has(key)) return clampAndSnap(current + state.step, state);\n if (STEP_KEYS_DOWN.has(key)) return clampAndSnap(current - state.step, state);\n if (key === \"PageUp\") return clampAndSnap(current + bigStep, state);\n if (key === \"PageDown\") return clampAndSnap(current - bigStep, state);\n if (key === \"Home\") return state.min;\n if (key === \"End\") return state.max;\n return null;\n}\n\n// --- Classes ------------------------------------------------------\n\nexport function getRootClasses(\n state: SliderState,\n isRange: boolean,\n showValueLabel: boolean,\n className?: string\n): string {\n return [\n \"nx-slider\",\n `nx-slider--${state.size}`,\n isRange && \"nx-slider--range\",\n state.isDisabled && \"nx-slider--disabled\",\n // The value-label bubble floats above the thumb (see\n // Slider.css) — with no room reserved for it, it overflows\n // .nx-slider's own box and lands on whatever's above it in\n // .nx-field, which for a slider with a `label` is the label\n // text itself. This adds the top margin the bubble actually\n // needs instead of relying on .nx-field's generic 4px gap.\n showValueLabel && \"nx-slider--with-value-label\",\n className,\n ]\n .filter(Boolean)\n .join(\" \");\n}\n\n// --- ARIA -----------------------------------------------------\n\nexport interface ThumbAriaInput {\n value: number;\n min: number;\n max: number;\n isDisabled: boolean;\n /** A human-readable value string (e.g. \"$42\" or \"3 items\") when the raw number alone wouldn't be clear announced aloud. */\n valueText?: string;\n labelledBy?: string;\n}\n\nexport function getThumbAriaProps(input: ThumbAriaInput) {\n return {\n role: \"slider\" as const,\n \"aria-valuemin\": input.min,\n \"aria-valuemax\": input.max,\n \"aria-valuenow\": input.value,\n \"aria-valuetext\": input.valueText,\n \"aria-orientation\": \"horizontal\" as const,\n \"aria-disabled\": input.isDisabled || undefined,\n \"aria-labelledby\": input.labelledBy,\n tabIndex: input.isDisabled ? -1 : 0,\n };\n}","import { clampAndSnap, percentToValue } from \"./slider\";\r\n\r\n// ============================================================\r\n// MEDIA — shared pure core for VideoPlayer and AudioPlayer.\r\n//\r\n// Both components are, underneath the different chrome (VideoPlayer\r\n// has a viewport + fullscreen + PiP, AudioPlayer is a compact bar),\r\n// the same interaction shape: a scrub position, a volume, a\r\n// play/pause toggle, a playback rate. That shape is pure and\r\n// unit-testable exactly like core/carousel.ts's index math or\r\n// core/floating.ts's positioning math, so it lives here once instead\r\n// of twice.\r\n//\r\n// The scrub/volume-drag math deliberately reuses core/slider.ts's\r\n// clampAndSnap/percentToValue rather than re-deriving pointer-to-value\r\n// math a second time (Rule 12) — a progress bar and a volume bar are\r\n// both just a continuous single-thumb slider with a different domain\r\n// (0..duration vs 0..1), so the same clamping function that's already\r\n// tested there covers this too.\r\n//\r\n// What's NOT here: the actual playback state (currentTime, duration,\r\n// paused, volume, buffered, fullscreen...). That's driven by real\r\n// <video>/<audio> element events and inherently lives as React state\r\n// in each component's own file — this module only ever formats,\r\n// clamps, and computes from values the caller already has, the same\r\n// division of labor core/floating.ts keeps between \"pure math\" and\r\n// \"the hook that calls it on real DOM events\".\r\n// ============================================================\r\n\r\nexport const PLAYBACK_RATES = [0.5, 0.75, 1, 1.25, 1.5, 1.75, 2] as const;\r\nexport type PlaybackRate = (typeof PLAYBACK_RATES)[number];\r\n\r\n/** mm:ss, or h:mm:ss once the duration crosses an hour. NaN/Infinity/negative all render as \"0:00\" rather than \"NaN:NaN\" — happens routinely before metadata has loaded. */\r\nexport function formatMediaTime(seconds: number): string {\r\n if (!Number.isFinite(seconds) || seconds < 0) return \"0:00\";\r\n const total = Math.floor(seconds);\r\n const h = Math.floor(total / 3600);\r\n const m = Math.floor((total % 3600) / 60);\r\n const s = total % 60;\r\n const paddedS = String(s).padStart(2, \"0\");\r\n if (h > 0) return `${h}:${String(m).padStart(2, \"0\")}:${paddedS}`;\r\n return `${m}:${paddedS}`;\r\n}\r\n\r\n/** Volume is always kept in [0, 1] — mirrors HTMLMediaElement.volume's own contract, so a value read off a real element round-trips through this unchanged. */\r\nexport function clampVolume(volume: number): number {\r\n return clampAndSnap(volume, { min: 0, max: 1, step: 0 });\r\n}\r\n\r\n/**\r\n * Pointer clientX + the track element's bounding rect -> the time\r\n * that position represents. Used for both click-to-seek and\r\n * drag-to-seek — a drag is just this function called on every\r\n * pointermove. Returns 0 for a degenerate track (zero width) or\r\n * unknown duration rather than NaN.\r\n */\r\nexport function positionToTime(clientX: number, trackRect: { left: number; width: number }, duration: number): number {\r\n if (trackRect.width <= 0 || !Number.isFinite(duration) || duration <= 0) return 0;\r\n const rawPercent = ((clientX - trackRect.left) / trackRect.width) * 100;\r\n const percent = clampAndSnap(rawPercent, { min: 0, max: 100, step: 0 });\r\n return percentToValue(percent, 0, duration);\r\n}\r\n\r\n/** Same math as positionToTime, generalized to any [0, max] domain — used for the volume bar (max = 1) so it doesn't need a second, duration-shaped version of the same function. */\r\nexport function positionToRatio(clientX: number, trackRect: { left: number; width: number }): number {\r\n return clampVolume(positionToTime(clientX, trackRect, 1));\r\n}\r\n\r\nexport function getBufferedPercent(bufferedEnd: number, duration: number): number {\r\n if (!Number.isFinite(duration) || duration <= 0) return 0;\r\n return clampAndSnap((bufferedEnd / duration) * 100, { min: 0, max: 100, step: 0 });\r\n}\r\n\r\nexport function getProgressPercent(currentTime: number, duration: number): number {\r\n return getBufferedPercent(currentTime, duration);\r\n}\r\n\r\n/**\r\n * Some containers (webm recorded via MediaRecorder, certain re-muxed\r\n * mp4s, growing/streamed files) never report a real duration up\r\n * front — `HTMLMediaElement.duration` comes back `Infinity`, `NaN`,\r\n * or a flat `0` even once the element is actively playing and\r\n * currentTime is genuinely advancing. `formatMediaTime` already\r\n * renders any of those as \"0:00\" rather than \"NaN:NaN\", but every\r\n * percent-based calculation in this file (getProgressPercent,\r\n * positionToTime) also treats duration <= 0 or non-finite as\r\n * \"unknown\" and returns 0 — which is what makes a real,\r\n * actively-playing video's scrub bar look permanently stuck at 0%\r\n * and a drag always snap back to the start. `duration <= 0` gets the\r\n * exact same \"unknown\" treatment here as it already gets in\r\n * getBufferedPercent/getProgressPercent, not just the non-finite\r\n * case — a literal 0 duration is just as broken for the UI as\r\n * Infinity, so it needs the same recovery attempt.\r\n *\r\n * The fix browsers actually need: seek far past the real end once,\r\n * which forces the element to walk its index/moov atom and discover\r\n * the true duration, then seek back to where playback actually was.\r\n * No single event reliably fires once that duration becomes known —\r\n * this is a well-documented cross-browser gap for exactly this bug,\r\n * not a corner case — so this polls `media.duration` directly on an\r\n * interval instead of betting on `timeupdate`/`seeked`/\r\n * `durationchange` firing at all; the event listeners stay as a\r\n * same-tick fast path when a browser does fire one, but the poll is\r\n * what actually guarantees this resolves. If the source genuinely\r\n * has no fixed duration (a live stream, or one that rejects the\r\n * forced seek outright), this gives up after `giveUpMs` rather than\r\n * polling forever, and reports 0 (still \"unknown\", per the same\r\n * convention) so the caller can fall back to an elapsed-only display\r\n * instead of a misleading \"/ 0:00\" next to a number that's visibly\r\n * still growing. Framework-agnostic (operates on any\r\n * HTMLMediaElement — audio or video, React ref or not) — same reason\r\n * positionToTime/clampVolume live here instead of duplicated per\r\n * component.\r\n */\r\nexport function recoverDuration(\r\n media: HTMLMediaElement,\r\n restoreTo: number,\r\n onDuration: (duration: number) => void,\r\n giveUpMs = 4000,\r\n pollMs = 100\r\n): () => void {\r\n function isKnown(duration: number): boolean {\r\n return Number.isFinite(duration) && duration > 0;\r\n }\r\n\r\n if (isKnown(media.duration)) {\r\n onDuration(media.duration);\r\n return () => {};\r\n }\r\n\r\n let settled = false;\r\n let pollHandle: ReturnType<typeof setInterval> | undefined;\r\n let giveUpHandle: ReturnType<typeof setTimeout> | undefined;\r\n\r\n function cleanup() {\r\n media.removeEventListener(\"timeupdate\", handleResolved);\r\n media.removeEventListener(\"seeked\", handleResolved);\r\n media.removeEventListener(\"durationchange\", handleResolved);\r\n media.removeEventListener(\"error\", handleError);\r\n if (pollHandle !== undefined) clearInterval(pollHandle);\r\n if (giveUpHandle !== undefined) clearTimeout(giveUpHandle);\r\n }\r\n\r\n function finish(resolvedDuration: number, restore: boolean) {\r\n if (settled) return;\r\n settled = true;\r\n cleanup();\r\n if (restore) media.currentTime = restoreTo;\r\n onDuration(resolvedDuration);\r\n }\r\n\r\n function handleResolved() {\r\n if (isKnown(media.duration)) finish(media.duration, true);\r\n }\r\n\r\n function handleError() {\r\n finish(0, false);\r\n }\r\n\r\n media.addEventListener(\"timeupdate\", handleResolved);\r\n media.addEventListener(\"seeked\", handleResolved);\r\n media.addEventListener(\"durationchange\", handleResolved);\r\n media.addEventListener(\"error\", handleError);\r\n\r\n pollHandle = setInterval(() => {\r\n if (isKnown(media.duration)) finish(media.duration, true);\r\n }, pollMs);\r\n\r\n giveUpHandle = setTimeout(() => finish(0, false), giveUpMs);\r\n\r\n media.currentTime = 1e101;\r\n\r\n return cleanup;\r\n}\r\n\r\n// --- ARIA -----------------------------------------------------------\r\n\r\nexport function getSeekAriaProps(currentTime: number, duration: number) {\r\n return {\r\n role: \"slider\" as const,\r\n \"aria-label\": \"Seek\",\r\n \"aria-valuemin\": 0,\r\n \"aria-valuemax\": Math.max(duration, 0),\r\n \"aria-valuenow\": Math.min(Math.max(currentTime, 0), Math.max(duration, 0)),\r\n \"aria-valuetext\": formatMediaTime(currentTime),\r\n };\r\n}\r\n\r\nexport function getVolumeAriaProps(volume: number, muted: boolean) {\r\n const pct = Math.round(clampVolume(volume) * 100);\r\n return {\r\n role: \"slider\" as const,\r\n \"aria-label\": \"Volume\",\r\n \"aria-valuemin\": 0,\r\n \"aria-valuemax\": 100,\r\n \"aria-valuenow\": muted ? 0 : pct,\r\n \"aria-valuetext\": muted ? \"Muted\" : `${pct}%`,\r\n };\r\n}\r\n\r\nexport function getPlayButtonAriaProps(isPlaying: boolean) {\r\n return { \"aria-label\": isPlaying ? \"Pause\" : \"Play\" };\r\n}\r\n\r\nexport function getMuteButtonAriaProps(isMuted: boolean) {\r\n return { \"aria-label\": isMuted ? \"Unmute\" : \"Mute\", \"aria-pressed\": isMuted };\r\n}\r\n\r\n/** Whether the \"muted\" speaker icon should show — true either because the element is explicitly muted, or because the volume itself has been dragged to 0 (a real player treats both the same visually). */\r\nexport function isEffectivelyMuted(volume: number, muted: boolean): boolean {\r\n return muted || volume <= 0;\r\n}\r\n\r\n/** Seconds each seek-back/seek-forward button press moves by, when the consumer doesn't say otherwise. */\r\nexport const SKIP_SECONDS = 10;\r\n\r\n/**\r\n * The skip amounts that have a dedicated, number-bearing seek icon\r\n * (`seek-minus-ten`, `seek-plus-thirty`, ...). Any other amount is\r\n * still perfectly valid — it just falls back to the unnumbered\r\n * `seek-minus`/`seek-plus` pair, so `skipSeconds={7}` never renders a\r\n * button whose icon claims \"10\".\r\n */\r\nexport const LABELED_SKIP_SECONDS = [5, 10, 15, 30] as const;\r\nexport type LabeledSkipSeconds = (typeof LABELED_SKIP_SECONDS)[number];\r\n\r\n/** Whether `seconds` is one of the amounts with a numbered seek icon — the icon lookup in the React layer is a `hasLabeledSkipIcon` check plus a map, so the \"which icon\" decision stays testable here rather than living only inside JSX. */\r\nexport function hasLabeledSkipIcon(seconds: number): seconds is LabeledSkipSeconds {\r\n return (LABELED_SKIP_SECONDS as readonly number[]).includes(seconds);\r\n}\r\n\r\n/** A skip amount is always a positive, finite number of seconds; anything else (0, negative, NaN, Infinity) would render a button that visibly does nothing, so it falls back to the default rather than being honored. Direction comes from which button was pressed, never from the sign of this prop. */\r\nexport function resolveSkipSeconds(skipSeconds?: number): number {\r\n if (skipSeconds === undefined) return SKIP_SECONDS;\r\n if (!Number.isFinite(skipSeconds) || skipSeconds <= 0) return SKIP_SECONDS;\r\n return skipSeconds;\r\n}\r\n\r\n/** Clamps a skip (±SKIP_SECONDS, or any delta) to stay inside [0, duration] — shared by the skip buttons and the seek bar's own arrow-key handling, both of which are \"move currentTime by N and clamp\" with the same domain. */\r\nexport function skipTime(currentTime: number, duration: number, deltaSeconds: number): number {\r\n const max = Number.isFinite(duration) && duration > 0 ? duration : Infinity;\r\n return Math.min(Math.max(currentTime + deltaSeconds, 0), max);\r\n}\r\n\r\nexport function getSkipButtonAriaProps(deltaSeconds: number) {\r\n return { \"aria-label\": deltaSeconds < 0 ? `Back ${Math.abs(deltaSeconds)} seconds` : `Forward ${deltaSeconds} seconds` };\r\n}\r\n\r\n// --- Transport: seek-by-seconds vs. previous/next track ---------------\r\n//\r\n// These are two different controls that a media player has always\r\n// needed to keep separate, and conflating them is exactly the bug\r\n// this section exists to prevent: \"skip\" moves currentTime inside the\r\n// ONE source that's loaded (±skipSeconds, clamped by skipTime), while\r\n// \"previous/next track\" swaps the source entirely and is the player's\r\n// caller's business, not the player's. So skip is unconditional — a\r\n// single video still wants ±10s — and track nav is opt-in, present\r\n// only when the consumer actually has a playlist to move through.\r\n//\r\n// Opt-in is keyed on the handlers themselves (onNext/onPrevious)\r\n// rather than on a separate `showTrackNav` boolean: a next button\r\n// with nothing to call is dead UI, and a boolean lets those two\r\n// states drift apart. `hasNext`/`hasPrevious` then handle the\r\n// orthogonal question of whether the ends of the playlist have been\r\n// reached — the button stays rendered (so the row doesn't reflow\r\n// mid-playlist) and goes disabled instead.\r\n\r\nexport interface MediaTransportProps {\r\n /** Seconds each seek-back/seek-forward press moves by. Default 10. Positive number; direction comes from the button, not the sign. */\r\n skipSeconds?: number;\r\n /** Called when the next-track button is pressed. Omit and no next-track button is rendered at all. */\r\n onNext?: () => void;\r\n /** Called when the previous-track button is pressed. Omit and no previous-track button is rendered at all. */\r\n onPrevious?: () => void;\r\n /** Whether a next track exists. Default true. False keeps the button in place but disables it. */\r\n hasNext?: boolean;\r\n /** Whether a previous track exists. Default true. False keeps the button in place but disables it. */\r\n hasPrevious?: boolean;\r\n}\r\n\r\nexport interface MediaTransportState {\r\n skipSeconds: number;\r\n showNext: boolean;\r\n showPrevious: boolean;\r\n nextDisabled: boolean;\r\n previousDisabled: boolean;\r\n}\r\n\r\nexport function resolveMediaTransport(props: MediaTransportProps): MediaTransportState {\r\n const showNext = props.onNext !== undefined;\r\n const showPrevious = props.onPrevious !== undefined;\r\n return {\r\n skipSeconds: resolveSkipSeconds(props.skipSeconds),\r\n showNext,\r\n showPrevious,\r\n nextDisabled: showNext && (props.hasNext ?? true) === false,\r\n previousDisabled: showPrevious && (props.hasPrevious ?? true) === false,\r\n };\r\n}\r\n\r\nexport function getTrackButtonAriaProps(direction: \"next\" | \"previous\") {\r\n return { \"aria-label\": direction === \"next\" ? \"Next track\" : \"Previous track\" };\r\n}\r\n\r\n// --- Player-wide keyboard shortcuts (space / arrows) -------------------\r\n//\r\n// The progress bar and volume bar each already have their own\r\n// onKeyDown (arrow keys nudge that specific control once it has\r\n// focus) — that's unrelated to this. This is the second, separate\r\n// expectation people bring to *any* video/audio player: press Space\r\n// or an arrow key while the player generally has focus — the poster,\r\n// a control button, the player's own outer frame — and play/pause,\r\n// seek, or volume responds, the same as YouTube, the native Firefox\r\n// video controls, etc. Without it, keyboard-only use is limited to\r\n// whichever single control currently has focus.\r\n//\r\n// A pure key -> action mapping (rather than each component switching\r\n// on e.key directly) is what's unit-testable here; e.key strings\r\n// aren't something a component test should need to know about.\r\n\r\nexport type MediaKeyAction = \"toggle-play\" | \"seek-back\" | \"seek-forward\" | \"volume-up\" | \"volume-down\";\r\n\r\n/** Maps a KeyboardEvent.key to the player-wide action it triggers, or null for a key this layer doesn't own. \"Spacebar\" is IE11/old-Edge's name for the same key \" \" reports everywhere else. */\r\nexport function getMediaKeyAction(key: string): MediaKeyAction | null {\r\n switch (key) {\r\n case \" \":\r\n case \"Spacebar\":\r\n return \"toggle-play\";\r\n case \"ArrowLeft\":\r\n return \"seek-back\";\r\n case \"ArrowRight\":\r\n return \"seek-forward\";\r\n case \"ArrowUp\":\r\n return \"volume-up\";\r\n case \"ArrowDown\":\r\n return \"volume-down\";\r\n default:\r\n return null;\r\n }\r\n}","// ============================================================\n// PDF VIEWER — pure core (Category F: complex/stateful).\n//\n// No PDF-parsing dependency of any kind, by design (see\n// PdfViewer.tsx's header for the full reasoning) — PdfViewer renders\n// through the browser's own native PDF viewer inside an <iframe>,\n// driven entirely by the \"open parameters\" URL-fragment convention\n// (#page=N&zoom=Z) that convention already documents and that\n// Chrome/Firefox/Edge's built-in PDF viewers already implement. This\n// file's only job is the page/zoom arithmetic and the fragment\n// string itself — both pure, both fully testable without a browser,\n// a PDF file, or any third-party library at all.\n// ============================================================\n\nexport interface PdfViewerCoreProps {\n /** Page to open on first render. Default 1. */\n initialPage?: number;\n /** Zoom level to open at, where 1 = 100%. Left unset, the viewer opens fit-to-width instead of a fixed percentage — see initialScale on PdfViewerState. */\n initialScale?: number;\n minScale?: number;\n maxScale?: number;\n /** How much each zoom-in/out step changes the scale. Default 0.25. */\n scaleStep?: number;\n}\n\n/**\n * \"auto\" means fit-to-width (the PDF open-parameters spec's own\n * `zoom=page-width`) rather than a fixed percentage — it's the\n * default so a page opens fully visible instead of at a literal 100%\n * that's wider than the viewer and gets clipped, forcing a manual\n * zoom-out just to see the page. It only ever applies before the\n * first zoom interaction: clicking zoom in/out always resolves to a\n * concrete percentage from there (see zoomIn/zoomOut below), since\n * \"step in/out from fit-to-width\" isn't a well-defined percentage.\n */\nexport type PdfZoom = number | \"auto\";\n\nexport interface PdfViewerState {\n initialPage: number;\n initialScale: PdfZoom;\n minScale: number;\n maxScale: number;\n scaleStep: number;\n}\n\nexport function resolvePdfViewerState(props: PdfViewerCoreProps): PdfViewerState {\n return {\n initialPage: props.initialPage ?? 1,\n initialScale: props.initialScale ?? \"auto\",\n minScale: props.minScale ?? 0.5,\n maxScale: props.maxScale ?? 3,\n scaleStep: props.scaleStep ?? 0.25,\n };\n}\n\nexport function getPdfViewerClasses(className?: string): string {\n return [\"nx-pdf-viewer\", className].filter(Boolean).join(\" \");\n}\n\n/** No known page count (the browser's native viewer owns pagination, not this component — see this file's header), so a page number only ever has a floor, never a ceiling. Typing past the real last page is harmless: the native viewer simply stays on its last page. */\nexport function clampPage(page: number): number {\n return Math.max(Math.round(page), 1);\n}\n\nexport function clampScale(scale: number, min: number, max: number): number {\n return Math.min(Math.max(scale, min), max);\n}\n\n/** Fit-to-width has no numeric baseline of its own to step from. Without `fitScale`, this falls back to 100% — a guess that can be far off from what \"fit\" is actually showing, since the real render happens inside the native plugin and isn't readable from outside the iframe. Pass `fitScale` (see estimateFitScale) whenever it's available so the first zoom click steps from something close to the size already on screen instead of jumping to an arbitrary 100%. */\nfunction resolveZoomBase(scale: PdfZoom, fitScale: number): number {\n return scale === \"auto\" ? fitScale : scale;\n}\n\nexport function zoomIn(scale: PdfZoom, step: number, max: number, fitScale = 1): number {\n return clampScale(resolveZoomBase(scale, fitScale) + step, 0, max);\n}\n\nexport function zoomOut(scale: PdfZoom, step: number, min: number, fitScale = 1): number {\n return clampScale(resolveZoomBase(scale, fitScale) - step, min, Infinity);\n}\n\n/** Fit-to-width isn't pinned to either bound, so the zoom-out/zoom-in buttons stay enabled from \"auto\" rather than looking already-maxed/-minned before the person has zoomed at all. */\nexport function isZoomAtMin(scale: PdfZoom, min: number): boolean {\n return scale !== \"auto\" && scale <= min;\n}\n\nexport function isZoomAtMax(scale: PdfZoom, max: number): boolean {\n return scale !== \"auto\" && scale >= max;\n}\n\n/**\n * Approximates the percentage that \"page-width\" fit is actually\n * rendering at, from the one thing about it we genuinely can measure\n * — the iframe's own rendered width. The PDF's real page width isn't\n * knowable without parsing the file (the whole reason this component\n * doesn't carry a parser), so this assumes a standard page width in\n * PDF points (US Letter, 612pt, by default — pass `pageWidthPt` for\n * A4 at ~595pt or a known custom size). That means the estimate can\n * be off for non-standard page sizes, same caveat as every other\n * page-count-unaware piece of this component — but it's still much\n * closer to what's on screen than the flat 100% zoomIn/zoomOut used\n * to fall back to, which is what made zooming from \"Fit\" look like\n * it jumped to an arbitrary, often-much-larger size.\n */\nexport function estimateFitScale(iframeWidthPx: number, pageWidthPt = 612): number {\n if (iframeWidthPx <= 0 || pageWidthPt <= 0) return 1;\n const pageWidthPx = pageWidthPt * (96 / 72);\n return iframeWidthPx / pageWidthPx;\n}\n\nexport function formatZoomPercent(scale: PdfZoom): string {\n return scale === \"auto\" ? \"Fit\" : `${Math.round(scale * 100)}%`;\n}\n\n/**\n * Builds the URL for the native-viewer <iframe>, appending the\n * standard PDF \"open parameters\" fragment (`#page=N&zoom=Z`, or\n * `#page=N&zoom=page-width` for the \"auto\" fit-to-width state) that\n * Chrome, Firefox, and Edge's built-in PDF viewers all honor on\n * first load. Re-navigating an *already-loaded* PDF plugin by\n * reassigning `iframe.src` to a fragment-only-different URL is not\n * reliable — several browsers treat it as a same-document no-op and\n * simply never re-read the new fragment, which is why prev/next/zoom\n * can look completely dead on a real page even though this string\n * itself is correct. PdfViewer.tsx works around that by keying the\n * `<iframe>` on this full string (not just the base URL), forcing a\n * genuine remount — and therefore a genuine fresh load honoring the\n * new fragment — on every page/zoom change. Safari's built-in PDF\n * support is inconsistent with these params; there, the iframe still\n * displays the document, just without reliable page/zoom control\n * from here.\n *\n * Any existing fragment on `src` is replaced outright — a caller\n * passing their own `#...` in `src` is unusual enough (and would\n * conflict with page/zoom control) that clobbering it is the right\n * default.\n */\nexport function buildPdfSrc(src: string, page: number, scale: PdfZoom): string {\n const base = src.split(\"#\")[0];\n const zoomParam = scale === \"auto\" ? \"page-width\" : Math.round(scale * 100);\n return `${base}#page=${clampPage(page)}&zoom=${zoomParam}`;\n}"],"mappings":";AA2JO,SAAS,YAAY,QAAkC;AAC5D,SAAO;AACT;AAGO,IAAM,mBAAmB;AAAA,EAC9B;AAAA,EAAQ;AAAA,EAAS;AAAA,EAAU;AAAA,EAAW;AAAA,EAAc;AAAA,EAAU;AAAA,EAAa;AAAA,EAAU;AAAA,EAAU;AACjG;AAYO,IAAM,uBAAuB;AAAA,EAClC;AAAA,EAAa;AAAA,EAAc;AAAA,EAC3B,GAAG,CAAC,KAAK,MAAM,OAAO,OAAO,OAAO,OAAO,OAAO,OAAO,OAAO,OAAO,OAAO,KAAK,EAAE,IAAI,CAAC,MAAM,iBAAiB,CAAC,EAAE;AAAA,EACpH,GAAI,CAAC,WAAW,UAAU,WAAW,MAAM,EAAY;AAAA,IAAQ,CAAC,MAC9D,CAAC,SAAS,UAAU,QAAQ,QAAQ,QAAQ,EAAE,IAAI,CAAC,MAAM,SAAS,CAAC,IAAI,CAAC,EAAE;AAAA,EAC5E;AAAA,EACA;AAAA,EAAoB;AAAA,EAAiB;AAAA,EAAuB;AAAA,EAAgB;AAAA,EAC5E;AAAA,EAAc;AAAA,EAAmB;AAAA,EACjC;AAAA,EAAa;AAAA,EAAa;AAAA,EAAa;AAAA,EAAa;AAAA,EAAc;AAAA,EAClE;AAAA,EAAc;AAAA,EAAc;AAAA,EAAc;AAAA,EAAc;AAAA,EAAc;AAAA,EACtE;AAAA,EAAuB;AAAA,EACvB;AAAA,EAAqB;AAAA,EAAqB;AAAA,EAAqB;AAAA,EAAqB;AAAA,EACpF;AAAA,EAA2B;AAAA,EAA2B;AAAA,EACtD;AAAA,EAA8B;AAAA,EAC9B;AAAA,EAAa;AAAA,EAAa;AAAA,EAAa;AAAA,EAAa;AAAA,EAAa;AAAA,EAAc;AAAA,EAC/E;AAAA,EAAsB;AAAA,EAAqB;AAAA,EAAmB;AAAA,EAAmB;AAAA,EACjF;AAAA,EAAuB;AAAA,EAAyB;AAAA,EAChD;AAAA,EAAuB;AAAA,EAAyB;AAAA,EAChD;AAAA,EAAoB;AAAA,EAAsB;AAAA,EAAqB;AAAA,EAC/D;AACF;AAEA,SAAS,UAAU,KAAc,MAAuB;AACtD,SAAO,KAAK,MAAM,GAAG,EAAE,OAAgB,CAAC,KAAK,QAAQ;AACnD,QAAI,QAAQ,QAAQ,OAAO,QAAQ,SAAU,QAAO;AACpD,WAAQ,IAAgC,GAAG;AAAA,EAC7C,GAAG,GAAG;AACR;AAGO,SAAS,sBAAsB,OAA0B;AAC9D,SAAO,qBAAqB,OAAO,CAAC,SAAS;AAC3C,UAAM,QAAQ,UAAU,OAAO,IAAI;AACnC,WAAO,UAAU,QAAQ,UAAU,UAAa,UAAU;AAAA,EAC5D,CAAC;AACH;;;AC/MO,IAAM,QAAQ,YAAY;AAAA,EAC/B,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,OAAO;AAAA,IACP,aAAa;AAAA,EACf;AAAA,EACA,OAAO;AAAA,IACL,SAAS;AAAA,MACP,GAAG;AAAA,MAAW,IAAI;AAAA,MAAW,KAAK;AAAA,MAAW,KAAK;AAAA,MAAW,KAAK;AAAA,MAClE,KAAK;AAAA,MAAW,KAAK;AAAA,MAAW,KAAK;AAAA,MAAW,KAAK;AAAA,MAAW,KAAK;AAAA,MACrE,KAAK;AAAA,MAAW,KAAK;AAAA,IACvB;AAAA,IACA,SAAS,EAAE,OAAO,WAAW,QAAQ,WAAW,MAAM,WAAW,MAAM,WAAW,QAAQ,UAAU;AAAA,IACpG,QAAQ,EAAE,OAAO,WAAW,QAAQ,WAAW,MAAM,WAAW,MAAM,WAAW,QAAQ,UAAU;AAAA,IACnG,SAAS,EAAE,OAAO,WAAW,QAAQ,WAAW,MAAM,WAAW,MAAM,WAAW,QAAQ,UAAU;AAAA,IACpG,MAAM,EAAE,OAAO,WAAW,QAAQ,WAAW,MAAM,WAAW,MAAM,WAAW,QAAQ,UAAU;AAAA,IACjG,YAAY;AAAA,IACZ,SAAS;AAAA,IACT,eAAe;AAAA,IACf,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA,IAKR,aAAa;AAAA,IACb,MAAM;AAAA,IACN,WAAW;AAAA,IACX,aAAa;AAAA,EACf;AAAA,EACA,QAAQ,EAAE,IAAI,OAAO,IAAI,QAAQ,IAAI,QAAQ,IAAI,QAAQ,OAAO,QAAQ,MAAM,SAAS;AAAA,EACvF,SAAS,EAAE,IAAI,OAAO,IAAI,OAAO,IAAI,QAAQ,IAAI,QAAQ,IAAI,QAAQ,OAAO,OAAO;AAAA,EACnF,YAAY;AAAA,IACV,UAAU;AAAA,IACV,UAAU;AAAA,IACV,QAAQ;AAAA,IAAQ,QAAQ;AAAA,IAAQ,QAAQ;AAAA,IAAQ,QAAQ;AAAA,IAAQ,QAAQ;AAAA,IACxE,cAAc;AAAA,IAAO,cAAc;AAAA,IAAO,YAAY;AAAA,IACtD,iBAAiB;AAAA,IAAO,kBAAkB;AAAA,EAC5C;AAAA,EACA,QAAQ;AAAA,IACN,IAAI;AAAA,IACJ,IAAI;AAAA,IACJ,IAAI;AAAA,IACJ,IAAI;AAAA,IACJ,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,OAAO;AAAA,EACT;AAAA,EACA,WAAW,EAAE,UAAU,MAAM,SAAS,MAAM,OAAO,KAAM,OAAO,MAAM,SAAS,KAAK;AAAA,EACpF,QAAQ;AAAA,IACN,cAAc;AAAA,IAAS,gBAAgB;AAAA,IAAS,cAAc;AAAA,IAC9D,cAAc;AAAA,IAAgC,gBAAgB;AAAA,IAC9D,iBAAiB;AAAA,EACnB;AAAA,EACA,QAAQ,EAAE,WAAW,OAAO,aAAa,SAAS,YAAY,OAAO,OAAO,QAAQ;AAAA,EACpF,SAAS,EAAE,cAAc,MAAM;AACjC,CAAC;;;ACvDM,IAAM,OAAO,YAAY;AAAA,EAC9B,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,OAAO;AAAA,IACP,aAAa;AAAA,EACf;AAAA,EACA,OAAO;AAAA,IACL,SAAS;AAAA,MACP,GAAG;AAAA,MAAW,IAAI;AAAA,MAAW,KAAK;AAAA,MAAW,KAAK;AAAA,MAAW,KAAK;AAAA,MAClE,KAAK;AAAA,MAAW,KAAK;AAAA,MAAW,KAAK;AAAA,MAAW,KAAK;AAAA,MAAW,KAAK;AAAA,MACrE,KAAK;AAAA,MAAW,KAAK;AAAA,IACvB;AAAA,IACA,SAAS,EAAE,OAAO,WAAW,QAAQ,WAAW,MAAM,WAAW,MAAM,WAAW,QAAQ,UAAU;AAAA,IACpG,QAAQ,EAAE,OAAO,WAAW,QAAQ,WAAW,MAAM,WAAW,MAAM,WAAW,QAAQ,UAAU;AAAA,IACnG,SAAS,EAAE,OAAO,WAAW,QAAQ,WAAW,MAAM,WAAW,MAAM,WAAW,QAAQ,UAAU;AAAA,IACpG,MAAM,EAAE,OAAO,WAAW,QAAQ,WAAW,MAAM,WAAW,MAAM,WAAW,QAAQ,UAAU;AAAA,IACjG,YAAY;AAAA,IACZ,SAAS;AAAA,IACT,eAAe;AAAA,IACf,QAAQ;AAAA,IACR,aAAa;AAAA,IACb,MAAM;AAAA,IACN,WAAW;AAAA,IACX,aAAa;AAAA,EACf;AAAA,EACA,QAAQ,EAAE,IAAI,OAAO,IAAI,QAAQ,IAAI,QAAQ,IAAI,QAAQ,OAAO,QAAQ,MAAM,SAAS;AAAA,EACvF,SAAS,EAAE,IAAI,OAAO,IAAI,OAAO,IAAI,QAAQ,IAAI,QAAQ,IAAI,QAAQ,OAAO,OAAO;AAAA,EACnF,YAAY;AAAA,IACV,UAAU;AAAA,IACV,UAAU;AAAA,IACV,QAAQ;AAAA,IAAQ,QAAQ;AAAA,IAAQ,QAAQ;AAAA,IAAQ,QAAQ;AAAA,IAAQ,QAAQ;AAAA,IACxE,cAAc;AAAA,IAAO,cAAc;AAAA,IAAO,YAAY;AAAA,IACtD,iBAAiB;AAAA,IAAO,kBAAkB;AAAA,EAC5C;AAAA,EACA,QAAQ;AAAA,IACN,IAAI;AAAA,IACJ,IAAI;AAAA,IACJ,IAAI;AAAA,IACJ,IAAI;AAAA,IACJ,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,OAAO;AAAA,EACT;AAAA,EACA,WAAW,EAAE,UAAU,MAAM,SAAS,MAAM,OAAO,KAAM,OAAO,MAAM,SAAS,KAAK;AAAA,EACpF,QAAQ;AAAA,IACN,cAAc;AAAA,IAAS,gBAAgB;AAAA,IAAS,cAAc;AAAA,IAC9D,cAAc;AAAA,IAAgC,gBAAgB;AAAA,IAC9D,iBAAiB;AAAA,EACnB;AAAA,EACA,QAAQ,EAAE,WAAW,OAAO,aAAa,SAAS,YAAY,OAAO,OAAO,QAAQ;AAAA,EACpF,SAAS,EAAE,cAAc,MAAM;AACjC,CAAC;;;ACnDM,IAAM,QAAQ,YAAY;AAAA,EAC/B,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,OAAO;AAAA,IACP,aAAa;AAAA,EACf;AAAA,EACA,OAAO;AAAA,IACL,SAAS;AAAA,MACP,GAAG;AAAA,MAAW,IAAI;AAAA,MAAW,KAAK;AAAA,MAAW,KAAK;AAAA,MAAW,KAAK;AAAA,MAClE,KAAK;AAAA,MAAW,KAAK;AAAA,MAAW,KAAK;AAAA,MAAW,KAAK;AAAA,MAAW,KAAK;AAAA,MACrE,KAAK;AAAA,MAAW,KAAK;AAAA,IACvB;AAAA,IACA,SAAS,EAAE,OAAO,WAAW,QAAQ,WAAW,MAAM,WAAW,MAAM,WAAW,QAAQ,UAAU;AAAA,IACpG,QAAQ,EAAE,OAAO,WAAW,QAAQ,WAAW,MAAM,WAAW,MAAM,WAAW,QAAQ,UAAU;AAAA,IACnG,SAAS,EAAE,OAAO,WAAW,QAAQ,WAAW,MAAM,WAAW,MAAM,WAAW,QAAQ,UAAU;AAAA,IACpG,MAAM,EAAE,OAAO,WAAW,QAAQ,WAAW,MAAM,WAAW,MAAM,WAAW,QAAQ,UAAU;AAAA,IACjG,YAAY;AAAA,IACZ,SAAS;AAAA,IACT,eAAe;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAWf,QAAQ;AAAA,IACR,aAAa;AAAA,IACb,MAAM;AAAA,IACN,WAAW;AAAA,IACX,aAAa;AAAA,EACf;AAAA;AAAA,EAEA,QAAQ,EAAE,IAAI,QAAQ,IAAI,QAAQ,IAAI,QAAQ,IAAI,QAAQ,OAAO,QAAQ,MAAM,SAAS;AAAA,EACxF,SAAS,EAAE,IAAI,OAAO,IAAI,OAAO,IAAI,QAAQ,IAAI,QAAQ,IAAI,QAAQ,OAAO,OAAO;AAAA,EACnF,YAAY;AAAA,IACV,UAAU;AAAA,IACV,UAAU;AAAA,IACV,QAAQ;AAAA,IAAQ,QAAQ;AAAA,IAAQ,QAAQ;AAAA,IAAQ,QAAQ;AAAA,IAAQ,QAAQ;AAAA,IACxE,cAAc;AAAA,IAAO,cAAc;AAAA,IAAO,YAAY;AAAA,IACtD,iBAAiB;AAAA,IAAO,kBAAkB;AAAA,EAC5C;AAAA,EACA,QAAQ;AAAA,IACN,IAAI;AAAA,IACJ,IAAI;AAAA,IACJ,IAAI;AAAA,IACJ,IAAI;AAAA,IACJ,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,OAAO;AAAA,EACT;AAAA,EACA,WAAW,EAAE,UAAU,MAAM,SAAS,MAAM,OAAO,KAAM,OAAO,MAAM,SAAS,KAAK;AAAA,EACpF,QAAQ;AAAA;AAAA,IAEN,cAAc;AAAA,IAAS,gBAAgB;AAAA,IAAS,cAAc;AAAA,IAC9D,cAAc;AAAA,IAAoC,gBAAgB;AAAA,IAClE,iBAAiB;AAAA,EACnB;AAAA,EACA,QAAQ,EAAE,WAAW,OAAO,aAAa,OAAO,YAAY,SAAS,OAAO,QAAQ;AAAA,EACpF,SAAS,EAAE,cAAc,OAAO;AAClC,CAAC;;;AC9DM,IAAM,YAAY,YAAY;AAAA,EACnC,GAAG;AAAA,EACH,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,OAAO;AAAA,IACP,aAAa;AAAA,EACf;AAAA,EACA,OAAO;AAAA,IACL,GAAG,KAAK;AAAA,IACR,YAAY;AAAA,IACZ,SAAS;AAAA,IACT,eAAe;AAAA,IACf,QAAQ;AAAA,EACV;AAAA,EACA,QAAQ,EAAE,IAAI,QAAQ,IAAI,QAAQ,IAAI,QAAQ,IAAI,QAAQ,OAAO,QAAQ,MAAM,SAAS;AAAA,EACxF,QAAQ;AAAA,IACN,IAAI;AAAA,IAA6B,IAAI;AAAA,IAA+B,IAAI;AAAA,IACxE,IAAI;AAAA,IAAgC,IAAI;AAAA,IAA+B,OAAO;AAAA,IAC9E,OAAO;AAAA,EACT;AAAA,EACA,QAAQ,EAAE,GAAG,KAAK,QAAQ,cAAc,SAAS,gBAAgB,SAAS,cAAc,QAAQ;AAAA,EAChG,SAAS,EAAE,cAAc,OAAO;AAClC,CAAC;;;ACtBM,IAAM,aAAa,YAAY;AAAA,EACpC,GAAG;AAAA,EACH,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,OAAO;AAAA,IACP,aAAa;AAAA,EACf;AAAA,EACA,OAAO;AAAA,IACL,GAAG,MAAM;AAAA,IACT,QAAQ;AAAA,IAAW,SAAS;AAAA,IAAW,eAAe;AAAA,EACxD;AAAA,EACA,QAAQ,EAAE,IAAI,OAAO,IAAI,OAAO,IAAI,OAAO,IAAI,OAAO,OAAO,OAAO,MAAM,SAAS;AAAA,EACnF,QAAQ,EAAE,IAAI,QAAQ,IAAI,QAAQ,IAAI,QAAQ,IAAI,QAAQ,IAAI,QAAQ,OAAO,QAAQ,OAAO,OAAO;AAAA,EACnG,QAAQ,EAAE,WAAW,OAAO,aAAa,OAAO,YAAY,OAAO,OAAO,QAAQ;AAAA,EAClF,SAAS,EAAE,cAAc,MAAM;AACjC,CAAC;;;ACfM,IAAM,YAAY,YAAY;AAAA,EACnC,GAAG;AAAA,EACH,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,OAAO;AAAA,IACP,aAAa;AAAA,EACf;AAAA,EACA,OAAO;AAAA,IACL,GAAG,MAAM;AAAA,IACT,YAAY;AAAA,IAAW,SAAS;AAAA,IAAW,eAAe;AAAA,IAC1D,QAAQ;AAAA,IAAW,aAAa;AAAA,IAAW,MAAM;AAAA,EACnD;AAAA,EACA,QAAQ,EAAE,IAAI,OAAO,IAAI,OAAO,IAAI,OAAO,IAAI,OAAO,OAAO,OAAO,MAAM,MAAM;AAAA,EAChF,YAAY,EAAE,GAAG,MAAM,YAAY,UAAU,4BAA4B,UAAU,2BAA2B;AAAA,EAC9G,QAAQ,EAAE,IAAI,QAAQ,IAAI,QAAQ,IAAI,QAAQ,IAAI,QAAQ,IAAI,QAAQ,OAAO,QAAQ,OAAO,OAAO;AAAA,EACnG,QAAQ,EAAE,WAAW,OAAO,aAAa,OAAO,YAAY,OAAO,OAAO,QAAQ;AAAA,EAClF,QAAQ,EAAE,GAAG,MAAM,QAAQ,cAAc,OAAO,gBAAgB,OAAO,cAAc,MAAM;AAAA,EAC3F,SAAS,EAAE,cAAc,MAAM;AACjC,CAAC;;;AClBM,IAAM,eAAe,YAAY;AAAA,EACtC,GAAG;AAAA,EACH,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,OAAO;AAAA,IACP,aAAa;AAAA,EACf;AAAA,EACA,OAAO;AAAA,IACL,GAAG,MAAM;AAAA,IACT,YAAY;AAAA,IAAW,SAAS;AAAA,IAAW,eAAe;AAAA,IAC1D,QAAQ;AAAA,IAAW,aAAa;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAMhC,SAAS,EAAE,GAAG,MAAM,MAAM,SAAS,OAAO,UAAU;AAAA,EACtD;AAAA,EACA,QAAQ,EAAE,IAAI,OAAO,IAAI,OAAO,IAAI,OAAO,IAAI,QAAQ,OAAO,QAAQ,MAAM,SAAS;AAAA,EACrF,QAAQ;AAAA,IACN,IAAI;AAAA,IAAqB,IAAI;AAAA,IAAqB,IAAI;AAAA,IACtD,IAAI;AAAA,IAAqB,IAAI;AAAA,IAAqB,OAAO;AAAA,IACzD,OAAO;AAAA,EACT;AAAA,EACA,QAAQ,EAAE,WAAW,OAAO,aAAa,OAAO,YAAY,OAAO,OAAO,QAAQ;AAAA,EAClF,QAAQ,EAAE,GAAG,MAAM,QAAQ,cAAc,QAAQ,gBAAgB,SAAS,cAAc,SAAS,cAAc,UAAU,gBAAgB,SAAS;AAAA,EAClJ,SAAS,EAAE,cAAc,MAAM;AACjC,CAAC;;;AC3BM,IAAM,YAAY,YAAY;AAAA,EACnC,GAAG;AAAA,EACH,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,OAAO;AAAA,IACP,aAAa;AAAA,EACf;AAAA,EACA,OAAO;AAAA,IACL,GAAG,KAAK;AAAA,IACR,YAAY;AAAA,IAAW,SAAS;AAAA,IAAW,eAAe;AAAA,IAC1D,QAAQ;AAAA,IAAW,aAAa;AAAA,IAAW,MAAM;AAAA,IAAW,WAAW;AAAA,EACzE;AAAA,EACA,QAAQ,EAAE,IAAI,OAAO,IAAI,OAAO,IAAI,OAAO,IAAI,OAAO,OAAO,QAAQ,MAAM,SAAS;AAAA,EACpF,QAAQ;AAAA,IACN,IAAI;AAAA,IAAgC,IAAI;AAAA,IACxC,IAAI;AAAA,IAAgC,IAAI;AAAA,IACxC,IAAI;AAAA,IAAiC,OAAO;AAAA,IAC5C,OAAO;AAAA,EACT;AAAA,EACA,QAAQ,EAAE,WAAW,OAAO,aAAa,SAAS,YAAY,OAAO,OAAO,QAAQ;AAAA,EACpF,QAAQ,EAAE,GAAG,KAAK,QAAQ,cAAc,+BAA+B,gBAAgB,4BAA4B;AAAA,EACnH,SAAS,EAAE,cAAc,MAAM;AACjC,CAAC;;;ACtBM,IAAM,WAAW,YAAY;AAAA,EAClC,GAAG;AAAA,EACH,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,OAAO;AAAA,IACP,aAAa;AAAA,EACf;AAAA,EACA,OAAO;AAAA,IACL,GAAG,MAAM;AAAA,IACT,YAAY;AAAA,IACZ,SAAS;AAAA,IACT,eAAe;AAAA,IACf,QAAQ;AAAA,IACR,aAAa;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAgBb,MAAM;AAAA,IACN,WAAW;AAAA,EACb;AAAA,EACA,QAAQ,EAAE,IAAI,QAAQ,IAAI,QAAQ,IAAI,QAAQ,IAAI,QAAQ,OAAO,QAAQ,MAAM,SAAS;AAAA,EACxF,QAAQ;AAAA,IACN,IAAI;AAAA,IAAoC,IAAI;AAAA,IAC5C,IAAI;AAAA,IAAoC,IAAI;AAAA,IAC5C,IAAI;AAAA,IAAoC,OAAO;AAAA,IAC/C,OAAO;AAAA,EACT;AAAA,EACA,QAAQ,EAAE,GAAG,MAAM,QAAQ,gBAAgB,SAAS,cAAc,QAAQ;AAAA,EAC1E,SAAS,EAAE,cAAc,MAAM;AACjC,CAAC;;;ACzCM,IAAM,WAAW,YAAY;AAAA,EAClC,GAAG;AAAA,EACH,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,OAAO;AAAA,IACP,aAAa;AAAA,EACf;AAAA,EACA,OAAO;AAAA,IACL,GAAG,MAAM;AAAA,IACT,SAAS;AAAA,IAAW,YAAY;AAAA,EAClC;AAAA,EACA,QAAQ,EAAE,IAAI,OAAO,IAAI,OAAO,IAAI,QAAQ,IAAI,QAAQ,OAAO,QAAQ,MAAM,SAAS;AAAA,EACtF,YAAY,EAAE,GAAG,MAAM,YAAY,UAAU,kCAAkC;AAAA,EAC/E,QAAQ;AAAA,IACN,IAAI;AAAA,IACJ,IAAI;AAAA,IACJ,IAAI;AAAA,IACJ,IAAI;AAAA,IACJ,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,OAAO;AAAA,EACT;AAAA,EACA,QAAQ,EAAE,GAAG,MAAM,QAAQ,cAAc,6BAA6B,gBAAgB,4BAA4B;AAAA,EAClH,SAAS,EAAE,cAAc,MAAM;AACjC,CAAC;;;ACxBM,IAAM,gBAAgB,YAAY;AAAA,EACvC,GAAG;AAAA,EACH,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,OAAO;AAAA,IACP,aAAa;AAAA,EACf;AAAA,EACA,OAAO;AAAA,IACL,GAAG,MAAM;AAAA,IACT,YAAY;AAAA,IAAW,SAAS;AAAA,IAAW,eAAe;AAAA,IAC1D,QAAQ;AAAA,IAAW,MAAM;AAAA,EAC3B;AAAA,EACA,QAAQ,EAAE,IAAI,OAAO,IAAI,QAAQ,IAAI,QAAQ,IAAI,QAAQ,OAAO,QAAQ,MAAM,SAAS;AAAA,EACvF,QAAQ;AAAA,IACN,IAAI;AAAA,IACJ,IAAI;AAAA,IACJ,IAAI;AAAA,IACJ,IAAI;AAAA,IACJ,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,OAAO;AAAA,EACT;AAAA,EACA,QAAQ,EAAE,GAAG,MAAM,QAAQ,gBAAgB,SAAS,cAAc,iCAAiC;AAAA,EACnG,SAAS,EAAE,cAAc,MAAM;AACjC,CAAC;;;ACbD,IAAM,WAAW,oBAAI,IAAyB;AAY9C,SAAS,eAAe,OAA0B;AAChD,QAAM,UAAU,sBAAsB,KAAK;AAC3C,MAAI,QAAQ,SAAS,GAAG;AACtB,UAAM,IAAI;AAAA,MACR,UAAU,MAAM,MAAM,QAAQ,SAAS,yCAAyC,QAAQ,KAAK,IAAI,CAAC;AAAA,IACpG;AAAA,EACF;AACF;AAEO,SAAS,cAAc,OAA0B;AACtD,iBAAe,KAAK;AACpB,WAAS,IAAI,MAAM,KAAK,MAAM,KAAK;AACrC;AAEO,SAAS,SAAS,MAAuC;AAC9D,SAAO,SAAS,IAAI,IAAI;AAC1B;AAEO,SAAS,aAA4B;AAC1C,SAAO,MAAM,KAAK,SAAS,OAAO,CAAC;AACrC;AAMA,cAAc,KAAK;AACnB,cAAc,IAAI;AAClB,cAAc,KAAK;AACnB,cAAc,SAAS;AACvB,cAAc,UAAU;AACxB,cAAc,SAAS;AACvB,cAAc,YAAY;AAC1B,cAAc,SAAS;AACvB,cAAc,QAAQ;AACtB,cAAc,QAAQ;AACtB,cAAc,aAAa;;;AC5DpB,IAAM,SAA2B;AAAA,EACtC,MAAM;AAAA,EACN,OAAO;AAAA,EACP,OAAO;AAAA,IACL,IAAI;AAAA,IAAW,KAAK;AAAA,IAAW,KAAK;AAAA,IAAW,KAAK;AAAA,IAAW,KAAK;AAAA,IACpE,KAAK;AAAA,IAAW,KAAK;AAAA,IAAW,KAAK;AAAA,IAAW,KAAK;AAAA,IAAW,KAAK;AAAA,EACvE;AACF;;;ACPO,IAAM,SAA2B;AAAA,EACtC,MAAM;AAAA,EACN,OAAO;AAAA,EACP,OAAO;AAAA,IACL,IAAI;AAAA,IAAW,KAAK;AAAA,IAAW,KAAK;AAAA,IAAW,KAAK;AAAA,IAAW,KAAK;AAAA,IACpE,KAAK;AAAA,IAAW,KAAK;AAAA,IAAW,KAAK;AAAA,IAAW,KAAK;AAAA,IAAW,KAAK;AAAA,EACvE;AACF;;;ACPO,IAAM,OAAyB;AAAA,EACpC,MAAM;AAAA,EACN,OAAO;AAAA,EACP,OAAO;AAAA,IACL,IAAI;AAAA,IAAW,KAAK;AAAA,IAAW,KAAK;AAAA,IAAW,KAAK;AAAA,IAAW,KAAK;AAAA,IACpE,KAAK;AAAA,IAAW,KAAK;AAAA,IAAW,KAAK;AAAA,IAAW,KAAK;AAAA,IAAW,KAAK;AAAA,EACvE;AACF;;;ACPO,IAAM,OAAyB;AAAA,EACpC,MAAM;AAAA,EACN,OAAO;AAAA,EACP,OAAO;AAAA,IACL,IAAI;AAAA,IAAW,KAAK;AAAA,IAAW,KAAK;AAAA,IAAW,KAAK;AAAA,IAAW,KAAK;AAAA,IACpE,KAAK;AAAA,IAAW,KAAK;AAAA,IAAW,KAAK;AAAA,IAAW,KAAK;AAAA,IAAW,KAAK;AAAA,EACvE;AACF;;;ACPO,IAAM,QAA0B;AAAA,EACrC,MAAM;AAAA,EACN,OAAO;AAAA,EACP,OAAO;AAAA,IACL,IAAI;AAAA,IAAW,KAAK;AAAA,IAAW,KAAK;AAAA,IAAW,KAAK;AAAA,IAAW,KAAK;AAAA,IACpE,KAAK;AAAA,IAAW,KAAK;AAAA,IAAW,KAAK;AAAA,IAAW,KAAK;AAAA,IAAW,KAAK;AAAA,EACvE;AACF;;;ACPO,IAAM,UAA4B;AAAA,EACvC,MAAM;AAAA,EACN,OAAO;AAAA,EACP,OAAO;AAAA,IACL,IAAI;AAAA,IAAW,KAAK;AAAA,IAAW,KAAK;AAAA,IAAW,KAAK;AAAA,IAAW,KAAK;AAAA,IACpE,KAAK;AAAA,IAAW,KAAK;AAAA,IAAW,KAAK;AAAA,IAAW,KAAK;AAAA,IAAW,KAAK;AAAA,EACvE;AACF;;;ACPO,IAAM,SAA2B;AAAA,EACtC,MAAM;AAAA,EACN,OAAO;AAAA,EACP,OAAO;AAAA,IACL,IAAI;AAAA,IAAW,KAAK;AAAA,IAAW,KAAK;AAAA,IAAW,KAAK;AAAA,IACpD,KAAK;AAAA,IAAW,KAAK;AAAA,IAAW,KAAK;AAAA,IAAW,KAAK;AAAA,IAAW,KAAK;AAAA,IAAW,KAAK;AAAA,EACvF;AACF;;;ACPO,IAAM,WAA6B;AAAA,EACxC,MAAM;AAAA,EACN,OAAO;AAAA,EACP,OAAO;AAAA,IACL,IAAI;AAAA,IAAW,KAAK;AAAA,IAAW,KAAK;AAAA,IAAW,KAAK;AAAA,IAAW,KAAK;AAAA,IACpE,KAAK;AAAA,IAAW,KAAK;AAAA,IAAW,KAAK;AAAA,IAAW,KAAK;AAAA,IAAW,KAAK;AAAA,EACvE;AACF;;;ACPO,IAAM,UAA4B;AAAA,EACvC,MAAM;AAAA,EACN,OAAO;AAAA,EACP,OAAO;AAAA,IACL,IAAI;AAAA,IAAW,KAAK;AAAA,IAAW,KAAK;AAAA,IAAW,KAAK;AAAA,IAAW,KAAK;AAAA,IACpE,KAAK;AAAA,IAAW,KAAK;AAAA,IAAW,KAAK;AAAA,IAAW,KAAK;AAAA,IAAW,KAAK;AAAA,EACvE;AACF;;;ACaA,IAAMA,YAAW,oBAAI,IAA8B;AAEnD,SAAS,oBAAoB,KAA6B;AACxD,QAAM,UAAW,CAAC,MAAM,OAAO,OAAO,OAAO,OAAO,OAAO,OAAO,OAAO,OAAO,KAAK,EAAY;AAAA,IAC/F,CAAC,MAAM,CAAC,IAAI,MAAM,CAAgC;AAAA,EACpD;AACA,MAAI,QAAQ,SAAS,GAAG;AACtB,UAAM,IAAI,MAAM,WAAW,IAAI,IAAI,+BAA+B,QAAQ,KAAK,IAAI,CAAC,EAAE;AAAA,EACxF;AACF;AAEO,SAAS,eAAe,KAA6B;AAC1D,sBAAoB,GAAG;AACvB,EAAAA,UAAS,IAAI,IAAI,MAAM,GAAG;AAC5B;AAEO,SAAS,UAAU,MAA4C;AACpE,SAAOA,UAAS,IAAI,IAAI;AAC1B;AAEO,SAAS,cAAkC;AAChD,SAAO,MAAM,KAAKA,UAAS,OAAO,CAAC;AACrC;AAeA,eAAe,MAAM;AACrB,eAAe,MAAM;AACrB,eAAe,IAAI;AACnB,eAAe,IAAI;AACnB,eAAe,KAAK;AACpB,eAAe,OAAO;AACtB,eAAe,MAAM;AACrB,eAAe,QAAQ;AACvB,eAAe,OAAO;AAEf,IAAM,iBAAiB;;;ACvCvB,SAAS,kBAAkB,OAA+B;AAC/D,SAAO;AAAA;AAAA;AAAA,IAGL,YAAY,MAAM,YAAY;AAAA,IAC9B,YAAY,MAAM,YAAY;AAAA,IAC9B,WAAW,MAAM,SAAS;AAAA,IAC1B,MAAM,MAAM,QAAQ;AAAA,EACtB;AACF;AAQO,SAAS,kBAAkB,OAAmB,aAAsB;AACzE,SAAO;AAAA,IACL,gBAAgB,MAAM,YAAa,OAAiB;AAAA,IACpD,iBAAiB,MAAM,aAAc,OAAiB;AAAA,IACtD,oBAAoB;AAAA,EACtB;AACF;;;ACjBO,IAAM,kBAA4B;AAAA,EACvC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEO,SAAS,wBAAwB,OAA+C;AACrF,SAAO;AAAA,IACL,GAAG,kBAAkB,KAAK;AAAA,IAC1B,SAAS,MAAM,WAAW;AAAA,IAC1B,SAAS,MAAM,WAAW;AAAA,EAC5B;AACF;AAIA,IAAM,SAAS;AAeR,SAAS,aAAa,MAA6B;AACxD,QAAM,UAAU,KAAK,KAAK;AAC1B,QAAM,QAAQ,QAAQ,MAAM,MAAM;AAClC,MAAI,CAAC,MAAO,QAAO;AACnB,MAAI,MAAM,MAAM,CAAC,EAAE,YAAY;AAC/B,MAAI,IAAI,WAAW,GAAG;AACpB,UAAM,IACH,MAAM,EAAE,EACR,IAAI,CAAC,MAAM,IAAI,CAAC,EAChB,KAAK,EAAE;AAAA,EACZ;AACA,SAAO,IAAI,GAAG;AAChB;AAGO,SAAS,YAAY,GAA8B,GAAuC;AAC/F,MAAI,CAAC,KAAK,CAAC,EAAG,QAAO,MAAM;AAC3B,QAAM,KAAK,aAAa,CAAC;AACzB,QAAM,KAAK,aAAa,CAAC;AACzB,SAAO,OAAO,QAAQ,OAAO;AAC/B;AAeO,SAAS,iBAAiB,OAA2D;AAC1F,SAAO;AAAA,IACL;AAAA,IACA,MAAM,cAAc;AAAA,IACpB,MAAM,YAAY;AAAA,EACpB,EACG,OAAO,OAAO,EACd,KAAK,GAAG;AACb;AAIO,SAAS,uBAAuB,OAAe;AACpD,SAAO,EAAE,MAAM,WAAoB,cAAc,MAAM;AACzD;AAEO,SAAS,mBAAmB,OAAqE;AACtG,SAAO;AAAA,IACL,MAAM;AAAA,IACN,iBAAiB,MAAM;AAAA,IACvB,cAAc,MAAM;AAAA,IACpB,UAAU,MAAM,cAAc,IAAI;AAAA,EACpC;AACF;AAEO,SAAS,yBAAyB,OAAmB,aAAsB;AAChF,SAAO,kBAAkB,OAAO,WAAW;AAC7C;AAgCO,IAAM,gBAA+B,CAAC,OAAO,OAAO,KAAK;AAEhE,SAAS,SAAS,GAAW,KAAa,KAAqB;AAC7D,SAAO,KAAK,IAAI,KAAK,IAAI,GAAG,GAAG,GAAG,GAAG;AACvC;AAGO,SAAS,SAAS,KAAyB;AAChD,QAAM,aAAa,aAAa,GAAG;AACnC,MAAI,CAAC,WAAY,QAAO;AACxB,SAAO;AAAA,IACL,GAAG,SAAS,WAAW,MAAM,GAAG,CAAC,GAAG,EAAE;AAAA,IACtC,GAAG,SAAS,WAAW,MAAM,GAAG,CAAC,GAAG,EAAE;AAAA,IACtC,GAAG,SAAS,WAAW,MAAM,GAAG,CAAC,GAAG,EAAE;AAAA,EACxC;AACF;AACO,SAAS,SAAS,EAAE,GAAG,GAAG,EAAE,GAAgB;AACjD,QAAM,UAAU,CAAC,MAAc,SAAS,KAAK,MAAM,CAAC,GAAG,GAAG,GAAG,EAAE,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG;AAC3F,SAAO,IAAI,QAAQ,CAAC,CAAC,GAAG,QAAQ,CAAC,CAAC,GAAG,QAAQ,CAAC,CAAC;AACjD;AAGO,SAAS,SAAS,EAAE,GAAG,GAAG,EAAE,GAAa;AAC9C,QAAM,KAAK,IAAI,KACb,KAAK,IAAI,KACT,KAAK,IAAI;AACX,QAAM,MAAM,KAAK,IAAI,IAAI,IAAI,EAAE,GAC7B,MAAM,KAAK,IAAI,IAAI,IAAI,EAAE;AAC3B,QAAM,IAAI,MAAM;AAChB,MAAI,IAAI;AACR,MAAI,MAAM,GAAG;AACX,QAAI,QAAQ,GAAI,MAAM,KAAK,MAAM,IAAK;AAAA,aAC7B,QAAQ,GAAI,MAAK,KAAK,MAAM,IAAI;AAAA,QACpC,MAAK,KAAK,MAAM,IAAI;AACzB,SAAK;AACL,QAAI,IAAI,EAAG,MAAK;AAAA,EAClB;AACA,QAAM,IAAI,QAAQ,IAAI,IAAI,IAAI;AAC9B,SAAO,EAAE,GAAG,GAAG,IAAI,KAAK,GAAG,MAAM,IAAI;AACvC;AAGO,SAAS,SAAS,EAAE,GAAG,GAAG,EAAE,GAAa;AAC9C,QAAM,KAAK,SAAS,GAAG,GAAG,GAAG,IAAI;AACjC,QAAM,KAAK,SAAS,GAAG,GAAG,GAAG,IAAI;AACjC,QAAM,MAAQ,IAAI,MAAO,OAAO,MAAO;AACvC,QAAM,IAAI,KAAK;AACf,QAAM,IAAI,KAAK,IAAI,KAAK,IAAK,KAAK,IAAK,CAAC;AACxC,QAAM,IAAI,KAAK;AACf,MAAI,CAAC,IAAI,IAAI,EAAE,IAAI,CAAC,GAAG,GAAG,CAAC;AAC3B,MAAI,KAAK,EAAG,EAAC,IAAI,IAAI,EAAE,IAAI,CAAC,GAAG,GAAG,CAAC;AAAA,WAC1B,KAAK,EAAG,EAAC,IAAI,IAAI,EAAE,IAAI,CAAC,GAAG,GAAG,CAAC;AAAA,WAC/B,KAAK,EAAG,EAAC,IAAI,IAAI,EAAE,IAAI,CAAC,GAAG,GAAG,CAAC;AAAA,WAC/B,KAAK,EAAG,EAAC,IAAI,IAAI,EAAE,IAAI,CAAC,GAAG,GAAG,CAAC;AAAA,WAC/B,KAAK,EAAG,EAAC,IAAI,IAAI,EAAE,IAAI,CAAC,GAAG,GAAG,CAAC;AAAA,MACnC,EAAC,IAAI,IAAI,EAAE,IAAI,CAAC,GAAG,GAAG,CAAC;AAC5B,SAAO,EAAE,IAAI,KAAK,KAAK,KAAK,IAAI,KAAK,KAAK,KAAK,IAAI,KAAK,KAAK,IAAI;AACnE;AAGO,SAAS,SAAS,EAAE,GAAG,GAAG,EAAE,GAAa;AAC9C,QAAM,KAAK,IAAI,KACb,KAAK,IAAI,KACT,KAAK,IAAI;AACX,QAAM,MAAM,KAAK,IAAI,IAAI,IAAI,EAAE,GAC7B,MAAM,KAAK,IAAI,IAAI,IAAI,EAAE;AAC3B,QAAM,KAAK,MAAM,OAAO;AACxB,QAAM,IAAI,MAAM;AAChB,MAAI,IAAI,GACN,IAAI;AACN,MAAI,MAAM,GAAG;AACX,QAAI,IAAI,MAAM,KAAK,IAAI,MAAM,OAAO,KAAK,MAAM;AAC/C,QAAI,QAAQ,GAAI,MAAM,KAAK,MAAM,IAAK;AAAA,aAC7B,QAAQ,GAAI,MAAK,KAAK,MAAM,IAAI;AAAA,QACpC,MAAK,KAAK,MAAM,IAAI;AACzB,SAAK;AACL,QAAI,IAAI,EAAG,MAAK;AAAA,EAClB;AACA,SAAO,EAAE,GAAG,GAAG,IAAI,KAAK,GAAG,IAAI,IAAI;AACrC;AAGO,SAAS,SAAS,EAAE,GAAG,GAAG,EAAE,GAAa;AAC9C,QAAM,KAAK,SAAS,GAAG,GAAG,GAAG,IAAI;AACjC,QAAM,KAAK,SAAS,GAAG,GAAG,GAAG,IAAI;AACjC,QAAM,KAAK,IAAI,KAAK,IAAI,IAAI,KAAK,CAAC,KAAK;AACvC,QAAM,MAAQ,IAAI,MAAO,OAAO,MAAO;AACvC,QAAM,IAAI,KAAK,IAAI,KAAK,IAAK,KAAK,IAAK,CAAC;AACxC,QAAM,IAAI,KAAK,IAAI;AACnB,MAAI,CAAC,IAAI,IAAI,EAAE,IAAI,CAAC,GAAG,GAAG,CAAC;AAC3B,MAAI,KAAK,EAAG,EAAC,IAAI,IAAI,EAAE,IAAI,CAAC,GAAG,GAAG,CAAC;AAAA,WAC1B,KAAK,EAAG,EAAC,IAAI,IAAI,EAAE,IAAI,CAAC,GAAG,GAAG,CAAC;AAAA,WAC/B,KAAK,EAAG,EAAC,IAAI,IAAI,EAAE,IAAI,CAAC,GAAG,GAAG,CAAC;AAAA,WAC/B,KAAK,EAAG,EAAC,IAAI,IAAI,EAAE,IAAI,CAAC,GAAG,GAAG,CAAC;AAAA,WAC/B,KAAK,EAAG,EAAC,IAAI,IAAI,EAAE,IAAI,CAAC,GAAG,GAAG,CAAC;AAAA,MACnC,EAAC,IAAI,IAAI,EAAE,IAAI,CAAC,GAAG,GAAG,CAAC;AAC5B,SAAO,EAAE,IAAI,KAAK,KAAK,KAAK,IAAI,KAAK,KAAK,KAAK,IAAI,KAAK,KAAK,IAAI;AACnE;AAGO,SAAS,SAAS,KAAyB;AAChD,QAAM,MAAM,SAAS,GAAG;AACxB,SAAO,MAAM,SAAS,GAAG,IAAI;AAC/B;AAGO,SAAS,SAAS,KAAkB;AACzC,SAAO,SAAS,SAAS,GAAG,CAAC;AAC/B;AAGO,SAAS,SAAS,KAAkB;AACzC,SAAO,SAAS,SAAS,GAAG,CAAC;AAC/B;AAUA,IAAM,kBAAkB;AACxB,IAAM,kBAAkB;AAGjB,SAAS,gBAAgB,MAA6B;AAC3D,QAAM,UAAU,KAAK,KAAK;AAC1B,QAAM,MAAM,aAAa,OAAO;AAChC,MAAI,IAAK,QAAO;AAEhB,QAAM,WAAW,QAAQ,MAAM,eAAe;AAC9C,MAAI,UAAU;AACZ,UAAM,CAAC,EAAE,GAAG,GAAG,CAAC,IAAI;AACpB,WAAO,SAAS,EAAE,GAAG,SAAS,OAAO,CAAC,GAAG,GAAG,GAAG,GAAG,GAAG,SAAS,OAAO,CAAC,GAAG,GAAG,GAAG,GAAG,GAAG,SAAS,OAAO,CAAC,GAAG,GAAG,GAAG,EAAE,CAAC;AAAA,EACpH;AAEA,QAAM,WAAW,QAAQ,MAAM,eAAe;AAC9C,MAAI,UAAU;AACZ,UAAM,CAAC,EAAE,GAAG,GAAG,CAAC,IAAI;AACpB,WAAO,SAAS,EAAE,GAAG,SAAS,OAAO,CAAC,GAAG,GAAG,GAAG,GAAG,GAAG,SAAS,OAAO,CAAC,GAAG,GAAG,GAAG,GAAG,GAAG,SAAS,OAAO,CAAC,GAAG,GAAG,GAAG,EAAE,CAAC;AAAA,EACpH;AAEA,SAAO;AACT;AAGO,SAAS,iBAAiB,KAAa,QAA6B;AACzE,MAAI,WAAW,MAAO,QAAO;AAC7B,QAAM,MAAM,SAAS,GAAG;AACxB,MAAI,CAAC,IAAK,QAAO;AACjB,MAAI,WAAW,MAAO,QAAO,OAAO,KAAK,MAAM,IAAI,CAAC,CAAC,KAAK,KAAK,MAAM,IAAI,CAAC,CAAC,KAAK,KAAK,MAAM,IAAI,CAAC,CAAC;AACjG,QAAM,MAAM,SAAS,GAAG;AACxB,SAAO,OAAO,KAAK,MAAM,IAAI,CAAC,CAAC,KAAK,KAAK,MAAM,IAAI,CAAC,CAAC,MAAM,KAAK,MAAM,IAAI,CAAC,CAAC;AAC9E;AAGO,SAAS,mBAAmB,KAAa,QAAoD;AAClG,QAAM,OAAO,SAAS,KAAK;AAC3B,UAAQ,KAAK;AAAA,IACX,KAAK;AACH,aAAO,EAAE,IAAI,CAAC,MAAM,IAAI,EAAE;AAAA,IAC5B,KAAK;AACH,aAAO,EAAE,IAAI,MAAM,IAAI,EAAE;AAAA,IAC3B,KAAK;AACH,aAAO,EAAE,IAAI,GAAG,IAAI,KAAK;AAAA,IAC3B,KAAK;AACH,aAAO,EAAE,IAAI,GAAG,IAAI,CAAC,KAAK;AAAA,IAC5B;AACE,aAAO;AAAA,EACX;AACF;AAGO,SAAS,aAAa,GAAmB;AAC9C,SAAO,SAAS,GAAG,GAAG,GAAG;AAC3B;AAIO,SAAS,oBAAoB,UAA2B;AAC7D,SAAO,CAAC,8BAA8B,YAAY,oCAAoC,EAAE,OAAO,OAAO,EAAE,KAAK,GAAG;AAClH;AAEO,SAAS,0BAA0B,OAAe;AACvD,SAAO,EAAE,MAAM,WAAoB,cAAc,MAAM;AACzD;AAEO,SAAS,sBAAsB,UAAmB;AACvD,SAAO,EAAE,MAAM,OAAgB,iBAAiB,UAAU,UAAU,WAAW,IAAI,GAAG;AACxF;AAEO,SAAS,oBAAoB,YAAoB,OAAe;AACrE,SAAO;AAAA,IACL,MAAM;AAAA,IACN,cAAc;AAAA,IACd,kBAAkB,cAAc,KAAK,MAAM,UAAU,CAAC,iBAAiB,KAAK,MAAM,KAAK,CAAC;AAAA,IACxF,UAAU;AAAA,EACZ;AACF;;;ACjVO,SAAS,kBAAkB,OAAmC;AACnE,SAAO;AAAA,IACL,MAAM,MAAM,QAAQ;AAAA,IACpB,SAAS,MAAM,WAAW;AAAA,IAC1B,UAAU,MAAM,YAAY;AAAA,IAC5B,cAAc,MAAM,gBAAgB;AAAA,EACtC;AACF;AAEO,SAAS,kBAAkB,WAA4B;AAC5D,SAAO,CAAC,oBAAoB,SAAS,EAAE,OAAO,OAAO,EAAE,KAAK,GAAG;AACjE;AAEO,SAAS,gBAAgB,OAAmB,WAA4B;AAC7E,SAAO;AAAA,IACL;AAAA,IACA,aAAa,MAAM,IAAI;AAAA,IACvB,MAAM,WAAW;AAAA,IACjB,MAAM,YAAY;AAAA,IAClB,MAAM,gBAAgB;AAAA,IACtB;AAAA,EACF,EACG,OAAO,OAAO,EACd,KAAK,GAAG;AACb;AAEO,SAAS,cAAc,UAAmB,WAAoB,WAA4B;AAC/F,SAAO;AAAA,IACL;AAAA,IACA,YAAY;AAAA,IACZ,aAAa;AAAA,IACb;AAAA,EACF,EACG,OAAO,OAAO,EACd,KAAK,GAAG;AACb;AAEO,SAAS,mBAAmB,UAAmB,WAA4B;AAChF,SAAO,CAAC,uBAAuB,YAAY,iCAAiC,SAAS,EAAE,OAAO,OAAO,EAAE,KAAK,GAAG;AACjH;AAEO,SAAS,eAAe,WAA4B;AACzD,SAAO,CAAC,kBAAkB,SAAS,EAAE,OAAO,OAAO,EAAE,KAAK,GAAG;AAC/D;AAOO,SAAS,qBAAqB,SAAuC;AAC1E,MAAI,YAAY,KAAM,QAAO;AAC7B,MAAI,YAAY,MAAO,QAAO;AAC9B,SAAO;AACT;AAEO,SAAS,iBAAiB,WAA0B;AACzD,SAAO;AAAA,IACL,aACE,cAAc,QAAS,cAAwB,cAAc,SAAU,eAA0B;AAAA,EACrG;AACF;AAOO,SAAS,kBAAkB,YAAoB,eAAuC;AAC3F,MAAI,cAAc,KAAK,iBAAiB,EAAG,QAAO;AAClD,MAAI,iBAAiB,WAAY,QAAO;AACxC,SAAO;AACT;;;ACxFO,IAAM,0BAA0B;AAWhC,SAAS,mBAAmB,OAAqC;AACtE,SAAO,EAAE,SAAS,MAAM,WAAW,MAAM;AAC3C;AAEO,SAAS,gBAAgB,OAAoB,WAA4B;AAC9E,SAAO,CAAC,aAAa,MAAM,WAAW,sBAAsB,SAAS,EAAE,OAAO,OAAO,EAAE,KAAK,GAAG;AACjG;AAEO,SAAS,iBAAiB,cAAuB,YAAqB,WAA4B;AACvG,SAAO;AAAA,IACL;AAAA,IACA,gBAAgB;AAAA,IAChB,cAAc;AAAA,IACd;AAAA,EACF,EACG,OAAO,OAAO,EACd,KAAK,GAAG;AACb;AAEO,SAAS,sBAAsB,WAA4B;AAChE,SAAO,CAAC,2BAA2B,aAAa,qCAAqC,EAAE,OAAO,OAAO,EAAE,KAAK,GAAG;AACjH;AAQO,SAAS,sBAAsB,MAAwB,WAA4B;AACxF,SAAO;AAAA,IACL;AAAA,IACA,KAAK,qBAAqB;AAAA,IAC1B,KAAK,cAAc;AAAA,IACnB,KAAK,YAAY;AAAA,IACjB;AAAA,EACF,EACG,OAAO,OAAO,EACd,KAAK,GAAG;AACb;AAmBO,SAAS,iBAAiB,OAAyB,QAA2C;AACnG,aAAW,UAAU,MAAM,SAAS;AAClC,UAAM,QAAQ,OAAO,QAAQ,QAAQ,MAAM;AAC3C,QAAI,UAAU,GAAI,QAAO,EAAE,UAAU,OAAO,IAAI,MAAM;AAAA,EACxD;AACA,SAAO;AACT;AASO,SAAS,WAAW,OAAyB,QAAkC;AACpF,QAAM,WAAW,iBAAiB,OAAO,MAAM;AAC/C,MAAI,CAAC,SAAU,QAAO;AACtB,SAAO;AAAA,IACL,SAAS,MAAM,QAAQ;AAAA,MAAI,CAAC,WAC1B,OAAO,OAAO,SAAS,WAAW,EAAE,GAAG,QAAQ,SAAS,OAAO,QAAQ,OAAO,CAAC,OAAO,OAAO,MAAM,EAAE,IAAI;AAAA,IAC3G;AAAA,EACF;AACF;AAiBO,SAAS,SAAS,OAAyB,QAA0C;AAC1F,QAAM,WAAW,iBAAiB,OAAO,OAAO,MAAM;AACtD,QAAM,aAAa,MAAM,QAAQ,KAAK,CAAC,WAAW,OAAO,OAAO,OAAO,UAAU;AACjF,MAAI,CAAC,YAAY,CAAC,WAAY,QAAO;AAErC,QAAM,cAAc,MAAM,QAAQ;AAAA,IAAI,CAAC,WACrC,OAAO,QAAQ,SAAS,OAAO,MAAM,IACjC,EAAE,GAAG,QAAQ,SAAS,OAAO,QAAQ,OAAO,CAAC,OAAO,OAAO,OAAO,MAAM,EAAE,IAC1E;AAAA,EACN;AAEA,SAAO;AAAA,IACL,SAAS,YAAY,IAAI,CAAC,WAAW;AACnC,UAAI,OAAO,OAAO,OAAO,WAAY,QAAO;AAC5C,YAAM,eAAe,KAAK,IAAI,GAAG,KAAK,IAAI,OAAO,SAAS,OAAO,QAAQ,MAAM,CAAC;AAChF,YAAM,UAAU,CAAC,GAAG,OAAO,OAAO;AAClC,cAAQ,OAAO,cAAc,GAAG,OAAO,MAAM;AAC7C,aAAO,EAAE,GAAG,QAAQ,QAAQ;AAAA,IAC9B,CAAC;AAAA,EACH;AACF;AAGO,SAAS,oBAAoB,OAAyB,UAAkB,WAAkC;AAC/G,QAAM,QAAQ,MAAM,QAAQ,UAAU,CAAC,WAAW,OAAO,OAAO,QAAQ;AACxE,MAAI,UAAU,GAAI,QAAO;AACzB,SAAO,MAAM,QAAQ,QAAQ,SAAS,GAAG,MAAM;AACjD;AAaO,SAAS,sBACd,OACA,QACA,WACuB;AACvB,QAAM,WAAW,iBAAiB,OAAO,MAAM;AAC/C,MAAI,CAAC,SAAU,QAAO;AACtB,QAAM,SAAS,MAAM,QAAQ,KAAK,CAAC,MAAM,EAAE,OAAO,SAAS,QAAQ;AACnE,MAAI,CAAC,OAAQ,QAAO;AAEpB,MAAI,cAAc,QAAQ,cAAc,QAAQ;AAC9C,UAAMC,WAAU,cAAc,OAAO,SAAS,QAAQ,IAAI,SAAS,QAAQ;AAC3E,QAAIA,WAAU,KAAKA,WAAU,OAAO,QAAQ,SAAS,EAAG,QAAO;AAC/D,WAAO,EAAE,QAAQ,YAAY,SAAS,UAAU,SAAAA,SAAQ;AAAA,EAC1D;AAEA,QAAM,mBAAmB,oBAAoB,OAAO,SAAS,UAAU,cAAc,SAAS,KAAK,CAAC;AACpG,MAAI,CAAC,iBAAkB,QAAO;AAC9B,QAAM,aAAa,MAAM,QAAQ,KAAK,CAAC,MAAM,EAAE,OAAO,gBAAgB;AACtE,MAAI,CAAC,WAAY,QAAO;AACxB,QAAM,UAAU,KAAK,IAAI,SAAS,OAAO,WAAW,QAAQ,MAAM;AAClE,SAAO,EAAE,QAAQ,YAAY,kBAAkB,QAAQ;AACzD;AAmBO,SAAS,iBAAiB,UAAkB,WAA+B;AAChF,WAAS,IAAI,GAAG,IAAI,UAAU,QAAQ,KAAK;AACzC,UAAM,YAAY,UAAU,CAAC,EAAE,MAAM,UAAU,CAAC,EAAE,UAAU;AAC5D,QAAI,WAAW,SAAU,QAAO;AAAA,EAClC;AACA,SAAO,UAAU;AACnB;AAGO,SAAS,kBACd,UACA,UACA,SACe;AACf,aAAW,UAAU,SAAS;AAC5B,QAAI,YAAY,OAAO,QAAQ,YAAY,OAAO,SAAS,YAAY,OAAO,OAAO,YAAY,OAAO,QAAQ;AAC9G,aAAO,OAAO;AAAA,IAChB;AAAA,EACF;AACA,SAAO;AACT;AAiBO,SAAS,WAAW,OAAyB,QAA4C;AAC9F,QAAM,YAAY,MAAM,QAAQ,UAAU,CAAC,MAAM,EAAE,OAAO,OAAO,QAAQ;AACzE,MAAI,cAAc,GAAI,QAAO;AAC7B,QAAM,UAAU,CAAC,GAAG,MAAM,OAAO;AACjC,QAAM,CAAC,KAAK,IAAI,QAAQ,OAAO,WAAW,CAAC;AAC3C,QAAM,eAAe,KAAK,IAAI,GAAG,KAAK,IAAI,OAAO,SAAS,QAAQ,MAAM,CAAC;AACzE,UAAQ,OAAO,cAAc,GAAG,KAAK;AACrC,SAAO,EAAE,QAAQ;AACnB;AAGO,SAAS,aAAa,OAAyB,UAAoC;AACxF,MAAI,CAAC,MAAM,QAAQ,KAAK,CAAC,MAAM,EAAE,OAAO,QAAQ,EAAG,QAAO;AAC1D,SAAO,EAAE,SAAS,MAAM,QAAQ,OAAO,CAAC,MAAM,EAAE,OAAO,QAAQ,EAAE;AACnE;AAKO,SAAS,4BACd,OACA,UACA,WACyB;AACzB,QAAM,YAAY,MAAM,QAAQ,UAAU,CAAC,MAAM,EAAE,OAAO,QAAQ;AAClE,MAAI,cAAc,GAAI,QAAO;AAC7B,QAAM,UAAU,cAAc,SAAS,YAAY,IAAI,YAAY;AACnE,MAAI,UAAU,KAAK,UAAU,MAAM,QAAQ,SAAS,EAAG,QAAO;AAC9D,SAAO,EAAE,UAAU,QAAQ;AAC7B;AAQO,SAAS,uBAAuB,UAAkB,aAAiC;AACxF,WAAS,IAAI,GAAG,IAAI,YAAY,QAAQ,KAAK;AAC3C,UAAM,YAAY,YAAY,CAAC,EAAE,OAAO,YAAY,CAAC,EAAE,SAAS;AAChE,QAAI,WAAW,SAAU,QAAO;AAAA,EAClC;AACA,SAAO,YAAY;AACrB;;;ACzNA,SAAS,aAAa,QAAgB,SAA+B;AACnE,QAAM,SAAsB,CAAC;AAC7B,QAAM,IAAI,OAAO;AACjB,MAAI,IAAI;AAER,QAAO,QAAO,IAAI,GAAG;AACnB,eAAW,QAAQ,QAAQ,OAAO;AAChC,UAAI,KAAK,SAAS;AAChB,aAAK,QAAQ,YAAY;AACzB,cAAM,IAAI,KAAK,QAAQ,KAAK,MAAM;AAClC,YAAI,KAAK,EAAE,UAAU,KAAK,EAAE,CAAC,EAAE,SAAS,GAAG;AACzC,iBAAO,KAAK,EAAE,MAAM,KAAK,MAAM,SAAS,EAAE,CAAC,EAAE,CAAC;AAC9C,eAAK,EAAE,CAAC,EAAE;AACV,mBAAS;AAAA,QACX;AAAA,MACF,WAAW,KAAK,OAAO;AACrB,cAAM,SAAS,KAAK,MAAM,QAAQ,GAAG,MAAM;AAC3C,YAAI,UAAU,OAAO,SAAS,GAAG;AAC/B,iBAAO,KAAK,GAAG,OAAO,MAAM;AAC5B,eAAK,OAAO;AACZ,mBAAS;AAAA,QACX;AAAA,MACF;AAAA,IACF;AACA,UAAM,KAAK,OAAO,CAAC;AACnB,UAAM,OAAO,OAAO,OAAO,SAAS,CAAC;AACrC,QAAI,QAAQ,KAAK,SAAS,QAAS,MAAK,WAAW;AAAA,QAC9C,QAAO,KAAK,EAAE,MAAM,SAAS,SAAS,GAAG,CAAC;AAC/C;AAAA,EACF;AAEA,SAAO;AACT;AAQA,SAAS,eAAe,QAAiC;AACvD,QAAM,QAAoB,CAAC,CAAC,CAAC;AAC7B,aAAW,OAAO,QAAQ;AACxB,UAAM,QAAQ,IAAI,QAAQ,MAAM,IAAI;AACpC,UAAM,QAAQ,CAAC,MAAM,QAAQ;AAC3B,UAAI,KAAK,SAAS,EAAG,OAAM,MAAM,SAAS,CAAC,EAAE,KAAK,EAAE,MAAM,IAAI,MAAM,SAAS,KAAK,CAAC;AACnF,UAAI,MAAM,MAAM,SAAS,EAAG,OAAM,KAAK,CAAC,CAAC;AAAA,IAC3C,CAAC;AAAA,EACH;AACA,SAAO;AACT;AAEA,IAAM,UAAU,CAAC,UAAkB,IAAI,OAAO,SAAS,KAAK,QAAQ,GAAG;AAYvE,SAAS,qBACP,QACA,OACAC,YACgD;AAChD,MAAI,OAAO,KAAK,MAAM,IAAK,QAAO;AAElC,QAAM,SAAsB,CAAC,EAAE,MAAM,UAAU,SAAS,IAAI,CAAC;AAC7D,MAAI,IAAI,QAAQ;AAChB,MAAI,MAAM;AACV,QAAM,QAAQ,MAAM;AAClB,QAAI,KAAK;AACP,aAAO,KAAK,EAAE,MAAM,UAAU,SAAS,IAAI,CAAC;AAC5C,YAAM;AAAA,IACR;AAAA,EACF;AAEA,SAAO,IAAI,OAAO,QAAQ;AACxB,UAAM,KAAK,OAAO,CAAC;AACnB,QAAI,OAAO,QAAQ,IAAI,IAAI,OAAO,QAAQ;AACxC,aAAO,KAAK,OAAO,IAAI,CAAC;AACxB,WAAK;AACL;AAAA,IACF;AACA,QAAI,OAAO,KAAK;AACd,YAAM;AACN,aAAO,KAAK,EAAE,MAAM,UAAU,SAAS,IAAI,CAAC;AAC5C;AACA,aAAO,EAAE,QAAQ,IAAI,OAAO,OAAO;AAAA,IACrC;AACA,QAAI,OAAO,OAAO,OAAO,IAAI,CAAC,MAAM,KAAK;AACvC,YAAM;AACN,aAAO,KAAK,EAAE,MAAM,eAAe,SAAS,KAAK,CAAC;AAClD,WAAK;AACL,YAAM,YAAY;AAClB,UAAI,QAAQ;AACZ,aAAO,IAAI,OAAO,UAAU,QAAQ,GAAG;AACrC,YAAI,OAAO,CAAC,MAAM,IAAK;AAAA,iBACd,OAAO,CAAC,MAAM,IAAK;AAC5B,YAAI,QAAQ,EAAG;AAAA,MACjB;AACA,YAAM,aAAa,OAAO,MAAM,WAAW,CAAC;AAC5C,aAAO,KAAK,GAAG,aAAa,YAAYA,UAAS,CAAC;AAClD,UAAI,IAAI,OAAO,QAAQ;AACrB,eAAO,KAAK,EAAE,MAAM,eAAe,SAAS,IAAI,CAAC;AACjD;AAAA,MACF;AACA;AAAA,IACF;AACA,WAAO;AACP;AAAA,EACF;AAIA,QAAM;AACN,SAAO,EAAE,QAAQ,IAAI,OAAO,OAAO;AACrC;AAEA,IAAM,4BAA4B,oBAAI,IAAI;AAAA,EACxC;AAAA,EAAU;AAAA,EAAU;AAAA,EAAc;AAAA,EAAM;AAAA,EAAM;AAAA,EAAO;AAAA,EAAU;AAAA,EAAQ;AAAA,EAAS;AAAA,EAAQ;AAAA,EAAM;AAAA,EAAQ;AAAA,EAAS;AACjH,CAAC;AAiBD,SAAS,kBAAkB,QAAgB,OAA+D;AACxG,MAAI,OAAO,KAAK,MAAM,OAAO,OAAO,QAAQ,CAAC,MAAM,OAAO,OAAO,QAAQ,CAAC,MAAM,IAAK,QAAO;AAE5F,MAAI,IAAI,QAAQ;AAChB,SAAO,KAAK,KAAK,KAAK,KAAK,OAAO,CAAC,CAAC,EAAG;AACvC,MAAI,KAAK,GAAG;AACV,UAAM,WAAW,OAAO,CAAC;AACzB,UAAM,oBAAoB,YAAY,KAAK,QAAQ,KAAK,aAAa,OAAO,aAAa,OAAO,aAAa;AAC7G,QAAI,mBAAmB;AACrB,YAAM,aAAa,OAAO,MAAM,GAAG,IAAI,CAAC,EAAE,MAAM,qBAAqB,IAAI,CAAC;AAC1E,UAAI,EAAE,cAAc,0BAA0B,IAAI,UAAU,GAAI,QAAO;AAAA,IACzE;AAAA,EACF;AAEA,MAAI,IAAI,QAAQ;AAChB,MAAI,UAAU;AACd,SAAO,IAAI,OAAO,QAAQ;AACxB,UAAM,KAAK,OAAO,CAAC;AACnB,QAAI,OAAO,MAAM;AACf,WAAK;AACL;AAAA,IACF;AACA,QAAI,OAAO,KAAM,QAAO;AACxB,QAAI,OAAO,IAAK,WAAU;AAAA,aACjB,OAAO,IAAK,WAAU;AAAA,aACtB,OAAO,OAAO,CAAC,SAAS;AAC/B;AACA;AAAA,IACF;AACA;AAAA,EACF;AACA,MAAI,OAAO,IAAI,CAAC,MAAM,IAAK,QAAO;AAElC,SAAO,IAAI,OAAO,UAAU,SAAS,KAAK,OAAO,CAAC,CAAC,EAAG;AACtD,SAAO,EAAE,QAAQ,IAAI,OAAO,QAAQ,CAAC,EAAE,MAAM,SAAS,SAAS,OAAO,MAAM,OAAO,CAAC,EAAE,CAAC,EAAE;AAC3F;AAEA,SAAS,eAAe,UAA4B;AAClD,QAAM,UAAmB,EAAE,OAAO,CAAC,EAAE;AACrC,QAAM,QAAQ,QAAQ;AAEtB,MAAI,UAAU;AACZ,UAAM,KAAK,EAAE,MAAM,OAAO,SAAS,uBAAuB,CAAC;AAAA,EAC7D;AAEA,QAAM;AAAA,IACJ,EAAE,MAAM,WAAW,SAAS,+BAA+B;AAAA,IAC3D,EAAE,MAAM,UAAU,SAAS,2CAA2C;AAAA,IACtE,EAAE,MAAM,mBAAmB,OAAO,CAAC,KAAK,QAAQ,qBAAqB,KAAK,KAAK,OAAO,EAAE;AAAA,IACxF,EAAE,MAAM,gBAAgB,OAAO,CAAC,KAAK,QAAQ,kBAAkB,KAAK,GAAG,EAAE;AAAA,IACzE,EAAE,MAAM,aAAa,SAAS,qBAAqB;AAAA,IACnD,EAAE,MAAM,WAAW,SAAS,QAAQ,2BAA2B,EAAE;AAAA,IACjE;AAAA,MACE,MAAM;AAAA,MACN,SAAS;AAAA,QACP;AAAA,MAKF;AAAA,IACF;AAAA,IACA;AAAA,MACE,MAAM;AAAA,MACN,SAAS;AAAA,QACP;AAAA,MAEF;AAAA,IACF;AAAA,IACA,EAAE,MAAM,UAAU,SAAS,0DAA0D;AAAA,IACrF,EAAE,MAAM,YAAY,SAAS,6BAA6B;AAAA,IAC1D,EAAE,MAAM,eAAe,SAAS,kBAAkB;AAAA,EACpD;AACA,SAAO;AACT;AAEA,IAAM,YAAY,eAAe,KAAK;AACtC,IAAM,aAAa,eAAe,IAAI;AAEtC,IAAM,cAAuB;AAAA,EAC3B,OAAO;AAAA,IACL,EAAE,MAAM,UAAU,SAAS,uBAAuB;AAAA,IAClD,EAAE,MAAM,WAAW,SAAS,QAAQ,iBAAiB,EAAE;AAAA,IACvD,EAAE,MAAM,UAAU,SAAS,oCAAoC;AAAA,IAC/D,EAAE,MAAM,eAAe,SAAS,aAAa;AAAA,EAC/C;AACF;AAEA,IAAM,aAAsB;AAAA,EAC1B,OAAO;AAAA,IACL,EAAE,MAAM,WAAW,SAAS,oBAAoB;AAAA,IAChD,EAAE,MAAM,UAAU,SAAS,2CAA2C;AAAA,IACtE,EAAE,MAAM,WAAW,SAAS,gBAAgB;AAAA,IAC5C,EAAE,MAAM,UAAU,SAAS,eAAe;AAAA;AAAA;AAAA,IAG1C,EAAE,MAAM,YAAY,SAAS,qBAAqB;AAAA,IAClD,EAAE,MAAM,YAAY,SAAS,qIAAqI;AAAA;AAAA,IAElK,EAAE,MAAM,UAAU,SAAS,iBAAiB;AAAA;AAAA,IAE5C,EAAE,MAAM,WAAW,SAAS,uBAAuB;AAAA,IACnD,EAAE,MAAM,YAAY,SAAS,wBAAwB;AAAA,IACrD,EAAE,MAAM,YAAY,SAAS,sBAAsB;AAAA,IACnD,EAAE,MAAM,UAAU,SAAS,mDAAmD;AAAA,IAC9E,EAAE,MAAM,eAAe,SAAS,aAAa;AAAA,EAC/C;AACF;AAEA,IAAM,cAAuB;AAAA,EAC3B,OAAO;AAAA,IACL,EAAE,MAAM,WAAW,SAAS,mBAAmB;AAAA,IAC/C,EAAE,MAAM,WAAW,SAAS,oBAAoB;AAAA,IAChD,EAAE,MAAM,OAAO,SAAS,uBAAuB;AAAA,IAC/C,EAAE,MAAM,YAAY,SAAS,sBAAsB;AAAA,IACnD,EAAE,MAAM,UAAU,SAAS,2CAA2C;AAAA,IACtE,EAAE,MAAM,WAAW,SAAS,mBAAmB;AAAA,IAC/C,EAAE,MAAM,eAAe,SAAS,UAAU;AAAA,EAC5C;AACF;AAEA,IAAM,cAAuB;AAAA,EAC3B,OAAO;AAAA,IACL,EAAE,MAAM,WAAW,SAAS,WAAW;AAAA,IACvC,EAAE,MAAM,UAAU,SAAS,iCAAiC;AAAA,IAC5D,EAAE,MAAM,YAAY,SAAS,eAAe;AAAA,IAC5C;AAAA,MACE,MAAM;AAAA,MACN,SAAS;AAAA,QACP;AAAA,MAEF;AAAA,IACF;AAAA,IACA,EAAE,MAAM,UAAU,SAAS,WAAW;AAAA,IACtC,EAAE,MAAM,YAAY,SAAS,8BAA8B;AAAA,IAC3D,EAAE,MAAM,eAAe,SAAS,WAAW;AAAA,EAC7C;AACF;AAEA,IAAM,gBAAyB;AAAA,EAC7B,OAAO;AAAA,IACL,EAAE,MAAM,WAAW,SAAS,WAAW;AAAA,IACvC,EAAE,MAAM,aAAa,SAAS,oBAAoB;AAAA,IAClD;AAAA;AAAA,MAEE,MAAM;AAAA,MACN,SAAS;AAAA,IACX;AAAA,IACA,EAAE,MAAM,WAAW,SAAS,QAAQ,iBAAiB,EAAE;AAAA,IACvD;AAAA,MACE,MAAM;AAAA,MACN,SAAS;AAAA,QACP;AAAA,MAGF;AAAA,IACF;AAAA,IACA,EAAE,MAAM,WAAW,SAAS,QAAQ,wIAAwI,EAAE;AAAA,IAC9K,EAAE,MAAM,UAAU,SAAS,iBAAiB;AAAA,IAC5C,EAAE,MAAM,YAAY,SAAS,yBAAyB;AAAA,IACtD,EAAE,MAAM,eAAe,SAAS,gBAAgB;AAAA,EAClD;AACF;AAEA,IAAM,kBAA2B;AAAA,EAC/B,OAAO;AAAA,IACL,EAAE,MAAM,WAAW,SAAS,kBAAkB;AAAA,IAC9C,EAAE,MAAM,WAAW,SAAS,kBAAkB;AAAA,IAC9C,EAAE,MAAM,eAAe,SAAS,6BAA6B;AAAA,IAC7D,EAAE,MAAM,YAAY,SAAS,aAAa;AAAA,IAC1C,EAAE,MAAM,WAAW,SAAS,mBAAmB;AAAA,IAC/C,EAAE,MAAM,YAAY,SAAS,eAAe;AAAA,IAC5C,EAAE,MAAM,UAAU,SAAS,eAAe;AAAA,IAC1C,EAAE,MAAM,OAAO,SAAS,6BAA6B;AAAA,IACrD,EAAE,MAAM,eAAe,SAAS,sBAAsB;AAAA,EACxD;AACF;AAEA,IAAM,cAAuB;AAAA,EAC3B,OAAO;AAAA,IACL,EAAE,MAAM,WAAW,SAAS,WAAW;AAAA,IACvC,EAAE,MAAM,YAAY,SAAS,2BAA2B;AAAA,IACxD,EAAE,MAAM,UAAU,SAAS,iCAAiC;AAAA,IAC5D,EAAE,MAAM,WAAW,SAAS,QAAQ,+CAA+C,EAAE;AAAA,IACrF,EAAE,MAAM,UAAU,SAAS,mBAAmB;AAAA,IAC9C,EAAE,MAAM,YAAY,SAAS,cAAc;AAAA,IAC3C,EAAE,MAAM,eAAe,SAAS,eAAe;AAAA,EACjD;AACF;AAEA,IAAM,aAAsB;AAAA,EAC1B,OAAO;AAAA,IACL,EAAE,MAAM,WAAW,SAAS,6BAA6B;AAAA,IACzD,EAAE,MAAM,UAAU,SAAS,uBAAuB;AAAA,IAClD;AAAA,MACE,MAAM;AAAA,MACN,SAAS,IAAI;AAAA,QACX;AAAA,QAIA;AAAA,MACF;AAAA,IACF;AAAA,IACA,EAAE,MAAM,UAAU,SAAS,iBAAiB;AAAA,IAC5C,EAAE,MAAM,YAAY,SAAS,2BAA2B;AAAA,IACxD,EAAE,MAAM,eAAe,SAAS,gBAAgB;AAAA,EAClD;AACF;AAEA,IAAM,WAAoC;AAAA,EACxC,YAAY;AAAA,EACZ,YAAY;AAAA,EACZ,KAAK;AAAA,EACL,KAAK;AAAA,EACL,MAAM;AAAA,EACN,KAAK;AAAA,EACL,MAAM;AAAA,EACN,MAAM;AAAA,EACN,QAAQ;AAAA,EACR,UAAU;AAAA,EACV,MAAM;AAAA,EACN,KAAK;AACP;AAIA,IAAM,UAAkC;AAAA,EACtC,IAAI;AAAA,EACJ,KAAK;AAAA,EACL,KAAK;AAAA,EACL,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,OAAO;AAAA,EACP,KAAK;AAAA,EACL,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,KAAK;AAAA,EACL,KAAK;AACP;AAGO,IAAM,kBAA0C;AAAA,EACrD,YAAY;AAAA,EACZ,YAAY;AAAA,EACZ,KAAK;AAAA,EACL,KAAK;AAAA,EACL,MAAM;AAAA,EACN,KAAK;AAAA,EACL,MAAM;AAAA,EACN,MAAM;AAAA,EACN,QAAQ;AAAA,EACR,UAAU;AAAA,EACV,MAAM;AAAA,EACN,KAAK;AAAA,EACL,WAAW;AACb;AAEA,SAAS,kBAAkB,UAA0B;AACnD,QAAM,QAAQ,SAAS,KAAK,EAAE,YAAY;AAC1C,SAAO,QAAQ,KAAK,KAAK;AAC3B;AAUO,IAAM,iBAAkC,CAAC,MAAM,aAAa;AACjE,QAAM,MAAM,kBAAkB,YAAY,WAAW;AACrD,QAAM,UAAU,SAAS,GAAG;AAC5B,MAAI,CAAC,QAAS,QAAO,eAAe,CAAC,EAAE,MAAM,SAAS,SAAS,KAAK,CAAC,CAAC;AACtE,MAAI;AACF,WAAO,eAAe,aAAa,MAAM,OAAO,CAAC;AAAA,EACnD,QAAQ;AACN,WAAO,eAAe,CAAC,EAAE,MAAM,SAAS,SAAS,KAAK,CAAC,CAAC;AAAA,EAC1D;AACF;;;AC3bO,IAAM,YAA0C;AAAA,EACrD,MAAM;AAAA,EACN,QAAQ;AAAA,EACR,WAAW;AAAA,EACX,QAAQ;AAAA,EACR,MAAM;AACR;AAEO,IAAM,aAA4C;AAAA,EACvD,WAAW;AAAA,EACX,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,YAAY;AAAA,EACZ,YAAY;AAAA,EACZ,aAAa;AACf;AAEA,IAAM,kBAAkB,oBAAI,IAAI,CAAC,KAAK,MAAM,MAAM,MAAM,cAAc,KAAK,CAAC;AAErE,SAAS,OAAO,SAAmD;AACxE,SAAO,WAAW;AACpB;AAEO,SAAS,QAAQ,SAAoD;AAC1E,SAAO,WAAW;AACpB;AAsBO,SAAS,gBAAgB,cAAwB,SAAmC;AACzF,MAAI,YAAY,OAAQ,QAAO,aAAa,SAAS,GAAG;AACxD,MAAI,YAAY,SAAU,QAAO;AACjC,MAAI,OAAO,OAAO,EAAG,QAAO,aAAa,SAAS,UAAU,OAAO,CAAC;AACpE,MAAI,QAAQ,OAAO,EAAG,QAAO,aAAa,SAAS,WAAW,OAAO,CAAC;AACtE,SAAO;AACT;AAOA,SAAS,kBAAkB,MAAmB,KAAa,MAAuC;AAChG,MAAI,IAAiB;AACrB,SAAO,KAAK,MAAM,MAAM;AACtB,QAAI,EAAE,aAAa,KAAK,gBAAiB,EAAc,YAAY,IAAK,QAAO;AAC/E,QAAI,EAAE;AAAA,EACR;AACA,SAAO;AACT;AAEA,SAAS,aAAa,MAAmB,MAAuC;AAC9E,MAAI,IAAiB,QAAQ,KAAK,aAAa,KAAK,eAAe,OAAO,MAAM,cAAc;AAC9F,SAAO,KAAK,MAAM,MAAM;AACtB,QAAI,EAAE,aAAa,KAAK,gBAAgB,gBAAgB,IAAK,EAAc,OAAO,EAAG,QAAO;AAC5F,QAAI,EAAE;AAAA,EACR;AACA,SAAO;AACT;AAEA,SAAS,cAAc,IAAuB;AAC5C,QAAM,SAAS,GAAG;AAClB,MAAI,CAAC,OAAQ;AACb,SAAO,GAAG,WAAY,QAAO,aAAa,GAAG,YAAY,EAAE;AAC3D,SAAO,YAAY,EAAE;AACvB;AAEA,SAAS,WAAW,IAAiB,KAA0B;AAC7D,QAAM,OAAO,SAAS,cAAc,GAAG;AACvC,SAAO,GAAG,WAAY,MAAK,YAAY,GAAG,UAAU;AACpD,KAAG,YAAY,aAAa,MAAM,EAAE;AACpC,SAAO;AACT;AAEA,SAAS,mBAAmB,MAAY,gBAAgB,OAAa;AACnE,QAAM,MAAM,OAAO,aAAa;AAChC,MAAI,CAAC,IAAK;AACV,QAAM,QAAQ,SAAS,YAAY;AACnC,QAAM,mBAAmB,IAAI;AAC7B,MAAI,cAAe,OAAM,SAAS,KAAK;AACvC,MAAI,gBAAgB;AACpB,MAAI,SAAS,KAAK;AACpB;AAEA,IAAM,mBAAmB;AAGzB,SAAS,eAAe,KAAgB,QAAc,QAAsB;AAC1E,QAAM,QAAQ,SAAS,YAAY;AACnC,QAAM,SAAS,QAAQ,MAAM;AAC7B,QAAM,SAAS,IAAI;AACnB,MAAI,gBAAgB;AACpB,MAAI,SAAS,KAAK;AACpB;AAYA,SAAS,kBAAkB,MAAmB,KAAmB;AAC/D,QAAM,MAAM,OAAO,aAAa;AAChC,MAAI,CAAC,OAAO,IAAI,eAAe,EAAG;AAClC,QAAM,QAAQ,IAAI,WAAW,CAAC;AAE9B,QAAM,WAAW,kBAAkB,MAAM,gBAAgB,KAAK,IAAI;AAClE,MAAI,UAAU;AAEZ,UAAMC,UAAS,SAAS,eAAe,gBAAgB;AACvD,aAAS,YAAY,aAAaA,SAAQ,SAAS,WAAW;AAC9D,mBAAe,KAAKA,SAAQ,CAAC;AAC7B;AAAA,EACF;AAEA,QAAM,UAAU,SAAS,cAAc,GAAG;AAC1C,QAAM,SAAS,SAAS,eAAe,gBAAgB;AACvD,UAAQ,YAAY,MAAM;AAC1B,QAAM,WAAW,OAAO;AACxB,iBAAe,KAAK,QAAQ,CAAC;AAC/B;AAEA,SAAS,WAAW,MAAmB,KAAmB;AACxD,QAAM,MAAM,OAAO,aAAa;AAChC,MAAI,CAAC,OAAO,IAAI,eAAe,EAAG;AAClC,MAAI,IAAI,aAAa;AACnB,sBAAkB,MAAM,GAAG;AAC3B;AAAA,EACF;AACA,QAAM,QAAQ,IAAI,WAAW,CAAC;AAE9B,QAAM,WAAW,kBAAkB,MAAM,yBAAyB,KAAK,IAAI;AAC3E,MAAI,UAAU;AACZ,kBAAc,QAAQ;AACtB;AAAA,EACF;AAEA,QAAM,UAAU,SAAS,cAAc,GAAG;AAC1C,UAAQ,YAAY,MAAM,gBAAgB,CAAC;AAC3C,QAAM,WAAW,OAAO;AACxB,qBAAmB,OAAO;AAC5B;AAsBO,SAAS,iBAAiB,OAAoB;AACnD,QAAM,MAAM,OAAO,aAAa;AAChC,MAAI,CAAC,IAAK;AACV,MAAI,gBAAgB;AACpB,MAAI,SAAS,KAAK;AACpB;AAEO,SAAS,kBAAkB,MAA4B;AAO5D,QAAM,gBAAgB,OAAO,aAAa,eAAe,KAAK,SAAS,SAAS,aAAa;AAC7F,QAAM,MAAM,gBAAgB,OAAO,aAAa,IAAI;AACpD,QAAM,YAAY,OAAO,IAAI,aAAa,IAAI,IAAI,WAAW,CAAC,EAAE,iBAAiB;AACjF,QAAM,gBAAgB,CAAC,GAAG,OAAO,OAAO,SAAS,GAAG,MAAM;AAC1D,MAAI,UAAU;AACd,aAAW,OAAO,eAAe;AAC/B,eAAW,MAAM,MAAM,KAAK,KAAK,qBAAqB,GAAG,CAAC,GAAG;AAC3D,UAAI,GAAG,gBAAgB,oBAAoB,EAAE,aAAa,GAAG,SAAS,SAAS,IAAI;AACjF,WAAG,OAAO;AACV,kBAAU;AAAA,MACZ;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AAEA,SAAS,YAAY,MAAmB,WAAyB;AAC/D,QAAM,MAAM,OAAO,aAAa;AAChC,MAAI,CAAC,OAAO,IAAI,eAAe,EAAG;AAClC,QAAM,QAAQ,aAAa,IAAI,WAAW,CAAC,EAAE,gBAAgB,IAAI;AACjE,MAAI,CAAC,MAAO;AACZ,QAAM,OAAO,WAAW,OAAO,MAAM,YAAY,YAAY,MAAM,SAAS;AAC5E,qBAAmB,MAAM,IAAI;AAC/B;AAEA,SAAS,WAAW,MAAmB,SAA4B;AACjE,QAAM,MAAM,OAAO,aAAa;AAChC,MAAI,CAAC,OAAO,IAAI,eAAe,EAAG;AAClC,QAAM,QAAQ,IAAI,WAAW,CAAC;AAE9B,QAAM,KAAK,kBAAkB,MAAM,gBAAgB,MAAM,IAAI;AAC7D,MAAI,MAAM,GAAG,eAAe,YAAY,SAAS;AAC/C,UAAMC,QAAO,GAAG;AAChB,UAAM,IAAI,SAAS,cAAc,GAAG;AACpC,WAAO,GAAG,WAAY,GAAE,YAAY,GAAG,UAAU;AACjD,IAAAA,MAAK,YAAY,aAAa,GAAGA,KAAI;AACrC,uBAAmB,GAAG,IAAI;AAC1B;AAAA,EACF;AAEA,QAAM,QAAQ,aAAa,MAAM,gBAAgB,IAAI;AACrD,MAAI,CAAC,MAAO;AACZ,QAAM,OAAO,SAAS,cAAc,OAAO;AAC3C,QAAM,OAAO,SAAS,cAAc,IAAI;AACxC,SAAO,MAAM,WAAY,MAAK,YAAY,MAAM,UAAU;AAC1D,OAAK,YAAY,IAAI;AACrB,QAAM,YAAY,aAAa,MAAM,KAAK;AAC1C,qBAAmB,MAAM,IAAI;AAC/B;AAEA,SAAS,QAAQ,MAAmB,KAAmB;AACrD,QAAM,MAAM,OAAO,aAAa;AAChC,MAAI,CAAC,OAAO,IAAI,eAAe,KAAK,IAAI,YAAa;AACrD,QAAM,QAAQ,IAAI,WAAW,CAAC;AAC9B,QAAM,WAAW,kBAAkB,MAAM,yBAAyB,KAAK,IAAI;AAC3E,MAAI,UAAU;AACZ,aAAS,aAAa,QAAQ,GAAG;AACjC;AAAA,EACF;AACA,QAAM,IAAI,SAAS,cAAc,GAAG;AACpC,IAAE,aAAa,QAAQ,GAAG;AAC1B,IAAE,aAAa,OAAO,qBAAqB;AAC3C,IAAE,YAAY,MAAM,gBAAgB,CAAC;AACrC,QAAM,WAAW,CAAC;AAClB,qBAAmB,CAAC;AACtB;AAEA,SAAS,OAAO,MAAyB;AACvC,QAAM,MAAM,OAAO,aAAa;AAChC,MAAI,CAAC,OAAO,IAAI,eAAe,EAAG;AAClC,QAAM,IAAI,kBAAkB,IAAI,WAAW,CAAC,EAAE,yBAAyB,KAAK,IAAI;AAChF,MAAI,EAAG,eAAc,CAAC;AACxB;AASA,SAAS,yBAAyB,MAAmB,UAAkB,MAAuC;AAC5G,MAAI,IAAiB;AACrB,SAAO,KAAK,MAAM,MAAM;AACtB,QAAI,EAAE,aAAa,KAAK,cAAc;AACpC,YAAM,KAAK;AACX,UAAI,GAAG,YAAY,UAAU,GAAG,MAAM,iBAAiB,QAAQ,EAAG,QAAO;AAAA,IAC3E;AACA,QAAI,EAAE;AAAA,EACR;AACA,SAAO;AACT;AAEA,SAAS,eAAe,MAAmB,UAAwC,OAAqB;AACtG,QAAM,MAAM,OAAO,aAAa;AAChC,MAAI,CAAC,OAAO,IAAI,eAAe,EAAG;AAClC,QAAM,QAAQ,IAAI,WAAW,CAAC;AAE9B,MAAI,IAAI,aAAa;AACnB,UAAMC,YAAW,yBAAyB,MAAM,gBAAgB,UAAU,IAAI;AAC9E,QAAIA,WAAU;AACZ,MAAAA,UAAS,MAAM,YAAY,UAAU,KAAK;AAC1C;AAAA,IACF;AACA,UAAMC,WAAU,SAAS,cAAc,MAAM;AAC7C,IAAAA,SAAQ,MAAM,YAAY,UAAU,KAAK;AACzC,UAAM,SAAS,SAAS,eAAe,gBAAgB;AACvD,IAAAA,SAAQ,YAAY,MAAM;AAC1B,UAAM,WAAWA,QAAO;AACxB,mBAAe,KAAK,QAAQ,CAAC;AAC7B;AAAA,EACF;AAEA,QAAM,WAAW,yBAAyB,MAAM,yBAAyB,UAAU,IAAI;AACvF,MAAI,UAAU;AACZ,aAAS,MAAM,YAAY,UAAU,KAAK;AAC1C;AAAA,EACF;AACA,QAAM,UAAU,SAAS,cAAc,MAAM;AAC7C,UAAQ,MAAM,YAAY,UAAU,KAAK;AACzC,UAAQ,YAAY,MAAM,gBAAgB,CAAC;AAC3C,QAAM,WAAW,OAAO;AACxB,qBAAmB,OAAO;AAC5B;AAEA,SAAS,iBAAiB,MAAmB,UAA8C;AACzF,QAAM,MAAM,OAAO,aAAa;AAChC,MAAI,CAAC,OAAO,IAAI,eAAe,EAAG;AAClC,QAAM,KAAK,yBAAyB,IAAI,WAAW,CAAC,EAAE,yBAAyB,UAAU,IAAI;AAC7F,MAAI,CAAC,GAAI;AACT,KAAG,MAAM,eAAe,QAAQ;AAChC,MAAI,CAAC,GAAG,aAAa,OAAO,EAAG,eAAc,EAAE;AACjD;AASO,SAAS,oBAAoB,MAAmB,SAA0B,OAAsB;AACrG,MAAI,OAAO,OAAO,EAAG,YAAW,MAAM,UAAU,OAAO,CAAC;AAAA,WAC/C,YAAY,aAAc,YAAW,MAAM,IAAI;AAAA,WAC/C,YAAY,cAAe,YAAW,MAAM,IAAI;AAAA,WAChD,QAAQ,OAAO,EAAG,aAAY,MAAM,WAAW,OAAO,CAAC;AAAA,WACvD,YAAY,UAAU,MAAO,SAAQ,MAAM,KAAK;AAAA,WAChD,YAAY,SAAU,QAAO,IAAI;AAAA,WACjC,YAAY,eAAe,MAAO,gBAAe,MAAM,SAAS,KAAK;AAAA,WACrE,YAAY,iBAAkB,kBAAiB,MAAM,OAAO;AAAA,WAC5D,YAAY,eAAe,MAAO,gBAAe,MAAM,oBAAoB,KAAK;AAAA,WAChF,YAAY,iBAAkB,kBAAiB,MAAM,kBAAkB;AAClF;AAQO,SAAS,kBAAkB,MAAyC;AACzE,QAAM,SAAS,oBAAI,IAAqB;AACxC,MAAI,OAAO,WAAW,YAAa,QAAO;AAC1C,QAAM,MAAM,OAAO,aAAa;AAChC,MAAI,CAAC,OAAO,IAAI,eAAe,EAAG,QAAO;AAEzC,QAAM,OAAiB,CAAC;AACxB,MAAI,OAAoB,IAAI;AAC5B,SAAO,QAAQ,SAAS,MAAM;AAC5B,QAAI,KAAK,aAAa,KAAK,aAAc,MAAK,KAAM,KAAiB,OAAO;AAC5E,WAAO,KAAK;AAAA,EACd;AAEA,QAAM,WAA8B,CAAC,GAAI,OAAO,KAAK,SAAS,GAAsB,GAAI,OAAO,KAAK,UAAU,GAAuB,MAAM;AAC3I,aAAW,WAAW,UAAU;AAC9B,QAAI,gBAAgB,MAAM,OAAO,EAAG,QAAO,IAAI,OAAO;AAAA,EACxD;AACA,SAAO;AACT;AASA,IAAM,eAAe,oBAAI,IAAI;AAAA,EAC3B;AAAA,EAAK;AAAA,EAAM;AAAA,EAAU;AAAA,EAAK;AAAA,EAAM;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAU;AAAA,EAAQ;AAAA,EACjE;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAc;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AACpD,CAAC;AAED,IAAM,gBAA0C;AAAA,EAC9C,GAAG,CAAC,MAAM;AAAA,EACV,MAAM,CAAC,OAAO;AAChB;AAQA,IAAM,sBAAsB,oBAAI,IAAI,CAAC,SAAS,kBAAkB,CAAC;AACjE,IAAM,mBAAmB;AAEzB,SAAS,kBAAkB,OAAuB;AAChD,QAAM,MAAgB,CAAC;AACvB,aAAW,QAAQ,MAAM,MAAM,GAAG,GAAG;AACnC,UAAM,QAAQ,KAAK,QAAQ,GAAG;AAC9B,QAAI,UAAU,GAAI;AAClB,UAAM,OAAO,KAAK,MAAM,GAAG,KAAK,EAAE,KAAK,EAAE,YAAY;AACrD,UAAM,MAAM,KAAK,MAAM,QAAQ,CAAC,EAAE,KAAK;AACvC,QAAI,oBAAoB,IAAI,IAAI,KAAK,iBAAiB,KAAK,GAAG,GAAG;AAC/D,UAAI,KAAK,GAAG,IAAI,KAAK,GAAG,EAAE;AAAA,IAC5B;AAAA,EACF;AACA,SAAO,IAAI,KAAK,IAAI;AACtB;AAGA,IAAM,iBAAiB,oBAAI,IAAI,CAAC,UAAU,SAAS,UAAU,UAAU,OAAO,CAAC;AAE/E,SAAS,gBAAgB,IAAmB;AAC1C,aAAW,SAAS,MAAM,KAAK,GAAG,UAAU,GAAG;AAC7C,QAAI,MAAM,aAAa,KAAK,UAAW;AAEvC,QAAI,MAAM,aAAa,KAAK,cAAc;AACxC,YAAM,YAAY,YAAY,KAAK;AACnC;AAAA,IACF;AAEA,UAAM,UAAU;AAEhB,QAAI,eAAe,IAAI,QAAQ,OAAO,GAAG;AACvC,cAAQ,YAAY,YAAY,OAAO;AACvC;AAAA,IACF;AAEA,QAAI,CAAC,aAAa,IAAI,QAAQ,OAAO,GAAG;AAGtC,sBAAgB,OAAO;AACvB,YAAM,SAAS,QAAQ;AACvB,aAAO,QAAQ,WAAY,SAAQ,aAAa,QAAQ,YAAY,OAAO;AAC3E,cAAQ,YAAY,OAAO;AAC3B;AAAA,IACF;AAEA,UAAM,eAAe,cAAc,QAAQ,OAAO,KAAK,CAAC;AACxD,eAAW,QAAQ,MAAM,KAAK,QAAQ,UAAU,GAAG;AACjD,YAAM,OAAO,KAAK,KAAK,YAAY;AACnC,UAAI,CAAC,aAAa,SAAS,IAAI,GAAG;AAChC,gBAAQ,gBAAgB,KAAK,IAAI;AACjC;AAAA,MACF;AACA,UAAI,SAAS,SAAS;AACpB,cAAM,UAAU,kBAAkB,KAAK,KAAK;AAC5C,YAAI,QAAS,SAAQ,aAAa,SAAS,OAAO;AAAA,YAC7C,SAAQ,gBAAgB,OAAO;AAAA,MACtC;AAAA,IACF;AACA,QAAI,QAAQ,YAAY,KAAK;AAC3B,YAAM,OAAO,QAAQ,aAAa,MAAM,KAAK;AAC7C,YAAM,OAAO,gBAAgB,KAAK,IAAI,KAAK,KAAK,WAAW,GAAG,KAAK,KAAK,WAAW,GAAG,KAAK,KAAK,WAAW,SAAS;AACpH,UAAI,CAAC,KAAM,SAAQ,gBAAgB,MAAM;AACzC,cAAQ,aAAa,OAAO,qBAAqB;AACjD,cAAQ,aAAa,UAAU,QAAQ;AAAA,IACzC;AAEA,oBAAgB,OAAO;AAAA,EACzB;AACF;AAUO,SAAS,aAAa,MAAsB;AACjD,MAAI,OAAO,cAAc,YAAa,QAAO;AAC7C,QAAM,MAAM,IAAI,UAAU,EAAE,gBAAgB,MAAM,WAAW;AAC7D,kBAAgB,IAAI,IAAI;AACxB,SAAO,IAAI,KAAK;AAClB;AASA,IAAM,gBAAgB;AAEf,SAAS,gBAAiC;AAC/C,SAAO,EAAE,MAAM,CAAC,GAAG,QAAQ,CAAC,EAAE;AAChC;AAGO,SAAS,YAAY,SAA0B,eAAwC;AAC5F,SAAO,EAAE,MAAM,CAAC,GAAG,QAAQ,MAAM,aAAa,EAAE,MAAM,CAAC,aAAa,GAAG,QAAQ,CAAC,EAAE;AACpF;AAEO,SAAS,YACd,SACA,cACoD;AACpD,MAAI,QAAQ,KAAK,WAAW,EAAG,QAAO;AACtC,QAAM,QAAQ,QAAQ,KAAK,QAAQ,KAAK,SAAS,CAAC;AAClD,SAAO;AAAA,IACL,SAAS,EAAE,MAAM,QAAQ,KAAK,MAAM,GAAG,EAAE,GAAG,QAAQ,CAAC,cAAc,GAAG,QAAQ,MAAM,EAAE;AAAA,IACtF;AAAA,EACF;AACF;AAEO,SAAS,YACd,SACA,cACoD;AACpD,MAAI,QAAQ,OAAO,WAAW,EAAG,QAAO;AACxC,QAAM,QAAQ,QAAQ,OAAO,CAAC;AAC9B,SAAO;AAAA,IACL,SAAS,EAAE,MAAM,CAAC,GAAG,QAAQ,MAAM,YAAY,GAAG,QAAQ,QAAQ,OAAO,MAAM,CAAC,EAAE;AAAA,IAClF;AAAA,EACF;AACF;AAkBO,SAAS,2BAA2B,OAAqD;AAC9F,SAAO;AAAA,IACL,YAAY,MAAM,YAAY;AAAA,IAC9B,YAAY,MAAM,YAAY;AAAA,IAC9B,aAAa,MAAM,eAAe;AAAA,IAClC,MAAM,MAAM,QAAQ;AAAA,EACtB;AACF;AAEO,SAAS,yBAAyB,OAAoC;AAC3E,SAAO;AAAA,IACL;AAAA,IACA,iBAAiB,MAAM,IAAI;AAAA,IAC3B,MAAM,cAAc;AAAA,IACpB,MAAM,cAAc;AAAA,EACtB,EACG,OAAO,OAAO,EACd,KAAK,GAAG;AACb;;;AC7jBO,SAAS,oBAAoB,OAAuC;AACzE,SAAO;AAAA,IACL,QAAQ,MAAM,UAAU;AAAA,IACxB,SAAS,MAAM,WAAW;AAAA,IAC1B,KAAK,MAAM;AAAA,IACX,iBAAiB,MAAM,mBAAmB;AAAA,EAC5C;AACF;AAEO,SAAS,kBAAkB,OAAqB,WAA4B;AACjF,SAAO,CAAC,cAAc,eAAe,MAAM,MAAM,IAAI,SAAS,EAAE,OAAO,OAAO,EAAE,KAAK,GAAG;AAC1F;AAEO,SAAS,gBAAgB,OAA0C;AACxE,QAAM,QAAgC,CAAC;AACvC,MAAI,MAAM,IAAK,OAAM,MAAM,MAAM;AACjC,MAAI,MAAM,WAAW,UAAU,MAAM,WAAW,WAAW;AACzD,UAAM,sBAAsB,IAAI,OAAO,MAAM,OAAO;AAAA,EACtD;AACA,SAAO;AACT;AAGO,SAAS,oBAAoB,OAAgB,QAAyB;AAC3E,MAAI,CAAC,SAAS,CAAC,UAAU,SAAS,KAAK,UAAU,EAAG,QAAO;AAC3D,SAAO,QAAQ;AACjB;AAsBO,SAAS,qBAAqB,cAAwB,gBAAwB,iBAAyB,KAA6B;AACzI,MAAI,aAAa,WAAW,KAAK,kBAAkB,KAAK,mBAAmB,EAAG,QAAO,CAAC;AAEtF,QAAM,OAAuB,CAAC;AAC9B,MAAI,aAAuB,CAAC;AAC5B,MAAI,mBAA6B,CAAC;AAClC,MAAI,cAAc;AAElB,WAAS,SAAS,OAAgB;AAChC,QAAI,WAAW,WAAW,EAAG;AAC7B,UAAM,YAAY,OAAO,WAAW,SAAS;AAC7C,UAAM,iBAAiB,iBAAiB;AACxC,UAAM,eAAe;AACrB,UAAM,cAAc,QAAQ,iBAAiB,eAAe;AAC5D,SAAK,KAAK;AAAA,MACR,SAAS;AAAA,MACT,QAAQ,kBAAkB;AAAA,MAC1B,QAAQ,iBAAiB,IAAI,CAAC,MAAM,IAAI,WAAW;AAAA,IACrD,CAAC;AACD,iBAAa,CAAC;AACd,uBAAmB,CAAC;AACpB,kBAAc;AAAA,EAChB;AAEA,WAAS,IAAI,GAAG,IAAI,aAAa,QAAQ,KAAK;AAC5C,UAAM,eAAe,aAAa,CAAC,IAAI;AACvC,UAAM,YAAY,MAAM,WAAW;AACnC,QAAI,WAAW,SAAS,KAAK,cAAc,eAAe,YAAY,gBAAgB;AACpF,eAAS,IAAI;AAAA,IACf;AACA,eAAW,KAAK,CAAC;AACjB,qBAAiB,KAAK,YAAY;AAClC,mBAAe;AAAA,EACjB;AAMA,QAAM,iBAAiB,OAAO,WAAW,SAAS;AAClD,QAAM,YAAY,cAAc,iBAAiB;AACjD,WAAS,SAAS;AAElB,SAAO;AACT;;;ACrHO,SAAS,aAAa,OAAwD;AACnF,SAAO,MAAM,QAAQ,KAAK;AAC5B;AAGO,SAAS,SAAS,OAA8B;AACrD,SAAO,aAAa,KAAK,IAAI,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC,CAAC,IAAI,CAAC,KAAK;AAC5D;AAGO,SAAS,WAAW,QAA2B,MAAgC;AACpF,SAAO,aAAa,IAAI,IAAI,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC,CAAC,IAAI,OAAO,CAAC;AAC/D;AAkBO,SAAS,mBAAmB,OAAqC;AACtE,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA,IAKL,KAAK,MAAM,OAAO;AAAA,IAClB,KAAK,MAAM,OAAO;AAAA,IAClB,MAAM,MAAM,QAAQ;AAAA,IACpB,YAAY,MAAM,YAAY;AAAA,IAC9B,MAAM,MAAM,QAAQ;AAAA,EACtB;AACF;AAGO,SAAS,aAAa,OAAe,OAA0D;AACpG,QAAM,EAAE,KAAK,KAAK,KAAK,IAAI;AAC3B,QAAM,UAAU,KAAK,IAAI,KAAK,IAAI,OAAO,GAAG,GAAG,GAAG;AAClD,MAAI,QAAQ,EAAG,QAAO;AACtB,QAAM,QAAQ,KAAK,OAAO,UAAU,OAAO,IAAI;AAC/C,QAAM,UAAU,MAAM,QAAQ;AAG9B,SAAO,KAAK,IAAI,KAAK,IAAI,SAAS,GAAG,GAAG,GAAG;AAC7C;AAEO,SAAS,eAAe,OAAe,KAAa,KAAqB;AAC9E,MAAI,QAAQ,IAAK,QAAO;AACxB,UAAS,QAAQ,QAAQ,MAAM,OAAQ;AACzC;AAEO,SAAS,eAAe,SAAiB,KAAa,KAAqB;AAChF,SAAO,MAAO,UAAU,OAAQ,MAAM;AACxC;AAQO,SAAS,gBAAgB,QAA2B,OAAc,MAAc,SAAS,GAAW;AACzG,MAAI,UAAU,EAAG,QAAO,KAAK,IAAI,MAAM,OAAO,CAAC,IAAI,MAAM;AACzD,SAAO,KAAK,IAAI,MAAM,OAAO,CAAC,IAAI,MAAM;AAC1C;AASO,SAAS,kBAAkB,QAAmC,aAA4B;AAC/F,QAAM,UAAU,KAAK,IAAI,OAAO,CAAC,IAAI,WAAW;AAChD,QAAM,WAAW,KAAK,IAAI,OAAO,CAAC,IAAI,WAAW;AACjD,SAAO,WAAW,UAAU,IAAI;AAClC;AAIA,IAAM,eAAe,oBAAI,IAAI,CAAC,cAAc,SAAS,CAAC;AACtD,IAAM,iBAAiB,oBAAI,IAAI,CAAC,aAAa,WAAW,CAAC;AAYlD,SAAS,iBACd,KACA,SACA,OACe;AACf,QAAM,UAAU,KAAK,IAAI,MAAM,OAAO,IAAI,MAAM,MAAM,MAAM,GAAG;AAC/D,MAAI,aAAa,IAAI,GAAG,EAAG,QAAO,aAAa,UAAU,MAAM,MAAM,KAAK;AAC1E,MAAI,eAAe,IAAI,GAAG,EAAG,QAAO,aAAa,UAAU,MAAM,MAAM,KAAK;AAC5E,MAAI,QAAQ,SAAU,QAAO,aAAa,UAAU,SAAS,KAAK;AAClE,MAAI,QAAQ,WAAY,QAAO,aAAa,UAAU,SAAS,KAAK;AACpE,MAAI,QAAQ,OAAQ,QAAO,MAAM;AACjC,MAAI,QAAQ,MAAO,QAAO,MAAM;AAChC,SAAO;AACT;AAIO,SAAS,eACd,OACA,SACA,gBACA,WACQ;AACR,SAAO;AAAA,IACL;AAAA,IACA,cAAc,MAAM,IAAI;AAAA,IACxB,WAAW;AAAA,IACX,MAAM,cAAc;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAOpB,kBAAkB;AAAA,IAClB;AAAA,EACF,EACG,OAAO,OAAO,EACd,KAAK,GAAG;AACb;AAcO,SAAS,kBAAkB,OAAuB;AACvD,SAAO;AAAA,IACL,MAAM;AAAA,IACN,iBAAiB,MAAM;AAAA,IACvB,iBAAiB,MAAM;AAAA,IACvB,iBAAiB,MAAM;AAAA,IACvB,kBAAkB,MAAM;AAAA,IACxB,oBAAoB;AAAA,IACpB,iBAAiB,MAAM,cAAc;AAAA,IACrC,mBAAmB,MAAM;AAAA,IACzB,UAAU,MAAM,aAAa,KAAK;AAAA,EACpC;AACF;;;AClKO,IAAM,iBAAiB,CAAC,KAAK,MAAM,GAAG,MAAM,KAAK,MAAM,CAAC;AAIxD,SAAS,gBAAgB,SAAyB;AACvD,MAAI,CAAC,OAAO,SAAS,OAAO,KAAK,UAAU,EAAG,QAAO;AACrD,QAAM,QAAQ,KAAK,MAAM,OAAO;AAChC,QAAM,IAAI,KAAK,MAAM,QAAQ,IAAI;AACjC,QAAM,IAAI,KAAK,MAAO,QAAQ,OAAQ,EAAE;AACxC,QAAM,IAAI,QAAQ;AAClB,QAAM,UAAU,OAAO,CAAC,EAAE,SAAS,GAAG,GAAG;AACzC,MAAI,IAAI,EAAG,QAAO,GAAG,CAAC,IAAI,OAAO,CAAC,EAAE,SAAS,GAAG,GAAG,CAAC,IAAI,OAAO;AAC/D,SAAO,GAAG,CAAC,IAAI,OAAO;AACxB;AAGO,SAAS,YAAY,QAAwB;AAClD,SAAO,aAAa,QAAQ,EAAE,KAAK,GAAG,KAAK,GAAG,MAAM,EAAE,CAAC;AACzD;AASO,SAAS,eAAe,SAAiB,WAA4C,UAA0B;AACpH,MAAI,UAAU,SAAS,KAAK,CAAC,OAAO,SAAS,QAAQ,KAAK,YAAY,EAAG,QAAO;AAChF,QAAM,cAAe,UAAU,UAAU,QAAQ,UAAU,QAAS;AACpE,QAAM,UAAU,aAAa,YAAY,EAAE,KAAK,GAAG,KAAK,KAAK,MAAM,EAAE,CAAC;AACtE,SAAO,eAAe,SAAS,GAAG,QAAQ;AAC5C;AAGO,SAAS,gBAAgB,SAAiB,WAAoD;AACnG,SAAO,YAAY,eAAe,SAAS,WAAW,CAAC,CAAC;AAC1D;AAEO,SAAS,mBAAmB,aAAqB,UAA0B;AAChF,MAAI,CAAC,OAAO,SAAS,QAAQ,KAAK,YAAY,EAAG,QAAO;AACxD,SAAO,aAAc,cAAc,WAAY,KAAK,EAAE,KAAK,GAAG,KAAK,KAAK,MAAM,EAAE,CAAC;AACnF;AAEO,SAAS,mBAAmB,aAAqB,UAA0B;AAChF,SAAO,mBAAmB,aAAa,QAAQ;AACjD;AAuCO,SAAS,gBACd,OACA,WACA,YACA,WAAW,KACX,SAAS,KACG;AACZ,WAAS,QAAQ,UAA2B;AAC1C,WAAO,OAAO,SAAS,QAAQ,KAAK,WAAW;AAAA,EACjD;AAEA,MAAI,QAAQ,MAAM,QAAQ,GAAG;AAC3B,eAAW,MAAM,QAAQ;AACzB,WAAO,MAAM;AAAA,IAAC;AAAA,EAChB;AAEA,MAAI,UAAU;AACd,MAAI;AACJ,MAAI;AAEJ,WAAS,UAAU;AACjB,UAAM,oBAAoB,cAAc,cAAc;AACtD,UAAM,oBAAoB,UAAU,cAAc;AAClD,UAAM,oBAAoB,kBAAkB,cAAc;AAC1D,UAAM,oBAAoB,SAAS,WAAW;AAC9C,QAAI,eAAe,OAAW,eAAc,UAAU;AACtD,QAAI,iBAAiB,OAAW,cAAa,YAAY;AAAA,EAC3D;AAEA,WAAS,OAAO,kBAA0B,SAAkB;AAC1D,QAAI,QAAS;AACb,cAAU;AACV,YAAQ;AACR,QAAI,QAAS,OAAM,cAAc;AACjC,eAAW,gBAAgB;AAAA,EAC7B;AAEA,WAAS,iBAAiB;AACxB,QAAI,QAAQ,MAAM,QAAQ,EAAG,QAAO,MAAM,UAAU,IAAI;AAAA,EAC1D;AAEA,WAAS,cAAc;AACrB,WAAO,GAAG,KAAK;AAAA,EACjB;AAEA,QAAM,iBAAiB,cAAc,cAAc;AACnD,QAAM,iBAAiB,UAAU,cAAc;AAC/C,QAAM,iBAAiB,kBAAkB,cAAc;AACvD,QAAM,iBAAiB,SAAS,WAAW;AAE3C,eAAa,YAAY,MAAM;AAC7B,QAAI,QAAQ,MAAM,QAAQ,EAAG,QAAO,MAAM,UAAU,IAAI;AAAA,EAC1D,GAAG,MAAM;AAET,iBAAe,WAAW,MAAM,OAAO,GAAG,KAAK,GAAG,QAAQ;AAE1D,QAAM,cAAc;AAEpB,SAAO;AACT;AAIO,SAAS,iBAAiB,aAAqB,UAAkB;AACtE,SAAO;AAAA,IACL,MAAM;AAAA,IACN,cAAc;AAAA,IACd,iBAAiB;AAAA,IACjB,iBAAiB,KAAK,IAAI,UAAU,CAAC;AAAA,IACrC,iBAAiB,KAAK,IAAI,KAAK,IAAI,aAAa,CAAC,GAAG,KAAK,IAAI,UAAU,CAAC,CAAC;AAAA,IACzE,kBAAkB,gBAAgB,WAAW;AAAA,EAC/C;AACF;AAEO,SAAS,mBAAmB,QAAgB,OAAgB;AACjE,QAAM,MAAM,KAAK,MAAM,YAAY,MAAM,IAAI,GAAG;AAChD,SAAO;AAAA,IACL,MAAM;AAAA,IACN,cAAc;AAAA,IACd,iBAAiB;AAAA,IACjB,iBAAiB;AAAA,IACjB,iBAAiB,QAAQ,IAAI;AAAA,IAC7B,kBAAkB,QAAQ,UAAU,GAAG,GAAG;AAAA,EAC5C;AACF;AAEO,SAAS,uBAAuB,WAAoB;AACzD,SAAO,EAAE,cAAc,YAAY,UAAU,OAAO;AACtD;AAEO,SAAS,uBAAuB,SAAkB;AACvD,SAAO,EAAE,cAAc,UAAU,WAAW,QAAQ,gBAAgB,QAAQ;AAC9E;AAGO,SAAS,mBAAmB,QAAgB,OAAyB;AAC1E,SAAO,SAAS,UAAU;AAC5B;AAGO,IAAM,eAAe;AASrB,IAAM,uBAAuB,CAAC,GAAG,IAAI,IAAI,EAAE;AAI3C,SAAS,mBAAmB,SAAgD;AACjF,SAAQ,qBAA2C,SAAS,OAAO;AACrE;AAGO,SAAS,mBAAmB,aAA8B;AAC/D,MAAI,gBAAgB,OAAW,QAAO;AACtC,MAAI,CAAC,OAAO,SAAS,WAAW,KAAK,eAAe,EAAG,QAAO;AAC9D,SAAO;AACT;AAGO,SAAS,SAAS,aAAqB,UAAkB,cAA8B;AAC5F,QAAM,MAAM,OAAO,SAAS,QAAQ,KAAK,WAAW,IAAI,WAAW;AACnE,SAAO,KAAK,IAAI,KAAK,IAAI,cAAc,cAAc,CAAC,GAAG,GAAG;AAC9D;AAEO,SAAS,uBAAuB,cAAsB;AAC3D,SAAO,EAAE,cAAc,eAAe,IAAI,QAAQ,KAAK,IAAI,YAAY,CAAC,aAAa,WAAW,YAAY,WAAW;AACzH;AA0CO,SAAS,sBAAsB,OAAiD;AACrF,QAAM,WAAW,MAAM,WAAW;AAClC,QAAM,eAAe,MAAM,eAAe;AAC1C,SAAO;AAAA,IACL,aAAa,mBAAmB,MAAM,WAAW;AAAA,IACjD;AAAA,IACA;AAAA,IACA,cAAc,aAAa,MAAM,WAAW,UAAU;AAAA,IACtD,kBAAkB,iBAAiB,MAAM,eAAe,UAAU;AAAA,EACpE;AACF;AAEO,SAAS,wBAAwB,WAAgC;AACtE,SAAO,EAAE,cAAc,cAAc,SAAS,eAAe,iBAAiB;AAChF;AAqBO,SAAS,kBAAkB,KAAoC;AACpE,UAAQ,KAAK;AAAA,IACX,KAAK;AAAA,IACL,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT;AACE,aAAO;AAAA,EACX;AACF;;;ACtSO,SAAS,sBAAsB,OAA2C;AAC/E,SAAO;AAAA,IACL,aAAa,MAAM,eAAe;AAAA,IAClC,cAAc,MAAM,gBAAgB;AAAA,IACpC,UAAU,MAAM,YAAY;AAAA,IAC5B,UAAU,MAAM,YAAY;AAAA,IAC5B,WAAW,MAAM,aAAa;AAAA,EAChC;AACF;AAEO,SAAS,oBAAoB,WAA4B;AAC9D,SAAO,CAAC,iBAAiB,SAAS,EAAE,OAAO,OAAO,EAAE,KAAK,GAAG;AAC9D;AAGO,SAAS,UAAU,MAAsB;AAC9C,SAAO,KAAK,IAAI,KAAK,MAAM,IAAI,GAAG,CAAC;AACrC;AAEO,SAAS,WAAW,OAAe,KAAa,KAAqB;AAC1E,SAAO,KAAK,IAAI,KAAK,IAAI,OAAO,GAAG,GAAG,GAAG;AAC3C;AAGA,SAAS,gBAAgB,OAAgB,UAA0B;AACjE,SAAO,UAAU,SAAS,WAAW;AACvC;AAEO,SAAS,OAAO,OAAgB,MAAc,KAAa,WAAW,GAAW;AACtF,SAAO,WAAW,gBAAgB,OAAO,QAAQ,IAAI,MAAM,GAAG,GAAG;AACnE;AAEO,SAAS,QAAQ,OAAgB,MAAc,KAAa,WAAW,GAAW;AACvF,SAAO,WAAW,gBAAgB,OAAO,QAAQ,IAAI,MAAM,KAAK,QAAQ;AAC1E;AAGO,SAAS,YAAY,OAAgB,KAAsB;AAChE,SAAO,UAAU,UAAU,SAAS;AACtC;AAEO,SAAS,YAAY,OAAgB,KAAsB;AAChE,SAAO,UAAU,UAAU,SAAS;AACtC;AAgBO,SAAS,iBAAiB,eAAuB,cAAc,KAAa;AACjF,MAAI,iBAAiB,KAAK,eAAe,EAAG,QAAO;AACnD,QAAM,cAAc,eAAe,KAAK;AACxC,SAAO,gBAAgB;AACzB;AAEO,SAAS,kBAAkB,OAAwB;AACxD,SAAO,UAAU,SAAS,QAAQ,GAAG,KAAK,MAAM,QAAQ,GAAG,CAAC;AAC9D;AAyBO,SAAS,YAAY,KAAa,MAAc,OAAwB;AAC7E,QAAM,OAAO,IAAI,MAAM,GAAG,EAAE,CAAC;AAC7B,QAAM,YAAY,UAAU,SAAS,eAAe,KAAK,MAAM,QAAQ,GAAG;AAC1E,SAAO,GAAG,IAAI,SAAS,UAAU,IAAI,CAAC,SAAS,SAAS;AAC1D;","names":["registry","toIndex","jsGrammar","marker","list","existing","wrapper"]}
|