@longd/layout-ui 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/README.md +257 -1
  2. package/dist/CATEditor-C-b6vybW.d.cts +381 -0
  3. package/dist/CATEditor-CLp6jZAf.d.ts +381 -0
  4. package/dist/chunk-BLJWR4ZV.js +11 -0
  5. package/dist/chunk-BLJWR4ZV.js.map +1 -0
  6. package/dist/{chunk-CZ3IMHZ6.js → chunk-H7SY4VJU.js} +7 -11
  7. package/dist/chunk-H7SY4VJU.js.map +1 -0
  8. package/dist/chunk-YXQGAND3.js +137 -0
  9. package/dist/chunk-YXQGAND3.js.map +1 -0
  10. package/dist/chunk-ZME2TTK5.js +2527 -0
  11. package/dist/chunk-ZME2TTK5.js.map +1 -0
  12. package/dist/index.cjs +2612 -3
  13. package/dist/index.cjs.map +1 -1
  14. package/dist/index.css +504 -0
  15. package/dist/index.css.map +1 -0
  16. package/dist/index.d.cts +3 -0
  17. package/dist/index.d.ts +3 -0
  18. package/dist/index.js +13 -2
  19. package/dist/layout/cat-editor.cjs +2669 -0
  20. package/dist/layout/cat-editor.cjs.map +1 -0
  21. package/dist/layout/cat-editor.css +504 -0
  22. package/dist/layout/cat-editor.css.map +1 -0
  23. package/dist/layout/cat-editor.d.cts +28 -0
  24. package/dist/layout/cat-editor.d.ts +28 -0
  25. package/dist/layout/cat-editor.js +29 -0
  26. package/dist/layout/cat-editor.js.map +1 -0
  27. package/dist/layout/select.cjs +2 -1
  28. package/dist/layout/select.cjs.map +1 -1
  29. package/dist/layout/select.js +2 -1
  30. package/dist/utils/detect-quotes.cjs +162 -0
  31. package/dist/utils/detect-quotes.cjs.map +1 -0
  32. package/dist/utils/detect-quotes.d.cts +88 -0
  33. package/dist/utils/detect-quotes.d.ts +88 -0
  34. package/dist/utils/detect-quotes.js +9 -0
  35. package/dist/utils/detect-quotes.js.map +1 -0
  36. package/package.json +39 -3
  37. package/dist/chunk-CZ3IMHZ6.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/layout/select.tsx","../src/lib/utils.ts"],"sourcesContent":["// ---------------------------------------------------------------------------\n// layout-ui — public API\n// ---------------------------------------------------------------------------\n// Add new component exports here as the library grows.\n// Each component module also has a dedicated deep-import entry point\n// (e.g. \"layout-ui/layout/select\") configured in package.json \"exports\".\n// ---------------------------------------------------------------------------\n\n// Layout\nexport { LayoutSelect, default as LayoutSelectDefault } from './layout/select'\nexport type { LayoutSelectProps, IOption, IconProp } from './layout/select'\n","import * as React from 'react'\nimport {\n useCallback,\n useEffect,\n useLayoutEffect,\n useMemo,\n useRef,\n useState,\n} from 'react'\nimport { Popover } from '@base-ui/react/popover'\nimport { Tooltip } from '@base-ui/react/tooltip'\nimport { Command } from 'cmdk'\nimport {\n DndContext,\n KeyboardSensor,\n PointerSensor,\n closestCenter,\n useSensor,\n useSensors,\n} from '@dnd-kit/core'\nimport {\n SortableContext,\n arrayMove,\n useSortable,\n verticalListSortingStrategy,\n} from '@dnd-kit/sortable'\nimport {\n restrictToFirstScrollableAncestor,\n restrictToVerticalAxis,\n} from '@dnd-kit/modifiers'\nimport { defaultRangeExtractor, useVirtualizer } from '@tanstack/react-virtual'\nimport { Check, ChevronDown, GripVertical, X } from 'lucide-react'\n\nimport type { Range } from '@tanstack/react-virtual'\nimport type {\n CollisionDetection,\n DragEndEvent,\n DragOverEvent,\n} from '@dnd-kit/core'\n\nimport { cn } from '@/lib/utils'\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/**\n * For `icon` we accept either a ReactNode (already rendered) or a **render\n * function** `() => ReactNode`. The render‑function form lets consumers pass\n * lazy/memoised icons so the component only mounts them when visible (better\n * perf for large lists with heavy SVG icons).\n */\nexport type IconProp = React.ReactNode | (() => React.ReactNode)\n\nexport interface IOption {\n label: string\n value: string | number\n icon?: IconProp\n disabled?: boolean\n disabledTooltip?: string\n /** Nested children – rendered as a visual group. */\n children?: Array<IOption>\n}\n\n// ---- Conditional selection types ----\n\ninterface SingleSelectProps {\n type: 'single'\n selectValue?: IOption | null\n onChange?: (value: IOption | null, selectedOption: IOption) => void\n collapsed?: never\n showItemsLength?: never\n}\n\ninterface MultipleSelectProps {\n type: 'multiple'\n selectValue?: Array<IOption>\n onChange?: (value: Array<IOption>, selectedOption: IOption) => void\n /** When `true` the trigger will expand to show all chips instead of\n * collapsing them into a \"+N\" overflow badge. */\n collapsed?: boolean\n /** Force a maximum number of visible chip items. Lower priority than the\n * automatic overflow detection when `collapsed` is not set. */\n showItemsLength?: number\n}\n\n// ---- Shared props ----\n\ninterface SharedSelectProps {\n /** All available options (may contain nested `children`). */\n options: Array<IOption>\n /** Placeholder shown when nothing is selected. */\n placeholder?: string\n /** Whether the entire select is disabled. */\n disabled?: boolean\n /** Whether the select is in read‑only mode (looks interactive but cannot\n * be changed). */\n readOnly?: boolean\n /** Marks the select as having a validation error. */\n error?: boolean\n /** Allow the user to clear the selection. */\n clearable?: boolean\n /** Custom class for the root wrapper. */\n className?: string\n /** Custom class for the trigger. */\n triggerClassName?: string\n /** Custom class for the popup. */\n popupClassName?: string\n\n // ---- Render overrides ----\n\n /** Replace the entire trigger UI. Receives the current value(s) and a\n * boolean indicating whether the popup is open. */\n renderTrigger?: (props: {\n value: IOption | Array<IOption> | null\n open: boolean\n disabled: boolean\n readOnly: boolean\n error: boolean\n placeholder: string\n }) => React.ReactNode\n\n /** Replace the default option row renderer inside the list. */\n renderItem?: (\n option: IOption,\n state: { selected: boolean; highlighted: boolean; disabled: boolean },\n ) => React.ReactNode\n\n // ---- List chrome ----\n\n /** Component(s) rendered *before* the option list inside the popup. */\n listPrefix?: React.ReactNode\n /** Component(s) rendered *after* the option list inside the popup. */\n listSuffix?: React.ReactNode\n\n // ---- Async / lazy ----\n\n /** Called when the popup opens. Return a list of options to *replace* the\n * current `options` prop (useful for lazy fetching). */\n queryFn?: () => Promise<Array<IOption>>\n\n /** Label rendered above the select (optional). */\n label?: string\n}\n\n// ---- Sortable types ----\n\ninterface SortableEnabledProps {\n /** Enable drag‑and‑drop reordering of options in the list. */\n sortable: true\n /** Called after the user finishes reordering. Receives the new sorted\n * array of options. Required when `sortable` is `true`. */\n onSortEnd: (sortedOptions: Array<IOption>) => void\n /** When `true` and options are grouped (have `children`), items can be\n * dragged across groups. By default sorting is scoped within each group. */\n sortableAcrossGroups?: boolean\n}\n\ninterface SortableDisabledProps {\n sortable?: false\n onSortEnd?: never\n sortableAcrossGroups?: never\n}\n\nexport type LayoutSelectProps = SharedSelectProps &\n (SingleSelectProps | MultipleSelectProps) &\n (SortableEnabledProps | SortableDisabledProps)\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/** Resolve an `IconProp` to a ReactNode – cheap for already‑rendered nodes,\n * and defers the call for function icons. */\nfunction resolveIcon(icon: IconProp | undefined): React.ReactNode {\n if (icon === undefined || icon === null) return null\n if (typeof icon === 'function') return (icon as () => React.ReactNode)()\n return icon\n}\n\n/** Flatten a potentially nested option tree into a flat list (depth‑first).\n * Group parents (options with `children`) are **excluded** – only leaves. */\nfunction flattenOptions(options: Array<IOption>): Array<IOption> {\n const result: Array<IOption> = []\n for (const opt of options) {\n if (opt.children && opt.children.length > 0) {\n result.push(...flattenOptions(opt.children))\n } else {\n result.push(opt)\n }\n }\n return result\n}\n\n// ---------------------------------------------------------------------------\n// Display row types for virtualised list (group headers + leaf options)\n// ---------------------------------------------------------------------------\n\ntype DisplayRow =\n | { kind: 'group-header'; label: string; groupValue: string | number }\n | { kind: 'option'; option: IOption; groupValue?: string | number }\n\n/** Build a display-row list from potentially grouped options.\n * If an option has `children`, emit a group-header row followed by child\n * option rows. Options without `children` are emitted as top-level\n * option rows (groupValue = undefined). */\nfunction buildDisplayRows(options: Array<IOption>): Array<DisplayRow> {\n const rows: Array<DisplayRow> = []\n for (const opt of options) {\n if (opt.children && opt.children.length > 0) {\n rows.push({\n kind: 'group-header',\n label: opt.label,\n groupValue: opt.value,\n })\n for (const child of opt.children) {\n rows.push({ kind: 'option', option: child, groupValue: opt.value })\n }\n } else {\n rows.push({ kind: 'option', option: opt })\n }\n }\n return rows\n}\n\n/** Check whether the options list contains any grouped entries. */\nfunction hasGroups(options: Array<IOption>): boolean {\n return options.some((o) => o.children && o.children.length > 0)\n}\n\n/** Simple value equality check for options. */\nfunction optionEq(a: IOption, b: IOption) {\n return a.value === b.value\n}\n\nfunction isSelected(option: IOption, value: IOption | Array<IOption> | null) {\n if (!value) return false\n if (Array.isArray(value)) return value.some((v) => optionEq(v, option))\n return optionEq(value, option)\n}\n\n// ---------------------------------------------------------------------------\n// Sortable item wrapper (for dnd‑kit inside virtual list)\n// ---------------------------------------------------------------------------\n\ninterface SortableItemProps {\n id: string\n children: React.ReactNode\n disabled?: boolean\n}\n\nfunction SortableItem({ id, children, disabled }: SortableItemProps) {\n const {\n setNodeRef,\n attributes,\n listeners,\n transform,\n transition,\n isDragging,\n } = useSortable({ id, disabled })\n\n const style: React.CSSProperties = {\n transform: transform\n ? `translate3d(${transform.x}px, ${transform.y}px, 0)`\n : undefined,\n transition,\n opacity: isDragging ? 0.5 : 1,\n position: 'relative',\n zIndex: isDragging ? 50 : undefined,\n }\n\n return (\n <div ref={setNodeRef} style={style} className=\"flex items-center\">\n {!disabled && (\n <button\n type=\"button\"\n className=\"flex shrink-0 cursor-grab items-center px-1 text-muted-foreground hover:text-foreground active:cursor-grabbing\"\n {...attributes}\n {...listeners}\n >\n <GripVertical className=\"size-3.5\" />\n </button>\n )}\n <div className=\"min-w-0 flex-1\">{children}</div>\n </div>\n )\n}\n\n// ---------------------------------------------------------------------------\n// DisabledTooltip wrapper\n// ---------------------------------------------------------------------------\n\nfunction MaybeTooltip({\n tooltip,\n children,\n}: {\n tooltip?: string\n children: React.ReactElement\n}) {\n if (!tooltip) return children\n return (\n <Tooltip.Root>\n <Tooltip.Trigger render={children} />\n <Tooltip.Portal>\n <Tooltip.Positioner sideOffset={6}>\n <Tooltip.Popup className=\"rounded-md bg-foreground px-2.5 py-1 text-xs text-background shadow-md\">\n {tooltip}\n </Tooltip.Popup>\n </Tooltip.Positioner>\n </Tooltip.Portal>\n </Tooltip.Root>\n )\n}\n\n// ---------------------------------------------------------------------------\n// Chip (for multiple‑select trigger)\n// ---------------------------------------------------------------------------\n\ninterface ChipProps {\n option: IOption\n onRemove?: () => void\n readOnly?: boolean\n disabled?: boolean\n className?: string\n /** Mark as the \"partial\" chip that may be squeezed to truncate. */\n partial?: boolean\n}\n\nfunction Chip({\n option,\n onRemove,\n readOnly,\n disabled,\n className,\n partial,\n}: ChipProps) {\n return (\n <span\n data-partial-chip={partial || undefined}\n className={cn(\n 'inline-flex max-w-35 items-center gap-1 rounded-md border border-border bg-secondary px-2 py-0.5 text-xs leading-5 text-secondary-foreground',\n disabled && 'opacity-50',\n className,\n )}\n >\n {option.icon && (\n <span className=\"flex shrink-0 items-center [&_svg]:size-3\">\n {resolveIcon(option.icon)}\n </span>\n )}\n <span className=\"truncate\">{option.label}</span>\n {!readOnly && !disabled && onRemove && (\n <button\n type=\"button\"\n className=\"ml-0.5 flex shrink-0 items-center rounded-sm text-muted-foreground hover:text-foreground\"\n onClick={(e) => {\n e.stopPropagation()\n onRemove()\n }}\n tabIndex={-1}\n aria-label={`Remove ${option.label}`}\n >\n <X className=\"size-3\" />\n </button>\n )}\n </span>\n )\n}\n\n// ---------------------------------------------------------------------------\n// Overflow chip badge\n// ---------------------------------------------------------------------------\n\nfunction OverflowBadge({\n items,\n onRemove,\n}: {\n items: Array<IOption>\n onRemove?: (option: IOption) => void\n}) {\n if (items.length === 0) return null\n return (\n <Tooltip.Root>\n <Tooltip.Trigger\n render={\n <span className=\"inline-flex shrink-0 items-center rounded-md border border-border bg-muted px-1.5 py-0.5 text-xs font-medium text-muted-foreground\">\n +{items.length}\n </span>\n }\n />\n <Tooltip.Portal>\n <Tooltip.Positioner sideOffset={6}>\n <Tooltip.Popup className=\"max-w-xs rounded-md bg-foreground px-3 py-2 text-xs text-background shadow-md\">\n <div className=\"flex flex-wrap gap-1\">\n {items.map((item) => (\n <span\n key={item.value}\n className=\"inline-flex items-center gap-1 rounded-md border border-background/20 bg-background/10 px-1.5 py-0.5 text-xs leading-4 text-background\"\n >\n {item.icon && (\n <span className=\"flex shrink-0 items-center [&_svg]:size-3\">\n {resolveIcon(item.icon)}\n </span>\n )}\n <span className=\"truncate\">{item.label}</span>\n {onRemove && (\n <button\n type=\"button\"\n className=\"ml-0.5 flex shrink-0 items-center rounded-sm text-background/60 hover:text-background\"\n onClick={(e) => {\n e.stopPropagation()\n onRemove(item)\n }}\n tabIndex={-1}\n aria-label={`Remove ${item.label}`}\n >\n <X className=\"size-3\" />\n </button>\n )}\n </span>\n ))}\n </div>\n </Tooltip.Popup>\n </Tooltip.Positioner>\n </Tooltip.Portal>\n </Tooltip.Root>\n )\n}\n\n// ---------------------------------------------------------------------------\n// Default trigger renderers\n// ---------------------------------------------------------------------------\n\nfunction SingleTriggerContent({\n value,\n placeholder,\n}: {\n value: IOption | null\n placeholder: string\n}) {\n if (!value) {\n return <span className=\"truncate text-muted-foreground\">{placeholder}</span>\n }\n return (\n <span className=\"flex items-center gap-2 truncate\">\n {value.icon && (\n <span className=\"flex shrink-0 items-center [&_svg]:size-4\">\n {resolveIcon(value.icon)}\n </span>\n )}\n <span className=\"truncate\">{value.label}</span>\n </span>\n )\n}\n\nfunction MultipleTriggerContent({\n value,\n placeholder,\n collapsed,\n showItemsLength,\n onRemove,\n readOnly,\n disabled,\n}: {\n value: Array<IOption>\n placeholder: string\n collapsed?: boolean\n showItemsLength?: number\n onRemove?: (option: IOption) => void\n readOnly?: boolean\n disabled?: boolean\n}) {\n // Shared layout classes so measure layer, display div, and wrappers\n // always use the same gap / alignment. Change once, applied everywhere.\n const chipRowClass = 'flex items-center gap-1'\n\n const wrapperRef = useRef<HTMLDivElement>(null)\n const measureRef = useRef<HTMLDivElement>(null)\n const [visibleCount, setVisibleCount] = useState(value.length)\n const [lastIsPartial, setLastIsPartial] = useState(false)\n const [measured, setMeasured] = useState(false)\n\n // Cap invisible measurement chips – no single-row trigger needs more than\n // ~20 chips, so we avoid creating thousands of DOM nodes for large lists.\n const measureCount = Math.min(value.length, 20)\n\n const calculate = React.useEffectEvent(() => {\n const measureContainer = measureRef.current\n if (!measureContainer) return\n\n const children = Array.from(measureContainer.children) as Array<HTMLElement>\n const containerRight = measureContainer.getBoundingClientRect().right\n const gap = parseFloat(getComputedStyle(measureContainer).columnGap) || 0\n // The invisible layer has no badge sibling, so always reserve space\n // for the badge that will appear in the visible layer.\n // Estimate badge width based on overflow digit count:\n // \"+N\" at text-xs with px-1.5 + border ≈ 14px + 8px per character.\n const estimateBadgeWidth = (overflowCount: number) =>\n overflowCount > 0 ? 14 + 8 * String(overflowCount).length : 0\n let count = 0\n let partial = false\n\n for (const child of children) {\n const childRight = child.getBoundingClientRect().right\n const overflow = value.length - (count + 1)\n const reserve =\n overflow > 0 ? Math.max(40, estimateBadgeWidth(overflow)) : 0\n if (childRight + reserve <= containerRight) {\n count++\n } else {\n break\n }\n }\n // Show at least 1 chip when there are items\n count = Math.max(1, count)\n\n // If there are overflow items, check whether an extra \"partial\"\n // (squeezed) chip can fit. Chips with icons need more space.\n if (count < value.length && children.length >= count) {\n const lastRight = children[count - 1].getBoundingClientRect().right\n // If the partial chip would be the LAST item, no badge is needed.\n const needsBadge = count + 1 < value.length\n const badgeReserve = needsBadge\n ? Math.max(40, estimateBadgeWidth(value.length - count - 1))\n : 0\n const spaceForPartial = containerRight - badgeReserve - lastRight - gap\n const nextChip = value[count]\n // Base minimum: 50px (covers label truncation + remove button).\n // If the chip has an icon, add the icon's actual rendered width + gap.\n let minPartialWidth = 50\n if (nextChip.icon && children.length > count) {\n const iconWrapper = children[count].firstElementChild\n if (iconWrapper) {\n minPartialWidth += iconWrapper.getBoundingClientRect().width + gap\n }\n }\n if (spaceForPartial >= minPartialWidth) {\n count++ // include partial chip in the count\n partial = true\n }\n }\n\n setVisibleCount(count)\n setLastIsPartial(partial)\n setMeasured(true)\n })\n\n useLayoutEffect(() => {\n if (collapsed || value.length === 0) {\n setVisibleCount(value.length)\n setLastIsPartial(false)\n setMeasured(true)\n return\n }\n\n const wrapper = wrapperRef.current\n const container = measureRef.current\n if (!wrapper || !container) return\n\n // Measure immediately (synchronous, before browser paint)\n calculate()\n\n // Re-measure whenever the wrapper resizes (window resize, container\n // layout change). Because we always measure the invisible layer\n // (which has ALL chips as shrink-0), this correctly handles both\n // resize-smaller AND resize-larger.\n const observer = new ResizeObserver(calculate)\n observer.observe(wrapper)\n return () => observer.disconnect()\n }, [collapsed, value])\n\n if (value.length === 0) {\n return <span className=\"truncate text-muted-foreground\">{placeholder}</span>\n }\n\n // Invisible measurement layer – always present, always has ALL chips\n // rendered at natural (shrink-0) size for stable measurement.\n const measureLayer = !collapsed && (\n <div\n ref={measureRef}\n className={cn(\n 'pointer-events-none absolute inset-0 overflow-hidden opacity-0',\n chipRowClass,\n )}\n aria-hidden\n >\n {value.slice(0, measureCount).map((opt) => (\n <Chip\n key={opt.value}\n option={opt}\n onRemove={onRemove ? () => {} : undefined}\n readOnly={readOnly}\n disabled={disabled}\n className=\"shrink-0\"\n />\n ))}\n </div>\n )\n\n // Wait for first measurement before showing real chips (SSR safety).\n const showContent = collapsed || measured\n if (!showContent) {\n return (\n <div\n ref={wrapperRef}\n className={cn('relative min-w-0 flex-1 overflow-hidden', chipRowClass)}\n >\n {measureLayer}\n <span className=\"truncate text-muted-foreground\">\n {value.length} selected\n </span>\n </div>\n )\n }\n\n // Determine maximum fully-visible chips.\n let maxVisible = collapsed ? value.length : visibleCount\n const hasExplicitLimit = !collapsed && showItemsLength !== undefined\n if (hasExplicitLimit) {\n maxVisible = showItemsLength\n }\n\n const hasOverflow = maxVisible < value.length\n const displayed = value.slice(0, maxVisible)\n const overflowItems = value.slice(maxVisible)\n\n return (\n <div\n ref={wrapperRef}\n className={cn('relative min-w-0 flex-1', chipRowClass)}\n >\n {measureLayer}\n <div\n className={cn(\n 'min-w-0 flex-1',\n chipRowClass,\n collapsed ? 'flex-wrap' : 'overflow-hidden',\n )}\n >\n {displayed.map((opt, i) => {\n const isPartial =\n (hasOverflow || lastIsPartial) &&\n !hasExplicitLimit &&\n i === displayed.length - 1\n const shouldShrink = isPartial || hasExplicitLimit\n return (\n <Chip\n key={opt.value}\n option={opt}\n onRemove={onRemove ? () => onRemove(opt) : undefined}\n readOnly={readOnly}\n disabled={disabled}\n partial={isPartial}\n className={shouldShrink ? 'min-w-0 shrink' : 'shrink-0'}\n />\n )\n })}\n </div>\n {overflowItems.length > 0 && (\n <span className=\"shrink-0\">\n <OverflowBadge items={overflowItems} onRemove={onRemove} />\n </span>\n )}\n </div>\n )\n}\n\n// ---------------------------------------------------------------------------\n// Option item (rendered inside Command.Item + virtual list)\n// ---------------------------------------------------------------------------\n\ninterface OptionRowProps {\n option: IOption\n selected: boolean\n renderItem?: LayoutSelectProps['renderItem']\n onSelect: (option: IOption) => void\n highlighted?: boolean\n}\n\nfunction OptionRow({\n option,\n selected,\n renderItem,\n onSelect,\n highlighted = false,\n}: OptionRowProps) {\n const content = renderItem ? (\n renderItem(option, {\n selected,\n highlighted,\n disabled: !!option.disabled,\n })\n ) : (\n <div className=\"flex w-full items-center gap-2\">\n {option.icon && (\n <span className=\"flex shrink-0 items-center [&_svg]:size-4\">\n {resolveIcon(option.icon)}\n </span>\n )}\n <span className=\"flex-1 truncate\">{option.label}</span>\n {selected && (\n <span className=\"ml-auto flex shrink-0 items-center text-primary\">\n <Check className=\"size-4\" />\n </span>\n )}\n </div>\n )\n\n const row = (\n <Command.Item\n value={`${option.value}`}\n disabled={option.disabled}\n onSelect={() => {\n if (!option.disabled) onSelect(option)\n }}\n className={cn(\n 'relative flex w-full cursor-default items-center rounded-sm px-2 py-1.5 text-sm outline-hidden select-none',\n 'data-[selected=true]:bg-accent data-[selected=true]:text-accent-foreground',\n option.disabled && 'pointer-events-none opacity-50',\n )}\n data-highlighted={highlighted || undefined}\n >\n {content}\n </Command.Item>\n )\n\n if (option.disabled && option.disabledTooltip) {\n return (\n <MaybeTooltip tooltip={option.disabledTooltip}>\n <div>{row}</div>\n </MaybeTooltip>\n )\n }\n\n return row\n}\n\n// ---------------------------------------------------------------------------\n// Virtual + Sortable list\n// ---------------------------------------------------------------------------\n\ninterface VirtualListProps {\n /** Raw (potentially grouped) options – used to build display rows. */\n options: Array<IOption>\n /** Flat leaf items for backward-compat (used when there are no groups). */\n items: Array<IOption>\n selectedValue: IOption | Array<IOption> | null\n renderItem?: LayoutSelectProps['renderItem']\n onSelect: (option: IOption) => void\n sortable?: boolean\n sortableAcrossGroups?: boolean\n onSortEnd?: (items: Array<IOption>) => void\n /** Called when items within a group are reordered (grouped mode). Receives\n * the group parent value and the new ordered children. */\n onGroupSortEnd?: (\n groupValue: string | number,\n children: Array<IOption>,\n ) => void\n /** Called when a cross-group drag produces a new grouped tree. */\n onTreeSort?: (tree: Array<IOption>) => void\n}\n\nfunction VirtualList({\n options,\n items,\n selectedValue,\n renderItem,\n onSelect,\n sortable,\n sortableAcrossGroups,\n onSortEnd,\n onGroupSortEnd,\n onTreeSort,\n}: VirtualListProps) {\n const parentRef = useRef<HTMLDivElement>(null)\n const [activeId, setActiveId] = useState<string | null>(null)\n\n // During a cross-group drag, holds the intermediate tree with the dragged\n // item moved to the destination group. When null, `options` prop is used.\n const [dragTree, setDragTree] = useState<Array<IOption> | null>(null)\n\n const effectiveOptions = dragTree ?? options\n\n const grouped = useMemo(() => hasGroups(effectiveOptions), [effectiveOptions])\n const displayRows = useMemo(\n () => (grouped ? buildDisplayRows(effectiveOptions) : undefined),\n [grouped, effectiveOptions],\n )\n\n // The flat list used by the virtualizer – either from display rows or the\n // legacy flat items.\n const flatDisplayRows = useMemo(\n () => items.map<DisplayRow>((o) => ({ kind: 'option', option: o })),\n [items],\n )\n const virtualItems = displayRows ?? flatDisplayRows\n\n // Derive activeIndex from activeId so it stays correct after cross-group\n // moves update the tree mid-drag.\n const activeIndex = useMemo(() => {\n if (!activeId) return null\n const idx = virtualItems.findIndex(\n (r) => r.kind === 'option' && `${r.option.value}` === activeId,\n )\n return idx !== -1 ? idx : null\n }, [activeId, virtualItems])\n\n // Custom range extractor: always include the actively-dragged item so the\n // virtualizer never unmounts it (dnd-kit needs the DOM node to stay alive).\n const rangeExtractor = useCallback(\n (range: Range) => {\n const result = defaultRangeExtractor(range)\n if (activeIndex !== null && !result.includes(activeIndex)) {\n result.push(activeIndex)\n result.sort((a, b) => a - b)\n }\n return result\n },\n [activeIndex],\n )\n\n const virtualizer = useVirtualizer({\n count: virtualItems.length,\n getScrollElement: () => parentRef.current,\n estimateSize: (index) =>\n virtualItems[index].kind === 'group-header' ? 28 : 36,\n overscan: 8,\n rangeExtractor,\n })\n\n // After a cross-group drop the tree rebuilds and displayRows changes.\n // Force the virtualizer to discard stale position caches BEFORE paint\n // so every row gets the correct translateY immediately.\n useLayoutEffect(() => {\n virtualizer.measure()\n }, [virtualizer, displayRows])\n\n const sensors = useSensors(\n useSensor(PointerSensor, { activationConstraint: { distance: 4 } }),\n useSensor(KeyboardSensor),\n )\n\n // Flat sortable IDs for the single SortableContext wrapping all items.\n const flatSortableIds = useMemo(\n () =>\n virtualItems\n .filter(\n (r): r is DisplayRow & { kind: 'option' } => r.kind === 'option',\n )\n .map((r) => `${r.option.value}`),\n [virtualItems],\n )\n\n // Custom collision detection: restrict collisions to same-group items\n // when sortableAcrossGroups is disabled.\n const sameGroupCollision: CollisionDetection = useCallback(\n (args) => {\n if (!displayRows) return closestCenter(args)\n const draggedId = args.active.id\n const activeRow = displayRows.find(\n (r) => r.kind === 'option' && `${r.option.value}` === `${draggedId}`,\n )\n if (!activeRow || activeRow.kind !== 'option') return closestCenter(args)\n const activeGroup = activeRow.groupValue\n const filtered = args.droppableContainers.filter((container) => {\n const row = displayRows.find(\n (r) =>\n r.kind === 'option' && `${r.option.value}` === `${container.id}`,\n )\n return row && row.kind === 'option' && row.groupValue === activeGroup\n })\n return closestCenter({ ...args, droppableContainers: filtered })\n },\n [displayRows],\n )\n\n // Custom sorting strategy for grouped options.\n // Only displace items that are in the same group as the active (dragged)\n // item. Cross-group items are never displaced — group headers are at\n // fixed virtualizer positions and can't participate in dnd-kit transforms,\n // so shifting items across groups would cause overlaps.\n const sortingStrategy = useMemo(() => {\n if (!grouped || !displayRows) return verticalListSortingStrategy\n const idToGroup = new Map<string, string | number | undefined>()\n for (const row of displayRows) {\n if (row.kind === 'option') {\n idToGroup.set(`${row.option.value}`, row.groupValue)\n }\n }\n const noMove = { x: 0, y: 0, scaleX: 1, scaleY: 1 }\n return (\n args: Parameters<typeof verticalListSortingStrategy>[0],\n ): ReturnType<typeof verticalListSortingStrategy> => {\n const draggedId = flatSortableIds[args.activeIndex]\n const currentId = flatSortableIds[args.index]\n if (\n draggedId &&\n currentId &&\n idToGroup.get(draggedId) !== idToGroup.get(currentId)\n ) {\n return noMove\n }\n return verticalListSortingStrategy(args)\n }\n }, [grouped, displayRows, flatSortableIds])\n\n // ---- onDragOver: move item between groups during drag ----\n const handleDragOver = useCallback(\n (event: DragOverEvent) => {\n if (!sortableAcrossGroups || !grouped) return\n const { active, over } = event\n if (!over || active.id === over.id) return\n\n const currentTree = dragTree ?? options\n const currentRows = buildDisplayRows(currentTree)\n\n const activeRow = currentRows.find(\n (r) => r.kind === 'option' && `${r.option.value}` === `${active.id}`,\n )\n const overRow = currentRows.find(\n (r) => r.kind === 'option' && `${r.option.value}` === `${over.id}`,\n )\n\n if (\n !activeRow ||\n activeRow.kind !== 'option' ||\n !overRow ||\n overRow.kind !== 'option'\n )\n return\n\n // Same group — let dnd-kit's sorting strategy handle displacement\n if (activeRow.groupValue === overRow.groupValue) return\n\n // Cross-group: move item from source group to destination group\n const newTree = currentTree.map((opt) => {\n if (!opt.children) return opt\n if (opt.value === activeRow.groupValue) {\n return {\n ...opt,\n children: opt.children.filter(\n (c) => `${c.value}` !== `${active.id}`,\n ),\n }\n }\n if (opt.value === overRow.groupValue) {\n // Remove first in case of duplicate, then insert at over position\n const destChildren = opt.children.filter(\n (c) => `${c.value}` !== `${active.id}`,\n )\n const overIdx = destChildren.findIndex(\n (c) => `${c.value}` === `${over.id}`,\n )\n destChildren.splice(\n overIdx !== -1 ? overIdx : destChildren.length,\n 0,\n activeRow.option,\n )\n return { ...opt, children: destChildren }\n }\n return opt\n })\n\n setDragTree(newTree)\n },\n [sortableAcrossGroups, grouped, dragTree, options],\n )\n\n // ---- Drag end handlers ----\n const handleDragEndFlat = useCallback(\n (event: DragEndEvent) => {\n setActiveId(null)\n setDragTree(null)\n const { active, over } = event\n if (!over || active.id === over.id || !onSortEnd) return\n const oldIndex = items.findIndex((i) => `${i.value}` === `${active.id}`)\n const newIndex = items.findIndex((i) => `${i.value}` === `${over.id}`)\n if (oldIndex !== -1 && newIndex !== -1) {\n onSortEnd(arrayMove(items, oldIndex, newIndex))\n }\n },\n [items, onSortEnd],\n )\n\n const handleDragEndGrouped = useCallback(\n (event: DragEndEvent) => {\n setActiveId(null)\n const { active, over } = event\n\n if (!over || active.id === over.id) {\n // No move — revert intermediate drag tree\n if (dragTree) onTreeSort?.(dragTree)\n setDragTree(null)\n return\n }\n\n if (!displayRows) {\n setDragTree(null)\n return\n }\n\n const activeRow = displayRows.find(\n (r) => r.kind === 'option' && `${r.option.value}` === `${active.id}`,\n )\n const overRow = displayRows.find(\n (r) => r.kind === 'option' && `${r.option.value}` === `${over.id}`,\n )\n\n if (\n !activeRow ||\n activeRow.kind !== 'option' ||\n !overRow ||\n overRow.kind !== 'option'\n ) {\n setDragTree(null)\n return\n }\n\n const activeGroup = activeRow.groupValue\n const overGroup = overRow.groupValue\n const baseTree = dragTree ?? options\n\n if (activeGroup === overGroup) {\n // Same-group reorder (applies on top of any earlier cross-group move)\n const groupChildren = displayRows\n .filter(\n (r): r is DisplayRow & { kind: 'option' } =>\n r.kind === 'option' && r.groupValue === activeGroup,\n )\n .map((r) => r.option)\n\n const oldIdx = groupChildren.findIndex(\n (i) => `${i.value}` === `${active.id}`,\n )\n const newIdx = groupChildren.findIndex(\n (i) => `${i.value}` === `${over.id}`,\n )\n\n if (oldIdx !== -1 && newIdx !== -1 && oldIdx !== newIdx) {\n const reordered = arrayMove(groupChildren, oldIdx, newIdx)\n if (dragTree) {\n // Cross-group move happened earlier — commit full tree\n const finalTree = baseTree.map((opt) => {\n if (opt.value === activeGroup && opt.children) {\n return { ...opt, children: reordered }\n }\n return opt\n })\n onTreeSort?.(finalTree)\n } else {\n // Pure within-group reorder\n onGroupSortEnd?.(activeGroup!, reordered)\n }\n } else if (dragTree) {\n // Cross-group happened but no final reorder within group\n onTreeSort?.(baseTree)\n }\n } else {\n // Fallback: active and over still in different groups at drop time\n const finalTree = baseTree.map((opt) => {\n if (!opt.children) return opt\n if (opt.value === activeGroup) {\n return {\n ...opt,\n children: opt.children.filter(\n (c) => `${c.value}` !== `${active.id}`,\n ),\n }\n }\n if (opt.value === overGroup) {\n const destChildren = [...opt.children]\n const overIdx = destChildren.findIndex(\n (c) => `${c.value}` === `${over.id}`,\n )\n destChildren.splice(\n overIdx !== -1 ? overIdx + 1 : destChildren.length,\n 0,\n activeRow.option,\n )\n return { ...opt, children: destChildren }\n }\n return opt\n })\n onTreeSort?.(finalTree)\n }\n\n setDragTree(null)\n },\n [dragTree, options, displayRows, onTreeSort, onGroupSortEnd],\n )\n\n // ---- Render the scrollable virtualised content ----\n const listContent = (\n <div\n ref={parentRef}\n className={cn(\n 'max-h-75 overflow-x-hidden',\n activeIndex !== null ? 'overflow-y-visible' : 'overflow-y-auto',\n )}\n >\n <div\n style={{\n height: `${virtualizer.getTotalSize()}px`,\n position: 'relative',\n width: '100%',\n }}\n >\n {virtualizer.getVirtualItems().map((vItem) => {\n const displayRow = virtualItems[vItem.index]\n\n // ----- Group header row -----\n if (displayRow.kind === 'group-header') {\n return (\n <div\n key={`gh-${displayRow.groupValue}`}\n style={{\n position: 'absolute',\n top: 0,\n left: 0,\n width: '100%',\n transform: `translateY(${vItem.start}px)`,\n }}\n data-index={vItem.index}\n ref={virtualizer.measureElement}\n >\n <div className=\"px-2 py-1 text-xs font-semibold text-muted-foreground\">\n {displayRow.label}\n </div>\n </div>\n )\n }\n\n // ----- Leaf option row -----\n const option = displayRow.option\n\n const row = (\n <OptionRow\n key={option.value}\n option={option}\n selected={isSelected(option, selectedValue)}\n renderItem={renderItem}\n onSelect={onSelect}\n />\n )\n\n const wrappedRow = sortable ? (\n <SortableItem\n key={option.value}\n id={`${option.value}`}\n disabled={option.disabled}\n >\n {row}\n </SortableItem>\n ) : (\n row\n )\n\n return (\n <div\n key={option.value}\n style={{\n position: 'absolute',\n top: 0,\n left: 0,\n width: '100%',\n transform: `translateY(${vItem.start}px)`,\n }}\n data-index={vItem.index}\n ref={virtualizer.measureElement}\n >\n {wrappedRow}\n </div>\n )\n })}\n </div>\n </div>\n )\n\n if (sortable) {\n const handleDragStart = (event: { active: { id: string | number } }) => {\n setActiveId(`${event.active.id}`)\n }\n const handleCancel = () => {\n setActiveId(null)\n setDragTree(null)\n }\n\n // Single SortableContext wrapping everything.\n // Custom sortingStrategy handles per-group displacement.\n return (\n <DndContext\n modifiers={[restrictToVerticalAxis, restrictToFirstScrollableAncestor]}\n sensors={sensors}\n collisionDetection={\n grouped && !sortableAcrossGroups ? sameGroupCollision : closestCenter\n }\n onDragStart={handleDragStart}\n onDragOver={handleDragOver}\n onDragEnd={grouped ? handleDragEndGrouped : handleDragEndFlat}\n onDragCancel={handleCancel}\n >\n <SortableContext items={flatSortableIds} strategy={sortingStrategy}>\n {listContent}\n </SortableContext>\n </DndContext>\n )\n }\n\n return listContent\n}\n\n// ---------------------------------------------------------------------------\n// Main component\n// ---------------------------------------------------------------------------\n\nexport function LayoutSelect(props: LayoutSelectProps) {\n const {\n type,\n options,\n placeholder = 'Select item',\n disabled = false,\n readOnly = false,\n error = false,\n clearable = false,\n className,\n triggerClassName,\n popupClassName,\n renderTrigger,\n renderItem,\n listPrefix,\n listSuffix,\n queryFn,\n label,\n } = props\n\n const [open, setOpen] = useState(false)\n const [search, setSearch] = useState('')\n const [asyncOptions, setAsyncOptions] = useState<Array<IOption> | null>(null)\n const [loading, setLoading] = useState(false)\n const [internalSortedOptions, setInternalSortedOptions] =\n useState<Array<IOption> | null>(null)\n\n // ---- Resolve current value ----\n const currentValue = useMemo(() => {\n if (type === 'single') {\n return (props as SingleSelectProps).selectValue ?? null\n }\n return (props as MultipleSelectProps).selectValue ?? []\n }, [type, props])\n\n // ---- Resolve display options ----\n const resolvedOptions = useMemo(() => {\n const base = asyncOptions ?? options\n return internalSortedOptions ?? base\n }, [asyncOptions, options, internalSortedOptions])\n\n const flatOptions = useMemo(\n () => flattenOptions(resolvedOptions),\n [resolvedOptions],\n )\n\n // ---- Filtered options (search) ----\n // For virtualisation we need a flat list of leaf options, and when options\n // are grouped we also keep a filtered version of the grouped tree so that\n // VirtualList can render group headers correctly.\n const filteredOptions = useMemo(() => {\n if (!search) return flatOptions\n const q = search.toLowerCase()\n return flatOptions.filter((o) => o.label.toLowerCase().includes(q))\n }, [flatOptions, search])\n\n /** Resolved options filtered by search, preserving group structure. */\n const filteredGroupedOptions = useMemo(() => {\n if (!search) return resolvedOptions\n const q = search.toLowerCase()\n return resolvedOptions\n .map((opt) => {\n if (opt.children && opt.children.length > 0) {\n const matched = opt.children.filter((c) =>\n c.label.toLowerCase().includes(q),\n )\n if (matched.length === 0) return null\n return { ...opt, children: matched }\n }\n return opt.label.toLowerCase().includes(q) ? opt : null\n })\n .filter(Boolean) as Array<IOption>\n }, [resolvedOptions, search])\n\n // ---- Async loading ----\n useEffect(() => {\n if (open && queryFn) {\n let cancelled = false\n setLoading(true)\n queryFn()\n .then((data) => {\n if (!cancelled) {\n setAsyncOptions(data)\n }\n })\n .finally(() => {\n if (!cancelled) setLoading(false)\n })\n return () => {\n cancelled = true\n }\n }\n }, [open, queryFn])\n\n // Reset search when closed\n useEffect(() => {\n if (!open) {\n setSearch('')\n }\n }, [open])\n\n // ---- Selection handler ----\n const handleSelect = useCallback(\n (option: IOption) => {\n if (readOnly) return\n\n if (type === 'single') {\n const onChange = (props as SingleSelectProps).onChange\n const current = currentValue as IOption | null\n if (clearable && current && optionEq(current, option)) {\n onChange?.(null, option)\n } else {\n onChange?.(option, option)\n }\n setOpen(false)\n } else {\n const onChange = (props as MultipleSelectProps).onChange\n const current = currentValue as Array<IOption>\n const exists = current.some((v) => optionEq(v, option))\n if (exists) {\n onChange?.(\n current.filter((v) => !optionEq(v, option)),\n option,\n )\n } else {\n onChange?.([...current, option], option)\n }\n }\n },\n [type, currentValue, clearable, readOnly, props],\n )\n\n // ---- Remove chip (multiple) ----\n const handleRemoveChip = useCallback(\n (option: IOption) => {\n if (type !== 'multiple' || readOnly || disabled) return\n const onChange = (props as MultipleSelectProps).onChange\n const current = currentValue as Array<IOption>\n onChange?.(\n current.filter((v) => !optionEq(v, option)),\n option,\n )\n },\n [type, currentValue, readOnly, disabled, props],\n )\n\n // ---- Select all / unselect all (multiple only) ----\n const handleToggleAll = useCallback(() => {\n if (type !== 'multiple' || readOnly || disabled) return\n const onChange = (props as MultipleSelectProps).onChange\n const current = currentValue as Array<IOption>\n const selectableOptions = flatOptions.filter((o) => !o.disabled)\n const allSelected =\n selectableOptions.length > 0 &&\n selectableOptions.every((o) => current.some((v) => optionEq(v, o)))\n\n if (allSelected) {\n // Keep only disabled options that were somehow selected\n const kept = current.filter((v) =>\n selectableOptions.every((o) => !optionEq(o, v)),\n )\n onChange?.(kept, selectableOptions[0])\n } else {\n // Merge: keep existing + add missing selectable options\n const missing = selectableOptions.filter(\n (o) => !current.some((v) => optionEq(v, o)),\n )\n onChange?.([...current, ...missing], selectableOptions[0])\n }\n }, [type, currentValue, flatOptions, readOnly, disabled, props])\n\n const allSelected = useMemo(() => {\n if (type !== 'multiple') return false\n const current = currentValue as Array<IOption>\n const selectableOptions = flatOptions.filter((o) => !o.disabled)\n return (\n selectableOptions.length > 0 &&\n selectableOptions.every((o) => current.some((v) => optionEq(v, o)))\n )\n }, [type, currentValue, flatOptions])\n\n const sortable = props.sortable ?? false\n const sortableAcrossGroups = props.sortable\n ? ((props as SortableEnabledProps).sortableAcrossGroups ?? false)\n : false\n const consumerOnSortEnd = props.sortable\n ? (props as SortableEnabledProps).onSortEnd\n : undefined\n\n // ---- Sort handler (flat) ----\n const handleSortEnd = useCallback(\n (sorted: Array<IOption>) => {\n setInternalSortedOptions(sorted)\n consumerOnSortEnd?.(sorted)\n },\n [consumerOnSortEnd],\n )\n\n // ---- Sort handler (within a group) ----\n const handleGroupSortEnd = useCallback(\n (groupValue: string | number, reorderedChildren: Array<IOption>) => {\n // Rebuild the full options tree with the updated group\n const updated = resolvedOptions.map((opt) => {\n if (opt.value === groupValue && opt.children) {\n return { ...opt, children: reorderedChildren }\n }\n return opt\n })\n setInternalSortedOptions(updated)\n consumerOnSortEnd?.(flattenOptions(updated))\n },\n [resolvedOptions, consumerOnSortEnd],\n )\n\n // ---- Sort handler (cross-group tree rebuild) ----\n const handleTreeSort = useCallback(\n (newTree: Array<IOption>) => {\n setInternalSortedOptions(newTree)\n consumerOnSortEnd?.(flattenOptions(newTree))\n },\n [consumerOnSortEnd],\n )\n\n // ---- Open handler ----\n const handleOpenChange = useCallback(\n (nextOpen: boolean) => {\n if (disabled || readOnly) return\n setOpen(nextOpen)\n },\n [disabled, readOnly],\n )\n\n // ---- Trigger content ----\n const triggerContent = useMemo(() => {\n if (renderTrigger) {\n return renderTrigger({\n value: currentValue,\n open,\n disabled,\n readOnly,\n error,\n placeholder,\n })\n }\n\n if (type === 'single') {\n return (\n <SingleTriggerContent\n value={currentValue as IOption | null}\n placeholder={placeholder}\n />\n )\n }\n\n return (\n <MultipleTriggerContent\n value={currentValue as Array<IOption>}\n placeholder={placeholder}\n collapsed={(props as MultipleSelectProps).collapsed}\n showItemsLength={(props as MultipleSelectProps).showItemsLength}\n onRemove={handleRemoveChip}\n readOnly={readOnly}\n disabled={disabled}\n />\n )\n }, [\n renderTrigger,\n type,\n currentValue,\n open,\n disabled,\n readOnly,\n error,\n placeholder,\n props,\n handleRemoveChip,\n ])\n\n return (\n <Tooltip.Provider>\n <div className={cn('relative inline-flex flex-col', className)}>\n {label && (\n <label className=\"mb-1 text-sm font-medium text-foreground\">\n {label}\n </label>\n )}\n <Popover.Root open={open} onOpenChange={handleOpenChange}>\n <Popover.Trigger\n disabled={disabled}\n render={\n <button\n type=\"button\"\n aria-expanded={open}\n aria-haspopup=\"listbox\"\n aria-invalid={error || undefined}\n data-readonly={readOnly || undefined}\n className={cn(\n 'border-input flex min-h-9 w-full min-w-45 items-center gap-2 rounded-md border bg-transparent px-3 py-1.5 text-sm shadow-xs transition-[color,box-shadow,border-color] outline-none',\n 'focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]',\n 'aria-invalid:border-destructive aria-invalid:ring-destructive/20',\n 'data-readonly:pointer-events-none data-readonly:opacity-70',\n 'disabled:cursor-not-allowed disabled:opacity-50',\n \"[&_svg:not([class*='text-'])]:text-muted-foreground\",\n triggerClassName,\n )}\n />\n }\n >\n <div className=\"flex min-w-0 flex-1 items-center\">\n {triggerContent}\n </div>\n <ChevronDown className=\"size-4 shrink-0 opacity-50\" />\n </Popover.Trigger>\n\n <Popover.Portal>\n <Popover.Positioner sideOffset={4}>\n <Popover.Popup\n className={cn(\n 'z-50 min-w-(--anchor-width) rounded-md border bg-popover text-popover-foreground shadow-md outline-none',\n 'data-starting-style:scale-95 data-starting-style:opacity-0',\n 'data-ending-style:scale-95 data-ending-style:opacity-0',\n 'origin-(--transform-origin) transition-[transform,scale,opacity] duration-150',\n popupClassName,\n )}\n >\n <Command shouldFilter={false} loop>\n {/* Search input */}\n <div className=\"border-b p-1\">\n <Command.Input\n value={search}\n onValueChange={setSearch}\n placeholder=\"Search...\"\n className=\"h-8 w-full rounded-sm bg-transparent px-2 text-sm outline-none placeholder:text-muted-foreground\"\n />\n </div>\n\n {/* List prefix */}\n {listPrefix && (\n <div className=\"border-b px-2 py-1.5\">{listPrefix}</div>\n )}\n\n {/* Select all / Unselect all */}\n {type === 'multiple' && !readOnly && (\n <div className=\"border-b px-1 py-1\">\n <button\n type=\"button\"\n onClick={handleToggleAll}\n className=\"w-full rounded-sm px-2 py-1 text-left text-sm text-muted-foreground hover:bg-accent hover:text-accent-foreground\"\n >\n {allSelected ? 'Unselect all' : 'Select all'}\n </button>\n </div>\n )}\n\n <Command.List className=\"p-1\">\n {loading && (\n <Command.Loading>\n <div className=\"flex items-center justify-center py-4 text-sm text-muted-foreground\">\n Loading…\n </div>\n </Command.Loading>\n )}\n\n {!loading && filteredOptions.length === 0 && (\n <Command.Empty className=\"py-4 text-center text-sm text-muted-foreground\">\n No results found.\n </Command.Empty>\n )}\n\n {!loading && filteredOptions.length > 0 && (\n <VirtualList\n options={filteredGroupedOptions}\n items={filteredOptions}\n selectedValue={currentValue}\n renderItem={renderItem}\n onSelect={handleSelect}\n sortable={sortable}\n sortableAcrossGroups={sortableAcrossGroups}\n onSortEnd={handleSortEnd}\n onGroupSortEnd={handleGroupSortEnd}\n onTreeSort={handleTreeSort}\n />\n )}\n </Command.List>\n\n {/* List suffix */}\n {listSuffix && (\n <div className=\"border-t px-2 py-1.5\">{listSuffix}</div>\n )}\n </Command>\n </Popover.Popup>\n </Popover.Positioner>\n </Popover.Portal>\n </Popover.Root>\n </div>\n </Tooltip.Provider>\n )\n}\n\nexport default LayoutSelect\n","import { clsx, type ClassValue } from 'clsx'\nimport { twMerge } from 'tailwind-merge'\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs))\n}\n"],"mappings":"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","names":["allSelected"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/layout/select.tsx","../src/lib/utils.ts","../src/layout/cat-editor/CATEditor.tsx","../src/layout/cat-editor/constants.ts","../src/layout/cat-editor/highlight-node.ts","../src/layout/cat-editor/mention-node.ts","../src/layout/cat-editor/mention-plugin.tsx","../src/layout/cat-editor/plugins.tsx","../src/utils/detect-quotes.ts","../src/layout/cat-editor/compute-segments.ts","../src/layout/cat-editor/selection-helpers.ts","../src/layout/cat-editor/popover.tsx"],"sourcesContent":["// ---------------------------------------------------------------------------\n// layout-ui — public API\n// ---------------------------------------------------------------------------\n// Add new component exports here as the library grows.\n// Each component module also has a dedicated deep-import entry point\n// (e.g. \"layout-ui/layout/select\") configured in package.json \"exports\".\n// ---------------------------------------------------------------------------\n\n// Layout\nexport { LayoutSelect, default as LayoutSelectDefault } from './layout/select'\nexport type { LayoutSelectProps, IOption, IconProp } from './layout/select'\n\n// Layout — CATEditor\nexport { CATEditor } from './layout/cat-editor'\nexport type { CATEditorProps } from './layout/cat-editor'\nexport type {\n CATEditorRef,\n MooRule,\n ISpellCheckRule,\n IKeywordsRule,\n ISpecialCharRule,\n ITagRule,\n IQuoteRule,\n ILinkRule,\n IMentionRule,\n IMentionUser,\n RuleAnnotation,\n PopoverContentRenderer,\n PopoverContentRendererProps,\n} from './layout/cat-editor'\n\n// Utils — Detect Quotes\nexport { detectQuotes, BUILTIN_ESCAPE_PATTERNS } from './utils/detect-quotes'\nexport type {\n QuoteRange,\n QuoteType,\n DetectQuotesOptions,\n EscapePatterns,\n} from './utils/detect-quotes'\n","import * as React from 'react'\nimport {\n useCallback,\n useEffect,\n useLayoutEffect,\n useMemo,\n useRef,\n useState,\n} from 'react'\nimport { Popover } from '@base-ui/react/popover'\nimport { Tooltip } from '@base-ui/react/tooltip'\nimport { Command } from 'cmdk'\nimport {\n DndContext,\n KeyboardSensor,\n PointerSensor,\n closestCenter,\n useSensor,\n useSensors,\n} from '@dnd-kit/core'\nimport {\n SortableContext,\n arrayMove,\n useSortable,\n verticalListSortingStrategy,\n} from '@dnd-kit/sortable'\nimport {\n restrictToFirstScrollableAncestor,\n restrictToVerticalAxis,\n} from '@dnd-kit/modifiers'\nimport { defaultRangeExtractor, useVirtualizer } from '@tanstack/react-virtual'\nimport { Check, ChevronDown, GripVertical, X } from 'lucide-react'\n\nimport type { Range } from '@tanstack/react-virtual'\nimport type {\n CollisionDetection,\n DragEndEvent,\n DragOverEvent,\n} from '@dnd-kit/core'\n\nimport { cn } from '@/lib/utils'\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/**\n * For `icon` we accept either a ReactNode (already rendered) or a **render\n * function** `() => ReactNode`. The render‑function form lets consumers pass\n * lazy/memoised icons so the component only mounts them when visible (better\n * perf for large lists with heavy SVG icons).\n */\nexport type IconProp = React.ReactNode | (() => React.ReactNode)\n\nexport interface IOption {\n label: string\n value: string | number\n icon?: IconProp\n disabled?: boolean\n disabledTooltip?: string\n /** Nested children – rendered as a visual group. */\n children?: Array<IOption>\n}\n\n// ---- Conditional selection types ----\n\ninterface SingleSelectProps {\n type: 'single'\n selectValue?: IOption | null\n onChange?: (value: IOption | null, selectedOption: IOption) => void\n collapsed?: never\n showItemsLength?: never\n}\n\ninterface MultipleSelectProps {\n type: 'multiple'\n selectValue?: Array<IOption>\n onChange?: (value: Array<IOption>, selectedOption: IOption) => void\n /** When `true` the trigger will expand to show all chips instead of\n * collapsing them into a \"+N\" overflow badge. */\n collapsed?: boolean\n /** Force a maximum number of visible chip items. Lower priority than the\n * automatic overflow detection when `collapsed` is not set. */\n showItemsLength?: number\n}\n\n// ---- Shared props ----\n\ninterface SharedSelectProps {\n /** All available options (may contain nested `children`). */\n options: Array<IOption>\n /** Placeholder shown when nothing is selected. */\n placeholder?: string\n /** Whether the entire select is disabled. */\n disabled?: boolean\n /** Whether the select is in read‑only mode (looks interactive but cannot\n * be changed). */\n readOnly?: boolean\n /** Marks the select as having a validation error. */\n error?: boolean\n /** Allow the user to clear the selection. */\n clearable?: boolean\n /** Custom class for the root wrapper. */\n className?: string\n /** Custom class for the trigger. */\n triggerClassName?: string\n /** Custom class for the popup. */\n popupClassName?: string\n\n // ---- Render overrides ----\n\n /** Replace the entire trigger UI. Receives the current value(s) and a\n * boolean indicating whether the popup is open. */\n renderTrigger?: (props: {\n value: IOption | Array<IOption> | null\n open: boolean\n disabled: boolean\n readOnly: boolean\n error: boolean\n placeholder: string\n }) => React.ReactNode\n\n /** Replace the default option row renderer inside the list. */\n renderItem?: (\n option: IOption,\n state: { selected: boolean; highlighted: boolean; disabled: boolean },\n ) => React.ReactNode\n\n // ---- List chrome ----\n\n /** Component(s) rendered *before* the option list inside the popup. */\n listPrefix?: React.ReactNode\n /** Component(s) rendered *after* the option list inside the popup. */\n listSuffix?: React.ReactNode\n\n // ---- Async / lazy ----\n\n /** Called when the popup opens. Return a list of options to *replace* the\n * current `options` prop (useful for lazy fetching). */\n queryFn?: () => Promise<Array<IOption>>\n\n /** Label rendered above the select (optional). */\n label?: string\n}\n\n// ---- Sortable types ----\n\ninterface SortableEnabledProps {\n /** Enable drag‑and‑drop reordering of options in the list. */\n sortable: true\n /** Called after the user finishes reordering. Receives the new sorted\n * array of options. Required when `sortable` is `true`. */\n onSortEnd: (sortedOptions: Array<IOption>) => void\n /** When `true` and options are grouped (have `children`), items can be\n * dragged across groups. By default sorting is scoped within each group. */\n sortableAcrossGroups?: boolean\n}\n\ninterface SortableDisabledProps {\n sortable?: false\n onSortEnd?: never\n sortableAcrossGroups?: never\n}\n\nexport type LayoutSelectProps = SharedSelectProps &\n (SingleSelectProps | MultipleSelectProps) &\n (SortableEnabledProps | SortableDisabledProps)\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/** Resolve an `IconProp` to a ReactNode – cheap for already‑rendered nodes,\n * and defers the call for function icons. */\nfunction resolveIcon(icon: IconProp | undefined): React.ReactNode {\n if (icon === undefined || icon === null) return null\n if (typeof icon === 'function') return (icon as () => React.ReactNode)()\n return icon\n}\n\n/** Flatten a potentially nested option tree into a flat list (depth‑first).\n * Group parents (options with `children`) are **excluded** – only leaves. */\nfunction flattenOptions(options: Array<IOption>): Array<IOption> {\n const result: Array<IOption> = []\n for (const opt of options) {\n if (opt.children && opt.children.length > 0) {\n result.push(...flattenOptions(opt.children))\n } else {\n result.push(opt)\n }\n }\n return result\n}\n\n// ---------------------------------------------------------------------------\n// Display row types for virtualised list (group headers + leaf options)\n// ---------------------------------------------------------------------------\n\ntype DisplayRow =\n | { kind: 'group-header'; label: string; groupValue: string | number }\n | { kind: 'option'; option: IOption; groupValue?: string | number }\n\n/** Build a display-row list from potentially grouped options.\n * If an option has `children`, emit a group-header row followed by child\n * option rows. Options without `children` are emitted as top-level\n * option rows (groupValue = undefined). */\nfunction buildDisplayRows(options: Array<IOption>): Array<DisplayRow> {\n const rows: Array<DisplayRow> = []\n for (const opt of options) {\n if (opt.children && opt.children.length > 0) {\n rows.push({\n kind: 'group-header',\n label: opt.label,\n groupValue: opt.value,\n })\n for (const child of opt.children) {\n rows.push({ kind: 'option', option: child, groupValue: opt.value })\n }\n } else {\n rows.push({ kind: 'option', option: opt })\n }\n }\n return rows\n}\n\n/** Check whether the options list contains any grouped entries. */\nfunction hasGroups(options: Array<IOption>): boolean {\n return options.some((o) => o.children && o.children.length > 0)\n}\n\n/** Simple value equality check for options. */\nfunction optionEq(a: IOption, b: IOption) {\n return a.value === b.value\n}\n\nfunction isSelected(option: IOption, value: IOption | Array<IOption> | null) {\n if (!value) return false\n if (Array.isArray(value)) return value.some((v) => optionEq(v, option))\n return optionEq(value, option)\n}\n\n// ---------------------------------------------------------------------------\n// Sortable item wrapper (for dnd‑kit inside virtual list)\n// ---------------------------------------------------------------------------\n\ninterface SortableItemProps {\n id: string\n children: React.ReactNode\n disabled?: boolean\n}\n\nfunction SortableItem({ id, children, disabled }: SortableItemProps) {\n const {\n setNodeRef,\n attributes,\n listeners,\n transform,\n transition,\n isDragging,\n } = useSortable({ id, disabled })\n\n const style: React.CSSProperties = {\n transform: transform\n ? `translate3d(${transform.x}px, ${transform.y}px, 0)`\n : undefined,\n transition,\n opacity: isDragging ? 0.5 : 1,\n position: 'relative',\n zIndex: isDragging ? 50 : undefined,\n }\n\n return (\n <div ref={setNodeRef} style={style} className=\"flex items-center\">\n {!disabled && (\n <button\n type=\"button\"\n className=\"flex shrink-0 cursor-grab items-center px-1 text-muted-foreground hover:text-foreground active:cursor-grabbing\"\n {...attributes}\n {...listeners}\n >\n <GripVertical className=\"size-3.5\" />\n </button>\n )}\n <div className=\"min-w-0 flex-1\">{children}</div>\n </div>\n )\n}\n\n// ---------------------------------------------------------------------------\n// DisabledTooltip wrapper\n// ---------------------------------------------------------------------------\n\nfunction MaybeTooltip({\n tooltip,\n children,\n}: {\n tooltip?: string\n children: React.ReactElement\n}) {\n if (!tooltip) return children\n return (\n <Tooltip.Root>\n <Tooltip.Trigger render={children} />\n <Tooltip.Portal>\n <Tooltip.Positioner sideOffset={6}>\n <Tooltip.Popup className=\"rounded-md bg-foreground px-2.5 py-1 text-xs text-background shadow-md\">\n {tooltip}\n </Tooltip.Popup>\n </Tooltip.Positioner>\n </Tooltip.Portal>\n </Tooltip.Root>\n )\n}\n\n// ---------------------------------------------------------------------------\n// Chip (for multiple‑select trigger)\n// ---------------------------------------------------------------------------\n\ninterface ChipProps {\n option: IOption\n onRemove?: () => void\n readOnly?: boolean\n disabled?: boolean\n className?: string\n /** Mark as the \"partial\" chip that may be squeezed to truncate. */\n partial?: boolean\n}\n\nfunction Chip({\n option,\n onRemove,\n readOnly,\n disabled,\n className,\n partial,\n}: ChipProps) {\n return (\n <span\n data-partial-chip={partial || undefined}\n className={cn(\n 'inline-flex max-w-35 items-center gap-1 rounded-md border border-border bg-secondary px-2 py-0.5 text-xs leading-5 text-secondary-foreground',\n disabled && 'opacity-50',\n className,\n )}\n >\n {option.icon && (\n <span className=\"flex shrink-0 items-center [&_svg]:size-3\">\n {resolveIcon(option.icon)}\n </span>\n )}\n <span className=\"truncate\">{option.label}</span>\n {!readOnly && !disabled && onRemove && (\n <button\n type=\"button\"\n className=\"ml-0.5 flex shrink-0 items-center rounded-sm text-muted-foreground hover:text-foreground\"\n onClick={(e) => {\n e.stopPropagation()\n onRemove()\n }}\n tabIndex={-1}\n aria-label={`Remove ${option.label}`}\n >\n <X className=\"size-3\" />\n </button>\n )}\n </span>\n )\n}\n\n// ---------------------------------------------------------------------------\n// Overflow chip badge\n// ---------------------------------------------------------------------------\n\nfunction OverflowBadge({\n items,\n onRemove,\n}: {\n items: Array<IOption>\n onRemove?: (option: IOption) => void\n}) {\n if (items.length === 0) return null\n return (\n <Tooltip.Root>\n <Tooltip.Trigger\n render={\n <span className=\"inline-flex shrink-0 items-center rounded-md border border-border bg-muted px-1.5 py-0.5 text-xs font-medium text-muted-foreground\">\n +{items.length}\n </span>\n }\n />\n <Tooltip.Portal>\n <Tooltip.Positioner sideOffset={6}>\n <Tooltip.Popup className=\"max-w-xs rounded-md bg-foreground px-3 py-2 text-xs text-background shadow-md\">\n <div className=\"flex flex-wrap gap-1\">\n {items.map((item) => (\n <span\n key={item.value}\n className=\"inline-flex items-center gap-1 rounded-md border border-background/20 bg-background/10 px-1.5 py-0.5 text-xs leading-4 text-background\"\n >\n {item.icon && (\n <span className=\"flex shrink-0 items-center [&_svg]:size-3\">\n {resolveIcon(item.icon)}\n </span>\n )}\n <span className=\"truncate\">{item.label}</span>\n {onRemove && (\n <button\n type=\"button\"\n className=\"ml-0.5 flex shrink-0 items-center rounded-sm text-background/60 hover:text-background\"\n onClick={(e) => {\n e.stopPropagation()\n onRemove(item)\n }}\n tabIndex={-1}\n aria-label={`Remove ${item.label}`}\n >\n <X className=\"size-3\" />\n </button>\n )}\n </span>\n ))}\n </div>\n </Tooltip.Popup>\n </Tooltip.Positioner>\n </Tooltip.Portal>\n </Tooltip.Root>\n )\n}\n\n// ---------------------------------------------------------------------------\n// Default trigger renderers\n// ---------------------------------------------------------------------------\n\nfunction SingleTriggerContent({\n value,\n placeholder,\n}: {\n value: IOption | null\n placeholder: string\n}) {\n if (!value) {\n return <span className=\"truncate text-muted-foreground\">{placeholder}</span>\n }\n return (\n <span className=\"flex items-center gap-2 truncate\">\n {value.icon && (\n <span className=\"flex shrink-0 items-center [&_svg]:size-4\">\n {resolveIcon(value.icon)}\n </span>\n )}\n <span className=\"truncate\">{value.label}</span>\n </span>\n )\n}\n\nfunction MultipleTriggerContent({\n value,\n placeholder,\n collapsed,\n showItemsLength,\n onRemove,\n readOnly,\n disabled,\n}: {\n value: Array<IOption>\n placeholder: string\n collapsed?: boolean\n showItemsLength?: number\n onRemove?: (option: IOption) => void\n readOnly?: boolean\n disabled?: boolean\n}) {\n // Shared layout classes so measure layer, display div, and wrappers\n // always use the same gap / alignment. Change once, applied everywhere.\n const chipRowClass = 'flex items-center gap-1'\n\n const wrapperRef = useRef<HTMLDivElement>(null)\n const measureRef = useRef<HTMLDivElement>(null)\n const [visibleCount, setVisibleCount] = useState(value.length)\n const [lastIsPartial, setLastIsPartial] = useState(false)\n const [measured, setMeasured] = useState(false)\n\n // Cap invisible measurement chips – no single-row trigger needs more than\n // ~20 chips, so we avoid creating thousands of DOM nodes for large lists.\n const measureCount = Math.min(value.length, 20)\n\n const calculate = React.useEffectEvent(() => {\n const measureContainer = measureRef.current\n if (!measureContainer) return\n\n const children = Array.from(measureContainer.children) as Array<HTMLElement>\n const containerRight = measureContainer.getBoundingClientRect().right\n const gap = parseFloat(getComputedStyle(measureContainer).columnGap) || 0\n // The invisible layer has no badge sibling, so always reserve space\n // for the badge that will appear in the visible layer.\n // Estimate badge width based on overflow digit count:\n // \"+N\" at text-xs with px-1.5 + border ≈ 14px + 8px per character.\n const estimateBadgeWidth = (overflowCount: number) =>\n overflowCount > 0 ? 14 + 8 * String(overflowCount).length : 0\n let count = 0\n let partial = false\n\n for (const child of children) {\n const childRight = child.getBoundingClientRect().right\n const overflow = value.length - (count + 1)\n const reserve =\n overflow > 0 ? Math.max(40, estimateBadgeWidth(overflow)) : 0\n if (childRight + reserve <= containerRight) {\n count++\n } else {\n break\n }\n }\n // Show at least 1 chip when there are items\n count = Math.max(1, count)\n\n // If there are overflow items, check whether an extra \"partial\"\n // (squeezed) chip can fit. Chips with icons need more space.\n if (count < value.length && children.length >= count) {\n const lastRight = children[count - 1].getBoundingClientRect().right\n // If the partial chip would be the LAST item, no badge is needed.\n const needsBadge = count + 1 < value.length\n const badgeReserve = needsBadge\n ? Math.max(40, estimateBadgeWidth(value.length - count - 1))\n : 0\n const spaceForPartial = containerRight - badgeReserve - lastRight - gap\n const nextChip = value[count]\n // Base minimum: 50px (covers label truncation + remove button).\n // If the chip has an icon, add the icon's actual rendered width + gap.\n let minPartialWidth = 50\n if (nextChip.icon && children.length > count) {\n const iconWrapper = children[count].firstElementChild\n if (iconWrapper) {\n minPartialWidth += iconWrapper.getBoundingClientRect().width + gap\n }\n }\n if (spaceForPartial >= minPartialWidth) {\n count++ // include partial chip in the count\n partial = true\n }\n }\n\n setVisibleCount(count)\n setLastIsPartial(partial)\n setMeasured(true)\n })\n\n useLayoutEffect(() => {\n if (collapsed || value.length === 0) {\n setVisibleCount(value.length)\n setLastIsPartial(false)\n setMeasured(true)\n return\n }\n\n const wrapper = wrapperRef.current\n const container = measureRef.current\n if (!wrapper || !container) return\n\n // Measure immediately (synchronous, before browser paint)\n calculate()\n\n // Re-measure whenever the wrapper resizes (window resize, container\n // layout change). Because we always measure the invisible layer\n // (which has ALL chips as shrink-0), this correctly handles both\n // resize-smaller AND resize-larger.\n const observer = new ResizeObserver(calculate)\n observer.observe(wrapper)\n return () => observer.disconnect()\n }, [collapsed, value])\n\n if (value.length === 0) {\n return <span className=\"truncate text-muted-foreground\">{placeholder}</span>\n }\n\n // Invisible measurement layer – always present, always has ALL chips\n // rendered at natural (shrink-0) size for stable measurement.\n const measureLayer = !collapsed && (\n <div\n ref={measureRef}\n className={cn(\n 'pointer-events-none absolute inset-0 overflow-hidden opacity-0',\n chipRowClass,\n )}\n aria-hidden\n >\n {value.slice(0, measureCount).map((opt) => (\n <Chip\n key={opt.value}\n option={opt}\n onRemove={onRemove ? () => {} : undefined}\n readOnly={readOnly}\n disabled={disabled}\n className=\"shrink-0\"\n />\n ))}\n </div>\n )\n\n // Wait for first measurement before showing real chips (SSR safety).\n const showContent = collapsed || measured\n if (!showContent) {\n return (\n <div\n ref={wrapperRef}\n className={cn('relative min-w-0 flex-1 overflow-hidden', chipRowClass)}\n >\n {measureLayer}\n <span className=\"truncate text-muted-foreground\">\n {value.length} selected\n </span>\n </div>\n )\n }\n\n // Determine maximum fully-visible chips.\n let maxVisible = collapsed ? value.length : visibleCount\n const hasExplicitLimit = !collapsed && showItemsLength !== undefined\n if (hasExplicitLimit) {\n maxVisible = Math.min(visibleCount, showItemsLength)\n }\n maxVisible = Math.max(1, maxVisible)\n\n const hasOverflow = maxVisible < value.length\n const displayed = value.slice(0, maxVisible)\n const overflowItems = value.slice(maxVisible)\n\n return (\n <div\n ref={wrapperRef}\n className={cn('relative min-w-0 flex-1', chipRowClass)}\n >\n {measureLayer}\n <div\n className={cn(\n 'min-w-0 flex-1',\n chipRowClass,\n collapsed ? 'flex-wrap' : 'overflow-hidden',\n )}\n >\n {displayed.map((opt, i) => {\n const isPartial =\n (hasOverflow || lastIsPartial) &&\n !hasExplicitLimit &&\n i === displayed.length - 1\n const shouldShrink = isPartial || hasExplicitLimit\n return (\n <Chip\n key={opt.value}\n option={opt}\n onRemove={onRemove ? () => onRemove(opt) : undefined}\n readOnly={readOnly}\n disabled={disabled}\n partial={isPartial}\n className={shouldShrink ? 'min-w-0 shrink' : 'shrink-0'}\n />\n )\n })}\n </div>\n {overflowItems.length > 0 && (\n <span className=\"shrink-0\">\n <OverflowBadge items={overflowItems} onRemove={onRemove} />\n </span>\n )}\n </div>\n )\n}\n\n// ---------------------------------------------------------------------------\n// Option item (rendered inside Command.Item + virtual list)\n// ---------------------------------------------------------------------------\n\ninterface OptionRowProps {\n option: IOption\n selected: boolean\n renderItem?: LayoutSelectProps['renderItem']\n onSelect: (option: IOption) => void\n highlighted?: boolean\n}\n\nfunction OptionRow({\n option,\n selected,\n renderItem,\n onSelect,\n highlighted = false,\n}: OptionRowProps) {\n const content = renderItem ? (\n renderItem(option, {\n selected,\n highlighted,\n disabled: !!option.disabled,\n })\n ) : (\n <div className=\"flex w-full items-center gap-2\">\n {option.icon && (\n <span className=\"flex shrink-0 items-center [&_svg]:size-4\">\n {resolveIcon(option.icon)}\n </span>\n )}\n <span className=\"flex-1 truncate\">{option.label}</span>\n {selected && (\n <span className=\"ml-auto flex shrink-0 items-center text-primary\">\n <Check className=\"size-4\" />\n </span>\n )}\n </div>\n )\n\n const row = (\n <Command.Item\n value={`${option.value}`}\n disabled={option.disabled}\n onSelect={() => {\n if (!option.disabled) onSelect(option)\n }}\n className={cn(\n 'relative flex w-full cursor-default items-center rounded-sm px-2 py-1.5 text-sm outline-hidden select-none',\n 'data-[selected=true]:bg-accent data-[selected=true]:text-accent-foreground',\n option.disabled && 'pointer-events-none opacity-50',\n )}\n data-highlighted={highlighted || undefined}\n >\n {content}\n </Command.Item>\n )\n\n if (option.disabled && option.disabledTooltip) {\n return (\n <MaybeTooltip tooltip={option.disabledTooltip}>\n <div>{row}</div>\n </MaybeTooltip>\n )\n }\n\n return row\n}\n\n// ---------------------------------------------------------------------------\n// Virtual + Sortable list\n// ---------------------------------------------------------------------------\n\ninterface VirtualListProps {\n /** Raw (potentially grouped) options – used to build display rows. */\n options: Array<IOption>\n /** Flat leaf items for backward-compat (used when there are no groups). */\n items: Array<IOption>\n selectedValue: IOption | Array<IOption> | null\n renderItem?: LayoutSelectProps['renderItem']\n onSelect: (option: IOption) => void\n sortable?: boolean\n sortableAcrossGroups?: boolean\n onSortEnd?: (items: Array<IOption>) => void\n /** Called when items within a group are reordered (grouped mode). Receives\n * the group parent value and the new ordered children. */\n onGroupSortEnd?: (\n groupValue: string | number,\n children: Array<IOption>,\n ) => void\n /** Called when a cross-group drag produces a new grouped tree. */\n onTreeSort?: (tree: Array<IOption>) => void\n}\n\nfunction VirtualList({\n options,\n items,\n selectedValue,\n renderItem,\n onSelect,\n sortable,\n sortableAcrossGroups,\n onSortEnd,\n onGroupSortEnd,\n onTreeSort,\n}: VirtualListProps) {\n const parentRef = useRef<HTMLDivElement>(null)\n const [activeId, setActiveId] = useState<string | null>(null)\n\n // During a cross-group drag, holds the intermediate tree with the dragged\n // item moved to the destination group. When null, `options` prop is used.\n const [dragTree, setDragTree] = useState<Array<IOption> | null>(null)\n\n const effectiveOptions = dragTree ?? options\n\n const grouped = useMemo(() => hasGroups(effectiveOptions), [effectiveOptions])\n const displayRows = useMemo(\n () => (grouped ? buildDisplayRows(effectiveOptions) : undefined),\n [grouped, effectiveOptions],\n )\n\n // The flat list used by the virtualizer – either from display rows or the\n // legacy flat items.\n const flatDisplayRows = useMemo(\n () => items.map<DisplayRow>((o) => ({ kind: 'option', option: o })),\n [items],\n )\n const virtualItems = displayRows ?? flatDisplayRows\n\n // Derive activeIndex from activeId so it stays correct after cross-group\n // moves update the tree mid-drag.\n const activeIndex = useMemo(() => {\n if (!activeId) return null\n const idx = virtualItems.findIndex(\n (r) => r.kind === 'option' && `${r.option.value}` === activeId,\n )\n return idx !== -1 ? idx : null\n }, [activeId, virtualItems])\n\n // Custom range extractor: always include the actively-dragged item so the\n // virtualizer never unmounts it (dnd-kit needs the DOM node to stay alive).\n const rangeExtractor = useCallback(\n (range: Range) => {\n const result = defaultRangeExtractor(range)\n if (activeIndex !== null && !result.includes(activeIndex)) {\n result.push(activeIndex)\n result.sort((a, b) => a - b)\n }\n return result\n },\n [activeIndex],\n )\n\n const virtualizer = useVirtualizer({\n count: virtualItems.length,\n getScrollElement: () => parentRef.current,\n estimateSize: (index) =>\n virtualItems[index].kind === 'group-header' ? 28 : 36,\n overscan: 8,\n rangeExtractor,\n })\n\n // After a cross-group drop the tree rebuilds and displayRows changes.\n // Force the virtualizer to discard stale position caches BEFORE paint\n // so every row gets the correct translateY immediately.\n useLayoutEffect(() => {\n virtualizer.measure()\n }, [virtualizer, displayRows])\n\n const sensors = useSensors(\n useSensor(PointerSensor, { activationConstraint: { distance: 4 } }),\n useSensor(KeyboardSensor),\n )\n\n // Flat sortable IDs for the single SortableContext wrapping all items.\n const flatSortableIds = useMemo(\n () =>\n virtualItems\n .filter(\n (r): r is DisplayRow & { kind: 'option' } => r.kind === 'option',\n )\n .map((r) => `${r.option.value}`),\n [virtualItems],\n )\n\n // Custom collision detection: restrict collisions to same-group items\n // when sortableAcrossGroups is disabled.\n const sameGroupCollision: CollisionDetection = useCallback(\n (args) => {\n if (!displayRows) return closestCenter(args)\n const draggedId = args.active.id\n const activeRow = displayRows.find(\n (r) => r.kind === 'option' && `${r.option.value}` === `${draggedId}`,\n )\n if (!activeRow || activeRow.kind !== 'option') return closestCenter(args)\n const activeGroup = activeRow.groupValue\n const filtered = args.droppableContainers.filter((container) => {\n const row = displayRows.find(\n (r) =>\n r.kind === 'option' && `${r.option.value}` === `${container.id}`,\n )\n return row && row.kind === 'option' && row.groupValue === activeGroup\n })\n return closestCenter({ ...args, droppableContainers: filtered })\n },\n [displayRows],\n )\n\n // Custom sorting strategy for grouped options.\n // Only displace items that are in the same group as the active (dragged)\n // item. Cross-group items are never displaced — group headers are at\n // fixed virtualizer positions and can't participate in dnd-kit transforms,\n // so shifting items across groups would cause overlaps.\n const sortingStrategy = useMemo(() => {\n if (!grouped || !displayRows) return verticalListSortingStrategy\n const idToGroup = new Map<string, string | number | undefined>()\n for (const row of displayRows) {\n if (row.kind === 'option') {\n idToGroup.set(`${row.option.value}`, row.groupValue)\n }\n }\n const noMove = { x: 0, y: 0, scaleX: 1, scaleY: 1 }\n return (\n args: Parameters<typeof verticalListSortingStrategy>[0],\n ): ReturnType<typeof verticalListSortingStrategy> => {\n const draggedId = flatSortableIds[args.activeIndex]\n const currentId = flatSortableIds[args.index]\n if (\n draggedId &&\n currentId &&\n idToGroup.get(draggedId) !== idToGroup.get(currentId)\n ) {\n return noMove\n }\n return verticalListSortingStrategy(args)\n }\n }, [grouped, displayRows, flatSortableIds])\n\n // ---- onDragOver: move item between groups during drag ----\n const handleDragOver = useCallback(\n (event: DragOverEvent) => {\n if (!sortableAcrossGroups || !grouped) return\n const { active, over } = event\n if (!over || active.id === over.id) return\n\n const currentTree = dragTree ?? options\n const currentRows = buildDisplayRows(currentTree)\n\n const activeRow = currentRows.find(\n (r) => r.kind === 'option' && `${r.option.value}` === `${active.id}`,\n )\n const overRow = currentRows.find(\n (r) => r.kind === 'option' && `${r.option.value}` === `${over.id}`,\n )\n\n if (\n !activeRow ||\n activeRow.kind !== 'option' ||\n !overRow ||\n overRow.kind !== 'option'\n )\n return\n\n // Same group — let dnd-kit's sorting strategy handle displacement\n if (activeRow.groupValue === overRow.groupValue) return\n\n // Cross-group: move item from source group to destination group\n const newTree = currentTree.map((opt) => {\n if (!opt.children) return opt\n if (opt.value === activeRow.groupValue) {\n return {\n ...opt,\n children: opt.children.filter(\n (c) => `${c.value}` !== `${active.id}`,\n ),\n }\n }\n if (opt.value === overRow.groupValue) {\n // Remove first in case of duplicate, then insert at over position\n const destChildren = opt.children.filter(\n (c) => `${c.value}` !== `${active.id}`,\n )\n const overIdx = destChildren.findIndex(\n (c) => `${c.value}` === `${over.id}`,\n )\n destChildren.splice(\n overIdx !== -1 ? overIdx : destChildren.length,\n 0,\n activeRow.option,\n )\n return { ...opt, children: destChildren }\n }\n return opt\n })\n\n setDragTree(newTree)\n },\n [sortableAcrossGroups, grouped, dragTree, options],\n )\n\n // ---- Drag end handlers ----\n const handleDragEndFlat = useCallback(\n (event: DragEndEvent) => {\n setActiveId(null)\n setDragTree(null)\n const { active, over } = event\n if (!over || active.id === over.id || !onSortEnd) return\n const oldIndex = items.findIndex((i) => `${i.value}` === `${active.id}`)\n const newIndex = items.findIndex((i) => `${i.value}` === `${over.id}`)\n if (oldIndex !== -1 && newIndex !== -1) {\n onSortEnd(arrayMove(items, oldIndex, newIndex))\n }\n },\n [items, onSortEnd],\n )\n\n const handleDragEndGrouped = useCallback(\n (event: DragEndEvent) => {\n setActiveId(null)\n const { active, over } = event\n\n if (!over || active.id === over.id) {\n // No move — revert intermediate drag tree\n if (dragTree) onTreeSort?.(dragTree)\n setDragTree(null)\n return\n }\n\n if (!displayRows) {\n setDragTree(null)\n return\n }\n\n const activeRow = displayRows.find(\n (r) => r.kind === 'option' && `${r.option.value}` === `${active.id}`,\n )\n const overRow = displayRows.find(\n (r) => r.kind === 'option' && `${r.option.value}` === `${over.id}`,\n )\n\n if (\n !activeRow ||\n activeRow.kind !== 'option' ||\n !overRow ||\n overRow.kind !== 'option'\n ) {\n setDragTree(null)\n return\n }\n\n const activeGroup = activeRow.groupValue\n const overGroup = overRow.groupValue\n const baseTree = dragTree ?? options\n\n if (activeGroup === overGroup) {\n // Same-group reorder (applies on top of any earlier cross-group move)\n const groupChildren = displayRows\n .filter(\n (r): r is DisplayRow & { kind: 'option' } =>\n r.kind === 'option' && r.groupValue === activeGroup,\n )\n .map((r) => r.option)\n\n const oldIdx = groupChildren.findIndex(\n (i) => `${i.value}` === `${active.id}`,\n )\n const newIdx = groupChildren.findIndex(\n (i) => `${i.value}` === `${over.id}`,\n )\n\n if (oldIdx !== -1 && newIdx !== -1 && oldIdx !== newIdx) {\n const reordered = arrayMove(groupChildren, oldIdx, newIdx)\n if (dragTree) {\n // Cross-group move happened earlier — commit full tree\n const finalTree = baseTree.map((opt) => {\n if (opt.value === activeGroup && opt.children) {\n return { ...opt, children: reordered }\n }\n return opt\n })\n onTreeSort?.(finalTree)\n } else {\n // Pure within-group reorder\n onGroupSortEnd?.(activeGroup!, reordered)\n }\n } else if (dragTree) {\n // Cross-group happened but no final reorder within group\n onTreeSort?.(baseTree)\n }\n } else {\n // Fallback: active and over still in different groups at drop time\n const finalTree = baseTree.map((opt) => {\n if (!opt.children) return opt\n if (opt.value === activeGroup) {\n return {\n ...opt,\n children: opt.children.filter(\n (c) => `${c.value}` !== `${active.id}`,\n ),\n }\n }\n if (opt.value === overGroup) {\n const destChildren = [...opt.children]\n const overIdx = destChildren.findIndex(\n (c) => `${c.value}` === `${over.id}`,\n )\n destChildren.splice(\n overIdx !== -1 ? overIdx + 1 : destChildren.length,\n 0,\n activeRow.option,\n )\n return { ...opt, children: destChildren }\n }\n return opt\n })\n onTreeSort?.(finalTree)\n }\n\n setDragTree(null)\n },\n [dragTree, options, displayRows, onTreeSort, onGroupSortEnd],\n )\n\n // ---- Render the scrollable virtualised content ----\n const listContent = (\n <div\n ref={parentRef}\n className={cn(\n 'max-h-75 overflow-x-hidden',\n activeIndex !== null ? 'overflow-y-visible' : 'overflow-y-auto',\n )}\n >\n <div\n style={{\n height: `${virtualizer.getTotalSize()}px`,\n position: 'relative',\n width: '100%',\n }}\n >\n {virtualizer.getVirtualItems().map((vItem) => {\n const displayRow = virtualItems[vItem.index]\n\n // ----- Group header row -----\n if (displayRow.kind === 'group-header') {\n return (\n <div\n key={`gh-${displayRow.groupValue}`}\n style={{\n position: 'absolute',\n top: 0,\n left: 0,\n width: '100%',\n transform: `translateY(${vItem.start}px)`,\n }}\n data-index={vItem.index}\n ref={virtualizer.measureElement}\n >\n <div className=\"px-2 py-1 text-xs font-semibold text-muted-foreground\">\n {displayRow.label}\n </div>\n </div>\n )\n }\n\n // ----- Leaf option row -----\n const option = displayRow.option\n\n const row = (\n <OptionRow\n key={option.value}\n option={option}\n selected={isSelected(option, selectedValue)}\n renderItem={renderItem}\n onSelect={onSelect}\n />\n )\n\n const wrappedRow = sortable ? (\n <SortableItem\n key={option.value}\n id={`${option.value}`}\n disabled={option.disabled}\n >\n {row}\n </SortableItem>\n ) : (\n row\n )\n\n return (\n <div\n key={option.value}\n style={{\n position: 'absolute',\n top: 0,\n left: 0,\n width: '100%',\n transform: `translateY(${vItem.start}px)`,\n }}\n data-index={vItem.index}\n ref={virtualizer.measureElement}\n >\n {wrappedRow}\n </div>\n )\n })}\n </div>\n </div>\n )\n\n if (sortable) {\n const handleDragStart = (event: { active: { id: string | number } }) => {\n setActiveId(`${event.active.id}`)\n }\n const handleCancel = () => {\n setActiveId(null)\n setDragTree(null)\n }\n\n // Single SortableContext wrapping everything.\n // Custom sortingStrategy handles per-group displacement.\n return (\n <DndContext\n modifiers={[restrictToVerticalAxis, restrictToFirstScrollableAncestor]}\n sensors={sensors}\n collisionDetection={\n grouped && !sortableAcrossGroups ? sameGroupCollision : closestCenter\n }\n onDragStart={handleDragStart}\n onDragOver={handleDragOver}\n onDragEnd={grouped ? handleDragEndGrouped : handleDragEndFlat}\n onDragCancel={handleCancel}\n >\n <SortableContext items={flatSortableIds} strategy={sortingStrategy}>\n {listContent}\n </SortableContext>\n </DndContext>\n )\n }\n\n return listContent\n}\n\n// ---------------------------------------------------------------------------\n// Main component\n// ---------------------------------------------------------------------------\n\nexport function LayoutSelect(props: LayoutSelectProps) {\n const {\n type,\n options,\n placeholder = 'Select item',\n disabled = false,\n readOnly = false,\n error = false,\n clearable = false,\n className,\n triggerClassName,\n popupClassName,\n renderTrigger,\n renderItem,\n listPrefix,\n listSuffix,\n queryFn,\n label,\n } = props\n\n const [open, setOpen] = useState(false)\n const [search, setSearch] = useState('')\n const [asyncOptions, setAsyncOptions] = useState<Array<IOption> | null>(null)\n const [loading, setLoading] = useState(false)\n const [internalSortedOptions, setInternalSortedOptions] =\n useState<Array<IOption> | null>(null)\n\n // ---- Resolve current value ----\n const currentValue = useMemo(() => {\n if (type === 'single') {\n return (props as SingleSelectProps).selectValue ?? null\n }\n return (props as MultipleSelectProps).selectValue ?? []\n }, [type, props])\n\n // ---- Resolve display options ----\n const resolvedOptions = useMemo(() => {\n const base = asyncOptions ?? options\n return internalSortedOptions ?? base\n }, [asyncOptions, options, internalSortedOptions])\n\n const flatOptions = useMemo(\n () => flattenOptions(resolvedOptions),\n [resolvedOptions],\n )\n\n // ---- Filtered options (search) ----\n // For virtualisation we need a flat list of leaf options, and when options\n // are grouped we also keep a filtered version of the grouped tree so that\n // VirtualList can render group headers correctly.\n const filteredOptions = useMemo(() => {\n if (!search) return flatOptions\n const q = search.toLowerCase()\n return flatOptions.filter((o) => o.label.toLowerCase().includes(q))\n }, [flatOptions, search])\n\n /** Resolved options filtered by search, preserving group structure. */\n const filteredGroupedOptions = useMemo(() => {\n if (!search) return resolvedOptions\n const q = search.toLowerCase()\n return resolvedOptions\n .map((opt) => {\n if (opt.children && opt.children.length > 0) {\n const matched = opt.children.filter((c) =>\n c.label.toLowerCase().includes(q),\n )\n if (matched.length === 0) return null\n return { ...opt, children: matched }\n }\n return opt.label.toLowerCase().includes(q) ? opt : null\n })\n .filter(Boolean) as Array<IOption>\n }, [resolvedOptions, search])\n\n // ---- Async loading ----\n useEffect(() => {\n if (open && queryFn) {\n let cancelled = false\n setLoading(true)\n queryFn()\n .then((data) => {\n if (!cancelled) {\n setAsyncOptions(data)\n }\n })\n .finally(() => {\n if (!cancelled) setLoading(false)\n })\n return () => {\n cancelled = true\n }\n }\n }, [open, queryFn])\n\n // Reset search when closed\n useEffect(() => {\n if (!open) {\n setSearch('')\n }\n }, [open])\n\n // ---- Selection handler ----\n const handleSelect = useCallback(\n (option: IOption) => {\n if (readOnly) return\n\n if (type === 'single') {\n const onChange = (props as SingleSelectProps).onChange\n const current = currentValue as IOption | null\n if (clearable && current && optionEq(current, option)) {\n onChange?.(null, option)\n } else {\n onChange?.(option, option)\n }\n setOpen(false)\n } else {\n const onChange = (props as MultipleSelectProps).onChange\n const current = currentValue as Array<IOption>\n const exists = current.some((v) => optionEq(v, option))\n if (exists) {\n onChange?.(\n current.filter((v) => !optionEq(v, option)),\n option,\n )\n } else {\n onChange?.([...current, option], option)\n }\n }\n },\n [type, currentValue, clearable, readOnly, props],\n )\n\n // ---- Remove chip (multiple) ----\n const handleRemoveChip = useCallback(\n (option: IOption) => {\n if (type !== 'multiple' || readOnly || disabled) return\n const onChange = (props as MultipleSelectProps).onChange\n const current = currentValue as Array<IOption>\n onChange?.(\n current.filter((v) => !optionEq(v, option)),\n option,\n )\n },\n [type, currentValue, readOnly, disabled, props],\n )\n\n // ---- Select all / unselect all (multiple only) ----\n const handleToggleAll = useCallback(() => {\n if (type !== 'multiple' || readOnly || disabled) return\n const onChange = (props as MultipleSelectProps).onChange\n const current = currentValue as Array<IOption>\n const selectableOptions = flatOptions.filter((o) => !o.disabled)\n const allSelected =\n selectableOptions.length > 0 &&\n selectableOptions.every((o) => current.some((v) => optionEq(v, o)))\n\n if (allSelected) {\n // Keep only disabled options that were somehow selected\n const kept = current.filter((v) =>\n selectableOptions.every((o) => !optionEq(o, v)),\n )\n onChange?.(kept, selectableOptions[0])\n } else {\n // Merge: keep existing + add missing selectable options\n const missing = selectableOptions.filter(\n (o) => !current.some((v) => optionEq(v, o)),\n )\n onChange?.([...current, ...missing], selectableOptions[0])\n }\n }, [type, currentValue, flatOptions, readOnly, disabled, props])\n\n const allSelected = useMemo(() => {\n if (type !== 'multiple') return false\n const current = currentValue as Array<IOption>\n const selectableOptions = flatOptions.filter((o) => !o.disabled)\n return (\n selectableOptions.length > 0 &&\n selectableOptions.every((o) => current.some((v) => optionEq(v, o)))\n )\n }, [type, currentValue, flatOptions])\n\n const sortable = props.sortable ?? false\n const sortableAcrossGroups = props.sortable\n ? ((props as SortableEnabledProps).sortableAcrossGroups ?? false)\n : false\n const consumerOnSortEnd = props.sortable\n ? (props as SortableEnabledProps).onSortEnd\n : undefined\n\n // ---- Sort handler (flat) ----\n const handleSortEnd = useCallback(\n (sorted: Array<IOption>) => {\n setInternalSortedOptions(sorted)\n consumerOnSortEnd?.(sorted)\n },\n [consumerOnSortEnd],\n )\n\n // ---- Sort handler (within a group) ----\n const handleGroupSortEnd = useCallback(\n (groupValue: string | number, reorderedChildren: Array<IOption>) => {\n // Rebuild the full options tree with the updated group\n const updated = resolvedOptions.map((opt) => {\n if (opt.value === groupValue && opt.children) {\n return { ...opt, children: reorderedChildren }\n }\n return opt\n })\n setInternalSortedOptions(updated)\n consumerOnSortEnd?.(flattenOptions(updated))\n },\n [resolvedOptions, consumerOnSortEnd],\n )\n\n // ---- Sort handler (cross-group tree rebuild) ----\n const handleTreeSort = useCallback(\n (newTree: Array<IOption>) => {\n setInternalSortedOptions(newTree)\n consumerOnSortEnd?.(flattenOptions(newTree))\n },\n [consumerOnSortEnd],\n )\n\n // ---- Open handler ----\n const handleOpenChange = useCallback(\n (nextOpen: boolean) => {\n if (disabled || readOnly) return\n setOpen(nextOpen)\n },\n [disabled, readOnly],\n )\n\n // ---- Trigger content ----\n const triggerContent = useMemo(() => {\n if (renderTrigger) {\n return renderTrigger({\n value: currentValue,\n open,\n disabled,\n readOnly,\n error,\n placeholder,\n })\n }\n\n if (type === 'single') {\n return (\n <SingleTriggerContent\n value={currentValue as IOption | null}\n placeholder={placeholder}\n />\n )\n }\n\n return (\n <MultipleTriggerContent\n value={currentValue as Array<IOption>}\n placeholder={placeholder}\n collapsed={(props as MultipleSelectProps).collapsed}\n showItemsLength={(props as MultipleSelectProps).showItemsLength}\n onRemove={handleRemoveChip}\n readOnly={readOnly}\n disabled={disabled}\n />\n )\n }, [\n renderTrigger,\n type,\n currentValue,\n open,\n disabled,\n readOnly,\n error,\n placeholder,\n props,\n handleRemoveChip,\n ])\n\n return (\n <Tooltip.Provider>\n <div className={cn('relative inline-flex flex-col', className)}>\n {label && (\n <label className=\"mb-1 text-sm font-medium text-foreground\">\n {label}\n </label>\n )}\n <Popover.Root open={open} onOpenChange={handleOpenChange}>\n <Popover.Trigger\n disabled={disabled}\n render={\n <button\n type=\"button\"\n aria-expanded={open}\n aria-haspopup=\"listbox\"\n aria-invalid={error || undefined}\n data-readonly={readOnly || undefined}\n className={cn(\n 'border-input flex min-h-9 w-full min-w-45 items-center gap-2 rounded-md border bg-transparent px-3 py-1.5 text-sm shadow-xs transition-[color,box-shadow,border-color] outline-none',\n 'focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]',\n 'aria-invalid:border-destructive aria-invalid:ring-destructive/20',\n 'data-readonly:pointer-events-none data-readonly:opacity-70',\n 'disabled:cursor-not-allowed disabled:opacity-50',\n \"[&_svg:not([class*='text-'])]:text-muted-foreground\",\n triggerClassName,\n )}\n />\n }\n >\n <div className=\"flex min-w-0 flex-1 items-center\">\n {triggerContent}\n </div>\n <ChevronDown className=\"size-4 shrink-0 opacity-50\" />\n </Popover.Trigger>\n\n <Popover.Portal>\n <Popover.Positioner sideOffset={4}>\n <Popover.Popup\n className={cn(\n 'z-50 min-w-(--anchor-width) rounded-md border bg-popover text-popover-foreground shadow-md outline-none',\n 'data-starting-style:scale-95 data-starting-style:opacity-0',\n 'data-ending-style:scale-95 data-ending-style:opacity-0',\n 'origin-(--transform-origin) transition-[transform,scale,opacity] duration-150',\n popupClassName,\n )}\n >\n <Command shouldFilter={false} loop>\n {/* Search input */}\n <div className=\"border-b p-1\">\n <Command.Input\n value={search}\n onValueChange={setSearch}\n placeholder=\"Search...\"\n className=\"h-8 w-full rounded-sm bg-transparent px-2 text-sm outline-none placeholder:text-muted-foreground\"\n />\n </div>\n\n {/* List prefix */}\n {listPrefix && (\n <div className=\"border-b px-2 py-1.5\">{listPrefix}</div>\n )}\n\n {/* Select all / Unselect all */}\n {type === 'multiple' && !readOnly && (\n <div className=\"border-b px-1 py-1\">\n <button\n type=\"button\"\n onClick={handleToggleAll}\n className=\"w-full rounded-sm px-2 py-1 text-left text-sm text-muted-foreground hover:bg-accent hover:text-accent-foreground\"\n >\n {allSelected ? 'Unselect all' : 'Select all'}\n </button>\n </div>\n )}\n\n <Command.List className=\"p-1\">\n {loading && (\n <Command.Loading>\n <div className=\"flex items-center justify-center py-4 text-sm text-muted-foreground\">\n Loading…\n </div>\n </Command.Loading>\n )}\n\n {!loading && filteredOptions.length === 0 && (\n <Command.Empty className=\"py-4 text-center text-sm text-muted-foreground\">\n No results found.\n </Command.Empty>\n )}\n\n {!loading && filteredOptions.length > 0 && (\n <VirtualList\n options={filteredGroupedOptions}\n items={filteredOptions}\n selectedValue={currentValue}\n renderItem={renderItem}\n onSelect={handleSelect}\n sortable={sortable}\n sortableAcrossGroups={sortableAcrossGroups}\n onSortEnd={handleSortEnd}\n onGroupSortEnd={handleGroupSortEnd}\n onTreeSort={handleTreeSort}\n />\n )}\n </Command.List>\n\n {/* List suffix */}\n {listSuffix && (\n <div className=\"border-t px-2 py-1.5\">{listSuffix}</div>\n )}\n </Command>\n </Popover.Popup>\n </Popover.Positioner>\n </Popover.Portal>\n </Popover.Root>\n </div>\n </Tooltip.Provider>\n )\n}\n\nexport default LayoutSelect\n","import { clsx, type ClassValue } from 'clsx'\nimport { twMerge } from 'tailwind-merge'\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs))\n}\n","import {\n forwardRef,\n useCallback,\n useEffect,\n useImperativeHandle,\n useMemo,\n useRef,\n useState,\n} from 'react'\nimport {\n $createParagraphNode,\n $createRangeSelection,\n $createTextNode,\n $getNodeByKey,\n $getRoot,\n $setSelection,\n COMMAND_PRIORITY_CRITICAL,\n KEY_DOWN_COMMAND,\n} from 'lexical'\nimport { LexicalComposer } from '@lexical/react/LexicalComposer'\nimport { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext'\nimport { ContentEditable } from '@lexical/react/LexicalContentEditable'\nimport { LexicalErrorBoundary } from '@lexical/react/LexicalErrorBoundary'\nimport { HistoryPlugin } from '@lexical/react/LexicalHistoryPlugin'\nimport { OnChangePlugin } from '@lexical/react/LexicalOnChangePlugin'\nimport { PlainTextPlugin } from '@lexical/react/LexicalPlainTextPlugin'\n\nimport { NL_MARKER_PREFIX } from './constants'\nimport { $isHighlightNode, HighlightNode } from './highlight-node'\nimport { MentionNode, setMentionNodeConfig } from './mention-node'\nimport { MentionPlugin } from './mention-plugin'\nimport {\n EditorRefPlugin,\n HighlightsPlugin,\n NLMarkerNavigationPlugin,\n} from './plugins'\nimport { HighlightPopover } from './popover'\nimport { $globalOffsetToPoint, $pointToGlobalOffset } from './selection-helpers'\n\nimport type { LexicalEditor } from 'lexical'\n\nimport type {\n CATEditorRef,\n IMentionRule,\n IMentionUser,\n MooRule,\n PopoverContentRenderer,\n PopoverState,\n RuleAnnotation,\n} from './types'\nimport type { MentionDOMRenderer } from './mention-node'\nimport { cn } from '@/lib/utils'\n\n// ─── Main CATEditor Component ────────────────────────────────────────────────\n\nexport interface CATEditorProps {\n /** Initial text content for the editor */\n initialText?: string\n /** Rules to apply for highlighting */\n rules?: Array<MooRule>\n /** Called when editor content changes */\n onChange?: (text: string) => void\n /** Called when a suggestion is applied.\n * Provides the ruleId, the replacement text, the original text\n * span (start / end / content), and the ruleType so consumers\n * can identify which rule triggered the replacement. */\n onSuggestionApply?: (\n ruleId: string,\n suggestion: string,\n range: { start: number; end: number; content: string },\n ruleType: RuleAnnotation['type'],\n ) => void\n /** Custom code-point → display symbol map. Merged on top of the\n * built-in `CODEPOINT_DISPLAY_MAP`. Pass `{ 0x00a0: '⍽' }` to\n * override the symbol shown for non-breaking spaces, etc. */\n codepointDisplayMap?: Record<number, string>\n /** Custom renderer for popover content per annotation.\n * Return `null`/`undefined` to use the built-in default. */\n renderPopoverContent?: PopoverContentRenderer\n /** Called when a link highlight is clicked. If not provided, links\n * open in a new browser tab via `window.open`. */\n onLinkClick?: (url: string) => void\n /** Whether clicking a link highlight should open the URL.\n * When `false`, link clicks are ignored (the click positions the\n * cursor in the editor text instead). Default: `true`. */\n openLinksOnClick?: boolean\n /** Called when a mention node is clicked. */\n onMentionClick?: (userId: string, userName: string) => void\n /** Called when a mention is inserted via the typeahead. */\n onMentionInsert?: (user: IMentionUser) => void\n /** Converts a mention ID to model text.\n * Default: `` id => `@{${id}}` `` producing `@{5}`, `@{user_abc}`, etc. */\n mentionSerialize?: (id: string) => string\n /** RegExp to detect mention patterns in pasted / imported text.\n * Must have one capture group for the ID and use the `g` flag.\n * Default: `/@\\{([^}]+)\\}/g` */\n mentionPattern?: RegExp\n /** Custom DOM renderer for mention nodes.\n * Receives the `<span>` host element, the mentionId and the\n * display name. Return `true` to take over rendering;\n * return `false`/`undefined` to use the default `@name` label. */\n renderMentionDOM?: MentionDOMRenderer\n /** Placeholder text */\n placeholder?: string\n /** Additional class name for the editor container */\n className?: string\n /** Text direction. `'ltr'` (default), `'rtl'`, or `'auto'`. */\n dir?: 'ltr' | 'rtl' | 'auto'\n /** Text direction for the highlight popover. Defaults to `'ltr'` so\n * that popover content stays left-to-right even when the editor is\n * RTL. Set to `'rtl'` or `'auto'` if your popover content should\n * follow the editor direction, or pass `'inherit'` to use the same\n * direction as the editor (`dir` prop). */\n popoverDir?: 'ltr' | 'rtl' | 'auto' | 'inherit'\n /** When `true`, applies a Japanese-optimised font stack to the editor. */\n jpFont?: boolean\n /** Whether the editor content is editable. Default: `true`.\n * When `false`, all text mutations are blocked.\n * @see readOnlySelectable */\n editable?: boolean\n /** When `editable` is `false` and this is `true`, the editor still\n * allows caret placement, range selection and copy — but rejects\n * any content changes. Default: `false`. */\n readOnlySelectable?: boolean\n /** Custom keydown handler. Called before Lexical processes the event.\n * Return `true` to prevent Lexical (and the browser) from handling\n * the key. Useful for intercepting Enter, Tab, Escape, etc.\n * @example\n * ```tsx\n * onKeyDown={(e) => {\n * if (e.key === 'Enter' && !e.shiftKey) {\n * e.preventDefault()\n * handleSubmit()\n * return true\n * }\n * return false\n * }}\n * ``` */\n onKeyDown?: (event: KeyboardEvent) => boolean\n /** @deprecated Use `editable` instead. */\n readOnly?: boolean\n}\n\nexport const CATEditor = forwardRef<CATEditorRef, CATEditorProps>(\n function CATEditor(\n {\n initialText = '',\n rules = [],\n onChange,\n onSuggestionApply,\n codepointDisplayMap,\n renderPopoverContent,\n onLinkClick,\n openLinksOnClick = true,\n onMentionClick,\n onMentionInsert,\n mentionSerialize,\n mentionPattern,\n renderMentionDOM,\n placeholder = 'Start typing or paste text here…',\n className,\n dir,\n popoverDir: popoverDirProp = 'ltr',\n jpFont = false,\n editable: editableProp,\n readOnlySelectable = false,\n onKeyDown: onKeyDownProp,\n readOnly: readOnlyLegacy = false,\n },\n ref,\n ) {\n // Resolve effective editable: new `editable` prop takes precedence,\n // falling back to the legacy `readOnly` prop (inverted).\n const isEditable =\n editableProp !== undefined ? editableProp : !readOnlyLegacy\n const annotationMapRef = useRef(new Map<string, RuleAnnotation>())\n const editorRef = useRef<LexicalEditor | null>(null)\n const savedSelectionRef = useRef<{\n anchor: number\n focus: number\n } | null>(null)\n const containerRef = useRef<HTMLDivElement>(null)\n const dismissTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null)\n\n // ── Sync MentionNode global config whenever props change ──────────────\n useEffect(() => {\n setMentionNodeConfig({\n renderDOM: renderMentionDOM,\n serialize: mentionSerialize,\n pattern: mentionPattern,\n })\n }, [renderMentionDOM, mentionSerialize, mentionPattern])\n\n // ── Flash highlight state ──────────────────────────────────────────────\n const flashIdRef = useRef<string | null>(null)\n const flashTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null)\n const flashEditUnregRef = useRef<(() => void) | null>(null)\n\n /** Apply the `cat-highlight-flash` class to all DOM elements whose\n * `data-rule-ids` contain the given annotation ID. */\n const applyFlashClass = useCallback((annotationId: string) => {\n const container = containerRef.current\n if (!container) return\n // Remove previous flash classes\n container\n .querySelectorAll('.cat-highlight-flash')\n .forEach((el) => el.classList.remove('cat-highlight-flash'))\n // Add to matching elements\n container.querySelectorAll('.cat-highlight').forEach((el) => {\n const ids = el.getAttribute('data-rule-ids')\n if (ids && ids.split(',').includes(annotationId)) {\n el.classList.add('cat-highlight-flash')\n }\n })\n }, [])\n\n /** Remove all flash highlight classes and clean up timers/listeners. */\n const clearFlashInner = useCallback(() => {\n flashIdRef.current = null\n if (flashTimerRef.current) {\n clearTimeout(flashTimerRef.current)\n flashTimerRef.current = null\n }\n if (flashEditUnregRef.current) {\n flashEditUnregRef.current()\n flashEditUnregRef.current = null\n }\n containerRef.current\n ?.querySelectorAll('.cat-highlight-flash')\n .forEach((el) => el.classList.remove('cat-highlight-flash'))\n }, [])\n\n const [popoverState, setPopoverState] = useState<PopoverState>({\n visible: false,\n x: 0,\n y: 0,\n ruleIds: [],\n })\n\n // ── Imperative API ─────────────────────────────────────────────────────\n useImperativeHandle(\n ref,\n () => ({\n insertText: (text: string) => {\n const editor = editorRef.current\n if (!editor) return\n\n editor.update(() => {\n const saved = savedSelectionRef.current\n if (saved) {\n // Restore selection at the saved global offset\n const anchorPt = $globalOffsetToPoint(saved.anchor)\n const focusPt = $globalOffsetToPoint(saved.focus)\n if (anchorPt && focusPt) {\n // Token-mode nodes (tags, special-chars) are atomic — calling\n // sel.insertText on them replaces the entire node. Instead,\n // insert a new text node before or after the token.\n const anchorNode = $getNodeByKey(anchorPt.key)\n if (\n anchorNode &&\n $isHighlightNode(anchorNode) &&\n anchorNode.getMode() === 'token' &&\n saved.anchor === saved.focus\n ) {\n const newText = $createTextNode(text)\n if (\n anchorPt.offset === 0 ||\n anchorPt.offset < anchorNode.getTextContentSize()\n ) {\n anchorNode.insertBefore(newText)\n } else {\n anchorNode.insertAfter(newText)\n }\n newText.selectEnd()\n return\n }\n\n const sel = $createRangeSelection()\n sel.anchor.set(anchorPt.key, anchorPt.offset, anchorPt.type)\n sel.focus.set(focusPt.key, focusPt.offset, focusPt.type)\n $setSelection(sel)\n sel.insertText(text)\n return\n }\n }\n // Fallback: append at the end of the last paragraph\n const root = $getRoot()\n const lastChild = root.getLastChild()\n if (lastChild) {\n lastChild.selectEnd()\n }\n const sel = $createRangeSelection()\n $setSelection(sel)\n sel.insertText(text)\n })\n\n // Re-focus the editor\n editor.focus()\n },\n focus: () => {\n editorRef.current?.focus()\n },\n getText: () => {\n let text = ''\n editorRef.current?.getEditorState().read(() => {\n text = $getRoot().getTextContent()\n })\n return text\n },\n flashHighlight: (annotationId: string, durationMs = 5000) => {\n // Clear any existing flash first\n clearFlashInner()\n\n flashIdRef.current = annotationId\n // Apply class to current DOM\n applyFlashClass(annotationId)\n\n // Auto-remove after timeout\n flashTimerRef.current = setTimeout(() => {\n clearFlashInner()\n }, durationMs)\n\n // Remove on first user edit (not our own highlight rebuilds).\n // The `applyHighlights` plugin tags its updates with 'cat-highlights'.\n const editor = editorRef.current\n if (editor) {\n flashEditUnregRef.current = editor.registerUpdateListener(\n ({ tags }) => {\n if (tags.has('cat-highlights')) {\n // Highlight rebuild — re-apply flash class to new DOM elements\n if (flashIdRef.current) {\n requestAnimationFrame(() =>\n applyFlashClass(flashIdRef.current!),\n )\n }\n return\n }\n // User edit — clear flash\n clearFlashInner()\n },\n )\n }\n },\n replaceAll: (search: string, replacement: string): number => {\n const editor = editorRef.current\n if (!editor || !search) return 0\n\n let count = 0\n editor.update(() => {\n const root = $getRoot()\n const fullText = root.getTextContent()\n\n // Count occurrences\n let idx = 0\n while ((idx = fullText.indexOf(search, idx)) !== -1) {\n count++\n idx += search.length\n }\n\n if (count === 0) return\n\n // Rebuild the content with all replacements applied\n const newText = fullText.split(search).join(replacement)\n root.clear()\n const lines = newText.split('\\n')\n for (const line of lines) {\n const p = $createParagraphNode()\n p.append($createTextNode(line))\n root.append(p)\n }\n })\n\n return count\n },\n clearFlash: () => {\n clearFlashInner()\n },\n }),\n [applyFlashClass, clearFlashInner],\n )\n\n const initialConfig = useMemo(\n () => ({\n namespace: 'CATEditor',\n theme: {\n root: 'cat-editor-root',\n paragraph: 'cat-editor-paragraph',\n text: {\n base: 'cat-editor-text',\n },\n },\n nodes: [HighlightNode, MentionNode],\n // When readOnlySelectable, Lexical must be editable so the caret\n // and selection work — we block mutations via KEY_DOWN_COMMAND.\n editable: isEditable || readOnlySelectable,\n onError: (error: Error) => {\n console.error('CATEditor Lexical error:', error)\n },\n editorState: () => {\n const root = $getRoot()\n const lines = initialText.split('\\n')\n for (const line of lines) {\n const p = $createParagraphNode()\n p.append($createTextNode(line))\n root.append(p)\n }\n },\n }),\n [], // intentional: initialConfig should not change\n )\n\n // ── Popover hover logic ────────────────────────────────────────────────\n const isOverHighlightRef = useRef(false)\n const isOverPopoverRef = useRef(false)\n\n const scheduleHide = useCallback(() => {\n if (dismissTimerRef.current) clearTimeout(dismissTimerRef.current)\n dismissTimerRef.current = setTimeout(() => {\n if (!isOverHighlightRef.current && !isOverPopoverRef.current) {\n setPopoverState((prev) => ({ ...prev, visible: false }))\n }\n }, 400)\n }, [])\n\n const cancelHide = useCallback(() => {\n if (dismissTimerRef.current) {\n clearTimeout(dismissTimerRef.current)\n dismissTimerRef.current = null\n }\n }, [])\n\n // Handle hover over highlighted DOM elements\n useEffect(() => {\n const container = containerRef.current\n if (!container) return\n\n const handleMouseOver = (e: MouseEvent) => {\n const target = (e.target as HTMLElement).closest('.cat-highlight')\n if (!target) {\n if (isOverHighlightRef.current) {\n isOverHighlightRef.current = false\n scheduleHide()\n }\n return\n }\n\n const ruleIdsAttr = target.getAttribute('data-rule-ids')\n if (!ruleIdsAttr) return\n\n const ruleIds = [\n ...new Set(\n ruleIdsAttr\n .split(',')\n .map((id) =>\n id.startsWith(NL_MARKER_PREFIX)\n ? id.slice(NL_MARKER_PREFIX.length)\n : id,\n ),\n ),\n ]\n\n isOverHighlightRef.current = true\n cancelHide()\n\n const rect = target.getBoundingClientRect()\n setPopoverState({\n visible: true,\n x: rect.left,\n y: rect.bottom,\n anchorRect: {\n top: rect.top,\n left: rect.left,\n bottom: rect.bottom,\n right: rect.right,\n width: rect.width,\n height: rect.height,\n },\n ruleIds,\n })\n }\n\n const handleMouseOut = (e: MouseEvent) => {\n const related = e.relatedTarget as HTMLElement | null\n if (related?.closest('.cat-highlight')) return\n isOverHighlightRef.current = false\n scheduleHide()\n }\n\n container.addEventListener('mouseover', handleMouseOver)\n container.addEventListener('mouseout', handleMouseOut)\n\n return () => {\n container.removeEventListener('mouseover', handleMouseOver)\n container.removeEventListener('mouseout', handleMouseOut)\n if (dismissTimerRef.current) clearTimeout(dismissTimerRef.current)\n }\n }, [scheduleHide, cancelHide])\n\n // Handle click on link / mention highlights\n useEffect(() => {\n const container = containerRef.current\n if (!container) return\n\n const handleClick = (e: MouseEvent) => {\n // Check for link highlights\n if (openLinksOnClick) {\n const highlightTarget = (e.target as HTMLElement).closest(\n '.cat-highlight',\n )\n if (highlightTarget) {\n const ruleIdsAttr = highlightTarget.getAttribute('data-rule-ids')\n if (ruleIdsAttr) {\n const ids = ruleIdsAttr.split(',')\n for (const id of ids) {\n const ann = annotationMapRef.current.get(id)\n if (!ann) continue\n\n if (ann.type === 'link') {\n e.preventDefault()\n if (onLinkClick) {\n onLinkClick(ann.data.url)\n } else {\n window.open(ann.data.url, '_blank', 'noopener,noreferrer')\n }\n return\n }\n }\n }\n }\n }\n\n // Check for mention nodes\n const mentionTarget = (e.target as HTMLElement).closest(\n '.cat-mention-node',\n )\n if (mentionTarget) {\n const mentionId = mentionTarget.getAttribute('data-mention-id')\n const mentionName = mentionTarget.getAttribute('data-mention-name')\n if (mentionId && mentionName) {\n e.preventDefault()\n onMentionClick?.(mentionId, mentionName)\n return\n }\n }\n }\n\n container.addEventListener('click', handleClick)\n return () => {\n container.removeEventListener('click', handleClick)\n }\n }, [onLinkClick, openLinksOnClick, onMentionClick])\n\n // Handle suggestion click -> replace the highlighted text\n const handleSuggestionClick = useCallback(\n (suggestion: string, ruleId: string) => {\n const editor = editorRef.current\n if (!editor) return\n\n let replacedRange:\n | { start: number; end: number; content: string }\n | undefined\n\n editor.update(() => {\n const root = $getRoot()\n const allNodes = root.getAllTextNodes()\n for (const node of allNodes) {\n if (\n $isHighlightNode(node) &&\n node.__ruleIds.split(',').includes(ruleId)\n ) {\n const globalOffset = $pointToGlobalOffset(node.getKey(), 0)\n const originalContent = node.getTextContent()\n replacedRange = {\n start: globalOffset,\n end: globalOffset + originalContent.length,\n content: originalContent,\n }\n const textNode = $createTextNode(suggestion)\n node.replace(textNode)\n break\n }\n }\n })\n\n setPopoverState((prev) => ({ ...prev, visible: false }))\n if (replacedRange !== undefined) {\n const annotation = annotationMapRef.current.get(ruleId)\n if (annotation) {\n onSuggestionApply?.(\n ruleId,\n suggestion,\n replacedRange,\n annotation.type,\n )\n }\n }\n },\n [onSuggestionApply],\n )\n\n const handleChange = useCallback(\n (editorState: { read: (fn: () => void) => void }) => {\n if (!onChange) return\n editorState.read(() => {\n const root = $getRoot()\n onChange(root.getTextContent())\n })\n },\n [onChange],\n )\n\n return (\n <div\n ref={containerRef}\n className={cn(\n 'cat-editor-container',\n jpFont && 'cat-editor-jp-font',\n className,\n )}\n dir={dir}\n >\n <LexicalComposer initialConfig={initialConfig}>\n <div className=\"cat-editor-inner\">\n <PlainTextPlugin\n contentEditable={\n <ContentEditable\n className={cn(\n 'cat-editor-editable',\n !isEditable && !readOnlySelectable && 'cat-editor-readonly',\n !isEditable &&\n readOnlySelectable &&\n 'cat-editor-readonly-selectable',\n )}\n />\n }\n placeholder={\n <div className=\"cat-editor-placeholder\">{placeholder}</div>\n }\n ErrorBoundary={LexicalErrorBoundary}\n />\n <HistoryPlugin />\n <OnChangePlugin onChange={handleChange} />\n <HighlightsPlugin\n rules={rules}\n annotationMapRef={annotationMapRef}\n codepointDisplayMap={codepointDisplayMap}\n />\n <EditorRefPlugin\n editorRef={editorRef}\n savedSelectionRef={savedSelectionRef}\n />\n <NLMarkerNavigationPlugin />\n {/* Block mutations when read-only but selectable */}\n {!isEditable && readOnlySelectable && <ReadOnlySelectablePlugin />}\n {/* Custom key-down handler */}\n {onKeyDownProp && <KeyDownPlugin onKeyDown={onKeyDownProp} />}\n {/* Strip Lexical's per-paragraph dir when an explicit dir is set */}\n {dir && dir !== 'auto' && <DirectionPlugin dir={dir} />}\n {/* Mention typeahead plugin — enabled when a mention rule is present */}\n {rules\n .filter((r): r is IMentionRule => r.type === 'mention')\n .map((mentionRule, i) => (\n <MentionPlugin\n key={`mention-${i}`}\n users={mentionRule.users}\n trigger={mentionRule.trigger}\n onMentionInsert={onMentionInsert}\n />\n ))}\n </div>\n </LexicalComposer>\n\n <HighlightPopover\n state={popoverState}\n annotationMap={annotationMapRef.current}\n onSuggestionClick={handleSuggestionClick}\n onLinkOpen={onLinkClick}\n onDismiss={() => {\n isOverPopoverRef.current = false\n scheduleHide()\n }}\n onPopoverEnter={() => {\n isOverPopoverRef.current = true\n cancelHide()\n }}\n renderPopoverContent={renderPopoverContent}\n dir={popoverDirProp === 'inherit' ? dir : popoverDirProp}\n />\n </div>\n )\n },\n)\n\nexport default CATEditor\n\n// ─── Internal Plugins ─────────────────────────────────────────────────────────\n\n/** Blocks all content-mutating key presses while keeping caret & selection\n * functional. Registered at CRITICAL priority so it fires before Lexical's\n * own handlers. */\nfunction ReadOnlySelectablePlugin() {\n const [editor] = useLexicalComposerContext()\n\n useEffect(() => {\n return editor.registerCommand(\n KEY_DOWN_COMMAND,\n (event: KeyboardEvent) => {\n // Allow navigation, selection, copy/paste-select, etc.\n if (\n event.key.startsWith('Arrow') ||\n event.key === 'Home' ||\n event.key === 'End' ||\n event.key === 'PageUp' ||\n event.key === 'PageDown' ||\n event.key === 'Shift' ||\n event.key === 'Control' ||\n event.key === 'Alt' ||\n event.key === 'Meta' ||\n event.key === 'Tab' ||\n event.key === 'Escape' ||\n event.key === 'F5' ||\n event.key === 'F12' ||\n // Ctrl/Cmd shortcuts that don't mutate: copy, select-all, find\n ((event.ctrlKey || event.metaKey) &&\n (event.key === 'c' ||\n event.key === 'a' ||\n event.key === 'f' ||\n event.key === 'g'))\n ) {\n return false // let it through\n }\n // Block everything else (typing, Enter, Backspace, Delete, paste, cut…)\n event.preventDefault()\n return true\n },\n COMMAND_PRIORITY_CRITICAL,\n )\n }, [editor])\n\n // Also block paste and cut at the DOM level\n useEffect(() => {\n const root = editor.getRootElement()\n if (!root) return\n const block = (e: Event) => e.preventDefault()\n root.addEventListener('paste', block)\n root.addEventListener('cut', block)\n root.addEventListener('drop', block)\n return () => {\n root.removeEventListener('paste', block)\n root.removeEventListener('cut', block)\n root.removeEventListener('drop', block)\n }\n }, [editor])\n\n return null\n}\n\n/** Fires the consumer's `onKeyDown` before Lexical processes the event.\n * If the callback returns `true`, the event is consumed (preventDefault +\n * stop Lexical propagation). */\nfunction KeyDownPlugin({\n onKeyDown,\n}: {\n onKeyDown: (event: KeyboardEvent) => boolean\n}) {\n const [editor] = useLexicalComposerContext()\n\n useEffect(() => {\n return editor.registerCommand(\n KEY_DOWN_COMMAND,\n (event: KeyboardEvent) => {\n return onKeyDown(event)\n },\n COMMAND_PRIORITY_CRITICAL,\n )\n }, [editor, onKeyDown])\n\n return null\n}\n\n/** Sets the Lexical root node's direction so that the reconciler stops\n * adding `dir=\"auto\"` to paragraph elements. When the root has a\n * direction, children inherit it naturally (no MutationObserver needed). */\nfunction DirectionPlugin({ dir }: { dir: 'ltr' | 'rtl' }) {\n const [editor] = useLexicalComposerContext()\n\n useEffect(() => {\n editor.update(() => {\n $getRoot().setDirection(dir)\n })\n return () => {\n // Reset to null (auto) when unmounting or direction changes\n editor.update(() => {\n $getRoot().setDirection(null)\n })\n }\n }, [editor, dir])\n\n return null\n}\n","/**\n * Code-point → visible display symbol.\n * Users can customise this map by importing and mutating it before rendering.\n * Used by HighlightNode to replace invisible characters with\n * visible placeholders in the editor DOM.\n */\nexport const CODEPOINT_DISPLAY_MAP: Record<number, string> = {\n 0x0000: '␀',\n 0x0009: '⇥',\n 0x000a: '↩',\n 0x000c: '␌',\n 0x000d: '↵',\n 0x00a0: '⍽',\n 0x2002: '␣',\n 0x2003: '␣',\n 0x2009: '·',\n 0x200a: '·',\n 0x200b: '∅',\n 0x200c: '⊘',\n 0x200d: '⊕',\n 0x2060: '⁀',\n 0x3000: '□',\n 0xfeff: '◊',\n}\n\n/** Runtime override map set by the component layer.\n * Merged on top of `CODEPOINT_DISPLAY_MAP` when resolving display symbols. */\nlet _codepointOverrides: Record<number, string> | undefined\n\n/** Set the runtime code-point display overrides. Called from the\n * HighlightsPlugin whenever the rules change. */\nexport function setCodepointOverrides(\n overrides: Record<number, string> | undefined,\n): void {\n _codepointOverrides = overrides\n}\n\n/** Resolve the effective display map (built-in + overrides). */\nexport function getEffectiveCodepointMap(): Record<number, string> {\n return _codepointOverrides\n ? { ...CODEPOINT_DISPLAY_MAP, ..._codepointOverrides }\n : CODEPOINT_DISPLAY_MAP\n}\n\n/** Prefix for line-break indicator node ruleIds — used to skip them when\n * collecting text so they don't duplicate on re-highlight passes. */\nexport const NL_MARKER_PREFIX = '__nl-'\n\n/** Replace invisible / special characters with visible display symbols.\n * When `overrides` is provided it is merged on top of `CODEPOINT_DISPLAY_MAP`. */\nexport function replaceInvisibleChars(\n text: string,\n overrides?: Record<number, string>,\n): string {\n const map = overrides\n ? { ...CODEPOINT_DISPLAY_MAP, ...overrides }\n : getEffectiveCodepointMap()\n let result = ''\n for (const ch of text) {\n const cp = ch.codePointAt(0) ?? 0\n result += map[cp] ?? ch\n }\n return result\n}\n","import { TextNode } from 'lexical'\n\nimport { NL_MARKER_PREFIX, replaceInvisibleChars } from './constants'\n\nimport type {\n EditorConfig,\n LexicalNode,\n NodeKey,\n SerializedTextNode,\n} from 'lexical'\n\n// ─── Serialized shape ─────────────────────────────────────────────────────────\n\ninterface SerializedHighlightNode extends SerializedTextNode {\n highlightTypes: string\n ruleIds: string\n displayText: string\n}\n\n// ─── HighlightNode ────────────────────────────────────────────────────────────\n\nexport class HighlightNode extends TextNode {\n __highlightTypes: string\n __ruleIds: string\n __displayText: string\n\n static getType(): string {\n return 'highlight'\n }\n\n static clone(node: HighlightNode): HighlightNode {\n return new HighlightNode(\n node.__text,\n node.__highlightTypes,\n node.__ruleIds,\n node.__displayText,\n node.__key,\n )\n }\n\n constructor(\n text: string,\n highlightTypes: string,\n ruleIds: string,\n displayText?: string,\n key?: NodeKey,\n ) {\n super(text, key)\n this.__highlightTypes = highlightTypes\n this.__ruleIds = ruleIds\n this.__displayText = displayText ?? ''\n }\n\n createDOM(config: EditorConfig): HTMLElement {\n const dom = super.createDOM(config)\n dom.classList.add('cat-highlight')\n for (const t of this.__highlightTypes.split(',')) {\n dom.classList.add(`cat-highlight-${t}`)\n if (t.startsWith('glossary-')) {\n dom.classList.add('cat-highlight-glossary')\n }\n if (t.startsWith('spellcheck-')) {\n dom.classList.add('cat-highlight-spellcheck')\n }\n }\n if (this.__highlightTypes.includes(',')) {\n dom.classList.add('cat-highlight-nested')\n }\n dom.dataset.highlightTypes = this.__highlightTypes\n dom.dataset.ruleIds = this.__ruleIds\n\n // NL-marker nodes are purely decorative — prevent selection but allow\n // cursor placement nearby (no contentEditable=false, which would block carets)\n if (this.__ruleIds.startsWith(NL_MARKER_PREFIX)) {\n dom.style.userSelect = 'none'\n dom.classList.add('cat-highlight-nl-marker')\n }\n\n // Tag nodes: store display text for CSS-based collapsed rendering\n if (this.__displayText) {\n dom.dataset.display = this.__displayText\n }\n\n // Collapsed tag nodes (token mode): put a single zero-width space\n // in the DOM so the browser cannot place a caret inside the\n // multi-char display text. The visual tag label (e.g. <1>) is\n // rendered by CSS ::before via data-display.\n // contentEditable=false prevents the browser from inserting typed\n // text into this element (DOM text ≠ model text length).\n if (this.__highlightTypes.split(',').includes('tag-collapsed')) {\n dom.textContent = '\\u200B'\n dom.contentEditable = 'false'\n }\n\n // Special-char nodes in token mode: cursor can only sit before/after,\n // so it's safe to replace textContent with the visible display symbol.\n if (this.__highlightTypes.split(',').includes('special-char')) {\n // Regular spaces use a CSS-only approach (::before pseudo-element)\n // to avoid modifying textContent, which can conflict with Lexical's\n // internal DOM text tracking and cause some nodes to lose their\n // HighlightNode identity.\n if (this.__text === ' ') {\n dom.classList.add('cat-highlight-space-char')\n dom.style.position = 'relative'\n } else {\n const replaced = replaceInvisibleChars(this.__text)\n if (replaced !== this.__text) {\n dom.textContent = replaced\n }\n }\n }\n\n // Quote nodes: replace textContent with a zero-width space (like\n // collapsed tags) so the original character takes no visual width.\n // CSS ::before via data-display renders the replacement character.\n // Since quotes are single characters, DOM length (1 = ZWS) matches\n // model length (1 = original char), avoiding any offset mismatch.\n // contentEditable=false prevents the browser from inserting text inside.\n if (\n this.__highlightTypes.split(',').includes('quote') &&\n this.__displayText\n ) {\n dom.classList.add('cat-highlight-quote-char')\n dom.textContent = '\\u200B'\n dom.contentEditable = 'false'\n }\n\n return dom\n }\n\n updateDOM(prevNode: this, dom: HTMLElement, config: EditorConfig): boolean {\n const updated = super.updateDOM(prevNode, dom, config)\n if (prevNode.__highlightTypes !== this.__highlightTypes) {\n for (const t of prevNode.__highlightTypes.split(',')) {\n dom.classList.remove(`cat-highlight-${t}`)\n if (t.startsWith('glossary-')) {\n dom.classList.remove('cat-highlight-glossary')\n }\n if (t.startsWith('spellcheck-')) {\n dom.classList.remove('cat-highlight-spellcheck')\n }\n }\n dom.classList.remove('cat-highlight-nested')\n for (const t of this.__highlightTypes.split(',')) {\n dom.classList.add(`cat-highlight-${t}`)\n if (t.startsWith('glossary-')) {\n dom.classList.add('cat-highlight-glossary')\n }\n if (t.startsWith('spellcheck-')) {\n dom.classList.add('cat-highlight-spellcheck')\n }\n }\n if (this.__highlightTypes.includes(',')) {\n dom.classList.add('cat-highlight-nested')\n }\n dom.dataset.highlightTypes = this.__highlightTypes\n }\n if (prevNode.__ruleIds !== this.__ruleIds) {\n dom.dataset.ruleIds = this.__ruleIds\n }\n\n // Keep display text in sync\n if (prevNode.__displayText !== this.__displayText) {\n if (this.__displayText) {\n dom.dataset.display = this.__displayText\n } else {\n delete dom.dataset.display\n }\n }\n\n // Collapsed tag nodes: keep DOM in sync\n if (this.__highlightTypes.split(',').includes('tag-collapsed')) {\n dom.textContent = '\\u200B'\n dom.contentEditable = 'false'\n } else if (dom.contentEditable === 'false') {\n // Clear if node transitioned away from collapsed\n dom.removeAttribute('contenteditable')\n }\n\n // Re-apply invisible char replacement after any DOM updates\n if (this.__highlightTypes.split(',').includes('special-char')) {\n if (this.__text === ' ') {\n dom.classList.add('cat-highlight-space-char')\n dom.style.position = 'relative'\n } else {\n const replaced = replaceInvisibleChars(this.__text)\n if (replaced !== this.__text) {\n dom.textContent = replaced\n }\n }\n }\n\n // Re-apply quote visual class + ZWS after DOM updates\n if (\n this.__highlightTypes.split(',').includes('quote') &&\n this.__displayText\n ) {\n dom.classList.add('cat-highlight-quote-char')\n dom.textContent = '\\u200B'\n dom.contentEditable = 'false'\n } else if (prevNode.__highlightTypes.split(',').includes('quote')) {\n dom.classList.remove('cat-highlight-quote-char')\n if (dom.contentEditable === 'false') {\n dom.removeAttribute('contenteditable')\n }\n }\n\n return updated\n }\n\n static importJSON(json: SerializedHighlightNode): HighlightNode {\n const node = new HighlightNode(\n json.text,\n json.highlightTypes,\n json.ruleIds,\n json.displayText,\n )\n node.setFormat(json.format)\n node.setDetail(json.detail)\n node.setMode(json.mode)\n node.setStyle(json.style)\n return node\n }\n\n exportJSON(): SerializedHighlightNode {\n return {\n ...super.exportJSON(),\n type: 'highlight',\n highlightTypes: this.__highlightTypes,\n ruleIds: this.__ruleIds,\n displayText: this.__displayText,\n }\n }\n\n /** NL-marker nodes must not leak the display symbol ↩ into\n * clipboard or getTextContent() calls — they are purely visual. */\n getTextContent(): string {\n if (this.__ruleIds.startsWith(NL_MARKER_PREFIX)) return ''\n return super.getTextContent()\n }\n\n canInsertTextBefore(): boolean {\n if (this.__ruleIds.startsWith(NL_MARKER_PREFIX)) return false\n // Token mode nodes (collapsed tags, quotes, special-chars) must\n // reject text insertion so typing creates a sibling TextNode\n // instead of merging into (and corrupting) the highlight node.\n if (this.getMode() === 'token') return false\n return true\n }\n\n canInsertTextAfter(): boolean {\n if (this.__ruleIds.startsWith(NL_MARKER_PREFIX)) return false\n if (this.getMode() === 'token') return false\n return true\n }\n\n isTextEntity(): boolean {\n return false\n }\n}\n\nexport function $createHighlightNode(\n text: string,\n highlightTypes: string,\n ruleIds: string,\n displayText?: string,\n /** Force token mode (atomic, non-editable). Special-char and NL-marker\n * nodes are always token; tag nodes should only be token when collapsed. */\n forceToken?: boolean,\n): HighlightNode {\n const node = new HighlightNode(text, highlightTypes, ruleIds, displayText)\n // Special-char and NL-marker nodes are always atomic.\n // Tag nodes are only atomic when explicitly requested (collapsed mode).\n if (\n highlightTypes.split(',').includes('special-char') ||\n ruleIds.startsWith(NL_MARKER_PREFIX) ||\n forceToken\n ) {\n node.setMode('token')\n }\n return node\n}\n\nexport function $isHighlightNode(\n node: LexicalNode | null | undefined,\n): node is HighlightNode {\n return node instanceof HighlightNode\n}\n","import { $applyNodeReplacement, TextNode } from 'lexical'\n\nimport type {\n DOMConversionMap,\n DOMConversionOutput,\n DOMExportOutput,\n EditorConfig,\n LexicalNode,\n NodeKey,\n SerializedTextNode,\n Spread,\n} from 'lexical'\n\n// ─── Serialized shape ─────────────────────────────────────────────────────────\n\nexport type SerializedMentionNode = Spread<\n {\n mentionId: string\n mentionName: string\n },\n SerializedTextNode\n>\n\n// ─── Global configuration ─────────────────────────────────────────────────────\n\n/** Render function that fills the DOM of a mention node.\n * Receives the host `<span>` element, the mentionId, and the display name.\n * Return `true` to signal that you handled the rendering;\n * return `false` / `undefined` to fall back to the built-in renderer. */\nexport type MentionDOMRenderer = (\n element: HTMLSpanElement,\n mentionId: string,\n mentionName: string,\n) => boolean | void\n\nexport interface MentionNodeConfig {\n /** Custom renderer for the mention node DOM content.\n * When provided, this function is called every time the mention DOM\n * is created or updated. Return `true` to take over rendering. */\n renderDOM?: MentionDOMRenderer\n /** Converts a mention ID to the model text stored in the editor.\n * Default: `` id => `@{${id}}` `` — produces `@{5}`, `@{user_abc}`, etc. */\n serialize?: (id: string) => string\n /** RegExp used to detect mention patterns in pasted / imported text.\n * Must contain exactly one capture group that extracts the mention ID.\n * The `g` flag is required.\n * Default: `/@\\{([^}]+)\\}/g` — matches `@{5}`, `@{user_abc}`, etc. */\n pattern?: RegExp\n}\n\n// ─── Default serialization ────────────────────────────────────────────────────\n\nconst DEFAULT_MENTION_SERIALIZE = (id: string): string => `@{${id}}`\nconst DEFAULT_MENTION_PATTERN = /@\\{([^}]+)\\}/g\n\n/** Module-level config — set by the editor component on mount. */\nlet _mentionNodeConfig: MentionNodeConfig = {}\n\n/** Configure the global MentionNode behaviour.\n * Call this from CATEditor whenever props change. */\nexport function setMentionNodeConfig(config: MentionNodeConfig) {\n _mentionNodeConfig = config\n}\n\n/** Get the model text for a mention with the given ID,\n * using the configured `serialize` function (or the default `@{id}`). */\nexport function getMentionModelText(id: string): string {\n return (_mentionNodeConfig.serialize ?? DEFAULT_MENTION_SERIALIZE)(id)\n}\n\n/** Get a fresh copy of the mention detection RegExp.\n * A new RegExp is returned every time so that `lastIndex` is always 0. */\nexport function getMentionPattern(): RegExp {\n const src = _mentionNodeConfig.pattern ?? DEFAULT_MENTION_PATTERN\n return new RegExp(src.source, src.flags)\n}\n\n// ─── Built-in DOM renderer ────────────────────────────────────────────────────\n\n/** Default rendering: `@DisplayName` */\nfunction renderDefaultMentionDOM(\n element: HTMLSpanElement,\n _mentionId: string,\n mentionName: string,\n): void {\n element.textContent = ''\n const label = document.createElement('span')\n label.className = 'cat-mention-label'\n label.textContent = `@${mentionName}`\n element.appendChild(label)\n}\n\n// ─── DOM import helper ────────────────────────────────────────────────────────\n\nfunction $convertMentionElement(\n domNode: HTMLElement,\n): DOMConversionOutput | null {\n const mentionId = domNode.getAttribute('data-mention-id')\n const mentionName = domNode.getAttribute('data-mention-name')\n\n if (mentionId !== null) {\n const node = $createMentionNode(mentionId, mentionName ?? mentionId)\n return { node }\n }\n return null\n}\n\n// ─── MentionNode ──────────────────────────────────────────────────────────────\n\n/**\n * A custom Lexical TextNode that represents a mention.\n *\n * **Model text** (what Lexical stores, what `getTextContent()` returns):\n * `@{mentionId}` — e.g. `@1`, `@user_abc`.\n *\n * **Visible DOM**: `@DisplayName` (+ optional avatar). The display is\n * controlled by a configurable renderer — see `setMentionNodeConfig`.\n *\n * The node uses \"segmented\" mode so it behaves as an atomic unit:\n * backspace removes the whole mention, typing at boundaries creates a\n * sibling TextNode instead of editing the mention text.\n */\nexport class MentionNode extends TextNode {\n __mentionId: string\n __mentionName: string\n\n static getType(): string {\n return 'mention'\n }\n\n static clone(node: MentionNode): MentionNode {\n return new MentionNode(\n node.__mentionId,\n node.__mentionName,\n node.__text,\n node.__key,\n )\n }\n\n static importJSON(serializedNode: SerializedMentionNode): MentionNode {\n return $createMentionNode(\n serializedNode.mentionId,\n serializedNode.mentionName,\n ).updateFromJSON(serializedNode)\n }\n\n constructor(\n mentionId: string,\n mentionName: string,\n text?: string,\n key?: NodeKey,\n ) {\n // Model text is always the serialized form (default `@{id}`) — this is\n // what getTextContent() returns, what HighlightsPlugin collects, and\n // what onChange emits.\n super(text ?? getMentionModelText(mentionId), key)\n this.__mentionId = mentionId\n this.__mentionName = mentionName\n }\n\n exportJSON(): SerializedMentionNode {\n return {\n ...super.exportJSON(),\n mentionId: this.__mentionId,\n mentionName: this.__mentionName,\n }\n }\n\n createDOM(config: EditorConfig): HTMLElement {\n const dom = super.createDOM(config)\n dom.className = 'cat-mention-node'\n dom.spellcheck = false\n dom.contentEditable = 'false'\n dom.setAttribute('data-mention-id', this.__mentionId)\n dom.setAttribute('data-mention-name', this.__mentionName)\n this._renderInnerDOM(dom as HTMLSpanElement)\n return dom\n }\n\n updateDOM(\n prevNode: MentionNode,\n dom: HTMLElement,\n _config: EditorConfig,\n ): boolean {\n // If id or name changed, re-render the inner content\n if (\n prevNode.__mentionId !== this.__mentionId ||\n prevNode.__mentionName !== this.__mentionName\n ) {\n dom.setAttribute('data-mention-id', this.__mentionId)\n dom.setAttribute('data-mention-name', this.__mentionName)\n this._renderInnerDOM(dom as HTMLSpanElement)\n }\n // Return false = Lexical should NOT recreate the DOM from scratch\n return false\n }\n\n /** Fill the span with visible content (default: @label, or custom). */\n private _renderInnerDOM(element: HTMLSpanElement): void {\n const renderer = _mentionNodeConfig.renderDOM\n if (renderer) {\n const handled = renderer(element, this.__mentionId, this.__mentionName)\n if (handled) return\n }\n renderDefaultMentionDOM(element, this.__mentionId, this.__mentionName)\n }\n\n exportDOM(): DOMExportOutput {\n const element = document.createElement('span')\n element.setAttribute('data-mention', 'true')\n element.setAttribute('data-mention-id', this.__mentionId)\n element.setAttribute('data-mention-name', this.__mentionName)\n element.textContent = this.__text\n return { element }\n }\n\n static importDOM(): DOMConversionMap | null {\n return {\n span: (domNode: HTMLElement) => {\n if (!domNode.hasAttribute('data-mention')) {\n return null\n }\n return {\n conversion: $convertMentionElement,\n priority: 1,\n }\n },\n }\n }\n\n isTextEntity(): true {\n return true\n }\n\n canInsertTextBefore(): boolean {\n return false\n }\n\n canInsertTextAfter(): boolean {\n return false\n }\n}\n\n// ─── Factory & guard ──────────────────────────────────────────────────────────\n\nexport function $createMentionNode(\n mentionId: string,\n mentionName: string,\n textContent?: string,\n): MentionNode {\n const node = new MentionNode(\n mentionId,\n mentionName,\n textContent ?? getMentionModelText(mentionId),\n )\n node.setMode('token').toggleDirectionless()\n return $applyNodeReplacement(node)\n}\n\nexport function $isMentionNode(\n node: LexicalNode | null | undefined,\n): node is MentionNode {\n return node instanceof MentionNode\n}\n","import { useCallback, useEffect, useMemo, useRef, useState } from 'react'\nimport * as ReactDOM from 'react-dom'\nimport { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext'\nimport {\n LexicalTypeaheadMenuPlugin,\n MenuOption,\n useBasicTypeaheadTriggerMatch,\n} from '@lexical/react/LexicalTypeaheadMenuPlugin'\nimport { useVirtualizer } from '@tanstack/react-virtual'\n\nimport { $createTextNode } from 'lexical'\nimport { $createMentionNode } from './mention-node'\n\nimport type { TextNode } from 'lexical'\nimport type { MenuTextMatch } from '@lexical/react/LexicalTypeaheadMenuPlugin'\nimport type { IMentionUser } from './types'\n\n// ─── Constants ────────────────────────────────────────────────────────────────\n\nconst SUGGESTION_LIST_LENGTH_LIMIT = 50\nconst ITEM_HEIGHT = 36\n\n// ─── Option class ─────────────────────────────────────────────────────────────\n\nclass MentionTypeaheadOption extends MenuOption {\n user: IMentionUser\n\n constructor(user: IMentionUser) {\n super(user.id)\n this.user = user\n }\n}\n\n// ─── Avatar helper ────────────────────────────────────────────────────────────\n\nfunction MentionAvatar({\n user,\n size = 24,\n}: {\n user: IMentionUser\n size?: number\n}) {\n if (user.avatar) {\n return (\n <span\n className=\"inline-flex items-center justify-center\"\n style={{ width: size, height: size }}\n >\n {user.avatar()}\n </span>\n )\n }\n\n // Fallback: initials avatar\n const initials = user.name\n .split(/\\s+/)\n .map((w) => w[0])\n .slice(0, 2)\n .join('')\n .toUpperCase()\n\n return (\n <span\n className=\"inline-flex items-center justify-center rounded-full bg-muted text-muted-foreground font-medium\"\n style={{ width: size, height: size, fontSize: size * 0.4 }}\n >\n {initials}\n </span>\n )\n}\n\n// ─── Individual menu item ─────────────────────────────────────────────────────\n\nfunction MentionMenuItem({\n option,\n isSelected,\n onClick,\n onMouseEnter,\n}: {\n option: MentionTypeaheadOption\n isSelected: boolean\n onClick: () => void\n onMouseEnter: () => void\n}) {\n return (\n <li\n key={option.key}\n tabIndex={-1}\n className={`flex items-center gap-2 px-2 py-1.5 text-sm cursor-default select-none rounded-sm ${\n isSelected\n ? 'bg-accent text-accent-foreground'\n : 'text-popover-foreground'\n }`}\n ref={option.setRefElement}\n role=\"option\"\n aria-selected={isSelected}\n onMouseEnter={onMouseEnter}\n onClick={onClick}\n >\n <MentionAvatar user={option.user} size={22} />\n <span className=\"truncate\">{option.user.name}</span>\n </li>\n )\n}\n\n// ─── Virtualized menu ─────────────────────────────────────────────────────────\n\nfunction VirtualizedMentionMenu({\n options,\n selectedIndex,\n selectOptionAndCleanUp,\n setHighlightedIndex,\n}: {\n options: Array<MentionTypeaheadOption>\n selectedIndex: number | null\n selectOptionAndCleanUp: (option: MentionTypeaheadOption) => void\n setHighlightedIndex: (index: number) => void\n}) {\n const parentRef = useRef<HTMLDivElement>(null)\n\n const virtualizer = useVirtualizer({\n count: options.length,\n getScrollElement: () => parentRef.current,\n estimateSize: () => ITEM_HEIGHT,\n overscan: 8,\n })\n\n // Scroll selected item into view\n useEffect(() => {\n if (selectedIndex !== null && selectedIndex >= 0) {\n virtualizer.scrollToIndex(selectedIndex, { align: 'auto' })\n }\n }, [selectedIndex, virtualizer])\n\n return (\n <div\n ref={parentRef}\n className=\"max-h-[280px] overflow-y-auto overflow-x-hidden\"\n >\n <ul\n role=\"listbox\"\n aria-label=\"Mention suggestions\"\n style={{\n height: `${virtualizer.getTotalSize()}px`,\n position: 'relative',\n width: '100%',\n }}\n >\n {virtualizer.getVirtualItems().map((vItem) => {\n const option = options[vItem.index]\n return (\n <div\n key={option.key}\n style={{\n position: 'absolute',\n top: 0,\n left: 0,\n width: '100%',\n transform: `translateY(${vItem.start}px)`,\n }}\n ref={virtualizer.measureElement}\n data-index={vItem.index}\n >\n <MentionMenuItem\n option={option}\n isSelected={selectedIndex === vItem.index}\n onClick={() => {\n setHighlightedIndex(vItem.index)\n selectOptionAndCleanUp(option)\n }}\n onMouseEnter={() => {\n setHighlightedIndex(vItem.index)\n }}\n />\n </div>\n )\n })}\n </ul>\n </div>\n )\n}\n\n// ─── Custom trigger match that also handles @ at start of text ────────────────\n\nfunction checkForMentionMatch(\n text: string,\n trigger: string,\n): MenuTextMatch | null {\n // Match: start-of-string or preceded by whitespace/punctuation,\n // then the trigger character, then valid mention chars.\n const escaped = trigger.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')\n const regex = new RegExp(\n `(^|\\\\s|\\\\()([${escaped}]((?:[^${escaped}\\\\s]){0,75}))$`,\n )\n const match = regex.exec(text)\n if (match !== null) {\n const maybeLeadingWhitespace = match[1]\n const matchingString = match[3]\n return {\n leadOffset: match.index + maybeLeadingWhitespace.length,\n matchingString,\n replaceableString: match[2],\n }\n }\n return null\n}\n\n// ─── Main Plugin ──────────────────────────────────────────────────────────────\n\nexport interface MentionPluginProps {\n /** List of users that can be mentioned. */\n users: Array<IMentionUser>\n /** Trigger character (default `@`). */\n trigger?: string\n /** Called when a mention is inserted. */\n onMentionInsert?: (user: IMentionUser) => void\n}\n\nexport function MentionPlugin({\n users,\n trigger = '@',\n onMentionInsert,\n}: MentionPluginProps) {\n const [editor] = useLexicalComposerContext()\n const [queryString, setQueryString] = useState<string | null>(null)\n\n // Also allow slash-trigger to pass through (avoid conflicts)\n const checkForSlashTriggerMatch = useBasicTypeaheadTriggerMatch('/', {\n minLength: 0,\n })\n\n // Filter users by query string\n const results = useMemo(() => {\n if (queryString === null)\n return users.slice(0, SUGGESTION_LIST_LENGTH_LIMIT)\n const q = queryString.toLowerCase()\n return users\n .filter((u) => u.name.toLowerCase().includes(q))\n .slice(0, SUGGESTION_LIST_LENGTH_LIMIT)\n }, [users, queryString])\n\n const options = useMemo(\n () => results.map((user) => new MentionTypeaheadOption(user)),\n [results],\n )\n\n const onSelectOption = useCallback(\n (\n selectedOption: MentionTypeaheadOption,\n nodeToReplace: TextNode | null,\n closeMenu: () => void,\n ) => {\n editor.update(() => {\n const mentionNode = $createMentionNode(\n selectedOption.user.id,\n selectedOption.user.name,\n )\n if (nodeToReplace) {\n nodeToReplace.replace(mentionNode)\n }\n // Insert a trailing space after the mention and place the caret there\n const spaceNode = $createTextNode(' ')\n mentionNode.insertAfter(spaceNode)\n spaceNode.selectEnd()\n closeMenu()\n })\n onMentionInsert?.(selectedOption.user)\n },\n [editor, onMentionInsert],\n )\n\n const checkForMentionTrigger = useCallback(\n (text: string) => {\n const slashMatch = checkForSlashTriggerMatch(text, editor)\n if (slashMatch !== null) {\n return null\n }\n return checkForMentionMatch(text, trigger)\n },\n [checkForSlashTriggerMatch, editor, trigger],\n )\n\n return (\n <LexicalTypeaheadMenuPlugin<MentionTypeaheadOption>\n onQueryChange={setQueryString}\n onSelectOption={onSelectOption}\n triggerFn={checkForMentionTrigger}\n options={options}\n menuRenderFn={(\n anchorElementRef,\n { selectedIndex, selectOptionAndCleanUp, setHighlightedIndex },\n ) =>\n anchorElementRef.current && options.length\n ? ReactDOM.createPortal(\n <div className=\"cat-mention-popover\">\n <VirtualizedMentionMenu\n options={options}\n selectedIndex={selectedIndex}\n selectOptionAndCleanUp={selectOptionAndCleanUp}\n setHighlightedIndex={setHighlightedIndex}\n />\n </div>,\n anchorElementRef.current,\n )\n : null\n }\n />\n )\n}\n","import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext'\nimport { $isParentElementRTL } from '@lexical/selection'\nimport {\n $addUpdateTag,\n $createParagraphNode,\n $createRangeSelection,\n $createTextNode,\n $getRoot,\n $getSelection,\n $isRangeSelection,\n $setSelection,\n COMMAND_PRIORITY_HIGH,\n KEY_ARROW_LEFT_COMMAND,\n KEY_ARROW_RIGHT_COMMAND,\n PASTE_COMMAND,\n SELECTION_CHANGE_COMMAND,\n} from 'lexical'\nimport { useCallback, useEffect, useRef } from 'react'\n\nimport { computeHighlightSegments } from './compute-segments'\nimport {\n CODEPOINT_DISPLAY_MAP,\n NL_MARKER_PREFIX,\n setCodepointOverrides,\n} from './constants'\nimport { $createHighlightNode, $isHighlightNode } from './highlight-node'\nimport {\n $createMentionNode,\n $isMentionNode,\n getMentionPattern,\n} from './mention-node'\nimport { $globalOffsetToPoint, $pointToGlobalOffset } from './selection-helpers'\n\nimport type {\n ElementNode,\n LexicalEditor,\n LexicalNode,\n PointType,\n} from 'lexical'\nimport type { IMentionRule, ITagRule, MooRule, RuleAnnotation } from './types'\n\n/** Saved mention position recorded during the text-collection phase. */\ninterface SavedMention {\n /** Global character offset where this mention starts */\n start: number\n /** Global character offset where this mention ends */\n end: number\n /** Data needed to recreate the MentionNode */\n mentionId: string\n mentionName: string\n text: string\n}\n\n// ─── Highlights Plugin ────────────────────────────────────────────────────────\n\ninterface HighlightsPluginProps {\n rules: Array<MooRule>\n annotationMapRef: React.MutableRefObject<Map<string, RuleAnnotation>>\n codepointDisplayMap?: Record<number, string>\n}\n\nexport function HighlightsPlugin({\n rules,\n annotationMapRef,\n codepointDisplayMap,\n}: HighlightsPluginProps) {\n const [editor] = useLexicalComposerContext()\n const rafRef = useRef<number | null>(null)\n\n const applyHighlights = useCallback(() => {\n // Sync code-point display overrides from the editor-level prop\n setCodepointOverrides(codepointDisplayMap)\n\n // Check focus BEFORE editor.update() — if the editor doesn't own\n // focus we must not restore (or leave) a selection, otherwise\n // Lexical's DOM reconciliation will move the browser caret back\n // into the editor, stealing focus from external inputs like the\n // search field.\n const editorElement = editor.getRootElement()\n const editorHasFocus =\n editorElement != null &&\n editorElement.contains(editorElement.ownerDocument.activeElement)\n\n editor.update(\n () => {\n // Tag so our own listener skips this update\n $addUpdateTag('cat-highlights')\n\n const root = $getRoot()\n\n // 1. Collect plain text content (flatten paragraphs).\n // Skip line-break marker nodes (prefixed __nl-) so that\n // the ↩ display symbol doesn't compound on each pass.\n // Record MentionNode positions so they survive the rebuild.\n const paragraphs = root.getChildren()\n const lines: Array<string> = []\n const savedMentions: Array<SavedMention> = []\n let collectOffset = 0\n\n for (let pIdx = 0; pIdx < paragraphs.length; pIdx++) {\n const p = paragraphs[pIdx]\n let lineText = ''\n if ('getChildren' in p) {\n for (const child of (p as ElementNode).getChildren()) {\n if (\n $isHighlightNode(child) &&\n child.__ruleIds.startsWith(NL_MARKER_PREFIX)\n ) {\n continue\n }\n if ($isMentionNode(child)) {\n const text = child.getTextContent()\n savedMentions.push({\n start: collectOffset + lineText.length,\n end: collectOffset + lineText.length + text.length,\n mentionId: child.__mentionId,\n mentionName: child.__mentionName,\n text,\n })\n }\n lineText += child.getTextContent()\n }\n } else {\n lineText = p.getTextContent()\n }\n lines.push(lineText)\n collectOffset += lineText.length + 1 // +1 for \\n between paragraphs\n }\n const fullText = lines.join('\\n')\n\n // 1b. Detect mention patterns in the text that aren't already\n // MentionNodes. This handles pasted text, initial text, or\n // programmatic insertions containing the mention pattern.\n const mentionRule = rules.find(\n (r): r is IMentionRule => r.type === 'mention',\n )\n if (mentionRule) {\n const pattern = getMentionPattern()\n let match: RegExpExecArray | null\n while ((match = pattern.exec(fullText)) !== null) {\n const matchStart = match.index\n const matchEnd = matchStart + match[0].length\n const matchId = match[1]\n\n // Skip if already tracked as a MentionNode\n const alreadySaved = savedMentions.some(\n (m) => m.start === matchStart && m.end === matchEnd,\n )\n if (alreadySaved) continue\n\n // Resolve user name from the mention rule's user list\n const user = mentionRule.users.find((u) => u.id === matchId)\n if (user) {\n savedMentions.push({\n start: matchStart,\n end: matchEnd,\n mentionId: user.id,\n mentionName: user.name,\n text: match[0],\n })\n }\n }\n }\n\n // 2. Compute highlight segments (supports nesting).\n // Replace mention ranges with placeholder chars so that\n // highlight patterns (e.g. tag pattern matching `{id}`)\n // don't produce segments inside mention model text.\n let segmentText = fullText\n for (let mi = savedMentions.length - 1; mi >= 0; mi--) {\n const m = savedMentions[mi]\n segmentText =\n segmentText.slice(0, m.start) +\n '\\x01'.repeat(m.end - m.start) +\n segmentText.slice(m.end)\n }\n const segments = computeHighlightSegments(segmentText, rules)\n\n // 3. Update annotation map for popover\n const newMap = new Map<string, RuleAnnotation>()\n for (const seg of segments) {\n for (const ann of seg.annotations) {\n newMap.set(ann.id, ann)\n }\n }\n annotationMapRef.current = newMap\n\n // ── Save selection as global offsets before rebuilding ──\n const prevSelection = $getSelection()\n let savedAnchor: number | null = null\n let savedFocus: number | null = null\n if ($isRangeSelection(prevSelection)) {\n savedAnchor = $pointToGlobalOffset(\n prevSelection.anchor.key,\n prevSelection.anchor.offset,\n )\n savedFocus = $pointToGlobalOffset(\n prevSelection.focus.key,\n prevSelection.focus.offset,\n )\n }\n\n // 4. Rebuild the content tree with highlights,\n // preserving MentionNodes at their original positions.\n root.clear()\n\n if (fullText.length === 0) {\n const p = $createParagraphNode()\n p.append($createTextNode(''))\n root.append(p)\n return\n }\n\n // Track emitted mentions to prevent duplicates when a mention\n // spans multiple segment/gap ranges.\n const emittedMentionStarts = new Set<number>()\n\n /** Append nodes for a text range, splitting around any\n * saved MentionNodes that overlap the range.\n * `makeNode` creates either a TextNode or HighlightNode\n * for the non-mention portions. */\n const appendWithMentions = (\n paragraph: ElementNode,\n rangeStart: number,\n rangeEnd: number,\n makeNode: (text: string) => LexicalNode,\n ) => {\n // Collect mentions that overlap this range (skip already-emitted)\n const overlapping = savedMentions.filter(\n (m) =>\n m.start < rangeEnd &&\n m.end > rangeStart &&\n !emittedMentionStarts.has(m.start),\n )\n if (overlapping.length === 0) {\n // No mentions — emit the full range with the provided factory\n const text = fullText.slice(rangeStart, rangeEnd)\n if (text.length > 0) {\n paragraph.append(makeNode(text))\n }\n return\n }\n\n // Sort mentions by start offset\n overlapping.sort((a, b) => a.start - b.start)\n let cursor = rangeStart\n\n for (const m of overlapping) {\n const mStart = Math.max(m.start, rangeStart)\n const mEnd = Math.min(m.end, rangeEnd)\n\n // Text before this mention\n if (mStart > cursor) {\n const beforeText = fullText.slice(cursor, mStart)\n if (beforeText.length > 0) {\n paragraph.append(makeNode(beforeText))\n }\n }\n\n // The mention node itself\n paragraph.append(\n $createMentionNode(m.mentionId, m.mentionName, m.text),\n )\n emittedMentionStarts.add(m.start)\n cursor = mEnd\n }\n\n // Text after the last mention\n if (cursor < rangeEnd) {\n const afterText = fullText.slice(cursor, rangeEnd)\n if (afterText.length > 0) {\n paragraph.append(makeNode(afterText))\n }\n }\n }\n\n const textLines = fullText.split('\\n')\n let globalOffset = 0\n\n for (const line of textLines) {\n const paragraph = $createParagraphNode()\n const lineStart = globalOffset\n const lineEnd = globalOffset + line.length\n\n // Filter segments within this line\n const lineSegments = segments.filter(\n (s) => s.start < lineEnd && s.end > lineStart,\n )\n\n let pos = lineStart\n for (const seg of lineSegments) {\n const sStart = Math.max(seg.start, lineStart)\n const sEnd = Math.min(seg.end, lineEnd)\n\n // Plain text before highlight\n if (sStart > pos) {\n appendWithMentions(paragraph, pos, sStart, (text) =>\n $createTextNode(text),\n )\n }\n\n // Detect collapsed-tag state early so it can influence types.\n const tagAnn = seg.annotations.find((a) => a.type === 'tag')\n const tagRule = rules.find((r): r is ITagRule => r.type === 'tag')\n const tagsCollapsed = !!tagRule?.collapsed\n // Decide if *this specific* tag should be collapsed.\n // When collapseScope is 'html-only', only HTML tags collapse.\n const collapseScope = tagRule?.collapseScope ?? 'all'\n const thisTagCollapsed =\n tagsCollapsed &&\n !!tagAnn &&\n (collapseScope === 'all' || tagAnn.data.isHtml)\n\n // Highlighted text — carries all annotation types & IDs\n // Glossary annotations include their label in the CSS type\n // so each label gets a unique highlight class.\n const typesArr = [\n ...new Set(\n seg.annotations.map((a) => {\n if (a.type === 'glossary') return `glossary-${a.data.label}`\n if (a.type === 'spellcheck')\n return `spellcheck-${a.data.categoryId}`\n return a.type\n }),\n ),\n ]\n // Mark collapsed tags explicitly so highlight-node.ts can\n // distinguish from non-collapsed tags that carry displayText\n // from a quote annotation.\n if (thisTagCollapsed) {\n typesArr.push('tag-collapsed')\n }\n const types = typesArr.join(',')\n const ids = seg.annotations.map((a) => a.id).join(',')\n // Only use tag displayText when collapsed — otherwise it would\n // override the quote replacement char for segments that carry\n // both tag and quote annotations.\n const tagDisplayText =\n tagAnn?.type === 'tag' && thisTagCollapsed\n ? tagAnn.data.displayText\n : undefined\n const isTagToken = thisTagCollapsed\n\n // Quote annotations: pass the replacement char as displayText\n // and force token mode, similar to special-char nodes.\n const quoteAnn = seg.annotations.find((a) => a.type === 'quote')\n const quoteDisplayText =\n quoteAnn?.type === 'quote'\n ? quoteAnn.data.replacementChar\n : undefined\n const isQuoteToken = !!quoteAnn\n\n // Check if this highlight segment entirely falls within a mention.\n // Mention content is masked before segment computation, so this\n // should rarely trigger — it's a safety net.\n const containingMention = savedMentions.find(\n (m) => m.start <= sStart && m.end >= sEnd,\n )\n\n if (containingMention) {\n // Mention takes precedence — create it if not yet emitted\n if (!emittedMentionStarts.has(containingMention.start)) {\n paragraph.append(\n $createMentionNode(\n containingMention.mentionId,\n containingMention.mentionName,\n containingMention.text,\n ),\n )\n emittedMentionStarts.add(containingMention.start)\n }\n // If already emitted, skip (no node for this range)\n } else {\n appendWithMentions(paragraph, sStart, sEnd, (text) =>\n $createHighlightNode(\n text,\n types,\n ids,\n tagDisplayText ?? quoteDisplayText,\n isTagToken || isQuoteToken,\n ),\n )\n }\n\n pos = sEnd\n }\n\n // Remaining plain text after last highlight\n if (pos < lineEnd) {\n appendWithMentions(paragraph, pos, lineEnd, (text) =>\n $createTextNode(text),\n )\n }\n\n // Line-break indicator: \\n sits at lineEnd in fullText but is a\n // paragraph boundary in Lexical — it never appears as inline text.\n // Append a visible ↩ symbol with a __nl- prefixed ruleId so we\n // can skip it when collecting text on the next pass.\n const nlPos = lineEnd\n if (nlPos < fullText.length && fullText[nlPos] === '\\n') {\n const nlSegments = segments.filter(\n (s) => s.start <= nlPos && s.end > nlPos,\n )\n if (nlSegments.length > 0) {\n const nlAnns = nlSegments.flatMap((s) => s.annotations)\n const types = [\n ...new Set(\n nlAnns.map((a) => {\n if (a.type === 'glossary') return `glossary-${a.data.label}`\n if (a.type === 'spellcheck')\n return `spellcheck-${a.data.categoryId}`\n return a.type\n }),\n ),\n ].join(',')\n const ids = nlAnns.map((a) => a.id).join(',')\n const symbol = CODEPOINT_DISPLAY_MAP[0x000a]\n // Ensure there's a text node before the NL marker so the\n // cursor has a landing spot (NL markers are contentEditable=false)\n if (paragraph.getChildrenSize() === 0) {\n paragraph.append($createTextNode(''))\n }\n paragraph.append(\n $createHighlightNode(symbol, types, NL_MARKER_PREFIX + ids),\n )\n }\n }\n\n // Ensure paragraph has at least one child\n if (paragraph.getChildrenSize() === 0) {\n paragraph.append($createTextNode(''))\n }\n\n root.append(paragraph)\n globalOffset = lineEnd + 1 // +1 for \\n\n }\n\n // ── Restore selection at the equivalent position ──\n // Only restore when the editor is focused, otherwise the\n // $setSelection call will steal focus from external inputs\n // (e.g. search field). Explicitly null-out the selection\n // so Lexical's DOM reconciliation doesn't try to apply a\n // stale selection either.\n if (editorHasFocus && savedAnchor !== null && savedFocus !== null) {\n const anchorPt = $globalOffsetToPoint(savedAnchor)\n const focusPt = $globalOffsetToPoint(savedFocus)\n if (anchorPt && focusPt) {\n const sel = $createRangeSelection()\n sel.anchor.set(anchorPt.key, anchorPt.offset, anchorPt.type)\n sel.focus.set(focusPt.key, focusPt.offset, focusPt.type)\n $setSelection(sel)\n }\n } else {\n $setSelection(null)\n }\n },\n { tag: 'historic' },\n )\n }, [editor, rules, annotationMapRef])\n\n // Run on mount and when rules change\n useEffect(() => {\n applyHighlights()\n }, [applyHighlights])\n\n // Re-run highlights after any content change (typing, paste, undo, …).\n // Skip our own 'cat-highlights' updates to avoid infinite loops.\n // The 'historic' tag keeps recomputation out of the undo stack.\n useEffect(() => {\n const unregister = editor.registerUpdateListener(\n ({ tags, dirtyElements, dirtyLeaves }) => {\n if (tags.has('cat-highlights')) return\n // Skip selection-only changes — only rebuild when actual\n // content changed. Without this guard every arrow-key press\n // triggers a full tree rebuild on the next frame, which can\n // fight with cursor movement.\n if (dirtyElements.size === 0 && dirtyLeaves.size === 0) return\n if (rafRef.current) cancelAnimationFrame(rafRef.current)\n rafRef.current = requestAnimationFrame(() => {\n applyHighlights()\n })\n },\n )\n return () => {\n unregister()\n if (rafRef.current) cancelAnimationFrame(rafRef.current)\n }\n }, [editor, applyHighlights])\n\n // Strip trailing newlines from pasted text. Some browsers / OSes add\n // a trailing \\n to clipboard content which causes Lexical to create an\n // extra empty paragraph after the pasted text.\n useEffect(() => {\n return editor.registerCommand(\n PASTE_COMMAND,\n (event: ClipboardEvent) => {\n const clipboardData = event.clipboardData\n if (!clipboardData) return false\n const text = clipboardData.getData('text/plain')\n if (text && text !== text.replace(/\\n+$/, '')) {\n event.preventDefault()\n const trimmed = text.replace(/\\n+$/, '')\n editor.update(() => {\n const selection = $getSelection()\n if ($isRangeSelection(selection)) {\n selection.insertRawText(trimmed)\n }\n })\n return true\n }\n return false\n },\n COMMAND_PRIORITY_HIGH,\n )\n }, [editor])\n\n return null\n}\n\n// ─── Editor Ref Plugin ────────────────────────────────────────────────────────\n\n/** Tracks the editor instance and persists the last known selection so that\n * `insertText` works even after the editor loses focus. */\nexport function EditorRefPlugin({\n editorRef,\n savedSelectionRef,\n}: {\n editorRef: React.MutableRefObject<LexicalEditor | null>\n savedSelectionRef: React.MutableRefObject<{\n anchor: number\n focus: number\n } | null>\n}) {\n const [editor] = useLexicalComposerContext()\n useEffect(() => {\n editorRef.current = editor\n }, [editor, editorRef])\n\n // Persist selection on every selection change so we can restore it later\n useEffect(() => {\n return editor.registerUpdateListener(({ editorState }) => {\n editorState.read(() => {\n const sel = $getSelection()\n if ($isRangeSelection(sel)) {\n savedSelectionRef.current = {\n anchor: $pointToGlobalOffset(sel.anchor.key, sel.anchor.offset),\n focus: $pointToGlobalOffset(sel.focus.key, sel.focus.offset),\n }\n }\n })\n })\n }, [editor, savedSelectionRef])\n return null\n}\n\n// ─── NL Marker Navigation Guard ─────────────────────────────────────────────\n\n/** Check whether a node is non-editable in the DOM.\n * Only nodes with contentEditable=\"false\" qualify: NL markers,\n * collapsed tags, and quote-char nodes. Special-char nodes are\n * token-mode but still navigable — they are NOT non-editable. */\nfunction $isNonEditableNode(node: LexicalNode): boolean {\n if (!$isHighlightNode(node)) return false\n if (node.__ruleIds.startsWith(NL_MARKER_PREFIX)) return true\n const types = node.__highlightTypes.split(',')\n if (types.includes('tag-collapsed')) return true\n if (types.includes('quote') && node.__displayText) return true\n return false\n}\n\n/** Given a selection point, if it sits on a non-editable node, move\n * it to the nearest editable landing spot.\n * Element-type positions (between children) are always valid — the\n * browser can render a caret at element boundaries even next to\n * contentEditable=false nodes — so we only clamp text-type points.\n *\n * IMPORTANT: Lexical internally resolves element positions to text\n * positions on the nearest text node. So `{ paragraph, 0, element }`\n * can become `{ firstChild, 0, text }` — which, if that child is\n * CE=false, would trigger clamping. To avoid \"bounce-back\" we detect\n * this case and return the corresponding element position instead of\n * jumping away. */\nfunction $clampPointAwayFromNonEditable(point: PointType): {\n key: string\n offset: number\n type: 'text' | 'element'\n} | null {\n // Element-type positions are always valid.\n if (point.type === 'element') return null\n\n const node = point.getNode()\n if (!$isNonEditableNode(node)) return null\n\n // The cursor landed on a CE=false node as a text-type point. This can\n // happen when Lexical resolves an element-type position (set by us or by\n // click) to the nearest text node. Convert it back to a valid element\n // gap position:\n // offset === 0 → before this child (element offset = childIndex)\n // offset > 0 → after this child (element offset = childIndex + 1)\n const parent = node.getParent()\n if (parent && 'getChildren' in parent) {\n const siblings = parent.getChildren()\n const idx = siblings.findIndex((s) => s.getKey() === node.getKey())\n if (idx >= 0) {\n const elemOffset = point.offset > 0 ? idx + 1 : idx\n return {\n key: parent.getKey(),\n offset: elemOffset,\n type: 'element',\n }\n }\n }\n\n return null\n}\n\n/** Find the landing position one step to the right of `startNode`.\n * - If startNode is an NL marker, cross to the next paragraph.\n * - If the immediate next sibling is editable, land on it.\n * - Otherwise return an element-type gap position just after startNode\n * (so arrow keys stop at each gap between adjacent CE=false nodes). */\nfunction $findNextEditable(\n startNode: LexicalNode,\n): { key: string; offset: number; type: 'text' | 'element' } | null {\n // NL marker → cross to the next paragraph.\n if (\n $isHighlightNode(startNode) &&\n startNode.__ruleIds.startsWith(NL_MARKER_PREFIX)\n ) {\n const paragraph = startNode.getParent()\n const nextParagraph = paragraph?.getNextSibling()\n if (nextParagraph && 'getChildren' in nextParagraph) {\n const first = (nextParagraph as ElementNode).getFirstChild()\n if (first && !$isNonEditableNode(first)) {\n return { key: first.getKey(), offset: 0, type: 'text' }\n }\n // First child is CE=false — land at paragraph start (before the tag).\n if (first) {\n return {\n key: nextParagraph.getKey(),\n offset: 0,\n type: 'element',\n }\n }\n }\n return null\n }\n\n // Immediate next sibling is editable — land on it.\n const next = startNode.getNextSibling()\n if (next && !$isNonEditableNode(next)) {\n return { key: next.getKey(), offset: 0, type: 'text' }\n }\n\n // Next sibling is CE=false or absent — return element position just\n // after startNode. This creates a gap the user can type into.\n const paragraph = startNode.getParent()\n if (paragraph && 'getChildren' in paragraph) {\n const children = paragraph.getChildren()\n const idx = children.findIndex((s) => s.getKey() === startNode.getKey())\n if (idx >= 0) {\n return { key: paragraph.getKey(), offset: idx + 1, type: 'element' }\n }\n }\n\n return null\n}\n\n/** Find the landing position one step to the left of `startNode`.\n * - If the immediate previous sibling is editable, land on it.\n * - Otherwise return an element-type gap position just before startNode\n * (so arrow keys stop at each gap between adjacent CE=false nodes). */\nfunction $findPrevEditable(\n startNode: LexicalNode,\n): { key: string; offset: number; type: 'text' | 'element' } | null {\n const prev = startNode.getPreviousSibling()\n\n // Previous sibling is editable — land at its end.\n if (prev && !$isNonEditableNode(prev)) {\n return {\n key: prev.getKey(),\n offset: prev.getTextContent().length,\n type: 'text',\n }\n }\n\n // Previous sibling is CE=false or absent — return element position\n // just before startNode.\n const paragraph = startNode.getParent()\n if (paragraph && 'getChildren' in paragraph) {\n const children = paragraph.getChildren()\n const idx = children.findIndex((s) => s.getKey() === startNode.getKey())\n if (idx >= 0) {\n return { key: paragraph.getKey(), offset: idx, type: 'element' }\n }\n }\n\n return null\n}\n\n/** Prevents the cursor from ever resting on a non-editable node.\n * - Intercepts Left/Right arrows to skip over non-editable nodes.\n * - Watches every selection change (mouse click, drag, keyboard) and\n * clamps the cursor back to the nearest editable node. */\nexport function NLMarkerNavigationPlugin() {\n const [editor] = useLexicalComposerContext()\n useEffect(() => {\n // ── Right arrow: skip over non-editable nodes (NL markers, collapsed\n // tags, quote-chars, special-chars). ──\n // In RTL, right arrow moves backward (toward start), so check\n // the previous sibling instead of the next one.\n const unregRight = editor.registerCommand(\n KEY_ARROW_RIGHT_COMMAND,\n (event) => {\n const selection = $getSelection()\n if (!$isRangeSelection(selection) || !selection.isCollapsed())\n return false\n\n const isRTL = $isParentElementRTL(selection)\n const { anchor } = selection\n const node = anchor.getNode()\n\n // Check if the adjacent sibling (based on direction) is non-editable\n let adjacentNode: LexicalNode | null = null\n if (isRTL) {\n // RTL: right arrow = backward = previous sibling\n if (anchor.type === 'text') {\n if (anchor.offset > 0) return false\n adjacentNode = node.getPreviousSibling()\n } else {\n const children = (node as ElementNode).getChildren()\n adjacentNode = children[anchor.offset - 1] ?? null\n }\n } else {\n // LTR: right arrow = forward = next sibling\n if (anchor.type === 'text') {\n if (anchor.offset < node.getTextContent().length) return false\n adjacentNode = node.getNextSibling()\n } else {\n const children = (node as ElementNode).getChildren()\n adjacentNode = children[anchor.offset] ?? null\n }\n }\n\n if (adjacentNode && $isNonEditableNode(adjacentNode)) {\n const target = isRTL\n ? $findPrevEditable(adjacentNode)\n : $findNextEditable(adjacentNode)\n if (target) {\n selection.anchor.set(target.key, target.offset, target.type)\n selection.focus.set(target.key, target.offset, target.type)\n event.preventDefault()\n return true\n }\n return false\n }\n\n // Also handle: cursor is ON a non-editable node (e.g. after click)\n if ($isNonEditableNode(node)) {\n const target = isRTL\n ? $findPrevEditable(node)\n : $findNextEditable(node)\n if (target) {\n selection.anchor.set(target.key, target.offset, target.type)\n selection.focus.set(target.key, target.offset, target.type)\n event.preventDefault()\n return true\n }\n return false\n }\n\n return false\n },\n COMMAND_PRIORITY_HIGH,\n )\n\n // ── Left arrow: skip over non-editable nodes. ──\n // In RTL, left arrow moves forward (toward end), so check\n // the next sibling instead of the previous one.\n const unregLeft = editor.registerCommand(\n KEY_ARROW_LEFT_COMMAND,\n (event) => {\n const selection = $getSelection()\n if (!$isRangeSelection(selection) || !selection.isCollapsed())\n return false\n\n const isRTL = $isParentElementRTL(selection)\n const { anchor } = selection\n const node = anchor.getNode()\n\n // Check if the adjacent sibling (based on direction) is non-editable\n let adjacentNode: LexicalNode | null = null\n if (isRTL) {\n // RTL: left arrow = forward = next sibling\n if (anchor.type === 'text') {\n if (anchor.offset < node.getTextContent().length) return false\n adjacentNode = node.getNextSibling()\n } else {\n const children = (node as ElementNode).getChildren()\n adjacentNode = children[anchor.offset] ?? null\n }\n } else {\n // LTR: left arrow = backward = previous sibling\n if (anchor.type === 'text') {\n if (anchor.offset > 0) return false\n adjacentNode = node.getPreviousSibling()\n } else {\n const children = (node as ElementNode).getChildren()\n adjacentNode = children[anchor.offset - 1] ?? null\n }\n }\n\n if (adjacentNode && $isNonEditableNode(adjacentNode)) {\n const target = isRTL\n ? $findNextEditable(adjacentNode)\n : $findPrevEditable(adjacentNode)\n if (target) {\n selection.anchor.set(target.key, target.offset, target.type)\n selection.focus.set(target.key, target.offset, target.type)\n event.preventDefault()\n return true\n }\n return false\n }\n\n // Also handle: cursor is ON a non-editable node (e.g. after click)\n if ($isNonEditableNode(node)) {\n const target = isRTL\n ? $findNextEditable(node)\n : $findPrevEditable(node)\n if (target) {\n selection.anchor.set(target.key, target.offset, target.type)\n selection.focus.set(target.key, target.offset, target.type)\n event.preventDefault()\n return true\n }\n return false\n }\n\n return false\n },\n COMMAND_PRIORITY_HIGH,\n )\n\n // ── Selection change: catch mouse clicks/drags that land on non-editable nodes ──\n const unregSel = editor.registerCommand(\n SELECTION_CHANGE_COMMAND,\n () => {\n const selection = $getSelection()\n if (!$isRangeSelection(selection)) return false\n\n const anchorFix = $clampPointAwayFromNonEditable(selection.anchor)\n const focusFix = $clampPointAwayFromNonEditable(selection.focus)\n\n if (anchorFix) {\n selection.anchor.set(anchorFix.key, anchorFix.offset, anchorFix.type)\n }\n if (focusFix) {\n selection.focus.set(focusFix.key, focusFix.offset, focusFix.type)\n }\n\n return false\n },\n COMMAND_PRIORITY_HIGH,\n )\n\n return () => {\n unregRight()\n unregLeft()\n unregSel()\n }\n }, [editor])\n return null\n}\n","// ─── Types ────────────────────────────────────────────────────────────────────\n\nexport type QuoteType = 'single' | 'double'\n\nexport interface QuoteRange {\n /** Character index where the opening quote sits */\n start: number\n /** Character index where the closing quote sits – `null` when unclosed */\n end: number | null\n /** Which kind of quote */\n quoteType: QuoteType\n /** The text between the quotes (empty string when unclosed) */\n content: string\n /** Whether the quote pair is properly closed */\n closed: boolean\n}\n\n/**\n * Language-specific patterns whose trailing single-quote should NOT be treated\n * as a quote delimiter.\n *\n * Each value is an array of suffixes that, when found immediately before a `'`,\n * cause that apostrophe to be skipped.\n *\n * Example – `\"english\"` ships with common English contractions:\n * `don't`, `can't`, `won't`, `isn't`, `it's`, …\n */\nexport interface EscapePatterns {\n [language: string]: Array<string>\n}\n\nexport const BUILTIN_ESCAPE_PATTERNS: EscapePatterns = {\n english: [\n \"n't\", // don't, can't, won't, shouldn't, …\n \"'s\", // it's, he's, she's, …\n \"'re\", // they're, we're, you're, …\n \"'ve\", // I've, they've, we've, …\n \"'ll\", // I'll, you'll, they'll, …\n \"'m\", // I'm\n \"'d\", // I'd, they'd, …\n ],\n default: [],\n}\n\nexport interface DetectQuotesOptions {\n /**\n * When `true` (the default), enable contraction-aware escaping so that\n * apostrophes inside words like `don't` are not treated as quote delimiters.\n */\n escapeContractions?: boolean\n\n /**\n * Which set of escape patterns to apply.\n * Pass a key of `BUILTIN_ESCAPE_PATTERNS` (e.g. `\"english\"`) or supply\n * your own object conforming to `EscapePatterns`.\n *\n * @default \"english\"\n */\n escapePatterns?: string | EscapePatterns\n\n /**\n * When `true`, allow independent tracking of both quote types so they can\n * overlap (e.g. `\"text 'a b\" c'` produces two overlapping ranges).\n *\n * When `false` (the default), properly nested quotes are still detected,\n * but if an inner quote of the other type has not closed by the time the\n * outer quote closes, the inner pending quote is discarded to prevent\n * overlapping ranges.\n *\n * @default false\n */\n allowNesting?: boolean\n\n /**\n * When `true` (the default), quotes of the other type that open *and close*\n * inside an already-open quote are detected as separate ranges\n * (e.g. `'run away'` inside `\"she told me 'run away' before dawn\"`).\n *\n * When `false`, any quote character of the other type is treated as plain\n * text while an outer quote is open — no inner ranges are produced.\n *\n * Has no effect when `allowNesting` is `true` (everything is tracked\n * independently in that mode).\n *\n * @default true\n */\n detectInnerQuotes?: boolean\n}\n\n// ─── Helpers ──────────────────────────────────────────────────────────────────\n\n/**\n * Resolve the effective escape-suffix list from the options bag.\n */\nfunction resolveEscapeSuffixes(opts: DetectQuotesOptions): Array<string> {\n const patternsOpt = opts.escapePatterns ?? 'english'\n\n let patterns: EscapePatterns\n if (typeof patternsOpt === 'string') {\n patterns = BUILTIN_ESCAPE_PATTERNS\n return patterns[patternsOpt] ?? []\n }\n\n // Merge all provided language arrays into one flat list\n return Object.values(patternsOpt).flat()\n}\n\n/**\n * Returns `true` when the single-quote at `index` is part of a known\n * contraction / possessive pattern and should be skipped.\n */\nfunction isContractionApostrophe(\n text: string,\n index: number,\n suffixes: Array<string>,\n): boolean {\n for (const suffix of suffixes) {\n // The suffix already contains the apostrophe (e.g. \"n't\", \"'s\").\n // We need to figure out where the apostrophe sits inside `suffix`.\n const apostrophePositions: Array<number> = []\n for (let i = 0; i < suffix.length; i++) {\n if (suffix[i] === \"'\") apostrophePositions.push(i)\n }\n\n for (const ap of apostrophePositions) {\n // Start position of the full suffix in `text`\n const suffixStart = index - ap\n if (suffixStart < 0) continue\n const suffixEnd = suffixStart + suffix.length\n if (suffixEnd > text.length) continue\n\n const slice = text.slice(suffixStart, suffixEnd)\n if (slice !== suffix) continue\n\n // The character before the suffix must be a word character\n // (otherwise `'t` at the start of a string would match).\n if (suffixStart > 0 && /\\w/.test(text[suffixStart - 1])) {\n return true\n }\n }\n }\n return false\n}\n\n// ─── Main ─────────────────────────────────────────────────────────────────────\n\n/**\n * Scan `text` and return every quote range found.\n *\n * The returned `Map` is keyed by **both** the start and end position indices\n * of each quote, so you can look up a `QuoteRange` by either boundary.\n * For unclosed quotes only the start key is stored (end is `null`).\n *\n * ```ts\n * const result = detectQuotes(`She said \"hello\" and 'goodbye'`);\n * // Map {\n * // 10 => { start: 10, end: 16, quoteType: \"double\", content: \"hello\", closed: true },\n * // 16 => { start: 10, end: 16, quoteType: \"double\", content: \"hello\", closed: true },\n * // 22 => { start: 22, end: 30, quoteType: \"single\", content: \"goodbye\", closed: true },\n * // 30 => { start: 22, end: 30, quoteType: \"single\", content: \"goodbye\", closed: true },\n * // }\n * ```\n */\nexport function detectQuotes(\n text: string,\n options: DetectQuotesOptions = {},\n): Map<number, QuoteRange> {\n const {\n escapeContractions = true,\n allowNesting = false,\n detectInnerQuotes = true,\n } = options\n\n const escapeSuffixes = escapeContractions\n ? resolveEscapeSuffixes(options)\n : []\n\n const result = new Map<number, QuoteRange>()\n\n // We maintain a small stack so that nested quotes work correctly.\n // Only one level per quote type can be \"open\" at a time.\n let openSingle: { start: number } | null = null\n let openDouble: { start: number } | null = null\n\n for (let i = 0; i < text.length; i++) {\n const ch = text[i]\n\n // ── Handle backslash escapes (e.g. \\\" or \\') ──────────────────────────\n if (ch === '\\\\') {\n i++ // skip the escaped character\n continue\n }\n\n // ── Double quote ──────────────────────────────────────────────────────\n if (ch === '\"') {\n // When inner detection is off (and not in free-nesting mode),\n // skip entirely if we’re inside an open single quote.\n if (!allowNesting && !detectInnerQuotes && openSingle) continue\n\n if (openDouble) {\n // When nesting is off, discard any inner single quote that started\n // after this double quote opened — it would create an overlap.\n if (\n !allowNesting &&\n openSingle &&\n openSingle.start > openDouble.start\n ) {\n openSingle = null\n }\n\n // Close the current double-quoted range\n const range: QuoteRange = {\n start: openDouble.start,\n end: i,\n quoteType: 'double',\n content: text.slice(openDouble.start + 1, i),\n closed: true,\n }\n result.set(openDouble.start, range)\n result.set(i, range)\n openDouble = null\n } else {\n // Open a new double-quoted range\n openDouble = { start: i }\n }\n continue\n }\n\n // ── Single quote / apostrophe ─────────────────────────────────────────\n if (ch === \"'\") {\n // When inner detection is off (and not in free-nesting mode),\n // skip entirely if we’re inside an open double quote.\n if (!allowNesting && !detectInnerQuotes && openDouble) continue\n\n // Check contraction / possessive escaping BEFORE treating as quote\n if (\n escapeContractions &&\n escapeSuffixes.length > 0 &&\n isContractionApostrophe(text, i, escapeSuffixes)\n ) {\n continue // skip – this apostrophe belongs to a contraction\n }\n\n if (openSingle) {\n // When nesting is off, discard any inner double quote that started\n // after this single quote opened — it would create an overlap.\n if (\n !allowNesting &&\n openDouble &&\n openDouble.start > openSingle.start\n ) {\n openDouble = null\n }\n\n // Close the current single-quoted range\n const range: QuoteRange = {\n start: openSingle.start,\n end: i,\n quoteType: 'single',\n content: text.slice(openSingle.start + 1, i),\n closed: true,\n }\n result.set(openSingle.start, range)\n result.set(i, range)\n openSingle = null\n } else {\n // Open a new single-quoted range\n openSingle = { start: i }\n }\n continue\n }\n }\n\n // ── Record any unclosed quotes ────────────────────────────────────────────\n if (openSingle) {\n result.set(openSingle.start, {\n start: openSingle.start,\n end: null,\n quoteType: 'single',\n content: text.slice(openSingle.start + 1),\n closed: false,\n })\n }\n\n if (openDouble) {\n result.set(openDouble.start, {\n start: openDouble.start,\n end: null,\n quoteType: 'double',\n content: text.slice(openDouble.start + 1),\n closed: false,\n })\n }\n\n return result\n}\n","import type {\n HighlightSegment,\n IQuoteRule,\n ISpecialCharEntry,\n MooRule,\n RawRange,\n} from './types'\n\nimport { detectQuotes } from '@/utils/detect-quotes'\n\n// ─── Tag detection helpers ────────────────────────────────────────────────────\n\n/** Matches opening, closing, and self-closing HTML tags. */\nconst TAG_RE = /<(\\/?)([a-zA-Z][a-zA-Z0-9]*)\\b[^>]*?(\\/?)>/g\n\ninterface DetectedTag {\n start: number\n end: number\n tagName: string\n isClosing: boolean\n isSelfClosing: boolean\n originalText: string\n}\n\n/** Find all HTML tags, pair them using a stack, and assign sequential numbers.\n * `detectInner` (default true) means innermost closing tags are matched first\n * via the stack (LIFO). Unpaired tags are silently skipped. */\nfunction detectAndPairTags(\n text: string,\n _detectInner = true,\n): Array<{\n start: number\n end: number\n tagName: string\n tagNumber: number\n isClosing: boolean\n isSelfClosing: boolean\n originalText: string\n displayText: string\n}> {\n // 1. Collect all tags\n const allTags: Array<DetectedTag> = []\n TAG_RE.lastIndex = 0\n let m: RegExpExecArray | null\n while ((m = TAG_RE.exec(text)) !== null) {\n const isClosing = m[1] === '/'\n const isSelfClosing = m[3] === '/' || (!isClosing && m[0].endsWith('/>'))\n allTags.push({\n start: m.index,\n end: m.index + m[0].length,\n tagName: m[2].toLowerCase(),\n isClosing,\n isSelfClosing,\n originalText: m[0],\n })\n }\n\n // 2. Pair using a stack; assign sequential numbers\n let nextNum = 1\n const stack: Array<{ name: string; num: number; idx: number }> = []\n const result: Array<{\n start: number\n end: number\n tagName: string\n tagNumber: number\n isClosing: boolean\n isSelfClosing: boolean\n originalText: string\n displayText: string\n }> = []\n\n for (let i = 0; i < allTags.length; i++) {\n const tag = allTags[i]\n\n if (tag.isSelfClosing) {\n const num = nextNum++\n result.push({\n ...tag,\n tagNumber: num,\n displayText: `<${num}/>`,\n })\n } else if (!tag.isClosing) {\n // Opening tag — push with assigned number\n const num = nextNum++\n stack.push({ name: tag.tagName, num, idx: i })\n } else {\n // Closing tag — find matching opening on the stack (innermost first)\n let matchIdx = -1\n for (let j = stack.length - 1; j >= 0; j--) {\n if (stack[j].name === tag.tagName) {\n matchIdx = j\n break\n }\n }\n if (matchIdx >= 0) {\n const openEntry = stack[matchIdx]\n stack.splice(matchIdx, 1)\n const openTag = allTags[openEntry.idx]\n\n // Add both opening and closing tags to the result\n result.push({\n ...openTag,\n tagNumber: openEntry.num,\n displayText: `<${openEntry.num}>`,\n })\n result.push({\n ...tag,\n tagNumber: openEntry.num,\n displayText: `</${openEntry.num}>`,\n })\n }\n // Unpaired closing tags are silently skipped\n }\n }\n\n // Sort by start position\n result.sort((a, b) => a.start - b.start)\n return result\n}\n\n/** Regex to classify a match as an HTML tag (opening, closing, self-closing). */\nconst HTML_TAG_CLASSIFY = /^<(\\/?)([a-zA-Z][a-zA-Z0-9]*)\\b[^>]*?(\\/?)>$/\n\n/** Detect tags using a custom pattern. Matches that look like HTML tags\n * are paired using a stack (same logic as `detectAndPairTags`). All other\n * matches are treated as standalone placeholders. Everything is numbered\n * with a shared counter so collapsed display texts are unique. */\nfunction detectCustomTags(\n text: string,\n patternSource: string,\n): Array<{\n start: number\n end: number\n tagName: string\n tagNumber: number\n isClosing: boolean\n isSelfClosing: boolean\n originalText: string\n displayText: string\n}> {\n let re: RegExp\n try {\n re = new RegExp(patternSource, 'g')\n } catch {\n return []\n }\n\n // 1. Collect all matches and classify HTML-like ones\n interface RawMatch {\n start: number\n end: number\n text: string\n htmlName: string | null // non-null when the match is an HTML tag\n isClosing: boolean\n isSelfClosing: boolean\n }\n const matches: Array<RawMatch> = []\n let m: RegExpExecArray | null\n while ((m = re.exec(text)) !== null) {\n if (m[0].length === 0) {\n re.lastIndex++\n continue\n }\n const htmlMatch = HTML_TAG_CLASSIFY.exec(m[0])\n if (htmlMatch) {\n matches.push({\n start: m.index,\n end: m.index + m[0].length,\n text: m[0],\n htmlName: htmlMatch[2].toLowerCase(),\n isClosing: htmlMatch[1] === '/',\n isSelfClosing:\n htmlMatch[3] === '/' || (!htmlMatch[1] && m[0].endsWith('/>')),\n })\n } else {\n matches.push({\n start: m.index,\n end: m.index + m[0].length,\n text: m[0],\n htmlName: null,\n isClosing: false,\n isSelfClosing: false,\n })\n }\n }\n\n // 2. Pair HTML tags using a stack, assign numbers to everything\n type ResultItem = {\n start: number\n end: number\n tagName: string\n tagNumber: number\n isClosing: boolean\n isSelfClosing: boolean\n originalText: string\n displayText: string\n }\n let nextNum = 1\n const stack: Array<{ name: string; num: number; idx: number }> = []\n const result: Array<ResultItem> = []\n\n for (let i = 0; i < matches.length; i++) {\n const raw = matches[i]\n\n if (!raw.htmlName) {\n // Non-HTML placeholder — standalone\n const num = nextNum++\n result.push({\n start: raw.start,\n end: raw.end,\n tagName: raw.text,\n tagNumber: num,\n isClosing: false,\n isSelfClosing: false,\n originalText: raw.text,\n displayText: `<${num}>`,\n })\n continue\n }\n\n // HTML tag\n if (raw.isSelfClosing) {\n const num = nextNum++\n result.push({\n start: raw.start,\n end: raw.end,\n tagName: raw.htmlName,\n tagNumber: num,\n isClosing: false,\n isSelfClosing: true,\n originalText: raw.text,\n displayText: `<${num}/>`,\n })\n } else if (!raw.isClosing) {\n // Opening — push onto stack\n const num = nextNum++\n stack.push({ name: raw.htmlName, num, idx: i })\n } else {\n // Closing — find matching opening (innermost first)\n let matchIdx = -1\n for (let j = stack.length - 1; j >= 0; j--) {\n if (stack[j].name === raw.htmlName) {\n matchIdx = j\n break\n }\n }\n if (matchIdx >= 0) {\n const openEntry = stack[matchIdx]\n stack.splice(matchIdx, 1)\n const openRaw = matches[openEntry.idx]\n result.push({\n start: openRaw.start,\n end: openRaw.end,\n tagName: openRaw.htmlName!,\n tagNumber: openEntry.num,\n isClosing: false,\n isSelfClosing: false,\n originalText: openRaw.text,\n displayText: `<${openEntry.num}>`,\n })\n result.push({\n start: raw.start,\n end: raw.end,\n tagName: raw.htmlName,\n tagNumber: openEntry.num,\n isClosing: true,\n isSelfClosing: false,\n originalText: raw.text,\n displayText: `</${openEntry.num}>`,\n })\n }\n // Unpaired closing tags are silently skipped\n }\n }\n\n result.sort((a, b) => a.start - b.start)\n return result\n}\n\n// ─── Highlight computation (sweep-line with nesting) ──────────────────────────\n\nexport function computeHighlightSegments(\n text: string,\n rules: Array<MooRule>,\n): Array<HighlightSegment> {\n // 1. Collect all raw ranges from rules\n const rawRanges: Array<RawRange> = []\n\n for (const rule of rules) {\n if (rule.type === 'spellcheck') {\n for (const v of rule.validations) {\n if (v.start < 0 || v.start >= v.end || !v.content) continue\n\n // Primary: use the provided offsets if the text still matches.\n // Fallback: if the text has been edited (typing, paste, etc.)\n // and the offsets are stale, search for the content string\n // near the original position. This keeps highlights correct\n // even when the user types and shifts the text around.\n let matchStart = -1\n let matchEnd = -1\n\n if (v.end <= text.length && text.slice(v.start, v.end) === v.content) {\n matchStart = v.start\n matchEnd = v.end\n } else {\n // Search within a window around the original offset\n const searchRadius = Math.max(64, v.content.length * 4)\n const searchFrom = Math.max(0, v.start - searchRadius)\n const searchTo = Math.min(text.length, v.end + searchRadius)\n const regionLower = text.slice(searchFrom, searchTo).toLowerCase()\n const contentLower = v.content.toLowerCase()\n const idx = regionLower.indexOf(contentLower)\n if (idx !== -1) {\n matchStart = searchFrom + idx\n matchEnd = matchStart + v.content.length\n }\n }\n\n if (matchStart >= 0) {\n rawRanges.push({\n start: matchStart,\n end: matchEnd,\n annotation: {\n type: 'spellcheck',\n id: `sc-${matchStart}-${matchEnd}`,\n data: v,\n },\n })\n }\n }\n } else if (rule.type === 'glossary') {\n const { label, entries } = rule\n for (const entry of entries) {\n if (!entry.term && !entry.pattern) continue\n\n if (entry.pattern) {\n // Entry has a regex pattern — use it for matching\n let re: RegExp\n try {\n re = new RegExp(entry.pattern, 'g')\n } catch {\n continue // skip invalid regex\n }\n let m: RegExpExecArray | null\n while ((m = re.exec(text)) !== null) {\n rawRanges.push({\n start: m.index,\n end: m.index + m[0].length,\n annotation: {\n type: 'glossary',\n id: `gl-${label}-${m.index}-${m.index + m[0].length}`,\n data: {\n label,\n term: entry.term || entry.pattern,\n description: entry.description,\n },\n },\n })\n // Prevent infinite loop for zero-length matches\n if (m[0].length === 0) re.lastIndex++\n }\n } else {\n // Exact string match (case-sensitive)\n let idx = 0\n while ((idx = text.indexOf(entry.term, idx)) !== -1) {\n rawRanges.push({\n start: idx,\n end: idx + entry.term.length,\n annotation: {\n type: 'glossary',\n id: `gl-${label}-${idx}-${idx + entry.term.length}`,\n data: {\n label,\n term: entry.term,\n description: entry.description,\n },\n },\n })\n idx += entry.term.length\n }\n }\n }\n } else if (rule.type === 'tag') {\n const pairs = rule.pattern\n ? detectCustomTags(text, rule.pattern)\n : detectAndPairTags(text, rule.detectInner ?? true)\n for (const p of pairs) {\n // isHtml: a match is HTML when it came from detectAndPairTags (always\n // HTML), or from detectCustomTags where tagName !== originalText\n // (i.e. the classifier extracted an HTML tag name).\n const isHtml = !rule.pattern || p.tagName !== p.originalText\n rawRanges.push({\n start: p.start,\n end: p.end,\n annotation: {\n type: 'tag',\n id: `tag-${p.start}-${p.end}`,\n data: {\n tagNumber: p.tagNumber,\n tagName: p.tagName,\n isClosing: p.isClosing,\n isSelfClosing: p.isSelfClosing,\n originalText: p.originalText,\n displayText: p.displayText,\n isHtml,\n },\n },\n })\n }\n } else if (rule.type === 'quote') {\n // Quote detection: detect quotes and produce annotations for each\n // opening/closing quote character with the configured replacement.\n const quoteMap = detectQuotes(text, rule.detectOptions)\n const seen = new Set<number>()\n for (const [, qr] of quoteMap) {\n // The Map is keyed by BOTH start and end positions (both\n // pointing to the same QuoteRange). Deduplicate by qr.start\n // so each quote pair is processed exactly once.\n if (seen.has(qr.start)) continue\n seen.add(qr.start)\n\n const mapping =\n qr.quoteType === 'single' ? rule.singleQuote : rule.doubleQuote\n const originalChar = qr.quoteType === 'single' ? \"'\" : '\"'\n\n // Opening quote\n rawRanges.push({\n start: qr.start,\n end: qr.start + 1,\n annotation: {\n type: 'quote',\n id: `q-${qr.quoteType}-open-${qr.start}`,\n data: {\n quoteType: qr.quoteType,\n position: 'opening',\n originalChar,\n replacementChar: mapping.opening,\n },\n },\n })\n\n // Closing quote (only if the pair is closed)\n if (qr.closed && qr.end !== null) {\n rawRanges.push({\n start: qr.end,\n end: qr.end + 1,\n annotation: {\n type: 'quote',\n id: `q-${qr.quoteType}-close-${qr.end}`,\n data: {\n quoteType: qr.quoteType,\n position: 'closing',\n originalChar,\n replacementChar: mapping.closing,\n },\n },\n })\n }\n }\n } else if (rule.type === 'special-char') {\n const allEntries: Array<ISpecialCharEntry> = [...rule.entries]\n for (const entry of allEntries) {\n // Make a global copy of the pattern so we can iterate all matches\n const flags = entry.pattern.flags.includes('g')\n ? entry.pattern.flags\n : entry.pattern.flags + 'g'\n const re = new RegExp(entry.pattern.source, flags)\n let m: RegExpExecArray | null\n while ((m = re.exec(text)) !== null) {\n const matchStr = m[0]\n const cp = matchStr\n .split('')\n .map(\n (c) =>\n 'U+' +\n (c.codePointAt(0) ?? 0)\n .toString(16)\n .toUpperCase()\n .padStart(4, '0'),\n )\n .join(' ')\n rawRanges.push({\n start: m.index,\n end: m.index + matchStr.length,\n annotation: {\n type: 'special-char',\n id: `sp-${m.index}-${m.index + matchStr.length}`,\n data: { name: entry.name, char: matchStr, codePoint: cp },\n },\n })\n }\n }\n } else if (rule.type === 'link') {\n const defaultPattern = String.raw`https?:\\/\\/[^\\s<>\"']+|www\\.[^\\s<>\"']+`\n const patternSource = rule.pattern ?? defaultPattern\n let re: RegExp\n try {\n re = new RegExp(patternSource, 'gi')\n } catch {\n continue\n }\n let m: RegExpExecArray | null\n while ((m = re.exec(text)) !== null) {\n if (m[0].length === 0) {\n re.lastIndex++\n continue\n }\n // Trim trailing punctuation that's likely not part of the URL\n let matched = m[0]\n const trailingPunct = /[.,;:!?)}\\]]+$/\n const trailingMatch = trailingPunct.exec(matched)\n if (trailingMatch) {\n matched = matched.slice(0, -trailingMatch[0].length)\n }\n const end = m.index + matched.length\n // Normalize: www. → https://www.\n const url = matched.startsWith('www.') ? 'https://' + matched : matched\n rawRanges.push({\n start: m.index,\n end,\n annotation: {\n type: 'link',\n id: `link-${m.index}-${end}`,\n data: {\n url,\n displayText: matched,\n },\n },\n })\n }\n }\n }\n\n if (rawRanges.length === 0) return []\n\n // 2. When tags are collapsed they must be atomic — suppress ALL non-tag\n // ranges that overlap with a tag so the sweep-line never splits a tag\n // into sub-segments. When tags are expanded (or absent), keep every\n // range so search/glossary can highlight inside tags normally.\n //\n // Quote annotations that overlap with tag ranges are suppressed by\n // default because quote characters inside HTML attributes (e.g.\n // href=\"…\") are tag syntax, not text-level quotes. The quote rule's\n // `detectInTags` flag lets users opt-in to showing them.\n const tagRules = rules.filter((r) => r.type === 'tag')\n const tagsCollapsed = tagRules.some((r) => r.collapsed)\n const tagRanges = rawRanges.filter((r) => r.annotation.type === 'tag')\n const quoteDetectInTags = rules\n .filter((r): r is IQuoteRule => r.type === 'quote')\n .some((r) => r.detectInTags)\n\n let filteredRanges = rawRanges\n if (tagRanges.length > 0) {\n filteredRanges = rawRanges.filter((r) => {\n if (r.annotation.type === 'tag') return true\n // Suppress quotes inside tags unless detectInTags is enabled\n if (r.annotation.type === 'quote' && !quoteDetectInTags) {\n return !tagRanges.some((t) => r.start >= t.start && r.end <= t.end)\n }\n // Suppress everything else only when tags are collapsed\n if (tagsCollapsed) {\n return !tagRanges.some((t) => r.start < t.end && r.end > t.start)\n }\n return true\n })\n }\n\n // 3. Sweep-line: collect all unique boundary points\n const points = new Set<number>()\n for (const r of filteredRanges) {\n points.add(r.start)\n points.add(r.end)\n }\n const sortedPoints = [...points].sort((a, b) => a - b)\n\n // 4. For each pair of consecutive points, find all covering ranges\n // This naturally handles nesting: overlapping ranges produce segments\n // that carry ALL applicable annotations.\n const segments: Array<HighlightSegment> = []\n for (let i = 0; i < sortedPoints.length - 1; i++) {\n const segStart = sortedPoints[i]\n const segEnd = sortedPoints[i + 1]\n const annotations = filteredRanges\n .filter((r) => r.start <= segStart && r.end >= segEnd)\n .map((r) => r.annotation)\n if (annotations.length > 0) {\n segments.push({ start: segStart, end: segEnd, annotations })\n }\n }\n\n return segments\n}\n","import { $getRoot } from 'lexical'\n\nimport { NL_MARKER_PREFIX } from './constants'\nimport { $isHighlightNode } from './highlight-node'\nimport { $isMentionNode } from './mention-node'\n\nimport type { ElementNode } from 'lexical'\nimport type { HighlightNode } from './highlight-node'\n\n// ─── Helpers ──────────────────────────────────────────────────────────────────\n\n/** Returns true for highlight nodes whose DOM has contentEditable=\"false\":\n * collapsed tags and quote-char nodes. These must be skipped when\n * mapping global offsets to Lexical selection points. */\nfunction $isCEFalseToken(node: HighlightNode): boolean {\n const types = node.__highlightTypes.split(',')\n if (types.includes('tag-collapsed')) return true\n if (types.includes('quote') && node.__displayText) return true\n return false\n}\n\n// ─── Selection ↔ global-offset helpers ────────────────────────────────────────\n\n/** Map a Lexical selection point (nodeKey + offset) to a global char offset,\n * skipping NL-marker indicator nodes. */\nexport function $pointToGlobalOffset(nodeKey: string, offset: number): number {\n const root = $getRoot()\n const paragraphs = root.getChildren()\n let global = 0\n\n for (let pi = 0; pi < paragraphs.length; pi++) {\n if (pi > 0) global += 1 // \\n between paragraphs\n const p = paragraphs[pi]\n\n // Selection sits on the paragraph element itself (e.g. empty paragraph)\n if (p.getKey() === nodeKey) {\n if ('getChildren' in p) {\n const children = (p as ElementNode).getChildren()\n let childChars = 0\n for (let ci = 0; ci < Math.min(offset, children.length); ci++) {\n const child = children[ci]\n if (\n $isHighlightNode(child) &&\n child.__ruleIds.startsWith(NL_MARKER_PREFIX)\n )\n continue\n childChars += child.getTextContent().length\n }\n return global + childChars\n }\n return global\n }\n\n if (!('getChildren' in p)) continue\n for (const child of (p as ElementNode).getChildren()) {\n const isNlMarker =\n $isHighlightNode(child) && child.__ruleIds.startsWith(NL_MARKER_PREFIX)\n\n if (child.getKey() === nodeKey) {\n // If cursor landed on a NL-marker node, clamp to end-of-line\n // (= current global offset, which is past all real text in this para)\n if (isNlMarker) return global\n\n // Token mode nodes (collapsed tags, quotes, special-chars) may\n // have a DOM text (e.g. ZWS, 1 char) that differs from the model\n // text (e.g. `<b>`, 3 chars). Map DOM offsets:\n // offset 0 → before token → global + 0\n // offset >0 → after token → global + full model length\n if ($isHighlightNode(child) && child.getMode() === 'token') {\n return global + (offset > 0 ? child.getTextContent().length : 0)\n }\n\n // MentionNode is a CEF token — cursor at 0 means before, >0 means after.\n if ($isMentionNode(child)) {\n return global + (offset > 0 ? child.getTextContent().length : 0)\n }\n\n return global + offset\n }\n\n if (isNlMarker) continue\n global += child.getTextContent().length\n }\n }\n return global\n}\n\n/** Map a global character offset back to a Lexical node key + offset. */\nexport function $globalOffsetToPoint(\n target: number,\n): { key: string; offset: number; type: 'text' | 'element' } | null {\n const root = $getRoot()\n const paragraphs = root.getChildren()\n let remaining = target\n\n for (let pi = 0; pi < paragraphs.length; pi++) {\n if (pi > 0) {\n if (remaining <= 0) {\n const p = paragraphs[pi]\n if ('getChildren' in p) {\n for (const child of (p as ElementNode).getChildren()) {\n if (\n $isHighlightNode(child) &&\n child.__ruleIds.startsWith(NL_MARKER_PREFIX)\n )\n continue\n return { key: child.getKey(), offset: 0, type: 'text' }\n }\n }\n return { key: paragraphs[pi].getKey(), offset: 0, type: 'element' }\n }\n remaining -= 1 // \\n\n }\n\n const p = paragraphs[pi]\n if (!('getChildren' in p)) continue\n\n const allChildren = (p as ElementNode).getChildren()\n\n for (let ci = 0; ci < allChildren.length; ci++) {\n const child = allChildren[ci]\n if (\n $isHighlightNode(child) &&\n child.__ruleIds.startsWith(NL_MARKER_PREFIX)\n )\n continue\n const len = child.getTextContent().length\n\n // CE=false token: cursor cannot land on it.\n // If remaining ≤ 0, the target is BEFORE this node —\n // return an element-type position at this child's index.\n if ($isHighlightNode(child) && $isCEFalseToken(child)) {\n if (remaining <= 0) {\n return { key: p.getKey(), offset: ci, type: 'element' }\n }\n remaining -= len\n continue\n }\n\n // MentionNode is CE=false token — cursor cannot enter it.\n if ($isMentionNode(child)) {\n if (remaining <= 0) {\n return { key: p.getKey(), offset: ci, type: 'element' }\n }\n remaining -= len\n continue\n }\n\n // Editable token nodes (special-chars): cursor can sit at 0 or len.\n if ($isHighlightNode(child) && child.getMode() === 'token') {\n if (remaining > len) {\n remaining -= len\n continue\n }\n return {\n key: child.getKey(),\n offset:\n remaining <= 0\n ? 0\n : remaining >= len\n ? len\n : remaining <= len / 2\n ? 0\n : len,\n type: 'text',\n }\n }\n\n // Regular text node\n if (remaining <= len) {\n return {\n key: child.getKey(),\n offset: Math.max(0, remaining),\n type: 'text',\n }\n }\n remaining -= len\n }\n\n // After all children: remaining ≤ 0 means the target falls within\n // this paragraph. Return element position after the last real child\n // (just before NL markers, if any).\n if (remaining <= 0) {\n let afterIdx = allChildren.length\n for (let ci = allChildren.length - 1; ci >= 0; ci--) {\n const c = allChildren[ci]\n if ($isHighlightNode(c) && c.__ruleIds.startsWith(NL_MARKER_PREFIX)) {\n afterIdx = ci\n } else {\n break\n }\n }\n return { key: p.getKey(), offset: afterIdx, type: 'element' }\n }\n }\n\n // Fallback: end of last paragraph\n for (let pi = paragraphs.length - 1; pi >= 0; pi--) {\n const p = paragraphs[pi]\n if ('getChildren' in p) {\n const allChildren = (p as ElementNode).getChildren()\n // Try to find last editable child\n for (let ci = allChildren.length - 1; ci >= 0; ci--) {\n const child = allChildren[ci]\n if (\n $isHighlightNode(child) &&\n child.__ruleIds.startsWith(NL_MARKER_PREFIX)\n )\n continue\n if ($isHighlightNode(child) && $isCEFalseToken(child)) continue\n if ($isMentionNode(child)) continue\n return {\n key: child.getKey(),\n offset: child.getTextContent().length,\n type: 'text',\n }\n }\n // All CE=false: element position after last real child\n let afterIdx = allChildren.length\n for (let ci = allChildren.length - 1; ci >= 0; ci--) {\n const c = allChildren[ci]\n if ($isHighlightNode(c) && c.__ruleIds.startsWith(NL_MARKER_PREFIX)) {\n afterIdx = ci\n } else {\n break\n }\n }\n return { key: p.getKey(), offset: afterIdx, type: 'element' }\n }\n }\n\n return null\n}\n","import * as React from 'react'\nimport { useLayoutEffect, useRef } from 'react'\nimport { createPopper } from '@popperjs/core'\n\nimport { getEffectiveCodepointMap } from './constants'\n\nimport type { Instance as PopperInstance } from '@popperjs/core'\nimport type {\n ISpellCheckValidation,\n PopoverContentRenderer,\n PopoverState,\n RuleAnnotation,\n} from './types'\n\n// ─── Popover Content Components ──────────────────────────────────────────────\n\nfunction SpellCheckPopoverContent({\n data,\n onSuggestionClick,\n}: {\n data: ISpellCheckValidation\n onSuggestionClick: (suggestion: string) => void\n}) {\n return (\n <div className=\"space-y-2.5 p-3 max-w-sm\">\n <div className=\"flex items-center gap-2\">\n <span className=\"cat-badge cat-badge-spell\">\n {data.shortMessage || 'Spelling'}\n </span>\n <span className=\"text-[11px] text-muted-foreground\">\n {data.categoryId}\n </span>\n </div>\n <p className=\"text-sm leading-relaxed text-foreground\">{data.message}</p>\n {data.content && (\n <p className=\"text-xs text-muted-foreground\">\n Found:{' '}\n <code className=\"rounded bg-muted px-1 py-0.5 font-mono text-destructive-foreground\">\n {data.content}\n </code>\n </p>\n )}\n {data.suggestions.length > 0 && (\n <div className=\"space-y-1.5\">\n <p className=\"text-xs font-medium text-muted-foreground\">\n Suggestions:\n </p>\n <div className=\"flex flex-wrap gap-1\">\n {data.suggestions.map((s, i) => (\n <button\n key={i}\n type=\"button\"\n className=\"cat-suggestion-btn\"\n onClick={() => onSuggestionClick(s.value)}\n >\n {s.value}\n </button>\n ))}\n </div>\n </div>\n )}\n {data.dictionaries && data.dictionaries.length > 0 && (\n <p className=\"text-[11px] text-muted-foreground\">\n Dictionaries: {data.dictionaries.join(', ')}\n </p>\n )}\n </div>\n )\n}\n\nfunction KeywordsPopoverContent({\n data,\n}: {\n data: { label: string; term: string; description?: string }\n}) {\n const displayLabel = data.label\n .split('-')\n .map((w) => w.charAt(0).toUpperCase() + w.slice(1))\n .join(' ')\n\n return (\n <div className=\"p-3 max-w-xs space-y-2\">\n <span\n className={`cat-badge cat-badge-glossary cat-badge-glossary-${data.label}`}\n >\n {displayLabel}\n </span>\n <p className=\"text-sm leading-relaxed text-foreground\">\n Term:{' '}\n <strong className=\"font-semibold text-foreground\">{data.term}</strong>\n </p>\n {data.description && (\n <p className=\"text-xs text-muted-foreground leading-relaxed\">\n {data.description}\n </p>\n )}\n </div>\n )\n}\n\nfunction SpecialCharPopoverContent({\n data,\n}: {\n data: { name: string; char: string; codePoint: string }\n}) {\n const cp = data.char.codePointAt(0) ?? 0\n const effectiveMap = getEffectiveCodepointMap()\n const displaySymbol =\n effectiveMap[cp] ?? (data.char.trim() === '' ? '·' : data.char)\n\n return (\n <div className=\"p-3 max-w-xs space-y-3\">\n <span className=\"cat-badge cat-badge-special-char\">Special Char</span>\n\n {/* Large centered visual symbol */}\n <div className=\"flex items-center justify-center\">\n <span className=\"inline-flex items-center justify-center min-w-12 min-h-12 rounded-lg border-2 border-border bg-muted px-3 py-2 text-2xl font-bold font-mono text-foreground select-none\">\n {displaySymbol}\n </span>\n </div>\n\n <p className=\"text-sm leading-relaxed text-foreground text-center\">\n <strong className=\"font-semibold\">{data.name}</strong>\n </p>\n <div className=\"flex items-center justify-center gap-3 text-xs text-muted-foreground\">\n <code className=\"rounded bg-muted px-1.5 py-0.5 font-mono\">\n {data.codePoint}\n </code>\n </div>\n </div>\n )\n}\n\nfunction TagPopoverContent({\n data,\n}: {\n data: {\n tagNumber: number\n tagName: string\n isClosing: boolean\n isSelfClosing: boolean\n originalText: string\n displayText: string\n }\n}) {\n // Non-HTML placeholders: tagName === originalText (the full match)\n const isPlaceholder =\n !data.isClosing && !data.isSelfClosing && data.tagName === data.originalText\n\n return (\n <div className=\"p-3 max-w-xs space-y-2\">\n <span className=\"cat-badge cat-badge-tag\">\n {isPlaceholder ? 'Placeholder' : 'Tag'} #{data.tagNumber}\n </span>\n <p className=\"text-sm leading-relaxed text-foreground\">\n {isPlaceholder\n ? 'Placeholder'\n : data.isClosing\n ? 'Closing tag'\n : data.isSelfClosing\n ? 'Self-closing tag'\n : 'Opening tag'}\n :{' '}\n <strong className=\"font-semibold text-foreground\">\n {data.originalText}\n </strong>\n </p>\n <p className=\"text-xs text-muted-foreground\">\n Collapsed:{' '}\n <code className=\"rounded bg-muted px-1 py-0.5 font-mono\">\n {data.displayText}\n </code>\n </p>\n <p className=\"text-xs text-muted-foreground break-all\">\n Original:{' '}\n <code className=\"rounded bg-muted px-1 py-0.5 font-mono\">\n {data.originalText}\n </code>\n </p>\n </div>\n )\n}\n\nfunction LinkPopoverContent({\n data,\n onOpen,\n}: {\n data: { url: string; displayText: string }\n onOpen: () => void\n}) {\n return (\n <div className=\"p-3 max-w-xs space-y-2\">\n <span className=\"cat-badge cat-badge-link\">Link</span>\n <p className=\"text-sm leading-relaxed text-foreground break-all\">\n <code className=\"rounded bg-muted px-1 py-0.5 font-mono text-xs\">\n {data.url}\n </code>\n </p>\n <button type=\"button\" className=\"cat-suggestion-btn\" onClick={onOpen}>\n Open link ↗\n </button>\n </div>\n )\n}\n\nfunction QuotePopoverContent({\n data,\n}: {\n data: {\n quoteType: 'single' | 'double'\n position: 'opening' | 'closing'\n originalChar: string\n replacementChar: string\n }\n}) {\n return (\n <div className=\"p-3 max-w-xs space-y-2\">\n <span\n className={`cat-badge cat-badge-quote cat-badge-quote-${data.quoteType}`}\n >\n {data.quoteType === 'single' ? 'Single Quote' : 'Double Quote'}\n </span>\n <p className=\"text-sm leading-relaxed text-foreground\">\n {data.position === 'opening' ? 'Opening' : 'Closing'} quote\n </p>\n <div className=\"flex items-center gap-2 text-xs text-muted-foreground\">\n <code className=\"rounded bg-muted px-1.5 py-0.5 font-mono\">\n {data.originalChar}\n </code>\n <span>→</span>\n <code className=\"rounded bg-muted px-1.5 py-0.5 font-mono\">\n {data.replacementChar}\n </code>\n </div>\n </div>\n )\n}\n\n// ─── Main Popover Wrapper ────────────────────────────────────────────────────\n\nexport function HighlightPopover({\n state,\n annotationMap,\n onSuggestionClick,\n onLinkOpen,\n onDismiss,\n onPopoverEnter,\n renderPopoverContent,\n dir,\n}: {\n state: PopoverState\n annotationMap: Map<string, RuleAnnotation>\n onSuggestionClick: (suggestion: string, ruleId: string) => void\n onLinkOpen?: (url: string) => void\n onDismiss: () => void\n onPopoverEnter: () => void\n renderPopoverContent?: PopoverContentRenderer\n /** Text direction for the popover container. */\n dir?: 'ltr' | 'rtl' | 'auto'\n}) {\n const popoverRef = useRef<HTMLDivElement>(null)\n const popperRef = useRef<PopperInstance | null>(null)\n\n // Manage the Popper instance imperatively inside useLayoutEffect so that\n // positioning is applied to the DOM BEFORE the browser paints —\n // eliminating the \"flash at (0,0)\" entirely.\n useLayoutEffect(() => {\n const el = popoverRef.current\n if (!el) {\n // Popover not in DOM (hidden via early return below)\n popperRef.current?.destroy()\n popperRef.current = null\n return\n }\n\n // Use the full anchor rect when available so that Popper's flip/shift\n // modifiers respect the real element bounds and never overlap the target.\n const ar = state.anchorRect\n const virtualEl = {\n getBoundingClientRect: (): DOMRect =>\n ({\n top: ar?.top ?? state.y,\n left: ar?.left ?? state.x,\n bottom: ar?.bottom ?? state.y,\n right: ar?.right ?? state.x,\n width: ar?.width ?? 0,\n height: ar?.height ?? 0,\n x: ar?.left ?? state.x,\n y: ar?.top ?? state.y,\n toJSON: () => {},\n }) as DOMRect,\n }\n\n // Hide before (re-)positioning so the old position doesn't flash\n el.style.visibility = 'hidden'\n\n if (popperRef.current) {\n // Reuse existing instance — just update the virtual reference\n popperRef.current.state.elements.reference =\n virtualEl as unknown as Element\n } else {\n popperRef.current = createPopper(virtualEl as unknown as Element, el, {\n strategy: 'fixed',\n placement: 'bottom-start',\n modifiers: [\n { name: 'offset', options: { offset: [0, 6] } },\n { name: 'preventOverflow', options: { padding: 16 } },\n { name: 'flip', options: { padding: 16 } },\n ],\n })\n }\n\n // forceUpdate() is SYNCHRONOUS — runs all Popper modifiers (including\n // applyStyles) immediately, so styles are written to the DOM before we\n // make the element visible.\n popperRef.current.forceUpdate()\n el.style.visibility = ''\n }, [state.visible, state.x, state.y, state.anchorRect])\n\n // Destroy popper on unmount\n useLayoutEffect(() => {\n return () => {\n popperRef.current?.destroy()\n popperRef.current = null\n }\n }, [])\n\n if (!state.visible) return null\n\n const annotations = state.ruleIds\n .map((id) => annotationMap.get(id))\n .filter((a): a is RuleAnnotation => a != null)\n\n if (annotations.length === 0) return null\n\n return (\n <div\n ref={popoverRef}\n className=\"cat-popover\"\n dir={dir}\n style={{\n position: 'fixed',\n left: 0,\n top: 0,\n zIndex: 1000,\n visibility: 'hidden',\n }}\n onMouseEnter={() => onPopoverEnter()}\n onMouseLeave={() => onDismiss()}\n >\n {annotations.map((ann, i) => {\n const custom = renderPopoverContent?.({\n annotation: ann,\n onSuggestionClick: (s) => onSuggestionClick(s, ann.id),\n })\n\n return (\n <React.Fragment key={ann.id}>\n {i > 0 && <hr className=\"border-border my-0\" />}\n {custom != null ? (\n custom\n ) : ann.type === 'spellcheck' ? (\n <SpellCheckPopoverContent\n data={ann.data}\n onSuggestionClick={(s) => onSuggestionClick(s, ann.id)}\n />\n ) : ann.type === 'glossary' ? (\n <KeywordsPopoverContent data={ann.data} />\n ) : ann.type === 'tag' ? (\n <TagPopoverContent data={ann.data} />\n ) : ann.type === 'quote' ? (\n <QuotePopoverContent data={ann.data} />\n ) : ann.type === 'link' ? (\n <LinkPopoverContent\n data={ann.data}\n onOpen={() => {\n if (onLinkOpen) {\n onLinkOpen(ann.data.url)\n } else {\n window.open(ann.data.url, '_blank', 'noopener,noreferrer')\n }\n }}\n />\n ) : (\n <SpecialCharPopoverContent data={ann.data} />\n )}\n </React.Fragment>\n )\n })}\n </div>\n )\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,YAAuB;AACvB,mBAOO;AACP,qBAAwB;AACxB,qBAAwB;AACxB,kBAAwB;AACxB,kBAOO;AACP,sBAKO;AACP,uBAGO;AACP,2BAAsD;AACtD,0BAAoD;;;AC/BpD,kBAAsC;AACtC,4BAAwB;AAEjB,SAAS,MAAM,QAAsB;AAC1C,aAAO,mCAAQ,kBAAK,MAAM,CAAC;AAC7B;;;AD2QI;AAlGJ,SAAS,YAAY,MAA6C;AAChE,MAAI,SAAS,UAAa,SAAS,KAAM,QAAO;AAChD,MAAI,OAAO,SAAS,WAAY,QAAQ,KAA+B;AACvE,SAAO;AACT;AAIA,SAAS,eAAe,SAAyC;AAC/D,QAAM,SAAyB,CAAC;AAChC,aAAW,OAAO,SAAS;AACzB,QAAI,IAAI,YAAY,IAAI,SAAS,SAAS,GAAG;AAC3C,aAAO,KAAK,GAAG,eAAe,IAAI,QAAQ,CAAC;AAAA,IAC7C,OAAO;AACL,aAAO,KAAK,GAAG;AAAA,IACjB;AAAA,EACF;AACA,SAAO;AACT;AAcA,SAAS,iBAAiB,SAA4C;AACpE,QAAM,OAA0B,CAAC;AACjC,aAAW,OAAO,SAAS;AACzB,QAAI,IAAI,YAAY,IAAI,SAAS,SAAS,GAAG;AAC3C,WAAK,KAAK;AAAA,QACR,MAAM;AAAA,QACN,OAAO,IAAI;AAAA,QACX,YAAY,IAAI;AAAA,MAClB,CAAC;AACD,iBAAW,SAAS,IAAI,UAAU;AAChC,aAAK,KAAK,EAAE,MAAM,UAAU,QAAQ,OAAO,YAAY,IAAI,MAAM,CAAC;AAAA,MACpE;AAAA,IACF,OAAO;AACL,WAAK,KAAK,EAAE,MAAM,UAAU,QAAQ,IAAI,CAAC;AAAA,IAC3C;AAAA,EACF;AACA,SAAO;AACT;AAGA,SAAS,UAAU,SAAkC;AACnD,SAAO,QAAQ,KAAK,CAAC,MAAM,EAAE,YAAY,EAAE,SAAS,SAAS,CAAC;AAChE;AAGA,SAAS,SAAS,GAAY,GAAY;AACxC,SAAO,EAAE,UAAU,EAAE;AACvB;AAEA,SAAS,WAAW,QAAiB,OAAwC;AAC3E,MAAI,CAAC,MAAO,QAAO;AACnB,MAAI,MAAM,QAAQ,KAAK,EAAG,QAAO,MAAM,KAAK,CAAC,MAAM,SAAS,GAAG,MAAM,CAAC;AACtE,SAAO,SAAS,OAAO,MAAM;AAC/B;AAYA,SAAS,aAAa,EAAE,IAAI,UAAU,SAAS,GAAsB;AACnE,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,QAAI,6BAAY,EAAE,IAAI,SAAS,CAAC;AAEhC,QAAM,QAA6B;AAAA,IACjC,WAAW,YACP,eAAe,UAAU,CAAC,OAAO,UAAU,CAAC,WAC5C;AAAA,IACJ;AAAA,IACA,SAAS,aAAa,MAAM;AAAA,IAC5B,UAAU;AAAA,IACV,QAAQ,aAAa,KAAK;AAAA,EAC5B;AAEA,SACE,6CAAC,SAAI,KAAK,YAAY,OAAc,WAAU,qBAC3C;AAAA,KAAC,YACA;AAAA,MAAC;AAAA;AAAA,QACC,MAAK;AAAA,QACL,WAAU;AAAA,QACT,GAAG;AAAA,QACH,GAAG;AAAA,QAEJ,sDAAC,oCAAa,WAAU,YAAW;AAAA;AAAA,IACrC;AAAA,IAEF,4CAAC,SAAI,WAAU,kBAAkB,UAAS;AAAA,KAC5C;AAEJ;AAMA,SAAS,aAAa;AAAA,EACpB;AAAA,EACA;AACF,GAGG;AACD,MAAI,CAAC,QAAS,QAAO;AACrB,SACE,6CAAC,uBAAQ,MAAR,EACC;AAAA,gDAAC,uBAAQ,SAAR,EAAgB,QAAQ,UAAU;AAAA,IACnC,4CAAC,uBAAQ,QAAR,EACC,sDAAC,uBAAQ,YAAR,EAAmB,YAAY,GAC9B,sDAAC,uBAAQ,OAAR,EAAc,WAAU,0EACtB,mBACH,GACF,GACF;AAAA,KACF;AAEJ;AAgBA,SAAS,KAAK;AAAA,EACZ;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAc;AACZ,SACE;AAAA,IAAC;AAAA;AAAA,MACC,qBAAmB,WAAW;AAAA,MAC9B,WAAW;AAAA,QACT;AAAA,QACA,YAAY;AAAA,QACZ;AAAA,MACF;AAAA,MAEC;AAAA,eAAO,QACN,4CAAC,UAAK,WAAU,6CACb,sBAAY,OAAO,IAAI,GAC1B;AAAA,QAEF,4CAAC,UAAK,WAAU,YAAY,iBAAO,OAAM;AAAA,QACxC,CAAC,YAAY,CAAC,YAAY,YACzB;AAAA,UAAC;AAAA;AAAA,YACC,MAAK;AAAA,YACL,WAAU;AAAA,YACV,SAAS,CAAC,MAAM;AACd,gBAAE,gBAAgB;AAClB,uBAAS;AAAA,YACX;AAAA,YACA,UAAU;AAAA,YACV,cAAY,UAAU,OAAO,KAAK;AAAA,YAElC,sDAAC,yBAAE,WAAU,UAAS;AAAA;AAAA,QACxB;AAAA;AAAA;AAAA,EAEJ;AAEJ;AAMA,SAAS,cAAc;AAAA,EACrB;AAAA,EACA;AACF,GAGG;AACD,MAAI,MAAM,WAAW,EAAG,QAAO;AAC/B,SACE,6CAAC,uBAAQ,MAAR,EACC;AAAA;AAAA,MAAC,uBAAQ;AAAA,MAAR;AAAA,QACC,QACE,6CAAC,UAAK,WAAU,sIAAqI;AAAA;AAAA,UACjJ,MAAM;AAAA,WACV;AAAA;AAAA,IAEJ;AAAA,IACA,4CAAC,uBAAQ,QAAR,EACC,sDAAC,uBAAQ,YAAR,EAAmB,YAAY,GAC9B,sDAAC,uBAAQ,OAAR,EAAc,WAAU,iFACvB,sDAAC,SAAI,WAAU,wBACZ,gBAAM,IAAI,CAAC,SACV;AAAA,MAAC;AAAA;AAAA,QAEC,WAAU;AAAA,QAET;AAAA,eAAK,QACJ,4CAAC,UAAK,WAAU,6CACb,sBAAY,KAAK,IAAI,GACxB;AAAA,UAEF,4CAAC,UAAK,WAAU,YAAY,eAAK,OAAM;AAAA,UACtC,YACC;AAAA,YAAC;AAAA;AAAA,cACC,MAAK;AAAA,cACL,WAAU;AAAA,cACV,SAAS,CAAC,MAAM;AACd,kBAAE,gBAAgB;AAClB,yBAAS,IAAI;AAAA,cACf;AAAA,cACA,UAAU;AAAA,cACV,cAAY,UAAU,KAAK,KAAK;AAAA,cAEhC,sDAAC,yBAAE,WAAU,UAAS;AAAA;AAAA,UACxB;AAAA;AAAA;AAAA,MArBG,KAAK;AAAA,IAuBZ,CACD,GACH,GACF,GACF,GACF;AAAA,KACF;AAEJ;AAMA,SAAS,qBAAqB;AAAA,EAC5B;AAAA,EACA;AACF,GAGG;AACD,MAAI,CAAC,OAAO;AACV,WAAO,4CAAC,UAAK,WAAU,kCAAkC,uBAAY;AAAA,EACvE;AACA,SACE,6CAAC,UAAK,WAAU,oCACb;AAAA,UAAM,QACL,4CAAC,UAAK,WAAU,6CACb,sBAAY,MAAM,IAAI,GACzB;AAAA,IAEF,4CAAC,UAAK,WAAU,YAAY,gBAAM,OAAM;AAAA,KAC1C;AAEJ;AAEA,SAAS,uBAAuB;AAAA,EAC9B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAQG;AAGD,QAAM,eAAe;AAErB,QAAM,iBAAa,qBAAuB,IAAI;AAC9C,QAAM,iBAAa,qBAAuB,IAAI;AAC9C,QAAM,CAAC,cAAc,eAAe,QAAI,uBAAS,MAAM,MAAM;AAC7D,QAAM,CAAC,eAAe,gBAAgB,QAAI,uBAAS,KAAK;AACxD,QAAM,CAAC,UAAU,WAAW,QAAI,uBAAS,KAAK;AAI9C,QAAM,eAAe,KAAK,IAAI,MAAM,QAAQ,EAAE;AAE9C,QAAM,YAAkB,qBAAe,MAAM;AAC3C,UAAM,mBAAmB,WAAW;AACpC,QAAI,CAAC,iBAAkB;AAEvB,UAAM,WAAW,MAAM,KAAK,iBAAiB,QAAQ;AACrD,UAAM,iBAAiB,iBAAiB,sBAAsB,EAAE;AAChE,UAAM,MAAM,WAAW,iBAAiB,gBAAgB,EAAE,SAAS,KAAK;AAKxE,UAAM,qBAAqB,CAAC,kBAC1B,gBAAgB,IAAI,KAAK,IAAI,OAAO,aAAa,EAAE,SAAS;AAC9D,QAAI,QAAQ;AACZ,QAAI,UAAU;AAEd,eAAW,SAAS,UAAU;AAC5B,YAAM,aAAa,MAAM,sBAAsB,EAAE;AACjD,YAAM,WAAW,MAAM,UAAU,QAAQ;AACzC,YAAM,UACJ,WAAW,IAAI,KAAK,IAAI,IAAI,mBAAmB,QAAQ,CAAC,IAAI;AAC9D,UAAI,aAAa,WAAW,gBAAgB;AAC1C;AAAA,MACF,OAAO;AACL;AAAA,MACF;AAAA,IACF;AAEA,YAAQ,KAAK,IAAI,GAAG,KAAK;AAIzB,QAAI,QAAQ,MAAM,UAAU,SAAS,UAAU,OAAO;AACpD,YAAM,YAAY,SAAS,QAAQ,CAAC,EAAE,sBAAsB,EAAE;AAE9D,YAAM,aAAa,QAAQ,IAAI,MAAM;AACrC,YAAM,eAAe,aACjB,KAAK,IAAI,IAAI,mBAAmB,MAAM,SAAS,QAAQ,CAAC,CAAC,IACzD;AACJ,YAAM,kBAAkB,iBAAiB,eAAe,YAAY;AACpE,YAAM,WAAW,MAAM,KAAK;AAG5B,UAAI,kBAAkB;AACtB,UAAI,SAAS,QAAQ,SAAS,SAAS,OAAO;AAC5C,cAAM,cAAc,SAAS,KAAK,EAAE;AACpC,YAAI,aAAa;AACf,6BAAmB,YAAY,sBAAsB,EAAE,QAAQ;AAAA,QACjE;AAAA,MACF;AACA,UAAI,mBAAmB,iBAAiB;AACtC;AACA,kBAAU;AAAA,MACZ;AAAA,IACF;AAEA,oBAAgB,KAAK;AACrB,qBAAiB,OAAO;AACxB,gBAAY,IAAI;AAAA,EAClB,CAAC;AAED,oCAAgB,MAAM;AACpB,QAAI,aAAa,MAAM,WAAW,GAAG;AACnC,sBAAgB,MAAM,MAAM;AAC5B,uBAAiB,KAAK;AACtB,kBAAY,IAAI;AAChB;AAAA,IACF;AAEA,UAAM,UAAU,WAAW;AAC3B,UAAM,YAAY,WAAW;AAC7B,QAAI,CAAC,WAAW,CAAC,UAAW;AAG5B,cAAU;AAMV,UAAM,WAAW,IAAI,eAAe,SAAS;AAC7C,aAAS,QAAQ,OAAO;AACxB,WAAO,MAAM,SAAS,WAAW;AAAA,EACnC,GAAG,CAAC,WAAW,KAAK,CAAC;AAErB,MAAI,MAAM,WAAW,GAAG;AACtB,WAAO,4CAAC,UAAK,WAAU,kCAAkC,uBAAY;AAAA,EACvE;AAIA,QAAM,eAAe,CAAC,aACpB;AAAA,IAAC;AAAA;AAAA,MACC,KAAK;AAAA,MACL,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MACA,eAAW;AAAA,MAEV,gBAAM,MAAM,GAAG,YAAY,EAAE,IAAI,CAAC,QACjC;AAAA,QAAC;AAAA;AAAA,UAEC,QAAQ;AAAA,UACR,UAAU,WAAW,MAAM;AAAA,UAAC,IAAI;AAAA,UAChC;AAAA,UACA;AAAA,UACA,WAAU;AAAA;AAAA,QALL,IAAI;AAAA,MAMX,CACD;AAAA;AAAA,EACH;AAIF,QAAM,cAAc,aAAa;AACjC,MAAI,CAAC,aAAa;AAChB,WACE;AAAA,MAAC;AAAA;AAAA,QACC,KAAK;AAAA,QACL,WAAW,GAAG,2CAA2C,YAAY;AAAA,QAEpE;AAAA;AAAA,UACD,6CAAC,UAAK,WAAU,kCACb;AAAA,kBAAM;AAAA,YAAO;AAAA,aAChB;AAAA;AAAA;AAAA,IACF;AAAA,EAEJ;AAGA,MAAI,aAAa,YAAY,MAAM,SAAS;AAC5C,QAAM,mBAAmB,CAAC,aAAa,oBAAoB;AAC3D,MAAI,kBAAkB;AACpB,iBAAa,KAAK,IAAI,cAAc,eAAe;AAAA,EACrD;AACA,eAAa,KAAK,IAAI,GAAG,UAAU;AAEnC,QAAM,cAAc,aAAa,MAAM;AACvC,QAAM,YAAY,MAAM,MAAM,GAAG,UAAU;AAC3C,QAAM,gBAAgB,MAAM,MAAM,UAAU;AAE5C,SACE;AAAA,IAAC;AAAA;AAAA,MACC,KAAK;AAAA,MACL,WAAW,GAAG,2BAA2B,YAAY;AAAA,MAEpD;AAAA;AAAA,QACD;AAAA,UAAC;AAAA;AAAA,YACC,WAAW;AAAA,cACT;AAAA,cACA;AAAA,cACA,YAAY,cAAc;AAAA,YAC5B;AAAA,YAEC,oBAAU,IAAI,CAAC,KAAK,MAAM;AACzB,oBAAM,aACH,eAAe,kBAChB,CAAC,oBACD,MAAM,UAAU,SAAS;AAC3B,oBAAM,eAAe,aAAa;AAClC,qBACE;AAAA,gBAAC;AAAA;AAAA,kBAEC,QAAQ;AAAA,kBACR,UAAU,WAAW,MAAM,SAAS,GAAG,IAAI;AAAA,kBAC3C;AAAA,kBACA;AAAA,kBACA,SAAS;AAAA,kBACT,WAAW,eAAe,mBAAmB;AAAA;AAAA,gBANxC,IAAI;AAAA,cAOX;AAAA,YAEJ,CAAC;AAAA;AAAA,QACH;AAAA,QACC,cAAc,SAAS,KACtB,4CAAC,UAAK,WAAU,YACd,sDAAC,iBAAc,OAAO,eAAe,UAAoB,GAC3D;AAAA;AAAA;AAAA,EAEJ;AAEJ;AAcA,SAAS,UAAU;AAAA,EACjB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,cAAc;AAChB,GAAmB;AACjB,QAAM,UAAU,aACd,WAAW,QAAQ;AAAA,IACjB;AAAA,IACA;AAAA,IACA,UAAU,CAAC,CAAC,OAAO;AAAA,EACrB,CAAC,IAED,6CAAC,SAAI,WAAU,kCACZ;AAAA,WAAO,QACN,4CAAC,UAAK,WAAU,6CACb,sBAAY,OAAO,IAAI,GAC1B;AAAA,IAEF,4CAAC,UAAK,WAAU,mBAAmB,iBAAO,OAAM;AAAA,IAC/C,YACC,4CAAC,UAAK,WAAU,mDACd,sDAAC,6BAAM,WAAU,UAAS,GAC5B;AAAA,KAEJ;AAGF,QAAM,MACJ;AAAA,IAAC,oBAAQ;AAAA,IAAR;AAAA,MACC,OAAO,GAAG,OAAO,KAAK;AAAA,MACtB,UAAU,OAAO;AAAA,MACjB,UAAU,MAAM;AACd,YAAI,CAAC,OAAO,SAAU,UAAS,MAAM;AAAA,MACvC;AAAA,MACA,WAAW;AAAA,QACT;AAAA,QACA;AAAA,QACA,OAAO,YAAY;AAAA,MACrB;AAAA,MACA,oBAAkB,eAAe;AAAA,MAEhC;AAAA;AAAA,EACH;AAGF,MAAI,OAAO,YAAY,OAAO,iBAAiB;AAC7C,WACE,4CAAC,gBAAa,SAAS,OAAO,iBAC5B,sDAAC,SAAK,eAAI,GACZ;AAAA,EAEJ;AAEA,SAAO;AACT;AA2BA,SAAS,YAAY;AAAA,EACnB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAqB;AACnB,QAAM,gBAAY,qBAAuB,IAAI;AAC7C,QAAM,CAAC,UAAU,WAAW,QAAI,uBAAwB,IAAI;AAI5D,QAAM,CAAC,UAAU,WAAW,QAAI,uBAAgC,IAAI;AAEpE,QAAM,mBAAmB,YAAY;AAErC,QAAM,cAAU,sBAAQ,MAAM,UAAU,gBAAgB,GAAG,CAAC,gBAAgB,CAAC;AAC7E,QAAM,kBAAc;AAAA,IAClB,MAAO,UAAU,iBAAiB,gBAAgB,IAAI;AAAA,IACtD,CAAC,SAAS,gBAAgB;AAAA,EAC5B;AAIA,QAAM,sBAAkB;AAAA,IACtB,MAAM,MAAM,IAAgB,CAAC,OAAO,EAAE,MAAM,UAAU,QAAQ,EAAE,EAAE;AAAA,IAClE,CAAC,KAAK;AAAA,EACR;AACA,QAAM,eAAe,eAAe;AAIpC,QAAM,kBAAc,sBAAQ,MAAM;AAChC,QAAI,CAAC,SAAU,QAAO;AACtB,UAAM,MAAM,aAAa;AAAA,MACvB,CAAC,MAAM,EAAE,SAAS,YAAY,GAAG,EAAE,OAAO,KAAK,OAAO;AAAA,IACxD;AACA,WAAO,QAAQ,KAAK,MAAM;AAAA,EAC5B,GAAG,CAAC,UAAU,YAAY,CAAC;AAI3B,QAAM,qBAAiB;AAAA,IACrB,CAAC,UAAiB;AAChB,YAAM,aAAS,4CAAsB,KAAK;AAC1C,UAAI,gBAAgB,QAAQ,CAAC,OAAO,SAAS,WAAW,GAAG;AACzD,eAAO,KAAK,WAAW;AACvB,eAAO,KAAK,CAAC,GAAG,MAAM,IAAI,CAAC;AAAA,MAC7B;AACA,aAAO;AAAA,IACT;AAAA,IACA,CAAC,WAAW;AAAA,EACd;AAEA,QAAM,kBAAc,qCAAe;AAAA,IACjC,OAAO,aAAa;AAAA,IACpB,kBAAkB,MAAM,UAAU;AAAA,IAClC,cAAc,CAAC,UACb,aAAa,KAAK,EAAE,SAAS,iBAAiB,KAAK;AAAA,IACrD,UAAU;AAAA,IACV;AAAA,EACF,CAAC;AAKD,oCAAgB,MAAM;AACpB,gBAAY,QAAQ;AAAA,EACtB,GAAG,CAAC,aAAa,WAAW,CAAC;AAE7B,QAAM,cAAU;AAAA,QACd,uBAAU,2BAAe,EAAE,sBAAsB,EAAE,UAAU,EAAE,EAAE,CAAC;AAAA,QAClE,uBAAU,0BAAc;AAAA,EAC1B;AAGA,QAAM,sBAAkB;AAAA,IACtB,MACE,aACG;AAAA,MACC,CAAC,MAA4C,EAAE,SAAS;AAAA,IAC1D,EACC,IAAI,CAAC,MAAM,GAAG,EAAE,OAAO,KAAK,EAAE;AAAA,IACnC,CAAC,YAAY;AAAA,EACf;AAIA,QAAM,yBAAyC;AAAA,IAC7C,CAAC,SAAS;AACR,UAAI,CAAC,YAAa,YAAO,2BAAc,IAAI;AAC3C,YAAM,YAAY,KAAK,OAAO;AAC9B,YAAM,YAAY,YAAY;AAAA,QAC5B,CAAC,MAAM,EAAE,SAAS,YAAY,GAAG,EAAE,OAAO,KAAK,OAAO,GAAG,SAAS;AAAA,MACpE;AACA,UAAI,CAAC,aAAa,UAAU,SAAS,SAAU,YAAO,2BAAc,IAAI;AACxE,YAAM,cAAc,UAAU;AAC9B,YAAM,WAAW,KAAK,oBAAoB,OAAO,CAAC,cAAc;AAC9D,cAAM,MAAM,YAAY;AAAA,UACtB,CAAC,MACC,EAAE,SAAS,YAAY,GAAG,EAAE,OAAO,KAAK,OAAO,GAAG,UAAU,EAAE;AAAA,QAClE;AACA,eAAO,OAAO,IAAI,SAAS,YAAY,IAAI,eAAe;AAAA,MAC5D,CAAC;AACD,iBAAO,2BAAc,EAAE,GAAG,MAAM,qBAAqB,SAAS,CAAC;AAAA,IACjE;AAAA,IACA,CAAC,WAAW;AAAA,EACd;AAOA,QAAM,sBAAkB,sBAAQ,MAAM;AACpC,QAAI,CAAC,WAAW,CAAC,YAAa,QAAO;AACrC,UAAM,YAAY,oBAAI,IAAyC;AAC/D,eAAW,OAAO,aAAa;AAC7B,UAAI,IAAI,SAAS,UAAU;AACzB,kBAAU,IAAI,GAAG,IAAI,OAAO,KAAK,IAAI,IAAI,UAAU;AAAA,MACrD;AAAA,IACF;AACA,UAAM,SAAS,EAAE,GAAG,GAAG,GAAG,GAAG,QAAQ,GAAG,QAAQ,EAAE;AAClD,WAAO,CACL,SACmD;AACnD,YAAM,YAAY,gBAAgB,KAAK,WAAW;AAClD,YAAM,YAAY,gBAAgB,KAAK,KAAK;AAC5C,UACE,aACA,aACA,UAAU,IAAI,SAAS,MAAM,UAAU,IAAI,SAAS,GACpD;AACA,eAAO;AAAA,MACT;AACA,iBAAO,6CAA4B,IAAI;AAAA,IACzC;AAAA,EACF,GAAG,CAAC,SAAS,aAAa,eAAe,CAAC;AAG1C,QAAM,qBAAiB;AAAA,IACrB,CAAC,UAAyB;AACxB,UAAI,CAAC,wBAAwB,CAAC,QAAS;AACvC,YAAM,EAAE,QAAQ,KAAK,IAAI;AACzB,UAAI,CAAC,QAAQ,OAAO,OAAO,KAAK,GAAI;AAEpC,YAAM,cAAc,YAAY;AAChC,YAAM,cAAc,iBAAiB,WAAW;AAEhD,YAAM,YAAY,YAAY;AAAA,QAC5B,CAAC,MAAM,EAAE,SAAS,YAAY,GAAG,EAAE,OAAO,KAAK,OAAO,GAAG,OAAO,EAAE;AAAA,MACpE;AACA,YAAM,UAAU,YAAY;AAAA,QAC1B,CAAC,MAAM,EAAE,SAAS,YAAY,GAAG,EAAE,OAAO,KAAK,OAAO,GAAG,KAAK,EAAE;AAAA,MAClE;AAEA,UACE,CAAC,aACD,UAAU,SAAS,YACnB,CAAC,WACD,QAAQ,SAAS;AAEjB;AAGF,UAAI,UAAU,eAAe,QAAQ,WAAY;AAGjD,YAAM,UAAU,YAAY,IAAI,CAAC,QAAQ;AACvC,YAAI,CAAC,IAAI,SAAU,QAAO;AAC1B,YAAI,IAAI,UAAU,UAAU,YAAY;AACtC,iBAAO;AAAA,YACL,GAAG;AAAA,YACH,UAAU,IAAI,SAAS;AAAA,cACrB,CAAC,MAAM,GAAG,EAAE,KAAK,OAAO,GAAG,OAAO,EAAE;AAAA,YACtC;AAAA,UACF;AAAA,QACF;AACA,YAAI,IAAI,UAAU,QAAQ,YAAY;AAEpC,gBAAM,eAAe,IAAI,SAAS;AAAA,YAChC,CAAC,MAAM,GAAG,EAAE,KAAK,OAAO,GAAG,OAAO,EAAE;AAAA,UACtC;AACA,gBAAM,UAAU,aAAa;AAAA,YAC3B,CAAC,MAAM,GAAG,EAAE,KAAK,OAAO,GAAG,KAAK,EAAE;AAAA,UACpC;AACA,uBAAa;AAAA,YACX,YAAY,KAAK,UAAU,aAAa;AAAA,YACxC;AAAA,YACA,UAAU;AAAA,UACZ;AACA,iBAAO,EAAE,GAAG,KAAK,UAAU,aAAa;AAAA,QAC1C;AACA,eAAO;AAAA,MACT,CAAC;AAED,kBAAY,OAAO;AAAA,IACrB;AAAA,IACA,CAAC,sBAAsB,SAAS,UAAU,OAAO;AAAA,EACnD;AAGA,QAAM,wBAAoB;AAAA,IACxB,CAAC,UAAwB;AACvB,kBAAY,IAAI;AAChB,kBAAY,IAAI;AAChB,YAAM,EAAE,QAAQ,KAAK,IAAI;AACzB,UAAI,CAAC,QAAQ,OAAO,OAAO,KAAK,MAAM,CAAC,UAAW;AAClD,YAAM,WAAW,MAAM,UAAU,CAAC,MAAM,GAAG,EAAE,KAAK,OAAO,GAAG,OAAO,EAAE,EAAE;AACvE,YAAM,WAAW,MAAM,UAAU,CAAC,MAAM,GAAG,EAAE,KAAK,OAAO,GAAG,KAAK,EAAE,EAAE;AACrE,UAAI,aAAa,MAAM,aAAa,IAAI;AACtC,sBAAU,2BAAU,OAAO,UAAU,QAAQ,CAAC;AAAA,MAChD;AAAA,IACF;AAAA,IACA,CAAC,OAAO,SAAS;AAAA,EACnB;AAEA,QAAM,2BAAuB;AAAA,IAC3B,CAAC,UAAwB;AACvB,kBAAY,IAAI;AAChB,YAAM,EAAE,QAAQ,KAAK,IAAI;AAEzB,UAAI,CAAC,QAAQ,OAAO,OAAO,KAAK,IAAI;AAElC,YAAI,SAAU,cAAa,QAAQ;AACnC,oBAAY,IAAI;AAChB;AAAA,MACF;AAEA,UAAI,CAAC,aAAa;AAChB,oBAAY,IAAI;AAChB;AAAA,MACF;AAEA,YAAM,YAAY,YAAY;AAAA,QAC5B,CAAC,MAAM,EAAE,SAAS,YAAY,GAAG,EAAE,OAAO,KAAK,OAAO,GAAG,OAAO,EAAE;AAAA,MACpE;AACA,YAAM,UAAU,YAAY;AAAA,QAC1B,CAAC,MAAM,EAAE,SAAS,YAAY,GAAG,EAAE,OAAO,KAAK,OAAO,GAAG,KAAK,EAAE;AAAA,MAClE;AAEA,UACE,CAAC,aACD,UAAU,SAAS,YACnB,CAAC,WACD,QAAQ,SAAS,UACjB;AACA,oBAAY,IAAI;AAChB;AAAA,MACF;AAEA,YAAM,cAAc,UAAU;AAC9B,YAAM,YAAY,QAAQ;AAC1B,YAAM,WAAW,YAAY;AAE7B,UAAI,gBAAgB,WAAW;AAE7B,cAAM,gBAAgB,YACnB;AAAA,UACC,CAAC,MACC,EAAE,SAAS,YAAY,EAAE,eAAe;AAAA,QAC5C,EACC,IAAI,CAAC,MAAM,EAAE,MAAM;AAEtB,cAAM,SAAS,cAAc;AAAA,UAC3B,CAAC,MAAM,GAAG,EAAE,KAAK,OAAO,GAAG,OAAO,EAAE;AAAA,QACtC;AACA,cAAM,SAAS,cAAc;AAAA,UAC3B,CAAC,MAAM,GAAG,EAAE,KAAK,OAAO,GAAG,KAAK,EAAE;AAAA,QACpC;AAEA,YAAI,WAAW,MAAM,WAAW,MAAM,WAAW,QAAQ;AACvD,gBAAM,gBAAY,2BAAU,eAAe,QAAQ,MAAM;AACzD,cAAI,UAAU;AAEZ,kBAAM,YAAY,SAAS,IAAI,CAAC,QAAQ;AACtC,kBAAI,IAAI,UAAU,eAAe,IAAI,UAAU;AAC7C,uBAAO,EAAE,GAAG,KAAK,UAAU,UAAU;AAAA,cACvC;AACA,qBAAO;AAAA,YACT,CAAC;AACD,yBAAa,SAAS;AAAA,UACxB,OAAO;AAEL,6BAAiB,aAAc,SAAS;AAAA,UAC1C;AAAA,QACF,WAAW,UAAU;AAEnB,uBAAa,QAAQ;AAAA,QACvB;AAAA,MACF,OAAO;AAEL,cAAM,YAAY,SAAS,IAAI,CAAC,QAAQ;AACtC,cAAI,CAAC,IAAI,SAAU,QAAO;AAC1B,cAAI,IAAI,UAAU,aAAa;AAC7B,mBAAO;AAAA,cACL,GAAG;AAAA,cACH,UAAU,IAAI,SAAS;AAAA,gBACrB,CAAC,MAAM,GAAG,EAAE,KAAK,OAAO,GAAG,OAAO,EAAE;AAAA,cACtC;AAAA,YACF;AAAA,UACF;AACA,cAAI,IAAI,UAAU,WAAW;AAC3B,kBAAM,eAAe,CAAC,GAAG,IAAI,QAAQ;AACrC,kBAAM,UAAU,aAAa;AAAA,cAC3B,CAAC,MAAM,GAAG,EAAE,KAAK,OAAO,GAAG,KAAK,EAAE;AAAA,YACpC;AACA,yBAAa;AAAA,cACX,YAAY,KAAK,UAAU,IAAI,aAAa;AAAA,cAC5C;AAAA,cACA,UAAU;AAAA,YACZ;AACA,mBAAO,EAAE,GAAG,KAAK,UAAU,aAAa;AAAA,UAC1C;AACA,iBAAO;AAAA,QACT,CAAC;AACD,qBAAa,SAAS;AAAA,MACxB;AAEA,kBAAY,IAAI;AAAA,IAClB;AAAA,IACA,CAAC,UAAU,SAAS,aAAa,YAAY,cAAc;AAAA,EAC7D;AAGA,QAAM,cACJ;AAAA,IAAC;AAAA;AAAA,MACC,KAAK;AAAA,MACL,WAAW;AAAA,QACT;AAAA,QACA,gBAAgB,OAAO,uBAAuB;AAAA,MAChD;AAAA,MAEA;AAAA,QAAC;AAAA;AAAA,UACC,OAAO;AAAA,YACL,QAAQ,GAAG,YAAY,aAAa,CAAC;AAAA,YACrC,UAAU;AAAA,YACV,OAAO;AAAA,UACT;AAAA,UAEC,sBAAY,gBAAgB,EAAE,IAAI,CAAC,UAAU;AAC5C,kBAAM,aAAa,aAAa,MAAM,KAAK;AAG3C,gBAAI,WAAW,SAAS,gBAAgB;AACtC,qBACE;AAAA,gBAAC;AAAA;AAAA,kBAEC,OAAO;AAAA,oBACL,UAAU;AAAA,oBACV,KAAK;AAAA,oBACL,MAAM;AAAA,oBACN,OAAO;AAAA,oBACP,WAAW,cAAc,MAAM,KAAK;AAAA,kBACtC;AAAA,kBACA,cAAY,MAAM;AAAA,kBAClB,KAAK,YAAY;AAAA,kBAEjB,sDAAC,SAAI,WAAU,yDACZ,qBAAW,OACd;AAAA;AAAA,gBAbK,MAAM,WAAW,UAAU;AAAA,cAclC;AAAA,YAEJ;AAGA,kBAAM,SAAS,WAAW;AAE1B,kBAAM,MACJ;AAAA,cAAC;AAAA;AAAA,gBAEC;AAAA,gBACA,UAAU,WAAW,QAAQ,aAAa;AAAA,gBAC1C;AAAA,gBACA;AAAA;AAAA,cAJK,OAAO;AAAA,YAKd;AAGF,kBAAM,aAAa,WACjB;AAAA,cAAC;AAAA;AAAA,gBAEC,IAAI,GAAG,OAAO,KAAK;AAAA,gBACnB,UAAU,OAAO;AAAA,gBAEhB;AAAA;AAAA,cAJI,OAAO;AAAA,YAKd,IAEA;AAGF,mBACE;AAAA,cAAC;AAAA;AAAA,gBAEC,OAAO;AAAA,kBACL,UAAU;AAAA,kBACV,KAAK;AAAA,kBACL,MAAM;AAAA,kBACN,OAAO;AAAA,kBACP,WAAW,cAAc,MAAM,KAAK;AAAA,gBACtC;AAAA,gBACA,cAAY,MAAM;AAAA,gBAClB,KAAK,YAAY;AAAA,gBAEhB;AAAA;AAAA,cAXI,OAAO;AAAA,YAYd;AAAA,UAEJ,CAAC;AAAA;AAAA,MACH;AAAA;AAAA,EACF;AAGF,MAAI,UAAU;AACZ,UAAM,kBAAkB,CAAC,UAA+C;AACtE,kBAAY,GAAG,MAAM,OAAO,EAAE,EAAE;AAAA,IAClC;AACA,UAAM,eAAe,MAAM;AACzB,kBAAY,IAAI;AAChB,kBAAY,IAAI;AAAA,IAClB;AAIA,WACE;AAAA,MAAC;AAAA;AAAA,QACC,WAAW,CAAC,yCAAwB,kDAAiC;AAAA,QACrE;AAAA,QACA,oBACE,WAAW,CAAC,uBAAuB,qBAAqB;AAAA,QAE1D,aAAa;AAAA,QACb,YAAY;AAAA,QACZ,WAAW,UAAU,uBAAuB;AAAA,QAC5C,cAAc;AAAA,QAEd,sDAAC,mCAAgB,OAAO,iBAAiB,UAAU,iBAChD,uBACH;AAAA;AAAA,IACF;AAAA,EAEJ;AAEA,SAAO;AACT;AAMO,SAAS,aAAa,OAA0B;AACrD,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA,cAAc;AAAA,IACd,WAAW;AAAA,IACX,WAAW;AAAA,IACX,QAAQ;AAAA,IACR,YAAY;AAAA,IACZ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,IAAI;AAEJ,QAAM,CAAC,MAAM,OAAO,QAAI,uBAAS,KAAK;AACtC,QAAM,CAAC,QAAQ,SAAS,QAAI,uBAAS,EAAE;AACvC,QAAM,CAAC,cAAc,eAAe,QAAI,uBAAgC,IAAI;AAC5E,QAAM,CAAC,SAAS,UAAU,QAAI,uBAAS,KAAK;AAC5C,QAAM,CAAC,uBAAuB,wBAAwB,QACpD,uBAAgC,IAAI;AAGtC,QAAM,mBAAe,sBAAQ,MAAM;AACjC,QAAI,SAAS,UAAU;AACrB,aAAQ,MAA4B,eAAe;AAAA,IACrD;AACA,WAAQ,MAA8B,eAAe,CAAC;AAAA,EACxD,GAAG,CAAC,MAAM,KAAK,CAAC;AAGhB,QAAM,sBAAkB,sBAAQ,MAAM;AACpC,UAAM,OAAO,gBAAgB;AAC7B,WAAO,yBAAyB;AAAA,EAClC,GAAG,CAAC,cAAc,SAAS,qBAAqB,CAAC;AAEjD,QAAM,kBAAc;AAAA,IAClB,MAAM,eAAe,eAAe;AAAA,IACpC,CAAC,eAAe;AAAA,EAClB;AAMA,QAAM,sBAAkB,sBAAQ,MAAM;AACpC,QAAI,CAAC,OAAQ,QAAO;AACpB,UAAM,IAAI,OAAO,YAAY;AAC7B,WAAO,YAAY,OAAO,CAAC,MAAM,EAAE,MAAM,YAAY,EAAE,SAAS,CAAC,CAAC;AAAA,EACpE,GAAG,CAAC,aAAa,MAAM,CAAC;AAGxB,QAAM,6BAAyB,sBAAQ,MAAM;AAC3C,QAAI,CAAC,OAAQ,QAAO;AACpB,UAAM,IAAI,OAAO,YAAY;AAC7B,WAAO,gBACJ,IAAI,CAAC,QAAQ;AACZ,UAAI,IAAI,YAAY,IAAI,SAAS,SAAS,GAAG;AAC3C,cAAM,UAAU,IAAI,SAAS;AAAA,UAAO,CAAC,MACnC,EAAE,MAAM,YAAY,EAAE,SAAS,CAAC;AAAA,QAClC;AACA,YAAI,QAAQ,WAAW,EAAG,QAAO;AACjC,eAAO,EAAE,GAAG,KAAK,UAAU,QAAQ;AAAA,MACrC;AACA,aAAO,IAAI,MAAM,YAAY,EAAE,SAAS,CAAC,IAAI,MAAM;AAAA,IACrD,CAAC,EACA,OAAO,OAAO;AAAA,EACnB,GAAG,CAAC,iBAAiB,MAAM,CAAC;AAG5B,8BAAU,MAAM;AACd,QAAI,QAAQ,SAAS;AACnB,UAAI,YAAY;AAChB,iBAAW,IAAI;AACf,cAAQ,EACL,KAAK,CAAC,SAAS;AACd,YAAI,CAAC,WAAW;AACd,0BAAgB,IAAI;AAAA,QACtB;AAAA,MACF,CAAC,EACA,QAAQ,MAAM;AACb,YAAI,CAAC,UAAW,YAAW,KAAK;AAAA,MAClC,CAAC;AACH,aAAO,MAAM;AACX,oBAAY;AAAA,MACd;AAAA,IACF;AAAA,EACF,GAAG,CAAC,MAAM,OAAO,CAAC;AAGlB,8BAAU,MAAM;AACd,QAAI,CAAC,MAAM;AACT,gBAAU,EAAE;AAAA,IACd;AAAA,EACF,GAAG,CAAC,IAAI,CAAC;AAGT,QAAM,mBAAe;AAAA,IACnB,CAAC,WAAoB;AACnB,UAAI,SAAU;AAEd,UAAI,SAAS,UAAU;AACrB,cAAM,WAAY,MAA4B;AAC9C,cAAM,UAAU;AAChB,YAAI,aAAa,WAAW,SAAS,SAAS,MAAM,GAAG;AACrD,qBAAW,MAAM,MAAM;AAAA,QACzB,OAAO;AACL,qBAAW,QAAQ,MAAM;AAAA,QAC3B;AACA,gBAAQ,KAAK;AAAA,MACf,OAAO;AACL,cAAM,WAAY,MAA8B;AAChD,cAAM,UAAU;AAChB,cAAM,SAAS,QAAQ,KAAK,CAAC,MAAM,SAAS,GAAG,MAAM,CAAC;AACtD,YAAI,QAAQ;AACV;AAAA,YACE,QAAQ,OAAO,CAAC,MAAM,CAAC,SAAS,GAAG,MAAM,CAAC;AAAA,YAC1C;AAAA,UACF;AAAA,QACF,OAAO;AACL,qBAAW,CAAC,GAAG,SAAS,MAAM,GAAG,MAAM;AAAA,QACzC;AAAA,MACF;AAAA,IACF;AAAA,IACA,CAAC,MAAM,cAAc,WAAW,UAAU,KAAK;AAAA,EACjD;AAGA,QAAM,uBAAmB;AAAA,IACvB,CAAC,WAAoB;AACnB,UAAI,SAAS,cAAc,YAAY,SAAU;AACjD,YAAM,WAAY,MAA8B;AAChD,YAAM,UAAU;AAChB;AAAA,QACE,QAAQ,OAAO,CAAC,MAAM,CAAC,SAAS,GAAG,MAAM,CAAC;AAAA,QAC1C;AAAA,MACF;AAAA,IACF;AAAA,IACA,CAAC,MAAM,cAAc,UAAU,UAAU,KAAK;AAAA,EAChD;AAGA,QAAM,sBAAkB,0BAAY,MAAM;AACxC,QAAI,SAAS,cAAc,YAAY,SAAU;AACjD,UAAM,WAAY,MAA8B;AAChD,UAAM,UAAU;AAChB,UAAM,oBAAoB,YAAY,OAAO,CAAC,MAAM,CAAC,EAAE,QAAQ;AAC/D,UAAMA,eACJ,kBAAkB,SAAS,KAC3B,kBAAkB,MAAM,CAAC,MAAM,QAAQ,KAAK,CAAC,MAAM,SAAS,GAAG,CAAC,CAAC,CAAC;AAEpE,QAAIA,cAAa;AAEf,YAAM,OAAO,QAAQ;AAAA,QAAO,CAAC,MAC3B,kBAAkB,MAAM,CAAC,MAAM,CAAC,SAAS,GAAG,CAAC,CAAC;AAAA,MAChD;AACA,iBAAW,MAAM,kBAAkB,CAAC,CAAC;AAAA,IACvC,OAAO;AAEL,YAAM,UAAU,kBAAkB;AAAA,QAChC,CAAC,MAAM,CAAC,QAAQ,KAAK,CAAC,MAAM,SAAS,GAAG,CAAC,CAAC;AAAA,MAC5C;AACA,iBAAW,CAAC,GAAG,SAAS,GAAG,OAAO,GAAG,kBAAkB,CAAC,CAAC;AAAA,IAC3D;AAAA,EACF,GAAG,CAAC,MAAM,cAAc,aAAa,UAAU,UAAU,KAAK,CAAC;AAE/D,QAAM,kBAAc,sBAAQ,MAAM;AAChC,QAAI,SAAS,WAAY,QAAO;AAChC,UAAM,UAAU;AAChB,UAAM,oBAAoB,YAAY,OAAO,CAAC,MAAM,CAAC,EAAE,QAAQ;AAC/D,WACE,kBAAkB,SAAS,KAC3B,kBAAkB,MAAM,CAAC,MAAM,QAAQ,KAAK,CAAC,MAAM,SAAS,GAAG,CAAC,CAAC,CAAC;AAAA,EAEtE,GAAG,CAAC,MAAM,cAAc,WAAW,CAAC;AAEpC,QAAM,WAAW,MAAM,YAAY;AACnC,QAAM,uBAAuB,MAAM,WAC7B,MAA+B,wBAAwB,QACzD;AACJ,QAAM,oBAAoB,MAAM,WAC3B,MAA+B,YAChC;AAGJ,QAAM,oBAAgB;AAAA,IACpB,CAAC,WAA2B;AAC1B,+BAAyB,MAAM;AAC/B,0BAAoB,MAAM;AAAA,IAC5B;AAAA,IACA,CAAC,iBAAiB;AAAA,EACpB;AAGA,QAAM,yBAAqB;AAAA,IACzB,CAAC,YAA6B,sBAAsC;AAElE,YAAM,UAAU,gBAAgB,IAAI,CAAC,QAAQ;AAC3C,YAAI,IAAI,UAAU,cAAc,IAAI,UAAU;AAC5C,iBAAO,EAAE,GAAG,KAAK,UAAU,kBAAkB;AAAA,QAC/C;AACA,eAAO;AAAA,MACT,CAAC;AACD,+BAAyB,OAAO;AAChC,0BAAoB,eAAe,OAAO,CAAC;AAAA,IAC7C;AAAA,IACA,CAAC,iBAAiB,iBAAiB;AAAA,EACrC;AAGA,QAAM,qBAAiB;AAAA,IACrB,CAAC,YAA4B;AAC3B,+BAAyB,OAAO;AAChC,0BAAoB,eAAe,OAAO,CAAC;AAAA,IAC7C;AAAA,IACA,CAAC,iBAAiB;AAAA,EACpB;AAGA,QAAM,uBAAmB;AAAA,IACvB,CAAC,aAAsB;AACrB,UAAI,YAAY,SAAU;AAC1B,cAAQ,QAAQ;AAAA,IAClB;AAAA,IACA,CAAC,UAAU,QAAQ;AAAA,EACrB;AAGA,QAAM,qBAAiB,sBAAQ,MAAM;AACnC,QAAI,eAAe;AACjB,aAAO,cAAc;AAAA,QACnB,OAAO;AAAA,QACP;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF,CAAC;AAAA,IACH;AAEA,QAAI,SAAS,UAAU;AACrB,aACE;AAAA,QAAC;AAAA;AAAA,UACC,OAAO;AAAA,UACP;AAAA;AAAA,MACF;AAAA,IAEJ;AAEA,WACE;AAAA,MAAC;AAAA;AAAA,QACC,OAAO;AAAA,QACP;AAAA,QACA,WAAY,MAA8B;AAAA,QAC1C,iBAAkB,MAA8B;AAAA,QAChD,UAAU;AAAA,QACV;AAAA,QACA;AAAA;AAAA,IACF;AAAA,EAEJ,GAAG;AAAA,IACD;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,CAAC;AAED,SACE,4CAAC,uBAAQ,UAAR,EACC,uDAAC,SAAI,WAAW,GAAG,iCAAiC,SAAS,GAC1D;AAAA,aACC,4CAAC,WAAM,WAAU,4CACd,iBACH;AAAA,IAEF,6CAAC,uBAAQ,MAAR,EAAa,MAAY,cAAc,kBACtC;AAAA;AAAA,QAAC,uBAAQ;AAAA,QAAR;AAAA,UACC;AAAA,UACA,QACE;AAAA,YAAC;AAAA;AAAA,cACC,MAAK;AAAA,cACL,iBAAe;AAAA,cACf,iBAAc;AAAA,cACd,gBAAc,SAAS;AAAA,cACvB,iBAAe,YAAY;AAAA,cAC3B,WAAW;AAAA,gBACT;AAAA,gBACA;AAAA,gBACA;AAAA,gBACA;AAAA,gBACA;AAAA,gBACA;AAAA,gBACA;AAAA,cACF;AAAA;AAAA,UACF;AAAA,UAGF;AAAA,wDAAC,SAAI,WAAU,oCACZ,0BACH;AAAA,YACA,4CAAC,mCAAY,WAAU,8BAA6B;AAAA;AAAA;AAAA,MACtD;AAAA,MAEA,4CAAC,uBAAQ,QAAR,EACC,sDAAC,uBAAQ,YAAR,EAAmB,YAAY,GAC9B;AAAA,QAAC,uBAAQ;AAAA,QAAR;AAAA,UACC,WAAW;AAAA,YACT;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,UACF;AAAA,UAEA,uDAAC,uBAAQ,cAAc,OAAO,MAAI,MAEhC;AAAA,wDAAC,SAAI,WAAU,gBACb;AAAA,cAAC,oBAAQ;AAAA,cAAR;AAAA,gBACC,OAAO;AAAA,gBACP,eAAe;AAAA,gBACf,aAAY;AAAA,gBACZ,WAAU;AAAA;AAAA,YACZ,GACF;AAAA,YAGC,cACC,4CAAC,SAAI,WAAU,wBAAwB,sBAAW;AAAA,YAInD,SAAS,cAAc,CAAC,YACvB,4CAAC,SAAI,WAAU,sBACb;AAAA,cAAC;AAAA;AAAA,gBACC,MAAK;AAAA,gBACL,SAAS;AAAA,gBACT,WAAU;AAAA,gBAET,wBAAc,iBAAiB;AAAA;AAAA,YAClC,GACF;AAAA,YAGF,6CAAC,oBAAQ,MAAR,EAAa,WAAU,OACrB;AAAA,yBACC,4CAAC,oBAAQ,SAAR,EACC,sDAAC,SAAI,WAAU,uEAAsE,2BAErF,GACF;AAAA,cAGD,CAAC,WAAW,gBAAgB,WAAW,KACtC,4CAAC,oBAAQ,OAAR,EAAc,WAAU,kDAAiD,+BAE1E;AAAA,cAGD,CAAC,WAAW,gBAAgB,SAAS,KACpC;AAAA,gBAAC;AAAA;AAAA,kBACC,SAAS;AAAA,kBACT,OAAO;AAAA,kBACP,eAAe;AAAA,kBACf;AAAA,kBACA,UAAU;AAAA,kBACV;AAAA,kBACA;AAAA,kBACA,WAAW;AAAA,kBACX,gBAAgB;AAAA,kBAChB,YAAY;AAAA;AAAA,cACd;AAAA,eAEJ;AAAA,YAGC,cACC,4CAAC,SAAI,WAAU,wBAAwB,sBAAW;AAAA,aAEtD;AAAA;AAAA,MACF,GACF,GACF;AAAA,OACF;AAAA,KACF,GACF;AAEJ;AAEA,IAAO,iBAAQ;;;AE/kDf,IAAAC,gBAQO;AACP,IAAAC,kBASO;AACP,6BAAgC;AAChC,IAAAC,iCAA0C;AAC1C,oCAAgC;AAChC,kCAAqC;AACrC,kCAA8B;AAC9B,mCAA+B;AAC/B,oCAAgC;;;ACnBzB,IAAM,wBAAgD;AAAA,EAC3D,GAAQ;AAAA,EACR,GAAQ;AAAA,EACR,IAAQ;AAAA,EACR,IAAQ;AAAA,EACR,IAAQ;AAAA,EACR,KAAQ;AAAA,EACR,MAAQ;AAAA,EACR,MAAQ;AAAA,EACR,MAAQ;AAAA,EACR,MAAQ;AAAA,EACR,MAAQ;AAAA,EACR,MAAQ;AAAA,EACR,MAAQ;AAAA,EACR,MAAQ;AAAA,EACR,OAAQ;AAAA,EACR,OAAQ;AACV;AAIA,IAAI;AAIG,SAAS,sBACd,WACM;AACN,wBAAsB;AACxB;AAGO,SAAS,2BAAmD;AACjE,SAAO,sBACH,EAAE,GAAG,uBAAuB,GAAG,oBAAoB,IACnD;AACN;AAIO,IAAM,mBAAmB;AAIzB,SAAS,sBACd,MACA,WACQ;AACR,QAAM,MAAM,YACR,EAAE,GAAG,uBAAuB,GAAG,UAAU,IACzC,yBAAyB;AAC7B,MAAI,SAAS;AACb,aAAW,MAAM,MAAM;AACrB,UAAM,KAAK,GAAG,YAAY,CAAC,KAAK;AAChC,cAAU,IAAI,EAAE,KAAK;AAAA,EACvB;AACA,SAAO;AACT;;;AC/DA,qBAAyB;AAqBlB,IAAM,gBAAN,MAAM,uBAAsB,wBAAS;AAAA,EAC1C;AAAA,EACA;AAAA,EACA;AAAA,EAEA,OAAO,UAAkB;AACvB,WAAO;AAAA,EACT;AAAA,EAEA,OAAO,MAAM,MAAoC;AAC/C,WAAO,IAAI;AAAA,MACT,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AAAA,IACP;AAAA,EACF;AAAA,EAEA,YACE,MACA,gBACA,SACA,aACA,KACA;AACA,UAAM,MAAM,GAAG;AACf,SAAK,mBAAmB;AACxB,SAAK,YAAY;AACjB,SAAK,gBAAgB,eAAe;AAAA,EACtC;AAAA,EAEA,UAAU,QAAmC;AAC3C,UAAM,MAAM,MAAM,UAAU,MAAM;AAClC,QAAI,UAAU,IAAI,eAAe;AACjC,eAAW,KAAK,KAAK,iBAAiB,MAAM,GAAG,GAAG;AAChD,UAAI,UAAU,IAAI,iBAAiB,CAAC,EAAE;AACtC,UAAI,EAAE,WAAW,WAAW,GAAG;AAC7B,YAAI,UAAU,IAAI,wBAAwB;AAAA,MAC5C;AACA,UAAI,EAAE,WAAW,aAAa,GAAG;AAC/B,YAAI,UAAU,IAAI,0BAA0B;AAAA,MAC9C;AAAA,IACF;AACA,QAAI,KAAK,iBAAiB,SAAS,GAAG,GAAG;AACvC,UAAI,UAAU,IAAI,sBAAsB;AAAA,IAC1C;AACA,QAAI,QAAQ,iBAAiB,KAAK;AAClC,QAAI,QAAQ,UAAU,KAAK;AAI3B,QAAI,KAAK,UAAU,WAAW,gBAAgB,GAAG;AAC/C,UAAI,MAAM,aAAa;AACvB,UAAI,UAAU,IAAI,yBAAyB;AAAA,IAC7C;AAGA,QAAI,KAAK,eAAe;AACtB,UAAI,QAAQ,UAAU,KAAK;AAAA,IAC7B;AAQA,QAAI,KAAK,iBAAiB,MAAM,GAAG,EAAE,SAAS,eAAe,GAAG;AAC9D,UAAI,cAAc;AAClB,UAAI,kBAAkB;AAAA,IACxB;AAIA,QAAI,KAAK,iBAAiB,MAAM,GAAG,EAAE,SAAS,cAAc,GAAG;AAK7D,UAAI,KAAK,WAAW,KAAK;AACvB,YAAI,UAAU,IAAI,0BAA0B;AAC5C,YAAI,MAAM,WAAW;AAAA,MACvB,OAAO;AACL,cAAM,WAAW,sBAAsB,KAAK,MAAM;AAClD,YAAI,aAAa,KAAK,QAAQ;AAC5B,cAAI,cAAc;AAAA,QACpB;AAAA,MACF;AAAA,IACF;AAQA,QACE,KAAK,iBAAiB,MAAM,GAAG,EAAE,SAAS,OAAO,KACjD,KAAK,eACL;AACA,UAAI,UAAU,IAAI,0BAA0B;AAC5C,UAAI,cAAc;AAClB,UAAI,kBAAkB;AAAA,IACxB;AAEA,WAAO;AAAA,EACT;AAAA,EAEA,UAAU,UAAgB,KAAkB,QAA+B;AACzE,UAAM,UAAU,MAAM,UAAU,UAAU,KAAK,MAAM;AACrD,QAAI,SAAS,qBAAqB,KAAK,kBAAkB;AACvD,iBAAW,KAAK,SAAS,iBAAiB,MAAM,GAAG,GAAG;AACpD,YAAI,UAAU,OAAO,iBAAiB,CAAC,EAAE;AACzC,YAAI,EAAE,WAAW,WAAW,GAAG;AAC7B,cAAI,UAAU,OAAO,wBAAwB;AAAA,QAC/C;AACA,YAAI,EAAE,WAAW,aAAa,GAAG;AAC/B,cAAI,UAAU,OAAO,0BAA0B;AAAA,QACjD;AAAA,MACF;AACA,UAAI,UAAU,OAAO,sBAAsB;AAC3C,iBAAW,KAAK,KAAK,iBAAiB,MAAM,GAAG,GAAG;AAChD,YAAI,UAAU,IAAI,iBAAiB,CAAC,EAAE;AACtC,YAAI,EAAE,WAAW,WAAW,GAAG;AAC7B,cAAI,UAAU,IAAI,wBAAwB;AAAA,QAC5C;AACA,YAAI,EAAE,WAAW,aAAa,GAAG;AAC/B,cAAI,UAAU,IAAI,0BAA0B;AAAA,QAC9C;AAAA,MACF;AACA,UAAI,KAAK,iBAAiB,SAAS,GAAG,GAAG;AACvC,YAAI,UAAU,IAAI,sBAAsB;AAAA,MAC1C;AACA,UAAI,QAAQ,iBAAiB,KAAK;AAAA,IACpC;AACA,QAAI,SAAS,cAAc,KAAK,WAAW;AACzC,UAAI,QAAQ,UAAU,KAAK;AAAA,IAC7B;AAGA,QAAI,SAAS,kBAAkB,KAAK,eAAe;AACjD,UAAI,KAAK,eAAe;AACtB,YAAI,QAAQ,UAAU,KAAK;AAAA,MAC7B,OAAO;AACL,eAAO,IAAI,QAAQ;AAAA,MACrB;AAAA,IACF;AAGA,QAAI,KAAK,iBAAiB,MAAM,GAAG,EAAE,SAAS,eAAe,GAAG;AAC9D,UAAI,cAAc;AAClB,UAAI,kBAAkB;AAAA,IACxB,WAAW,IAAI,oBAAoB,SAAS;AAE1C,UAAI,gBAAgB,iBAAiB;AAAA,IACvC;AAGA,QAAI,KAAK,iBAAiB,MAAM,GAAG,EAAE,SAAS,cAAc,GAAG;AAC7D,UAAI,KAAK,WAAW,KAAK;AACvB,YAAI,UAAU,IAAI,0BAA0B;AAC5C,YAAI,MAAM,WAAW;AAAA,MACvB,OAAO;AACL,cAAM,WAAW,sBAAsB,KAAK,MAAM;AAClD,YAAI,aAAa,KAAK,QAAQ;AAC5B,cAAI,cAAc;AAAA,QACpB;AAAA,MACF;AAAA,IACF;AAGA,QACE,KAAK,iBAAiB,MAAM,GAAG,EAAE,SAAS,OAAO,KACjD,KAAK,eACL;AACA,UAAI,UAAU,IAAI,0BAA0B;AAC5C,UAAI,cAAc;AAClB,UAAI,kBAAkB;AAAA,IACxB,WAAW,SAAS,iBAAiB,MAAM,GAAG,EAAE,SAAS,OAAO,GAAG;AACjE,UAAI,UAAU,OAAO,0BAA0B;AAC/C,UAAI,IAAI,oBAAoB,SAAS;AACnC,YAAI,gBAAgB,iBAAiB;AAAA,MACvC;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA,EAEA,OAAO,WAAW,MAA8C;AAC9D,UAAM,OAAO,IAAI;AAAA,MACf,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AAAA,IACP;AACA,SAAK,UAAU,KAAK,MAAM;AAC1B,SAAK,UAAU,KAAK,MAAM;AAC1B,SAAK,QAAQ,KAAK,IAAI;AACtB,SAAK,SAAS,KAAK,KAAK;AACxB,WAAO;AAAA,EACT;AAAA,EAEA,aAAsC;AACpC,WAAO;AAAA,MACL,GAAG,MAAM,WAAW;AAAA,MACpB,MAAM;AAAA,MACN,gBAAgB,KAAK;AAAA,MACrB,SAAS,KAAK;AAAA,MACd,aAAa,KAAK;AAAA,IACpB;AAAA,EACF;AAAA;AAAA;AAAA,EAIA,iBAAyB;AACvB,QAAI,KAAK,UAAU,WAAW,gBAAgB,EAAG,QAAO;AACxD,WAAO,MAAM,eAAe;AAAA,EAC9B;AAAA,EAEA,sBAA+B;AAC7B,QAAI,KAAK,UAAU,WAAW,gBAAgB,EAAG,QAAO;AAIxD,QAAI,KAAK,QAAQ,MAAM,QAAS,QAAO;AACvC,WAAO;AAAA,EACT;AAAA,EAEA,qBAA8B;AAC5B,QAAI,KAAK,UAAU,WAAW,gBAAgB,EAAG,QAAO;AACxD,QAAI,KAAK,QAAQ,MAAM,QAAS,QAAO;AACvC,WAAO;AAAA,EACT;AAAA,EAEA,eAAwB;AACtB,WAAO;AAAA,EACT;AACF;AAEO,SAAS,qBACd,MACA,gBACA,SACA,aAGA,YACe;AACf,QAAM,OAAO,IAAI,cAAc,MAAM,gBAAgB,SAAS,WAAW;AAGzE,MACE,eAAe,MAAM,GAAG,EAAE,SAAS,cAAc,KACjD,QAAQ,WAAW,gBAAgB,KACnC,YACA;AACA,SAAK,QAAQ,OAAO;AAAA,EACtB;AACA,SAAO;AACT;AAEO,SAAS,iBACd,MACuB;AACvB,SAAO,gBAAgB;AACzB;;;AC/RA,IAAAC,kBAAgD;AAoDhD,IAAM,4BAA4B,CAAC,OAAuB,KAAK,EAAE;AACjE,IAAM,0BAA0B;AAGhC,IAAI,qBAAwC,CAAC;AAItC,SAAS,qBAAqB,QAA2B;AAC9D,uBAAqB;AACvB;AAIO,SAAS,oBAAoB,IAAoB;AACtD,UAAQ,mBAAmB,aAAa,2BAA2B,EAAE;AACvE;AAIO,SAAS,oBAA4B;AAC1C,QAAM,MAAM,mBAAmB,WAAW;AAC1C,SAAO,IAAI,OAAO,IAAI,QAAQ,IAAI,KAAK;AACzC;AAKA,SAAS,wBACP,SACA,YACA,aACM;AACN,UAAQ,cAAc;AACtB,QAAM,QAAQ,SAAS,cAAc,MAAM;AAC3C,QAAM,YAAY;AAClB,QAAM,cAAc,IAAI,WAAW;AACnC,UAAQ,YAAY,KAAK;AAC3B;AAIA,SAAS,uBACP,SAC4B;AAC5B,QAAM,YAAY,QAAQ,aAAa,iBAAiB;AACxD,QAAM,cAAc,QAAQ,aAAa,mBAAmB;AAE5D,MAAI,cAAc,MAAM;AACtB,UAAM,OAAO,mBAAmB,WAAW,eAAe,SAAS;AACnE,WAAO,EAAE,KAAK;AAAA,EAChB;AACA,SAAO;AACT;AAiBO,IAAM,cAAN,MAAM,qBAAoB,yBAAS;AAAA,EACxC;AAAA,EACA;AAAA,EAEA,OAAO,UAAkB;AACvB,WAAO;AAAA,EACT;AAAA,EAEA,OAAO,MAAM,MAAgC;AAC3C,WAAO,IAAI;AAAA,MACT,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AAAA,IACP;AAAA,EACF;AAAA,EAEA,OAAO,WAAW,gBAAoD;AACpE,WAAO;AAAA,MACL,eAAe;AAAA,MACf,eAAe;AAAA,IACjB,EAAE,eAAe,cAAc;AAAA,EACjC;AAAA,EAEA,YACE,WACA,aACA,MACA,KACA;AAIA,UAAM,QAAQ,oBAAoB,SAAS,GAAG,GAAG;AACjD,SAAK,cAAc;AACnB,SAAK,gBAAgB;AAAA,EACvB;AAAA,EAEA,aAAoC;AAClC,WAAO;AAAA,MACL,GAAG,MAAM,WAAW;AAAA,MACpB,WAAW,KAAK;AAAA,MAChB,aAAa,KAAK;AAAA,IACpB;AAAA,EACF;AAAA,EAEA,UAAU,QAAmC;AAC3C,UAAM,MAAM,MAAM,UAAU,MAAM;AAClC,QAAI,YAAY;AAChB,QAAI,aAAa;AACjB,QAAI,kBAAkB;AACtB,QAAI,aAAa,mBAAmB,KAAK,WAAW;AACpD,QAAI,aAAa,qBAAqB,KAAK,aAAa;AACxD,SAAK,gBAAgB,GAAsB;AAC3C,WAAO;AAAA,EACT;AAAA,EAEA,UACE,UACA,KACA,SACS;AAET,QACE,SAAS,gBAAgB,KAAK,eAC9B,SAAS,kBAAkB,KAAK,eAChC;AACA,UAAI,aAAa,mBAAmB,KAAK,WAAW;AACpD,UAAI,aAAa,qBAAqB,KAAK,aAAa;AACxD,WAAK,gBAAgB,GAAsB;AAAA,IAC7C;AAEA,WAAO;AAAA,EACT;AAAA;AAAA,EAGQ,gBAAgB,SAAgC;AACtD,UAAM,WAAW,mBAAmB;AACpC,QAAI,UAAU;AACZ,YAAM,UAAU,SAAS,SAAS,KAAK,aAAa,KAAK,aAAa;AACtE,UAAI,QAAS;AAAA,IACf;AACA,4BAAwB,SAAS,KAAK,aAAa,KAAK,aAAa;AAAA,EACvE;AAAA,EAEA,YAA6B;AAC3B,UAAM,UAAU,SAAS,cAAc,MAAM;AAC7C,YAAQ,aAAa,gBAAgB,MAAM;AAC3C,YAAQ,aAAa,mBAAmB,KAAK,WAAW;AACxD,YAAQ,aAAa,qBAAqB,KAAK,aAAa;AAC5D,YAAQ,cAAc,KAAK;AAC3B,WAAO,EAAE,QAAQ;AAAA,EACnB;AAAA,EAEA,OAAO,YAAqC;AAC1C,WAAO;AAAA,MACL,MAAM,CAAC,YAAyB;AAC9B,YAAI,CAAC,QAAQ,aAAa,cAAc,GAAG;AACzC,iBAAO;AAAA,QACT;AACA,eAAO;AAAA,UACL,YAAY;AAAA,UACZ,UAAU;AAAA,QACZ;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EAEA,eAAqB;AACnB,WAAO;AAAA,EACT;AAAA,EAEA,sBAA+B;AAC7B,WAAO;AAAA,EACT;AAAA,EAEA,qBAA8B;AAC5B,WAAO;AAAA,EACT;AACF;AAIO,SAAS,mBACd,WACA,aACA,aACa;AACb,QAAM,OAAO,IAAI;AAAA,IACf;AAAA,IACA;AAAA,IACA,eAAe,oBAAoB,SAAS;AAAA,EAC9C;AACA,OAAK,QAAQ,OAAO,EAAE,oBAAoB;AAC1C,aAAO,uCAAsB,IAAI;AACnC;AAEO,SAAS,eACd,MACqB;AACrB,SAAO,gBAAgB;AACzB;;;ACvQA,IAAAC,gBAAkE;AAClE,eAA0B;AAC1B,oCAA0C;AAC1C,wCAIO;AACP,IAAAC,wBAA+B;AAE/B,IAAAC,kBAAgC;AAkC1B,IAAAC,sBAAA;AAzBN,IAAM,+BAA+B;AACrC,IAAM,cAAc;AAIpB,IAAM,yBAAN,cAAqC,6CAAW;AAAA,EAC9C;AAAA,EAEA,YAAY,MAAoB;AAC9B,UAAM,KAAK,EAAE;AACb,SAAK,OAAO;AAAA,EACd;AACF;AAIA,SAAS,cAAc;AAAA,EACrB;AAAA,EACA,OAAO;AACT,GAGG;AACD,MAAI,KAAK,QAAQ;AACf,WACE;AAAA,MAAC;AAAA;AAAA,QACC,WAAU;AAAA,QACV,OAAO,EAAE,OAAO,MAAM,QAAQ,KAAK;AAAA,QAElC,eAAK,OAAO;AAAA;AAAA,IACf;AAAA,EAEJ;AAGA,QAAM,WAAW,KAAK,KACnB,MAAM,KAAK,EACX,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,EACf,MAAM,GAAG,CAAC,EACV,KAAK,EAAE,EACP,YAAY;AAEf,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAU;AAAA,MACV,OAAO,EAAE,OAAO,MAAM,QAAQ,MAAM,UAAU,OAAO,IAAI;AAAA,MAExD;AAAA;AAAA,EACH;AAEJ;AAIA,SAAS,gBAAgB;AAAA,EACvB;AAAA,EACA,YAAAC;AAAA,EACA;AAAA,EACA;AACF,GAKG;AACD,SACE;AAAA,IAAC;AAAA;AAAA,MAEC,UAAU;AAAA,MACV,WAAW,qFACTA,cACI,qCACA,yBACN;AAAA,MACA,KAAK,OAAO;AAAA,MACZ,MAAK;AAAA,MACL,iBAAeA;AAAA,MACf;AAAA,MACA;AAAA,MAEA;AAAA,qDAAC,iBAAc,MAAM,OAAO,MAAM,MAAM,IAAI;AAAA,QAC5C,6CAAC,UAAK,WAAU,YAAY,iBAAO,KAAK,MAAK;AAAA;AAAA;AAAA,IAdxC,OAAO;AAAA,EAed;AAEJ;AAIA,SAAS,uBAAuB;AAAA,EAC9B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAKG;AACD,QAAM,gBAAY,sBAAuB,IAAI;AAE7C,QAAM,kBAAc,sCAAe;AAAA,IACjC,OAAO,QAAQ;AAAA,IACf,kBAAkB,MAAM,UAAU;AAAA,IAClC,cAAc,MAAM;AAAA,IACpB,UAAU;AAAA,EACZ,CAAC;AAGD,+BAAU,MAAM;AACd,QAAI,kBAAkB,QAAQ,iBAAiB,GAAG;AAChD,kBAAY,cAAc,eAAe,EAAE,OAAO,OAAO,CAAC;AAAA,IAC5D;AAAA,EACF,GAAG,CAAC,eAAe,WAAW,CAAC;AAE/B,SACE;AAAA,IAAC;AAAA;AAAA,MACC,KAAK;AAAA,MACL,WAAU;AAAA,MAEV;AAAA,QAAC;AAAA;AAAA,UACC,MAAK;AAAA,UACL,cAAW;AAAA,UACX,OAAO;AAAA,YACL,QAAQ,GAAG,YAAY,aAAa,CAAC;AAAA,YACrC,UAAU;AAAA,YACV,OAAO;AAAA,UACT;AAAA,UAEC,sBAAY,gBAAgB,EAAE,IAAI,CAAC,UAAU;AAC5C,kBAAM,SAAS,QAAQ,MAAM,KAAK;AAClC,mBACE;AAAA,cAAC;AAAA;AAAA,gBAEC,OAAO;AAAA,kBACL,UAAU;AAAA,kBACV,KAAK;AAAA,kBACL,MAAM;AAAA,kBACN,OAAO;AAAA,kBACP,WAAW,cAAc,MAAM,KAAK;AAAA,gBACtC;AAAA,gBACA,KAAK,YAAY;AAAA,gBACjB,cAAY,MAAM;AAAA,gBAElB;AAAA,kBAAC;AAAA;AAAA,oBACC;AAAA,oBACA,YAAY,kBAAkB,MAAM;AAAA,oBACpC,SAAS,MAAM;AACb,0CAAoB,MAAM,KAAK;AAC/B,6CAAuB,MAAM;AAAA,oBAC/B;AAAA,oBACA,cAAc,MAAM;AAClB,0CAAoB,MAAM,KAAK;AAAA,oBACjC;AAAA;AAAA,gBACF;AAAA;AAAA,cArBK,OAAO;AAAA,YAsBd;AAAA,UAEJ,CAAC;AAAA;AAAA,MACH;AAAA;AAAA,EACF;AAEJ;AAIA,SAAS,qBACP,MACA,SACsB;AAGtB,QAAM,UAAU,QAAQ,QAAQ,uBAAuB,MAAM;AAC7D,QAAM,QAAQ,IAAI;AAAA,IAChB,gBAAgB,OAAO,UAAU,OAAO;AAAA,EAC1C;AACA,QAAM,QAAQ,MAAM,KAAK,IAAI;AAC7B,MAAI,UAAU,MAAM;AAClB,UAAM,yBAAyB,MAAM,CAAC;AACtC,UAAM,iBAAiB,MAAM,CAAC;AAC9B,WAAO;AAAA,MACL,YAAY,MAAM,QAAQ,uBAAuB;AAAA,MACjD;AAAA,MACA,mBAAmB,MAAM,CAAC;AAAA,IAC5B;AAAA,EACF;AACA,SAAO;AACT;AAaO,SAAS,cAAc;AAAA,EAC5B;AAAA,EACA,UAAU;AAAA,EACV;AACF,GAAuB;AACrB,QAAM,CAAC,MAAM,QAAI,yDAA0B;AAC3C,QAAM,CAAC,aAAa,cAAc,QAAI,wBAAwB,IAAI;AAGlE,QAAM,gCAA4B,iEAA8B,KAAK;AAAA,IACnE,WAAW;AAAA,EACb,CAAC;AAGD,QAAM,cAAU,uBAAQ,MAAM;AAC5B,QAAI,gBAAgB;AAClB,aAAO,MAAM,MAAM,GAAG,4BAA4B;AACpD,UAAM,IAAI,YAAY,YAAY;AAClC,WAAO,MACJ,OAAO,CAAC,MAAM,EAAE,KAAK,YAAY,EAAE,SAAS,CAAC,CAAC,EAC9C,MAAM,GAAG,4BAA4B;AAAA,EAC1C,GAAG,CAAC,OAAO,WAAW,CAAC;AAEvB,QAAM,cAAU;AAAA,IACd,MAAM,QAAQ,IAAI,CAAC,SAAS,IAAI,uBAAuB,IAAI,CAAC;AAAA,IAC5D,CAAC,OAAO;AAAA,EACV;AAEA,QAAM,qBAAiB;AAAA,IACrB,CACE,gBACA,eACA,cACG;AACH,aAAO,OAAO,MAAM;AAClB,cAAM,cAAc;AAAA,UAClB,eAAe,KAAK;AAAA,UACpB,eAAe,KAAK;AAAA,QACtB;AACA,YAAI,eAAe;AACjB,wBAAc,QAAQ,WAAW;AAAA,QACnC;AAEA,cAAM,gBAAY,iCAAgB,GAAG;AACrC,oBAAY,YAAY,SAAS;AACjC,kBAAU,UAAU;AACpB,kBAAU;AAAA,MACZ,CAAC;AACD,wBAAkB,eAAe,IAAI;AAAA,IACvC;AAAA,IACA,CAAC,QAAQ,eAAe;AAAA,EAC1B;AAEA,QAAM,6BAAyB;AAAA,IAC7B,CAAC,SAAiB;AAChB,YAAM,aAAa,0BAA0B,MAAM,MAAM;AACzD,UAAI,eAAe,MAAM;AACvB,eAAO;AAAA,MACT;AACA,aAAO,qBAAqB,MAAM,OAAO;AAAA,IAC3C;AAAA,IACA,CAAC,2BAA2B,QAAQ,OAAO;AAAA,EAC7C;AAEA,SACE;AAAA,IAAC;AAAA;AAAA,MACC,eAAe;AAAA,MACf;AAAA,MACA,WAAW;AAAA,MACX;AAAA,MACA,cAAc,CACZ,kBACA,EAAE,eAAe,wBAAwB,oBAAoB,MAE7D,iBAAiB,WAAW,QAAQ,SACvB;AAAA,QACP,6CAAC,SAAI,WAAU,uBACb;AAAA,UAAC;AAAA;AAAA,YACC;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA;AAAA,QACF,GACF;AAAA,QACA,iBAAiB;AAAA,MACnB,IACA;AAAA;AAAA,EAER;AAEJ;;;ACpTA,IAAAC,iCAA0C;AAC1C,uBAAoC;AACpC,IAAAC,kBAcO;AACP,IAAAC,gBAA+C;;;ACcxC,IAAM,0BAA0C;AAAA,EACrD,SAAS;AAAA,IACP;AAAA;AAAA,IACA;AAAA;AAAA,IACA;AAAA;AAAA,IACA;AAAA;AAAA,IACA;AAAA;AAAA,IACA;AAAA;AAAA,IACA;AAAA;AAAA,EACF;AAAA,EACA,SAAS,CAAC;AACZ;AAoDA,SAAS,sBAAsB,MAA0C;AACvE,QAAM,cAAc,KAAK,kBAAkB;AAE3C,MAAI;AACJ,MAAI,OAAO,gBAAgB,UAAU;AACnC,eAAW;AACX,WAAO,SAAS,WAAW,KAAK,CAAC;AAAA,EACnC;AAGA,SAAO,OAAO,OAAO,WAAW,EAAE,KAAK;AACzC;AAMA,SAAS,wBACP,MACA,OACA,UACS;AACT,aAAW,UAAU,UAAU;AAG7B,UAAM,sBAAqC,CAAC;AAC5C,aAAS,IAAI,GAAG,IAAI,OAAO,QAAQ,KAAK;AACtC,UAAI,OAAO,CAAC,MAAM,IAAK,qBAAoB,KAAK,CAAC;AAAA,IACnD;AAEA,eAAW,MAAM,qBAAqB;AAEpC,YAAM,cAAc,QAAQ;AAC5B,UAAI,cAAc,EAAG;AACrB,YAAM,YAAY,cAAc,OAAO;AACvC,UAAI,YAAY,KAAK,OAAQ;AAE7B,YAAM,QAAQ,KAAK,MAAM,aAAa,SAAS;AAC/C,UAAI,UAAU,OAAQ;AAItB,UAAI,cAAc,KAAK,KAAK,KAAK,KAAK,cAAc,CAAC,CAAC,GAAG;AACvD,eAAO;AAAA,MACT;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AAqBO,SAAS,aACd,MACA,UAA+B,CAAC,GACP;AACzB,QAAM;AAAA,IACJ,qBAAqB;AAAA,IACrB,eAAe;AAAA,IACf,oBAAoB;AAAA,EACtB,IAAI;AAEJ,QAAM,iBAAiB,qBACnB,sBAAsB,OAAO,IAC7B,CAAC;AAEL,QAAM,SAAS,oBAAI,IAAwB;AAI3C,MAAI,aAAuC;AAC3C,MAAI,aAAuC;AAE3C,WAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;AACpC,UAAM,KAAK,KAAK,CAAC;AAGjB,QAAI,OAAO,MAAM;AACf;AACA;AAAA,IACF;AAGA,QAAI,OAAO,KAAK;AAGd,UAAI,CAAC,gBAAgB,CAAC,qBAAqB,WAAY;AAEvD,UAAI,YAAY;AAGd,YACE,CAAC,gBACD,cACA,WAAW,QAAQ,WAAW,OAC9B;AACA,uBAAa;AAAA,QACf;AAGA,cAAM,QAAoB;AAAA,UACxB,OAAO,WAAW;AAAA,UAClB,KAAK;AAAA,UACL,WAAW;AAAA,UACX,SAAS,KAAK,MAAM,WAAW,QAAQ,GAAG,CAAC;AAAA,UAC3C,QAAQ;AAAA,QACV;AACA,eAAO,IAAI,WAAW,OAAO,KAAK;AAClC,eAAO,IAAI,GAAG,KAAK;AACnB,qBAAa;AAAA,MACf,OAAO;AAEL,qBAAa,EAAE,OAAO,EAAE;AAAA,MAC1B;AACA;AAAA,IACF;AAGA,QAAI,OAAO,KAAK;AAGd,UAAI,CAAC,gBAAgB,CAAC,qBAAqB,WAAY;AAGvD,UACE,sBACA,eAAe,SAAS,KACxB,wBAAwB,MAAM,GAAG,cAAc,GAC/C;AACA;AAAA,MACF;AAEA,UAAI,YAAY;AAGd,YACE,CAAC,gBACD,cACA,WAAW,QAAQ,WAAW,OAC9B;AACA,uBAAa;AAAA,QACf;AAGA,cAAM,QAAoB;AAAA,UACxB,OAAO,WAAW;AAAA,UAClB,KAAK;AAAA,UACL,WAAW;AAAA,UACX,SAAS,KAAK,MAAM,WAAW,QAAQ,GAAG,CAAC;AAAA,UAC3C,QAAQ;AAAA,QACV;AACA,eAAO,IAAI,WAAW,OAAO,KAAK;AAClC,eAAO,IAAI,GAAG,KAAK;AACnB,qBAAa;AAAA,MACf,OAAO;AAEL,qBAAa,EAAE,OAAO,EAAE;AAAA,MAC1B;AACA;AAAA,IACF;AAAA,EACF;AAGA,MAAI,YAAY;AACd,WAAO,IAAI,WAAW,OAAO;AAAA,MAC3B,OAAO,WAAW;AAAA,MAClB,KAAK;AAAA,MACL,WAAW;AAAA,MACX,SAAS,KAAK,MAAM,WAAW,QAAQ,CAAC;AAAA,MACxC,QAAQ;AAAA,IACV,CAAC;AAAA,EACH;AAEA,MAAI,YAAY;AACd,WAAO,IAAI,WAAW,OAAO;AAAA,MAC3B,OAAO,WAAW;AAAA,MAClB,KAAK;AAAA,MACL,WAAW;AAAA,MACX,SAAS,KAAK,MAAM,WAAW,QAAQ,CAAC;AAAA,MACxC,QAAQ;AAAA,IACV,CAAC;AAAA,EACH;AAEA,SAAO;AACT;;;AC1RA,IAAM,SAAS;AAcf,SAAS,kBACP,MACA,eAAe,MAUd;AAED,QAAM,UAA8B,CAAC;AACrC,SAAO,YAAY;AACnB,MAAI;AACJ,UAAQ,IAAI,OAAO,KAAK,IAAI,OAAO,MAAM;AACvC,UAAM,YAAY,EAAE,CAAC,MAAM;AAC3B,UAAM,gBAAgB,EAAE,CAAC,MAAM,OAAQ,CAAC,aAAa,EAAE,CAAC,EAAE,SAAS,IAAI;AACvE,YAAQ,KAAK;AAAA,MACX,OAAO,EAAE;AAAA,MACT,KAAK,EAAE,QAAQ,EAAE,CAAC,EAAE;AAAA,MACpB,SAAS,EAAE,CAAC,EAAE,YAAY;AAAA,MAC1B;AAAA,MACA;AAAA,MACA,cAAc,EAAE,CAAC;AAAA,IACnB,CAAC;AAAA,EACH;AAGA,MAAI,UAAU;AACd,QAAM,QAA2D,CAAC;AAClE,QAAM,SASD,CAAC;AAEN,WAAS,IAAI,GAAG,IAAI,QAAQ,QAAQ,KAAK;AACvC,UAAM,MAAM,QAAQ,CAAC;AAErB,QAAI,IAAI,eAAe;AACrB,YAAM,MAAM;AACZ,aAAO,KAAK;AAAA,QACV,GAAG;AAAA,QACH,WAAW;AAAA,QACX,aAAa,IAAI,GAAG;AAAA,MACtB,CAAC;AAAA,IACH,WAAW,CAAC,IAAI,WAAW;AAEzB,YAAM,MAAM;AACZ,YAAM,KAAK,EAAE,MAAM,IAAI,SAAS,KAAK,KAAK,EAAE,CAAC;AAAA,IAC/C,OAAO;AAEL,UAAI,WAAW;AACf,eAAS,IAAI,MAAM,SAAS,GAAG,KAAK,GAAG,KAAK;AAC1C,YAAI,MAAM,CAAC,EAAE,SAAS,IAAI,SAAS;AACjC,qBAAW;AACX;AAAA,QACF;AAAA,MACF;AACA,UAAI,YAAY,GAAG;AACjB,cAAM,YAAY,MAAM,QAAQ;AAChC,cAAM,OAAO,UAAU,CAAC;AACxB,cAAM,UAAU,QAAQ,UAAU,GAAG;AAGrC,eAAO,KAAK;AAAA,UACV,GAAG;AAAA,UACH,WAAW,UAAU;AAAA,UACrB,aAAa,IAAI,UAAU,GAAG;AAAA,QAChC,CAAC;AACD,eAAO,KAAK;AAAA,UACV,GAAG;AAAA,UACH,WAAW,UAAU;AAAA,UACrB,aAAa,KAAK,UAAU,GAAG;AAAA,QACjC,CAAC;AAAA,MACH;AAAA,IAEF;AAAA,EACF;AAGA,SAAO,KAAK,CAAC,GAAG,MAAM,EAAE,QAAQ,EAAE,KAAK;AACvC,SAAO;AACT;AAGA,IAAM,oBAAoB;AAM1B,SAAS,iBACP,MACA,eAUC;AACD,MAAI;AACJ,MAAI;AACF,SAAK,IAAI,OAAO,eAAe,GAAG;AAAA,EACpC,QAAQ;AACN,WAAO,CAAC;AAAA,EACV;AAWA,QAAM,UAA2B,CAAC;AAClC,MAAI;AACJ,UAAQ,IAAI,GAAG,KAAK,IAAI,OAAO,MAAM;AACnC,QAAI,EAAE,CAAC,EAAE,WAAW,GAAG;AACrB,SAAG;AACH;AAAA,IACF;AACA,UAAM,YAAY,kBAAkB,KAAK,EAAE,CAAC,CAAC;AAC7C,QAAI,WAAW;AACb,cAAQ,KAAK;AAAA,QACX,OAAO,EAAE;AAAA,QACT,KAAK,EAAE,QAAQ,EAAE,CAAC,EAAE;AAAA,QACpB,MAAM,EAAE,CAAC;AAAA,QACT,UAAU,UAAU,CAAC,EAAE,YAAY;AAAA,QACnC,WAAW,UAAU,CAAC,MAAM;AAAA,QAC5B,eACE,UAAU,CAAC,MAAM,OAAQ,CAAC,UAAU,CAAC,KAAK,EAAE,CAAC,EAAE,SAAS,IAAI;AAAA,MAChE,CAAC;AAAA,IACH,OAAO;AACL,cAAQ,KAAK;AAAA,QACX,OAAO,EAAE;AAAA,QACT,KAAK,EAAE,QAAQ,EAAE,CAAC,EAAE;AAAA,QACpB,MAAM,EAAE,CAAC;AAAA,QACT,UAAU;AAAA,QACV,WAAW;AAAA,QACX,eAAe;AAAA,MACjB,CAAC;AAAA,IACH;AAAA,EACF;AAaA,MAAI,UAAU;AACd,QAAM,QAA2D,CAAC;AAClE,QAAM,SAA4B,CAAC;AAEnC,WAAS,IAAI,GAAG,IAAI,QAAQ,QAAQ,KAAK;AACvC,UAAM,MAAM,QAAQ,CAAC;AAErB,QAAI,CAAC,IAAI,UAAU;AAEjB,YAAM,MAAM;AACZ,aAAO,KAAK;AAAA,QACV,OAAO,IAAI;AAAA,QACX,KAAK,IAAI;AAAA,QACT,SAAS,IAAI;AAAA,QACb,WAAW;AAAA,QACX,WAAW;AAAA,QACX,eAAe;AAAA,QACf,cAAc,IAAI;AAAA,QAClB,aAAa,IAAI,GAAG;AAAA,MACtB,CAAC;AACD;AAAA,IACF;AAGA,QAAI,IAAI,eAAe;AACrB,YAAM,MAAM;AACZ,aAAO,KAAK;AAAA,QACV,OAAO,IAAI;AAAA,QACX,KAAK,IAAI;AAAA,QACT,SAAS,IAAI;AAAA,QACb,WAAW;AAAA,QACX,WAAW;AAAA,QACX,eAAe;AAAA,QACf,cAAc,IAAI;AAAA,QAClB,aAAa,IAAI,GAAG;AAAA,MACtB,CAAC;AAAA,IACH,WAAW,CAAC,IAAI,WAAW;AAEzB,YAAM,MAAM;AACZ,YAAM,KAAK,EAAE,MAAM,IAAI,UAAU,KAAK,KAAK,EAAE,CAAC;AAAA,IAChD,OAAO;AAEL,UAAI,WAAW;AACf,eAAS,IAAI,MAAM,SAAS,GAAG,KAAK,GAAG,KAAK;AAC1C,YAAI,MAAM,CAAC,EAAE,SAAS,IAAI,UAAU;AAClC,qBAAW;AACX;AAAA,QACF;AAAA,MACF;AACA,UAAI,YAAY,GAAG;AACjB,cAAM,YAAY,MAAM,QAAQ;AAChC,cAAM,OAAO,UAAU,CAAC;AACxB,cAAM,UAAU,QAAQ,UAAU,GAAG;AACrC,eAAO,KAAK;AAAA,UACV,OAAO,QAAQ;AAAA,UACf,KAAK,QAAQ;AAAA,UACb,SAAS,QAAQ;AAAA,UACjB,WAAW,UAAU;AAAA,UACrB,WAAW;AAAA,UACX,eAAe;AAAA,UACf,cAAc,QAAQ;AAAA,UACtB,aAAa,IAAI,UAAU,GAAG;AAAA,QAChC,CAAC;AACD,eAAO,KAAK;AAAA,UACV,OAAO,IAAI;AAAA,UACX,KAAK,IAAI;AAAA,UACT,SAAS,IAAI;AAAA,UACb,WAAW,UAAU;AAAA,UACrB,WAAW;AAAA,UACX,eAAe;AAAA,UACf,cAAc,IAAI;AAAA,UAClB,aAAa,KAAK,UAAU,GAAG;AAAA,QACjC,CAAC;AAAA,MACH;AAAA,IAEF;AAAA,EACF;AAEA,SAAO,KAAK,CAAC,GAAG,MAAM,EAAE,QAAQ,EAAE,KAAK;AACvC,SAAO;AACT;AAIO,SAAS,yBACd,MACA,OACyB;AAEzB,QAAM,YAA6B,CAAC;AAEpC,aAAW,QAAQ,OAAO;AACxB,QAAI,KAAK,SAAS,cAAc;AAC9B,iBAAW,KAAK,KAAK,aAAa;AAChC,YAAI,EAAE,QAAQ,KAAK,EAAE,SAAS,EAAE,OAAO,CAAC,EAAE,QAAS;AAOnD,YAAI,aAAa;AACjB,YAAI,WAAW;AAEf,YAAI,EAAE,OAAO,KAAK,UAAU,KAAK,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,EAAE,SAAS;AACpE,uBAAa,EAAE;AACf,qBAAW,EAAE;AAAA,QACf,OAAO;AAEL,gBAAM,eAAe,KAAK,IAAI,IAAI,EAAE,QAAQ,SAAS,CAAC;AACtD,gBAAM,aAAa,KAAK,IAAI,GAAG,EAAE,QAAQ,YAAY;AACrD,gBAAM,WAAW,KAAK,IAAI,KAAK,QAAQ,EAAE,MAAM,YAAY;AAC3D,gBAAM,cAAc,KAAK,MAAM,YAAY,QAAQ,EAAE,YAAY;AACjE,gBAAM,eAAe,EAAE,QAAQ,YAAY;AAC3C,gBAAM,MAAM,YAAY,QAAQ,YAAY;AAC5C,cAAI,QAAQ,IAAI;AACd,yBAAa,aAAa;AAC1B,uBAAW,aAAa,EAAE,QAAQ;AAAA,UACpC;AAAA,QACF;AAEA,YAAI,cAAc,GAAG;AACnB,oBAAU,KAAK;AAAA,YACb,OAAO;AAAA,YACP,KAAK;AAAA,YACL,YAAY;AAAA,cACV,MAAM;AAAA,cACN,IAAI,MAAM,UAAU,IAAI,QAAQ;AAAA,cAChC,MAAM;AAAA,YACR;AAAA,UACF,CAAC;AAAA,QACH;AAAA,MACF;AAAA,IACF,WAAW,KAAK,SAAS,YAAY;AACnC,YAAM,EAAE,OAAO,QAAQ,IAAI;AAC3B,iBAAW,SAAS,SAAS;AAC3B,YAAI,CAAC,MAAM,QAAQ,CAAC,MAAM,QAAS;AAEnC,YAAI,MAAM,SAAS;AAEjB,cAAI;AACJ,cAAI;AACF,iBAAK,IAAI,OAAO,MAAM,SAAS,GAAG;AAAA,UACpC,QAAQ;AACN;AAAA,UACF;AACA,cAAI;AACJ,kBAAQ,IAAI,GAAG,KAAK,IAAI,OAAO,MAAM;AACnC,sBAAU,KAAK;AAAA,cACb,OAAO,EAAE;AAAA,cACT,KAAK,EAAE,QAAQ,EAAE,CAAC,EAAE;AAAA,cACpB,YAAY;AAAA,gBACV,MAAM;AAAA,gBACN,IAAI,MAAM,KAAK,IAAI,EAAE,KAAK,IAAI,EAAE,QAAQ,EAAE,CAAC,EAAE,MAAM;AAAA,gBACnD,MAAM;AAAA,kBACJ;AAAA,kBACA,MAAM,MAAM,QAAQ,MAAM;AAAA,kBAC1B,aAAa,MAAM;AAAA,gBACrB;AAAA,cACF;AAAA,YACF,CAAC;AAED,gBAAI,EAAE,CAAC,EAAE,WAAW,EAAG,IAAG;AAAA,UAC5B;AAAA,QACF,OAAO;AAEL,cAAI,MAAM;AACV,kBAAQ,MAAM,KAAK,QAAQ,MAAM,MAAM,GAAG,OAAO,IAAI;AACnD,sBAAU,KAAK;AAAA,cACb,OAAO;AAAA,cACP,KAAK,MAAM,MAAM,KAAK;AAAA,cACtB,YAAY;AAAA,gBACV,MAAM;AAAA,gBACN,IAAI,MAAM,KAAK,IAAI,GAAG,IAAI,MAAM,MAAM,KAAK,MAAM;AAAA,gBACjD,MAAM;AAAA,kBACJ;AAAA,kBACA,MAAM,MAAM;AAAA,kBACZ,aAAa,MAAM;AAAA,gBACrB;AAAA,cACF;AAAA,YACF,CAAC;AACD,mBAAO,MAAM,KAAK;AAAA,UACpB;AAAA,QACF;AAAA,MACF;AAAA,IACF,WAAW,KAAK,SAAS,OAAO;AAC9B,YAAM,QAAQ,KAAK,UACf,iBAAiB,MAAM,KAAK,OAAO,IACnC,kBAAkB,MAAM,KAAK,eAAe,IAAI;AACpD,iBAAW,KAAK,OAAO;AAIrB,cAAM,SAAS,CAAC,KAAK,WAAW,EAAE,YAAY,EAAE;AAChD,kBAAU,KAAK;AAAA,UACb,OAAO,EAAE;AAAA,UACT,KAAK,EAAE;AAAA,UACP,YAAY;AAAA,YACV,MAAM;AAAA,YACN,IAAI,OAAO,EAAE,KAAK,IAAI,EAAE,GAAG;AAAA,YAC3B,MAAM;AAAA,cACJ,WAAW,EAAE;AAAA,cACb,SAAS,EAAE;AAAA,cACX,WAAW,EAAE;AAAA,cACb,eAAe,EAAE;AAAA,cACjB,cAAc,EAAE;AAAA,cAChB,aAAa,EAAE;AAAA,cACf;AAAA,YACF;AAAA,UACF;AAAA,QACF,CAAC;AAAA,MACH;AAAA,IACF,WAAW,KAAK,SAAS,SAAS;AAGhC,YAAM,WAAW,aAAa,MAAM,KAAK,aAAa;AACtD,YAAM,OAAO,oBAAI,IAAY;AAC7B,iBAAW,CAAC,EAAE,EAAE,KAAK,UAAU;AAI7B,YAAI,KAAK,IAAI,GAAG,KAAK,EAAG;AACxB,aAAK,IAAI,GAAG,KAAK;AAEjB,cAAM,UACJ,GAAG,cAAc,WAAW,KAAK,cAAc,KAAK;AACtD,cAAM,eAAe,GAAG,cAAc,WAAW,MAAM;AAGvD,kBAAU,KAAK;AAAA,UACb,OAAO,GAAG;AAAA,UACV,KAAK,GAAG,QAAQ;AAAA,UAChB,YAAY;AAAA,YACV,MAAM;AAAA,YACN,IAAI,KAAK,GAAG,SAAS,SAAS,GAAG,KAAK;AAAA,YACtC,MAAM;AAAA,cACJ,WAAW,GAAG;AAAA,cACd,UAAU;AAAA,cACV;AAAA,cACA,iBAAiB,QAAQ;AAAA,YAC3B;AAAA,UACF;AAAA,QACF,CAAC;AAGD,YAAI,GAAG,UAAU,GAAG,QAAQ,MAAM;AAChC,oBAAU,KAAK;AAAA,YACb,OAAO,GAAG;AAAA,YACV,KAAK,GAAG,MAAM;AAAA,YACd,YAAY;AAAA,cACV,MAAM;AAAA,cACN,IAAI,KAAK,GAAG,SAAS,UAAU,GAAG,GAAG;AAAA,cACrC,MAAM;AAAA,gBACJ,WAAW,GAAG;AAAA,gBACd,UAAU;AAAA,gBACV;AAAA,gBACA,iBAAiB,QAAQ;AAAA,cAC3B;AAAA,YACF;AAAA,UACF,CAAC;AAAA,QACH;AAAA,MACF;AAAA,IACF,WAAW,KAAK,SAAS,gBAAgB;AACvC,YAAM,aAAuC,CAAC,GAAG,KAAK,OAAO;AAC7D,iBAAW,SAAS,YAAY;AAE9B,cAAM,QAAQ,MAAM,QAAQ,MAAM,SAAS,GAAG,IAC1C,MAAM,QAAQ,QACd,MAAM,QAAQ,QAAQ;AAC1B,cAAM,KAAK,IAAI,OAAO,MAAM,QAAQ,QAAQ,KAAK;AACjD,YAAI;AACJ,gBAAQ,IAAI,GAAG,KAAK,IAAI,OAAO,MAAM;AACnC,gBAAM,WAAW,EAAE,CAAC;AACpB,gBAAM,KAAK,SACR,MAAM,EAAE,EACR;AAAA,YACC,CAAC,MACC,QACC,EAAE,YAAY,CAAC,KAAK,GAClB,SAAS,EAAE,EACX,YAAY,EACZ,SAAS,GAAG,GAAG;AAAA,UACtB,EACC,KAAK,GAAG;AACX,oBAAU,KAAK;AAAA,YACb,OAAO,EAAE;AAAA,YACT,KAAK,EAAE,QAAQ,SAAS;AAAA,YACxB,YAAY;AAAA,cACV,MAAM;AAAA,cACN,IAAI,MAAM,EAAE,KAAK,IAAI,EAAE,QAAQ,SAAS,MAAM;AAAA,cAC9C,MAAM,EAAE,MAAM,MAAM,MAAM,MAAM,UAAU,WAAW,GAAG;AAAA,YAC1D;AAAA,UACF,CAAC;AAAA,QACH;AAAA,MACF;AAAA,IACF,WAAW,KAAK,SAAS,QAAQ;AAC/B,YAAM,iBAAiB,OAAO;AAC9B,YAAM,gBAAgB,KAAK,WAAW;AACtC,UAAI;AACJ,UAAI;AACF,aAAK,IAAI,OAAO,eAAe,IAAI;AAAA,MACrC,QAAQ;AACN;AAAA,MACF;AACA,UAAI;AACJ,cAAQ,IAAI,GAAG,KAAK,IAAI,OAAO,MAAM;AACnC,YAAI,EAAE,CAAC,EAAE,WAAW,GAAG;AACrB,aAAG;AACH;AAAA,QACF;AAEA,YAAI,UAAU,EAAE,CAAC;AACjB,cAAM,gBAAgB;AACtB,cAAM,gBAAgB,cAAc,KAAK,OAAO;AAChD,YAAI,eAAe;AACjB,oBAAU,QAAQ,MAAM,GAAG,CAAC,cAAc,CAAC,EAAE,MAAM;AAAA,QACrD;AACA,cAAM,MAAM,EAAE,QAAQ,QAAQ;AAE9B,cAAM,MAAM,QAAQ,WAAW,MAAM,IAAI,aAAa,UAAU;AAChE,kBAAU,KAAK;AAAA,UACb,OAAO,EAAE;AAAA,UACT;AAAA,UACA,YAAY;AAAA,YACV,MAAM;AAAA,YACN,IAAI,QAAQ,EAAE,KAAK,IAAI,GAAG;AAAA,YAC1B,MAAM;AAAA,cACJ;AAAA,cACA,aAAa;AAAA,YACf;AAAA,UACF;AAAA,QACF,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AAEA,MAAI,UAAU,WAAW,EAAG,QAAO,CAAC;AAWpC,QAAM,WAAW,MAAM,OAAO,CAAC,MAAM,EAAE,SAAS,KAAK;AACrD,QAAM,gBAAgB,SAAS,KAAK,CAAC,MAAM,EAAE,SAAS;AACtD,QAAM,YAAY,UAAU,OAAO,CAAC,MAAM,EAAE,WAAW,SAAS,KAAK;AACrE,QAAM,oBAAoB,MACvB,OAAO,CAAC,MAAuB,EAAE,SAAS,OAAO,EACjD,KAAK,CAAC,MAAM,EAAE,YAAY;AAE7B,MAAI,iBAAiB;AACrB,MAAI,UAAU,SAAS,GAAG;AACxB,qBAAiB,UAAU,OAAO,CAAC,MAAM;AACvC,UAAI,EAAE,WAAW,SAAS,MAAO,QAAO;AAExC,UAAI,EAAE,WAAW,SAAS,WAAW,CAAC,mBAAmB;AACvD,eAAO,CAAC,UAAU,KAAK,CAAC,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,OAAO,EAAE,GAAG;AAAA,MACpE;AAEA,UAAI,eAAe;AACjB,eAAO,CAAC,UAAU,KAAK,CAAC,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK;AAAA,MAClE;AACA,aAAO;AAAA,IACT,CAAC;AAAA,EACH;AAGA,QAAM,SAAS,oBAAI,IAAY;AAC/B,aAAW,KAAK,gBAAgB;AAC9B,WAAO,IAAI,EAAE,KAAK;AAClB,WAAO,IAAI,EAAE,GAAG;AAAA,EAClB;AACA,QAAM,eAAe,CAAC,GAAG,MAAM,EAAE,KAAK,CAAC,GAAG,MAAM,IAAI,CAAC;AAKrD,QAAM,WAAoC,CAAC;AAC3C,WAAS,IAAI,GAAG,IAAI,aAAa,SAAS,GAAG,KAAK;AAChD,UAAM,WAAW,aAAa,CAAC;AAC/B,UAAM,SAAS,aAAa,IAAI,CAAC;AACjC,UAAM,cAAc,eACjB,OAAO,CAAC,MAAM,EAAE,SAAS,YAAY,EAAE,OAAO,MAAM,EACpD,IAAI,CAAC,MAAM,EAAE,UAAU;AAC1B,QAAI,YAAY,SAAS,GAAG;AAC1B,eAAS,KAAK,EAAE,OAAO,UAAU,KAAK,QAAQ,YAAY,CAAC;AAAA,IAC7D;AAAA,EACF;AAEA,SAAO;AACT;;;AC/kBA,IAAAC,kBAAyB;AAczB,SAAS,gBAAgB,MAA8B;AACrD,QAAM,QAAQ,KAAK,iBAAiB,MAAM,GAAG;AAC7C,MAAI,MAAM,SAAS,eAAe,EAAG,QAAO;AAC5C,MAAI,MAAM,SAAS,OAAO,KAAK,KAAK,cAAe,QAAO;AAC1D,SAAO;AACT;AAMO,SAAS,qBAAqB,SAAiB,QAAwB;AAC5E,QAAM,WAAO,0BAAS;AACtB,QAAM,aAAa,KAAK,YAAY;AACpC,MAAI,SAAS;AAEb,WAAS,KAAK,GAAG,KAAK,WAAW,QAAQ,MAAM;AAC7C,QAAI,KAAK,EAAG,WAAU;AACtB,UAAM,IAAI,WAAW,EAAE;AAGvB,QAAI,EAAE,OAAO,MAAM,SAAS;AAC1B,UAAI,iBAAiB,GAAG;AACtB,cAAM,WAAY,EAAkB,YAAY;AAChD,YAAI,aAAa;AACjB,iBAAS,KAAK,GAAG,KAAK,KAAK,IAAI,QAAQ,SAAS,MAAM,GAAG,MAAM;AAC7D,gBAAM,QAAQ,SAAS,EAAE;AACzB,cACE,iBAAiB,KAAK,KACtB,MAAM,UAAU,WAAW,gBAAgB;AAE3C;AACF,wBAAc,MAAM,eAAe,EAAE;AAAA,QACvC;AACA,eAAO,SAAS;AAAA,MAClB;AACA,aAAO;AAAA,IACT;AAEA,QAAI,EAAE,iBAAiB,GAAI;AAC3B,eAAW,SAAU,EAAkB,YAAY,GAAG;AACpD,YAAM,aACJ,iBAAiB,KAAK,KAAK,MAAM,UAAU,WAAW,gBAAgB;AAExE,UAAI,MAAM,OAAO,MAAM,SAAS;AAG9B,YAAI,WAAY,QAAO;AAOvB,YAAI,iBAAiB,KAAK,KAAK,MAAM,QAAQ,MAAM,SAAS;AAC1D,iBAAO,UAAU,SAAS,IAAI,MAAM,eAAe,EAAE,SAAS;AAAA,QAChE;AAGA,YAAI,eAAe,KAAK,GAAG;AACzB,iBAAO,UAAU,SAAS,IAAI,MAAM,eAAe,EAAE,SAAS;AAAA,QAChE;AAEA,eAAO,SAAS;AAAA,MAClB;AAEA,UAAI,WAAY;AAChB,gBAAU,MAAM,eAAe,EAAE;AAAA,IACnC;AAAA,EACF;AACA,SAAO;AACT;AAGO,SAAS,qBACd,QACkE;AAClE,QAAM,WAAO,0BAAS;AACtB,QAAM,aAAa,KAAK,YAAY;AACpC,MAAI,YAAY;AAEhB,WAAS,KAAK,GAAG,KAAK,WAAW,QAAQ,MAAM;AAC7C,QAAI,KAAK,GAAG;AACV,UAAI,aAAa,GAAG;AAClB,cAAMC,KAAI,WAAW,EAAE;AACvB,YAAI,iBAAiBA,IAAG;AACtB,qBAAW,SAAUA,GAAkB,YAAY,GAAG;AACpD,gBACE,iBAAiB,KAAK,KACtB,MAAM,UAAU,WAAW,gBAAgB;AAE3C;AACF,mBAAO,EAAE,KAAK,MAAM,OAAO,GAAG,QAAQ,GAAG,MAAM,OAAO;AAAA,UACxD;AAAA,QACF;AACA,eAAO,EAAE,KAAK,WAAW,EAAE,EAAE,OAAO,GAAG,QAAQ,GAAG,MAAM,UAAU;AAAA,MACpE;AACA,mBAAa;AAAA,IACf;AAEA,UAAM,IAAI,WAAW,EAAE;AACvB,QAAI,EAAE,iBAAiB,GAAI;AAE3B,UAAM,cAAe,EAAkB,YAAY;AAEnD,aAAS,KAAK,GAAG,KAAK,YAAY,QAAQ,MAAM;AAC9C,YAAM,QAAQ,YAAY,EAAE;AAC5B,UACE,iBAAiB,KAAK,KACtB,MAAM,UAAU,WAAW,gBAAgB;AAE3C;AACF,YAAM,MAAM,MAAM,eAAe,EAAE;AAKnC,UAAI,iBAAiB,KAAK,KAAK,gBAAgB,KAAK,GAAG;AACrD,YAAI,aAAa,GAAG;AAClB,iBAAO,EAAE,KAAK,EAAE,OAAO,GAAG,QAAQ,IAAI,MAAM,UAAU;AAAA,QACxD;AACA,qBAAa;AACb;AAAA,MACF;AAGA,UAAI,eAAe,KAAK,GAAG;AACzB,YAAI,aAAa,GAAG;AAClB,iBAAO,EAAE,KAAK,EAAE,OAAO,GAAG,QAAQ,IAAI,MAAM,UAAU;AAAA,QACxD;AACA,qBAAa;AACb;AAAA,MACF;AAGA,UAAI,iBAAiB,KAAK,KAAK,MAAM,QAAQ,MAAM,SAAS;AAC1D,YAAI,YAAY,KAAK;AACnB,uBAAa;AACb;AAAA,QACF;AACA,eAAO;AAAA,UACL,KAAK,MAAM,OAAO;AAAA,UAClB,QACE,aAAa,IACT,IACA,aAAa,MACX,MACA,aAAa,MAAM,IACjB,IACA;AAAA,UACV,MAAM;AAAA,QACR;AAAA,MACF;AAGA,UAAI,aAAa,KAAK;AACpB,eAAO;AAAA,UACL,KAAK,MAAM,OAAO;AAAA,UAClB,QAAQ,KAAK,IAAI,GAAG,SAAS;AAAA,UAC7B,MAAM;AAAA,QACR;AAAA,MACF;AACA,mBAAa;AAAA,IACf;AAKA,QAAI,aAAa,GAAG;AAClB,UAAI,WAAW,YAAY;AAC3B,eAAS,KAAK,YAAY,SAAS,GAAG,MAAM,GAAG,MAAM;AACnD,cAAM,IAAI,YAAY,EAAE;AACxB,YAAI,iBAAiB,CAAC,KAAK,EAAE,UAAU,WAAW,gBAAgB,GAAG;AACnE,qBAAW;AAAA,QACb,OAAO;AACL;AAAA,QACF;AAAA,MACF;AACA,aAAO,EAAE,KAAK,EAAE,OAAO,GAAG,QAAQ,UAAU,MAAM,UAAU;AAAA,IAC9D;AAAA,EACF;AAGA,WAAS,KAAK,WAAW,SAAS,GAAG,MAAM,GAAG,MAAM;AAClD,UAAM,IAAI,WAAW,EAAE;AACvB,QAAI,iBAAiB,GAAG;AACtB,YAAM,cAAe,EAAkB,YAAY;AAEnD,eAAS,KAAK,YAAY,SAAS,GAAG,MAAM,GAAG,MAAM;AACnD,cAAM,QAAQ,YAAY,EAAE;AAC5B,YACE,iBAAiB,KAAK,KACtB,MAAM,UAAU,WAAW,gBAAgB;AAE3C;AACF,YAAI,iBAAiB,KAAK,KAAK,gBAAgB,KAAK,EAAG;AACvD,YAAI,eAAe,KAAK,EAAG;AAC3B,eAAO;AAAA,UACL,KAAK,MAAM,OAAO;AAAA,UAClB,QAAQ,MAAM,eAAe,EAAE;AAAA,UAC/B,MAAM;AAAA,QACR;AAAA,MACF;AAEA,UAAI,WAAW,YAAY;AAC3B,eAAS,KAAK,YAAY,SAAS,GAAG,MAAM,GAAG,MAAM;AACnD,cAAM,IAAI,YAAY,EAAE;AACxB,YAAI,iBAAiB,CAAC,KAAK,EAAE,UAAU,WAAW,gBAAgB,GAAG;AACnE,qBAAW;AAAA,QACb,OAAO;AACL;AAAA,QACF;AAAA,MACF;AACA,aAAO,EAAE,KAAK,EAAE,OAAO,GAAG,QAAQ,UAAU,MAAM,UAAU;AAAA,IAC9D;AAAA,EACF;AAEA,SAAO;AACT;;;AH3KO,SAAS,iBAAiB;AAAA,EAC/B;AAAA,EACA;AAAA,EACA;AACF,GAA0B;AACxB,QAAM,CAAC,MAAM,QAAI,0DAA0B;AAC3C,QAAM,aAAS,sBAAsB,IAAI;AAEzC,QAAM,sBAAkB,2BAAY,MAAM;AAExC,0BAAsB,mBAAmB;AAOzC,UAAM,gBAAgB,OAAO,eAAe;AAC5C,UAAM,iBACJ,iBAAiB,QACjB,cAAc,SAAS,cAAc,cAAc,aAAa;AAElE,WAAO;AAAA,MACL,MAAM;AAEJ,2CAAc,gBAAgB;AAE9B,cAAM,WAAO,0BAAS;AAMtB,cAAM,aAAa,KAAK,YAAY;AACpC,cAAM,QAAuB,CAAC;AAC9B,cAAM,gBAAqC,CAAC;AAC5C,YAAI,gBAAgB;AAEpB,iBAAS,OAAO,GAAG,OAAO,WAAW,QAAQ,QAAQ;AACnD,gBAAM,IAAI,WAAW,IAAI;AACzB,cAAI,WAAW;AACf,cAAI,iBAAiB,GAAG;AACtB,uBAAW,SAAU,EAAkB,YAAY,GAAG;AACpD,kBACE,iBAAiB,KAAK,KACtB,MAAM,UAAU,WAAW,gBAAgB,GAC3C;AACA;AAAA,cACF;AACA,kBAAI,eAAe,KAAK,GAAG;AACzB,sBAAM,OAAO,MAAM,eAAe;AAClC,8BAAc,KAAK;AAAA,kBACjB,OAAO,gBAAgB,SAAS;AAAA,kBAChC,KAAK,gBAAgB,SAAS,SAAS,KAAK;AAAA,kBAC5C,WAAW,MAAM;AAAA,kBACjB,aAAa,MAAM;AAAA,kBACnB;AAAA,gBACF,CAAC;AAAA,cACH;AACA,0BAAY,MAAM,eAAe;AAAA,YACnC;AAAA,UACF,OAAO;AACL,uBAAW,EAAE,eAAe;AAAA,UAC9B;AACA,gBAAM,KAAK,QAAQ;AACnB,2BAAiB,SAAS,SAAS;AAAA,QACrC;AACA,cAAM,WAAW,MAAM,KAAK,IAAI;AAKhC,cAAM,cAAc,MAAM;AAAA,UACxB,CAAC,MAAyB,EAAE,SAAS;AAAA,QACvC;AACA,YAAI,aAAa;AACf,gBAAM,UAAU,kBAAkB;AAClC,cAAI;AACJ,kBAAQ,QAAQ,QAAQ,KAAK,QAAQ,OAAO,MAAM;AAChD,kBAAM,aAAa,MAAM;AACzB,kBAAM,WAAW,aAAa,MAAM,CAAC,EAAE;AACvC,kBAAM,UAAU,MAAM,CAAC;AAGvB,kBAAM,eAAe,cAAc;AAAA,cACjC,CAAC,MAAM,EAAE,UAAU,cAAc,EAAE,QAAQ;AAAA,YAC7C;AACA,gBAAI,aAAc;AAGlB,kBAAM,OAAO,YAAY,MAAM,KAAK,CAAC,MAAM,EAAE,OAAO,OAAO;AAC3D,gBAAI,MAAM;AACR,4BAAc,KAAK;AAAA,gBACjB,OAAO;AAAA,gBACP,KAAK;AAAA,gBACL,WAAW,KAAK;AAAA,gBAChB,aAAa,KAAK;AAAA,gBAClB,MAAM,MAAM,CAAC;AAAA,cACf,CAAC;AAAA,YACH;AAAA,UACF;AAAA,QACF;AAMA,YAAI,cAAc;AAClB,iBAAS,KAAK,cAAc,SAAS,GAAG,MAAM,GAAG,MAAM;AACrD,gBAAM,IAAI,cAAc,EAAE;AAC1B,wBACE,YAAY,MAAM,GAAG,EAAE,KAAK,IAC5B,IAAO,OAAO,EAAE,MAAM,EAAE,KAAK,IAC7B,YAAY,MAAM,EAAE,GAAG;AAAA,QAC3B;AACA,cAAM,WAAW,yBAAyB,aAAa,KAAK;AAG5D,cAAM,SAAS,oBAAI,IAA4B;AAC/C,mBAAW,OAAO,UAAU;AAC1B,qBAAW,OAAO,IAAI,aAAa;AACjC,mBAAO,IAAI,IAAI,IAAI,GAAG;AAAA,UACxB;AAAA,QACF;AACA,yBAAiB,UAAU;AAG3B,cAAM,oBAAgB,+BAAc;AACpC,YAAI,cAA6B;AACjC,YAAI,aAA4B;AAChC,gBAAI,mCAAkB,aAAa,GAAG;AACpC,wBAAc;AAAA,YACZ,cAAc,OAAO;AAAA,YACrB,cAAc,OAAO;AAAA,UACvB;AACA,uBAAa;AAAA,YACX,cAAc,MAAM;AAAA,YACpB,cAAc,MAAM;AAAA,UACtB;AAAA,QACF;AAIA,aAAK,MAAM;AAEX,YAAI,SAAS,WAAW,GAAG;AACzB,gBAAM,QAAI,sCAAqB;AAC/B,YAAE,WAAO,iCAAgB,EAAE,CAAC;AAC5B,eAAK,OAAO,CAAC;AACb;AAAA,QACF;AAIA,cAAM,uBAAuB,oBAAI,IAAY;AAM7C,cAAM,qBAAqB,CACzB,WACA,YACA,UACA,aACG;AAEH,gBAAM,cAAc,cAAc;AAAA,YAChC,CAAC,MACC,EAAE,QAAQ,YACV,EAAE,MAAM,cACR,CAAC,qBAAqB,IAAI,EAAE,KAAK;AAAA,UACrC;AACA,cAAI,YAAY,WAAW,GAAG;AAE5B,kBAAM,OAAO,SAAS,MAAM,YAAY,QAAQ;AAChD,gBAAI,KAAK,SAAS,GAAG;AACnB,wBAAU,OAAO,SAAS,IAAI,CAAC;AAAA,YACjC;AACA;AAAA,UACF;AAGA,sBAAY,KAAK,CAAC,GAAG,MAAM,EAAE,QAAQ,EAAE,KAAK;AAC5C,cAAI,SAAS;AAEb,qBAAW,KAAK,aAAa;AAC3B,kBAAM,SAAS,KAAK,IAAI,EAAE,OAAO,UAAU;AAC3C,kBAAM,OAAO,KAAK,IAAI,EAAE,KAAK,QAAQ;AAGrC,gBAAI,SAAS,QAAQ;AACnB,oBAAM,aAAa,SAAS,MAAM,QAAQ,MAAM;AAChD,kBAAI,WAAW,SAAS,GAAG;AACzB,0BAAU,OAAO,SAAS,UAAU,CAAC;AAAA,cACvC;AAAA,YACF;AAGA,sBAAU;AAAA,cACR,mBAAmB,EAAE,WAAW,EAAE,aAAa,EAAE,IAAI;AAAA,YACvD;AACA,iCAAqB,IAAI,EAAE,KAAK;AAChC,qBAAS;AAAA,UACX;AAGA,cAAI,SAAS,UAAU;AACrB,kBAAM,YAAY,SAAS,MAAM,QAAQ,QAAQ;AACjD,gBAAI,UAAU,SAAS,GAAG;AACxB,wBAAU,OAAO,SAAS,SAAS,CAAC;AAAA,YACtC;AAAA,UACF;AAAA,QACF;AAEA,cAAM,YAAY,SAAS,MAAM,IAAI;AACrC,YAAI,eAAe;AAEnB,mBAAW,QAAQ,WAAW;AAC5B,gBAAM,gBAAY,sCAAqB;AACvC,gBAAM,YAAY;AAClB,gBAAM,UAAU,eAAe,KAAK;AAGpC,gBAAM,eAAe,SAAS;AAAA,YAC5B,CAAC,MAAM,EAAE,QAAQ,WAAW,EAAE,MAAM;AAAA,UACtC;AAEA,cAAI,MAAM;AACV,qBAAW,OAAO,cAAc;AAC9B,kBAAM,SAAS,KAAK,IAAI,IAAI,OAAO,SAAS;AAC5C,kBAAM,OAAO,KAAK,IAAI,IAAI,KAAK,OAAO;AAGtC,gBAAI,SAAS,KAAK;AAChB;AAAA,gBAAmB;AAAA,gBAAW;AAAA,gBAAK;AAAA,gBAAQ,CAAC,aAC1C,iCAAgB,IAAI;AAAA,cACtB;AAAA,YACF;AAGA,kBAAM,SAAS,IAAI,YAAY,KAAK,CAAC,MAAM,EAAE,SAAS,KAAK;AAC3D,kBAAM,UAAU,MAAM,KAAK,CAAC,MAAqB,EAAE,SAAS,KAAK;AACjE,kBAAM,gBAAgB,CAAC,CAAC,SAAS;AAGjC,kBAAM,gBAAgB,SAAS,iBAAiB;AAChD,kBAAM,mBACJ,iBACA,CAAC,CAAC,WACD,kBAAkB,SAAS,OAAO,KAAK;AAK1C,kBAAM,WAAW;AAAA,cACf,GAAG,IAAI;AAAA,gBACL,IAAI,YAAY,IAAI,CAAC,MAAM;AACzB,sBAAI,EAAE,SAAS,WAAY,QAAO,YAAY,EAAE,KAAK,KAAK;AAC1D,sBAAI,EAAE,SAAS;AACb,2BAAO,cAAc,EAAE,KAAK,UAAU;AACxC,yBAAO,EAAE;AAAA,gBACX,CAAC;AAAA,cACH;AAAA,YACF;AAIA,gBAAI,kBAAkB;AACpB,uBAAS,KAAK,eAAe;AAAA,YAC/B;AACA,kBAAM,QAAQ,SAAS,KAAK,GAAG;AAC/B,kBAAM,MAAM,IAAI,YAAY,IAAI,CAAC,MAAM,EAAE,EAAE,EAAE,KAAK,GAAG;AAIrD,kBAAM,iBACJ,QAAQ,SAAS,SAAS,mBACtB,OAAO,KAAK,cACZ;AACN,kBAAM,aAAa;AAInB,kBAAM,WAAW,IAAI,YAAY,KAAK,CAAC,MAAM,EAAE,SAAS,OAAO;AAC/D,kBAAM,mBACJ,UAAU,SAAS,UACf,SAAS,KAAK,kBACd;AACN,kBAAM,eAAe,CAAC,CAAC;AAKvB,kBAAM,oBAAoB,cAAc;AAAA,cACtC,CAAC,MAAM,EAAE,SAAS,UAAU,EAAE,OAAO;AAAA,YACvC;AAEA,gBAAI,mBAAmB;AAErB,kBAAI,CAAC,qBAAqB,IAAI,kBAAkB,KAAK,GAAG;AACtD,0BAAU;AAAA,kBACR;AAAA,oBACE,kBAAkB;AAAA,oBAClB,kBAAkB;AAAA,oBAClB,kBAAkB;AAAA,kBACpB;AAAA,gBACF;AACA,qCAAqB,IAAI,kBAAkB,KAAK;AAAA,cAClD;AAAA,YAEF,OAAO;AACL;AAAA,gBAAmB;AAAA,gBAAW;AAAA,gBAAQ;AAAA,gBAAM,CAAC,SAC3C;AAAA,kBACE;AAAA,kBACA;AAAA,kBACA;AAAA,kBACA,kBAAkB;AAAA,kBAClB,cAAc;AAAA,gBAChB;AAAA,cACF;AAAA,YACF;AAEA,kBAAM;AAAA,UACR;AAGA,cAAI,MAAM,SAAS;AACjB;AAAA,cAAmB;AAAA,cAAW;AAAA,cAAK;AAAA,cAAS,CAAC,aAC3C,iCAAgB,IAAI;AAAA,YACtB;AAAA,UACF;AAMA,gBAAM,QAAQ;AACd,cAAI,QAAQ,SAAS,UAAU,SAAS,KAAK,MAAM,MAAM;AACvD,kBAAM,aAAa,SAAS;AAAA,cAC1B,CAAC,MAAM,EAAE,SAAS,SAAS,EAAE,MAAM;AAAA,YACrC;AACA,gBAAI,WAAW,SAAS,GAAG;AACzB,oBAAM,SAAS,WAAW,QAAQ,CAAC,MAAM,EAAE,WAAW;AACtD,oBAAM,QAAQ;AAAA,gBACZ,GAAG,IAAI;AAAA,kBACL,OAAO,IAAI,CAAC,MAAM;AAChB,wBAAI,EAAE,SAAS,WAAY,QAAO,YAAY,EAAE,KAAK,KAAK;AAC1D,wBAAI,EAAE,SAAS;AACb,6BAAO,cAAc,EAAE,KAAK,UAAU;AACxC,2BAAO,EAAE;AAAA,kBACX,CAAC;AAAA,gBACH;AAAA,cACF,EAAE,KAAK,GAAG;AACV,oBAAM,MAAM,OAAO,IAAI,CAAC,MAAM,EAAE,EAAE,EAAE,KAAK,GAAG;AAC5C,oBAAM,SAAS,sBAAsB,EAAM;AAG3C,kBAAI,UAAU,gBAAgB,MAAM,GAAG;AACrC,0BAAU,WAAO,iCAAgB,EAAE,CAAC;AAAA,cACtC;AACA,wBAAU;AAAA,gBACR,qBAAqB,QAAQ,OAAO,mBAAmB,GAAG;AAAA,cAC5D;AAAA,YACF;AAAA,UACF;AAGA,cAAI,UAAU,gBAAgB,MAAM,GAAG;AACrC,sBAAU,WAAO,iCAAgB,EAAE,CAAC;AAAA,UACtC;AAEA,eAAK,OAAO,SAAS;AACrB,yBAAe,UAAU;AAAA,QAC3B;AAQA,YAAI,kBAAkB,gBAAgB,QAAQ,eAAe,MAAM;AACjE,gBAAM,WAAW,qBAAqB,WAAW;AACjD,gBAAM,UAAU,qBAAqB,UAAU;AAC/C,cAAI,YAAY,SAAS;AACvB,kBAAM,UAAM,uCAAsB;AAClC,gBAAI,OAAO,IAAI,SAAS,KAAK,SAAS,QAAQ,SAAS,IAAI;AAC3D,gBAAI,MAAM,IAAI,QAAQ,KAAK,QAAQ,QAAQ,QAAQ,IAAI;AACvD,+CAAc,GAAG;AAAA,UACnB;AAAA,QACF,OAAO;AACL,6CAAc,IAAI;AAAA,QACpB;AAAA,MACF;AAAA,MACA,EAAE,KAAK,WAAW;AAAA,IACpB;AAAA,EACF,GAAG,CAAC,QAAQ,OAAO,gBAAgB,CAAC;AAGpC,+BAAU,MAAM;AACd,oBAAgB;AAAA,EAClB,GAAG,CAAC,eAAe,CAAC;AAKpB,+BAAU,MAAM;AACd,UAAM,aAAa,OAAO;AAAA,MACxB,CAAC,EAAE,MAAM,eAAe,YAAY,MAAM;AACxC,YAAI,KAAK,IAAI,gBAAgB,EAAG;AAKhC,YAAI,cAAc,SAAS,KAAK,YAAY,SAAS,EAAG;AACxD,YAAI,OAAO,QAAS,sBAAqB,OAAO,OAAO;AACvD,eAAO,UAAU,sBAAsB,MAAM;AAC3C,0BAAgB;AAAA,QAClB,CAAC;AAAA,MACH;AAAA,IACF;AACA,WAAO,MAAM;AACX,iBAAW;AACX,UAAI,OAAO,QAAS,sBAAqB,OAAO,OAAO;AAAA,IACzD;AAAA,EACF,GAAG,CAAC,QAAQ,eAAe,CAAC;AAK5B,+BAAU,MAAM;AACd,WAAO,OAAO;AAAA,MACZ;AAAA,MACA,CAAC,UAA0B;AACzB,cAAM,gBAAgB,MAAM;AAC5B,YAAI,CAAC,cAAe,QAAO;AAC3B,cAAM,OAAO,cAAc,QAAQ,YAAY;AAC/C,YAAI,QAAQ,SAAS,KAAK,QAAQ,QAAQ,EAAE,GAAG;AAC7C,gBAAM,eAAe;AACrB,gBAAM,UAAU,KAAK,QAAQ,QAAQ,EAAE;AACvC,iBAAO,OAAO,MAAM;AAClB,kBAAM,gBAAY,+BAAc;AAChC,oBAAI,mCAAkB,SAAS,GAAG;AAChC,wBAAU,cAAc,OAAO;AAAA,YACjC;AAAA,UACF,CAAC;AACD,iBAAO;AAAA,QACT;AACA,eAAO;AAAA,MACT;AAAA,MACA;AAAA,IACF;AAAA,EACF,GAAG,CAAC,MAAM,CAAC;AAEX,SAAO;AACT;AAMO,SAAS,gBAAgB;AAAA,EAC9B;AAAA,EACA;AACF,GAMG;AACD,QAAM,CAAC,MAAM,QAAI,0DAA0B;AAC3C,+BAAU,MAAM;AACd,cAAU,UAAU;AAAA,EACtB,GAAG,CAAC,QAAQ,SAAS,CAAC;AAGtB,+BAAU,MAAM;AACd,WAAO,OAAO,uBAAuB,CAAC,EAAE,YAAY,MAAM;AACxD,kBAAY,KAAK,MAAM;AACrB,cAAM,UAAM,+BAAc;AAC1B,gBAAI,mCAAkB,GAAG,GAAG;AAC1B,4BAAkB,UAAU;AAAA,YAC1B,QAAQ,qBAAqB,IAAI,OAAO,KAAK,IAAI,OAAO,MAAM;AAAA,YAC9D,OAAO,qBAAqB,IAAI,MAAM,KAAK,IAAI,MAAM,MAAM;AAAA,UAC7D;AAAA,QACF;AAAA,MACF,CAAC;AAAA,IACH,CAAC;AAAA,EACH,GAAG,CAAC,QAAQ,iBAAiB,CAAC;AAC9B,SAAO;AACT;AAQA,SAAS,mBAAmB,MAA4B;AACtD,MAAI,CAAC,iBAAiB,IAAI,EAAG,QAAO;AACpC,MAAI,KAAK,UAAU,WAAW,gBAAgB,EAAG,QAAO;AACxD,QAAM,QAAQ,KAAK,iBAAiB,MAAM,GAAG;AAC7C,MAAI,MAAM,SAAS,eAAe,EAAG,QAAO;AAC5C,MAAI,MAAM,SAAS,OAAO,KAAK,KAAK,cAAe,QAAO;AAC1D,SAAO;AACT;AAcA,SAAS,+BAA+B,OAI/B;AAEP,MAAI,MAAM,SAAS,UAAW,QAAO;AAErC,QAAM,OAAO,MAAM,QAAQ;AAC3B,MAAI,CAAC,mBAAmB,IAAI,EAAG,QAAO;AAQtC,QAAM,SAAS,KAAK,UAAU;AAC9B,MAAI,UAAU,iBAAiB,QAAQ;AACrC,UAAM,WAAW,OAAO,YAAY;AACpC,UAAM,MAAM,SAAS,UAAU,CAAC,MAAM,EAAE,OAAO,MAAM,KAAK,OAAO,CAAC;AAClE,QAAI,OAAO,GAAG;AACZ,YAAM,aAAa,MAAM,SAAS,IAAI,MAAM,IAAI;AAChD,aAAO;AAAA,QACL,KAAK,OAAO,OAAO;AAAA,QACnB,QAAQ;AAAA,QACR,MAAM;AAAA,MACR;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;AAOA,SAAS,kBACP,WACkE;AAElE,MACE,iBAAiB,SAAS,KAC1B,UAAU,UAAU,WAAW,gBAAgB,GAC/C;AACA,UAAMC,aAAY,UAAU,UAAU;AACtC,UAAM,gBAAgBA,YAAW,eAAe;AAChD,QAAI,iBAAiB,iBAAiB,eAAe;AACnD,YAAM,QAAS,cAA8B,cAAc;AAC3D,UAAI,SAAS,CAAC,mBAAmB,KAAK,GAAG;AACvC,eAAO,EAAE,KAAK,MAAM,OAAO,GAAG,QAAQ,GAAG,MAAM,OAAO;AAAA,MACxD;AAEA,UAAI,OAAO;AACT,eAAO;AAAA,UACL,KAAK,cAAc,OAAO;AAAA,UAC1B,QAAQ;AAAA,UACR,MAAM;AAAA,QACR;AAAA,MACF;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAGA,QAAM,OAAO,UAAU,eAAe;AACtC,MAAI,QAAQ,CAAC,mBAAmB,IAAI,GAAG;AACrC,WAAO,EAAE,KAAK,KAAK,OAAO,GAAG,QAAQ,GAAG,MAAM,OAAO;AAAA,EACvD;AAIA,QAAM,YAAY,UAAU,UAAU;AACtC,MAAI,aAAa,iBAAiB,WAAW;AAC3C,UAAM,WAAW,UAAU,YAAY;AACvC,UAAM,MAAM,SAAS,UAAU,CAAC,MAAM,EAAE,OAAO,MAAM,UAAU,OAAO,CAAC;AACvE,QAAI,OAAO,GAAG;AACZ,aAAO,EAAE,KAAK,UAAU,OAAO,GAAG,QAAQ,MAAM,GAAG,MAAM,UAAU;AAAA,IACrE;AAAA,EACF;AAEA,SAAO;AACT;AAMA,SAAS,kBACP,WACkE;AAClE,QAAM,OAAO,UAAU,mBAAmB;AAG1C,MAAI,QAAQ,CAAC,mBAAmB,IAAI,GAAG;AACrC,WAAO;AAAA,MACL,KAAK,KAAK,OAAO;AAAA,MACjB,QAAQ,KAAK,eAAe,EAAE;AAAA,MAC9B,MAAM;AAAA,IACR;AAAA,EACF;AAIA,QAAM,YAAY,UAAU,UAAU;AACtC,MAAI,aAAa,iBAAiB,WAAW;AAC3C,UAAM,WAAW,UAAU,YAAY;AACvC,UAAM,MAAM,SAAS,UAAU,CAAC,MAAM,EAAE,OAAO,MAAM,UAAU,OAAO,CAAC;AACvE,QAAI,OAAO,GAAG;AACZ,aAAO,EAAE,KAAK,UAAU,OAAO,GAAG,QAAQ,KAAK,MAAM,UAAU;AAAA,IACjE;AAAA,EACF;AAEA,SAAO;AACT;AAMO,SAAS,2BAA2B;AACzC,QAAM,CAAC,MAAM,QAAI,0DAA0B;AAC3C,+BAAU,MAAM;AAKd,UAAM,aAAa,OAAO;AAAA,MACxB;AAAA,MACA,CAAC,UAAU;AACT,cAAM,gBAAY,+BAAc;AAChC,YAAI,KAAC,mCAAkB,SAAS,KAAK,CAAC,UAAU,YAAY;AAC1D,iBAAO;AAET,cAAM,YAAQ,sCAAoB,SAAS;AAC3C,cAAM,EAAE,OAAO,IAAI;AACnB,cAAM,OAAO,OAAO,QAAQ;AAG5B,YAAI,eAAmC;AACvC,YAAI,OAAO;AAET,cAAI,OAAO,SAAS,QAAQ;AAC1B,gBAAI,OAAO,SAAS,EAAG,QAAO;AAC9B,2BAAe,KAAK,mBAAmB;AAAA,UACzC,OAAO;AACL,kBAAM,WAAY,KAAqB,YAAY;AACnD,2BAAe,SAAS,OAAO,SAAS,CAAC,KAAK;AAAA,UAChD;AAAA,QACF,OAAO;AAEL,cAAI,OAAO,SAAS,QAAQ;AAC1B,gBAAI,OAAO,SAAS,KAAK,eAAe,EAAE,OAAQ,QAAO;AACzD,2BAAe,KAAK,eAAe;AAAA,UACrC,OAAO;AACL,kBAAM,WAAY,KAAqB,YAAY;AACnD,2BAAe,SAAS,OAAO,MAAM,KAAK;AAAA,UAC5C;AAAA,QACF;AAEA,YAAI,gBAAgB,mBAAmB,YAAY,GAAG;AACpD,gBAAM,SAAS,QACX,kBAAkB,YAAY,IAC9B,kBAAkB,YAAY;AAClC,cAAI,QAAQ;AACV,sBAAU,OAAO,IAAI,OAAO,KAAK,OAAO,QAAQ,OAAO,IAAI;AAC3D,sBAAU,MAAM,IAAI,OAAO,KAAK,OAAO,QAAQ,OAAO,IAAI;AAC1D,kBAAM,eAAe;AACrB,mBAAO;AAAA,UACT;AACA,iBAAO;AAAA,QACT;AAGA,YAAI,mBAAmB,IAAI,GAAG;AAC5B,gBAAM,SAAS,QACX,kBAAkB,IAAI,IACtB,kBAAkB,IAAI;AAC1B,cAAI,QAAQ;AACV,sBAAU,OAAO,IAAI,OAAO,KAAK,OAAO,QAAQ,OAAO,IAAI;AAC3D,sBAAU,MAAM,IAAI,OAAO,KAAK,OAAO,QAAQ,OAAO,IAAI;AAC1D,kBAAM,eAAe;AACrB,mBAAO;AAAA,UACT;AACA,iBAAO;AAAA,QACT;AAEA,eAAO;AAAA,MACT;AAAA,MACA;AAAA,IACF;AAKA,UAAM,YAAY,OAAO;AAAA,MACvB;AAAA,MACA,CAAC,UAAU;AACT,cAAM,gBAAY,+BAAc;AAChC,YAAI,KAAC,mCAAkB,SAAS,KAAK,CAAC,UAAU,YAAY;AAC1D,iBAAO;AAET,cAAM,YAAQ,sCAAoB,SAAS;AAC3C,cAAM,EAAE,OAAO,IAAI;AACnB,cAAM,OAAO,OAAO,QAAQ;AAG5B,YAAI,eAAmC;AACvC,YAAI,OAAO;AAET,cAAI,OAAO,SAAS,QAAQ;AAC1B,gBAAI,OAAO,SAAS,KAAK,eAAe,EAAE,OAAQ,QAAO;AACzD,2BAAe,KAAK,eAAe;AAAA,UACrC,OAAO;AACL,kBAAM,WAAY,KAAqB,YAAY;AACnD,2BAAe,SAAS,OAAO,MAAM,KAAK;AAAA,UAC5C;AAAA,QACF,OAAO;AAEL,cAAI,OAAO,SAAS,QAAQ;AAC1B,gBAAI,OAAO,SAAS,EAAG,QAAO;AAC9B,2BAAe,KAAK,mBAAmB;AAAA,UACzC,OAAO;AACL,kBAAM,WAAY,KAAqB,YAAY;AACnD,2BAAe,SAAS,OAAO,SAAS,CAAC,KAAK;AAAA,UAChD;AAAA,QACF;AAEA,YAAI,gBAAgB,mBAAmB,YAAY,GAAG;AACpD,gBAAM,SAAS,QACX,kBAAkB,YAAY,IAC9B,kBAAkB,YAAY;AAClC,cAAI,QAAQ;AACV,sBAAU,OAAO,IAAI,OAAO,KAAK,OAAO,QAAQ,OAAO,IAAI;AAC3D,sBAAU,MAAM,IAAI,OAAO,KAAK,OAAO,QAAQ,OAAO,IAAI;AAC1D,kBAAM,eAAe;AACrB,mBAAO;AAAA,UACT;AACA,iBAAO;AAAA,QACT;AAGA,YAAI,mBAAmB,IAAI,GAAG;AAC5B,gBAAM,SAAS,QACX,kBAAkB,IAAI,IACtB,kBAAkB,IAAI;AAC1B,cAAI,QAAQ;AACV,sBAAU,OAAO,IAAI,OAAO,KAAK,OAAO,QAAQ,OAAO,IAAI;AAC3D,sBAAU,MAAM,IAAI,OAAO,KAAK,OAAO,QAAQ,OAAO,IAAI;AAC1D,kBAAM,eAAe;AACrB,mBAAO;AAAA,UACT;AACA,iBAAO;AAAA,QACT;AAEA,eAAO;AAAA,MACT;AAAA,MACA;AAAA,IACF;AAGA,UAAM,WAAW,OAAO;AAAA,MACtB;AAAA,MACA,MAAM;AACJ,cAAM,gBAAY,+BAAc;AAChC,YAAI,KAAC,mCAAkB,SAAS,EAAG,QAAO;AAE1C,cAAM,YAAY,+BAA+B,UAAU,MAAM;AACjE,cAAM,WAAW,+BAA+B,UAAU,KAAK;AAE/D,YAAI,WAAW;AACb,oBAAU,OAAO,IAAI,UAAU,KAAK,UAAU,QAAQ,UAAU,IAAI;AAAA,QACtE;AACA,YAAI,UAAU;AACZ,oBAAU,MAAM,IAAI,SAAS,KAAK,SAAS,QAAQ,SAAS,IAAI;AAAA,QAClE;AAEA,eAAO;AAAA,MACT;AAAA,MACA;AAAA,IACF;AAEA,WAAO,MAAM;AACX,iBAAW;AACX,gBAAU;AACV,eAAS;AAAA,IACX;AAAA,EACF,GAAG,CAAC,MAAM,CAAC;AACX,SAAO;AACT;;;AIz2BA,IAAAC,SAAuB;AACvB,IAAAC,gBAAwC;AACxC,IAAAC,eAA6B;AAuBvB,IAAAC,sBAAA;AATN,SAAS,yBAAyB;AAAA,EAChC;AAAA,EACA;AACF,GAGG;AACD,SACE,8CAAC,SAAI,WAAU,4BACb;AAAA,kDAAC,SAAI,WAAU,2BACb;AAAA,mDAAC,UAAK,WAAU,6BACb,eAAK,gBAAgB,YACxB;AAAA,MACA,6CAAC,UAAK,WAAU,qCACb,eAAK,YACR;AAAA,OACF;AAAA,IACA,6CAAC,OAAE,WAAU,2CAA2C,eAAK,SAAQ;AAAA,IACpE,KAAK,WACJ,8CAAC,OAAE,WAAU,iCAAgC;AAAA;AAAA,MACpC;AAAA,MACP,6CAAC,UAAK,WAAU,sEACb,eAAK,SACR;AAAA,OACF;AAAA,IAED,KAAK,YAAY,SAAS,KACzB,8CAAC,SAAI,WAAU,eACb;AAAA,mDAAC,OAAE,WAAU,6CAA4C,0BAEzD;AAAA,MACA,6CAAC,SAAI,WAAU,wBACZ,eAAK,YAAY,IAAI,CAAC,GAAG,MACxB;AAAA,QAAC;AAAA;AAAA,UAEC,MAAK;AAAA,UACL,WAAU;AAAA,UACV,SAAS,MAAM,kBAAkB,EAAE,KAAK;AAAA,UAEvC,YAAE;AAAA;AAAA,QALE;AAAA,MAMP,CACD,GACH;AAAA,OACF;AAAA,IAED,KAAK,gBAAgB,KAAK,aAAa,SAAS,KAC/C,8CAAC,OAAE,WAAU,qCAAoC;AAAA;AAAA,MAChC,KAAK,aAAa,KAAK,IAAI;AAAA,OAC5C;AAAA,KAEJ;AAEJ;AAEA,SAAS,uBAAuB;AAAA,EAC9B;AACF,GAEG;AACD,QAAM,eAAe,KAAK,MACvB,MAAM,GAAG,EACT,IAAI,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY,IAAI,EAAE,MAAM,CAAC,CAAC,EACjD,KAAK,GAAG;AAEX,SACE,8CAAC,SAAI,WAAU,0BACb;AAAA;AAAA,MAAC;AAAA;AAAA,QACC,WAAW,mDAAmD,KAAK,KAAK;AAAA,QAEvE;AAAA;AAAA,IACH;AAAA,IACA,8CAAC,OAAE,WAAU,2CAA0C;AAAA;AAAA,MAC/C;AAAA,MACN,6CAAC,YAAO,WAAU,iCAAiC,eAAK,MAAK;AAAA,OAC/D;AAAA,IACC,KAAK,eACJ,6CAAC,OAAE,WAAU,iDACV,eAAK,aACR;AAAA,KAEJ;AAEJ;AAEA,SAAS,0BAA0B;AAAA,EACjC;AACF,GAEG;AACD,QAAM,KAAK,KAAK,KAAK,YAAY,CAAC,KAAK;AACvC,QAAM,eAAe,yBAAyB;AAC9C,QAAM,gBACJ,aAAa,EAAE,MAAM,KAAK,KAAK,KAAK,MAAM,KAAK,SAAM,KAAK;AAE5D,SACE,8CAAC,SAAI,WAAU,0BACb;AAAA,iDAAC,UAAK,WAAU,oCAAmC,0BAAY;AAAA,IAG/D,6CAAC,SAAI,WAAU,oCACb,uDAAC,UAAK,WAAU,2KACb,yBACH,GACF;AAAA,IAEA,6CAAC,OAAE,WAAU,uDACX,uDAAC,YAAO,WAAU,iBAAiB,eAAK,MAAK,GAC/C;AAAA,IACA,6CAAC,SAAI,WAAU,wEACb,uDAAC,UAAK,WAAU,4CACb,eAAK,WACR,GACF;AAAA,KACF;AAEJ;AAEA,SAAS,kBAAkB;AAAA,EACzB;AACF,GASG;AAED,QAAM,gBACJ,CAAC,KAAK,aAAa,CAAC,KAAK,iBAAiB,KAAK,YAAY,KAAK;AAElE,SACE,8CAAC,SAAI,WAAU,0BACb;AAAA,kDAAC,UAAK,WAAU,2BACb;AAAA,sBAAgB,gBAAgB;AAAA,MAAM;AAAA,MAAG,KAAK;AAAA,OACjD;AAAA,IACA,8CAAC,OAAE,WAAU,2CACV;AAAA,sBACG,gBACA,KAAK,YACH,gBACA,KAAK,gBACH,qBACA;AAAA,MAAc;AAAA,MACpB;AAAA,MACF,6CAAC,YAAO,WAAU,iCACf,eAAK,cACR;AAAA,OACF;AAAA,IACA,8CAAC,OAAE,WAAU,iCAAgC;AAAA;AAAA,MAChC;AAAA,MACX,6CAAC,UAAK,WAAU,0CACb,eAAK,aACR;AAAA,OACF;AAAA,IACA,8CAAC,OAAE,WAAU,2CAA0C;AAAA;AAAA,MAC3C;AAAA,MACV,6CAAC,UAAK,WAAU,0CACb,eAAK,cACR;AAAA,OACF;AAAA,KACF;AAEJ;AAEA,SAAS,mBAAmB;AAAA,EAC1B;AAAA,EACA;AACF,GAGG;AACD,SACE,8CAAC,SAAI,WAAU,0BACb;AAAA,iDAAC,UAAK,WAAU,4BAA2B,kBAAI;AAAA,IAC/C,6CAAC,OAAE,WAAU,qDACX,uDAAC,UAAK,WAAU,kDACb,eAAK,KACR,GACF;AAAA,IACA,6CAAC,YAAO,MAAK,UAAS,WAAU,sBAAqB,SAAS,QAAQ,8BAEtE;AAAA,KACF;AAEJ;AAEA,SAAS,oBAAoB;AAAA,EAC3B;AACF,GAOG;AACD,SACE,8CAAC,SAAI,WAAU,0BACb;AAAA;AAAA,MAAC;AAAA;AAAA,QACC,WAAW,6CAA6C,KAAK,SAAS;AAAA,QAErE,eAAK,cAAc,WAAW,iBAAiB;AAAA;AAAA,IAClD;AAAA,IACA,8CAAC,OAAE,WAAU,2CACV;AAAA,WAAK,aAAa,YAAY,YAAY;AAAA,MAAU;AAAA,OACvD;AAAA,IACA,8CAAC,SAAI,WAAU,yDACb;AAAA,mDAAC,UAAK,WAAU,4CACb,eAAK,cACR;AAAA,MACA,6CAAC,UAAK,oBAAC;AAAA,MACP,6CAAC,UAAK,WAAU,4CACb,eAAK,iBACR;AAAA,OACF;AAAA,KACF;AAEJ;AAIO,SAAS,iBAAiB;AAAA,EAC/B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAUG;AACD,QAAM,iBAAa,sBAAuB,IAAI;AAC9C,QAAM,gBAAY,sBAA8B,IAAI;AAKpD,qCAAgB,MAAM;AACpB,UAAM,KAAK,WAAW;AACtB,QAAI,CAAC,IAAI;AAEP,gBAAU,SAAS,QAAQ;AAC3B,gBAAU,UAAU;AACpB;AAAA,IACF;AAIA,UAAM,KAAK,MAAM;AACjB,UAAM,YAAY;AAAA,MAChB,uBAAuB,OACpB;AAAA,QACC,KAAK,IAAI,OAAO,MAAM;AAAA,QACtB,MAAM,IAAI,QAAQ,MAAM;AAAA,QACxB,QAAQ,IAAI,UAAU,MAAM;AAAA,QAC5B,OAAO,IAAI,SAAS,MAAM;AAAA,QAC1B,OAAO,IAAI,SAAS;AAAA,QACpB,QAAQ,IAAI,UAAU;AAAA,QACtB,GAAG,IAAI,QAAQ,MAAM;AAAA,QACrB,GAAG,IAAI,OAAO,MAAM;AAAA,QACpB,QAAQ,MAAM;AAAA,QAAC;AAAA,MACjB;AAAA,IACJ;AAGA,OAAG,MAAM,aAAa;AAEtB,QAAI,UAAU,SAAS;AAErB,gBAAU,QAAQ,MAAM,SAAS,YAC/B;AAAA,IACJ,OAAO;AACL,gBAAU,cAAU,2BAAa,WAAiC,IAAI;AAAA,QACpE,UAAU;AAAA,QACV,WAAW;AAAA,QACX,WAAW;AAAA,UACT,EAAE,MAAM,UAAU,SAAS,EAAE,QAAQ,CAAC,GAAG,CAAC,EAAE,EAAE;AAAA,UAC9C,EAAE,MAAM,mBAAmB,SAAS,EAAE,SAAS,GAAG,EAAE;AAAA,UACpD,EAAE,MAAM,QAAQ,SAAS,EAAE,SAAS,GAAG,EAAE;AAAA,QAC3C;AAAA,MACF,CAAC;AAAA,IACH;AAKA,cAAU,QAAQ,YAAY;AAC9B,OAAG,MAAM,aAAa;AAAA,EACxB,GAAG,CAAC,MAAM,SAAS,MAAM,GAAG,MAAM,GAAG,MAAM,UAAU,CAAC;AAGtD,qCAAgB,MAAM;AACpB,WAAO,MAAM;AACX,gBAAU,SAAS,QAAQ;AAC3B,gBAAU,UAAU;AAAA,IACtB;AAAA,EACF,GAAG,CAAC,CAAC;AAEL,MAAI,CAAC,MAAM,QAAS,QAAO;AAE3B,QAAM,cAAc,MAAM,QACvB,IAAI,CAAC,OAAO,cAAc,IAAI,EAAE,CAAC,EACjC,OAAO,CAAC,MAA2B,KAAK,IAAI;AAE/C,MAAI,YAAY,WAAW,EAAG,QAAO;AAErC,SACE;AAAA,IAAC;AAAA;AAAA,MACC,KAAK;AAAA,MACL,WAAU;AAAA,MACV;AAAA,MACA,OAAO;AAAA,QACL,UAAU;AAAA,QACV,MAAM;AAAA,QACN,KAAK;AAAA,QACL,QAAQ;AAAA,QACR,YAAY;AAAA,MACd;AAAA,MACA,cAAc,MAAM,eAAe;AAAA,MACnC,cAAc,MAAM,UAAU;AAAA,MAE7B,sBAAY,IAAI,CAAC,KAAK,MAAM;AAC3B,cAAM,SAAS,uBAAuB;AAAA,UACpC,YAAY;AAAA,UACZ,mBAAmB,CAAC,MAAM,kBAAkB,GAAG,IAAI,EAAE;AAAA,QACvD,CAAC;AAED,eACE,8CAAO,iBAAN,EACE;AAAA,cAAI,KAAK,6CAAC,QAAG,WAAU,sBAAqB;AAAA,UAC5C,UAAU,OACT,SACE,IAAI,SAAS,eACf;AAAA,YAAC;AAAA;AAAA,cACC,MAAM,IAAI;AAAA,cACV,mBAAmB,CAAC,MAAM,kBAAkB,GAAG,IAAI,EAAE;AAAA;AAAA,UACvD,IACE,IAAI,SAAS,aACf,6CAAC,0BAAuB,MAAM,IAAI,MAAM,IACtC,IAAI,SAAS,QACf,6CAAC,qBAAkB,MAAM,IAAI,MAAM,IACjC,IAAI,SAAS,UACf,6CAAC,uBAAoB,MAAM,IAAI,MAAM,IACnC,IAAI,SAAS,SACf;AAAA,YAAC;AAAA;AAAA,cACC,MAAM,IAAI;AAAA,cACV,QAAQ,MAAM;AACZ,oBAAI,YAAY;AACd,6BAAW,IAAI,KAAK,GAAG;AAAA,gBACzB,OAAO;AACL,yBAAO,KAAK,IAAI,KAAK,KAAK,UAAU,qBAAqB;AAAA,gBAC3D;AAAA,cACF;AAAA;AAAA,UACF,IAEA,6CAAC,6BAA0B,MAAM,IAAI,MAAM;AAAA,aA3B1B,IAAI,EA6BzB;AAAA,MAEJ,CAAC;AAAA;AAAA,EACH;AAEJ;;;ATuOU,IAAAC,sBAAA;AA/dH,IAAM,gBAAY;AAAA,EACvB,SAASC,WACP;AAAA,IACE,cAAc;AAAA,IACd,QAAQ,CAAC;AAAA,IACT;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,mBAAmB;AAAA,IACnB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,cAAc;AAAA,IACd;AAAA,IACA;AAAA,IACA,YAAY,iBAAiB;AAAA,IAC7B,SAAS;AAAA,IACT,UAAU;AAAA,IACV,qBAAqB;AAAA,IACrB,WAAW;AAAA,IACX,UAAU,iBAAiB;AAAA,EAC7B,GACA,KACA;AAGA,UAAM,aACJ,iBAAiB,SAAY,eAAe,CAAC;AAC/C,UAAM,uBAAmB,sBAAO,oBAAI,IAA4B,CAAC;AACjE,UAAM,gBAAY,sBAA6B,IAAI;AACnD,UAAM,wBAAoB,sBAGhB,IAAI;AACd,UAAM,mBAAe,sBAAuB,IAAI;AAChD,UAAM,sBAAkB,sBAA6C,IAAI;AAGzE,iCAAU,MAAM;AACd,2BAAqB;AAAA,QACnB,WAAW;AAAA,QACX,WAAW;AAAA,QACX,SAAS;AAAA,MACX,CAAC;AAAA,IACH,GAAG,CAAC,kBAAkB,kBAAkB,cAAc,CAAC;AAGvD,UAAM,iBAAa,sBAAsB,IAAI;AAC7C,UAAM,oBAAgB,sBAA6C,IAAI;AACvE,UAAM,wBAAoB,sBAA4B,IAAI;AAI1D,UAAM,sBAAkB,2BAAY,CAAC,iBAAyB;AAC5D,YAAM,YAAY,aAAa;AAC/B,UAAI,CAAC,UAAW;AAEhB,gBACG,iBAAiB,sBAAsB,EACvC,QAAQ,CAAC,OAAO,GAAG,UAAU,OAAO,qBAAqB,CAAC;AAE7D,gBAAU,iBAAiB,gBAAgB,EAAE,QAAQ,CAAC,OAAO;AAC3D,cAAM,MAAM,GAAG,aAAa,eAAe;AAC3C,YAAI,OAAO,IAAI,MAAM,GAAG,EAAE,SAAS,YAAY,GAAG;AAChD,aAAG,UAAU,IAAI,qBAAqB;AAAA,QACxC;AAAA,MACF,CAAC;AAAA,IACH,GAAG,CAAC,CAAC;AAGL,UAAM,sBAAkB,2BAAY,MAAM;AACxC,iBAAW,UAAU;AACrB,UAAI,cAAc,SAAS;AACzB,qBAAa,cAAc,OAAO;AAClC,sBAAc,UAAU;AAAA,MAC1B;AACA,UAAI,kBAAkB,SAAS;AAC7B,0BAAkB,QAAQ;AAC1B,0BAAkB,UAAU;AAAA,MAC9B;AACA,mBAAa,SACT,iBAAiB,sBAAsB,EACxC,QAAQ,CAAC,OAAO,GAAG,UAAU,OAAO,qBAAqB,CAAC;AAAA,IAC/D,GAAG,CAAC,CAAC;AAEL,UAAM,CAAC,cAAc,eAAe,QAAI,wBAAuB;AAAA,MAC7D,SAAS;AAAA,MACT,GAAG;AAAA,MACH,GAAG;AAAA,MACH,SAAS,CAAC;AAAA,IACZ,CAAC;AAGD;AAAA,MACE;AAAA,MACA,OAAO;AAAA,QACL,YAAY,CAAC,SAAiB;AAC5B,gBAAM,SAAS,UAAU;AACzB,cAAI,CAAC,OAAQ;AAEb,iBAAO,OAAO,MAAM;AAClB,kBAAM,QAAQ,kBAAkB;AAChC,gBAAI,OAAO;AAET,oBAAM,WAAW,qBAAqB,MAAM,MAAM;AAClD,oBAAM,UAAU,qBAAqB,MAAM,KAAK;AAChD,kBAAI,YAAY,SAAS;AAIvB,sBAAM,iBAAa,+BAAc,SAAS,GAAG;AAC7C,oBACE,cACA,iBAAiB,UAAU,KAC3B,WAAW,QAAQ,MAAM,WACzB,MAAM,WAAW,MAAM,OACvB;AACA,wBAAM,cAAU,iCAAgB,IAAI;AACpC,sBACE,SAAS,WAAW,KACpB,SAAS,SAAS,WAAW,mBAAmB,GAChD;AACA,+BAAW,aAAa,OAAO;AAAA,kBACjC,OAAO;AACL,+BAAW,YAAY,OAAO;AAAA,kBAChC;AACA,0BAAQ,UAAU;AAClB;AAAA,gBACF;AAEA,sBAAMC,WAAM,uCAAsB;AAClC,gBAAAA,KAAI,OAAO,IAAI,SAAS,KAAK,SAAS,QAAQ,SAAS,IAAI;AAC3D,gBAAAA,KAAI,MAAM,IAAI,QAAQ,KAAK,QAAQ,QAAQ,QAAQ,IAAI;AACvD,mDAAcA,IAAG;AACjB,gBAAAA,KAAI,WAAW,IAAI;AACnB;AAAA,cACF;AAAA,YACF;AAEA,kBAAM,WAAO,0BAAS;AACtB,kBAAM,YAAY,KAAK,aAAa;AACpC,gBAAI,WAAW;AACb,wBAAU,UAAU;AAAA,YACtB;AACA,kBAAM,UAAM,uCAAsB;AAClC,+CAAc,GAAG;AACjB,gBAAI,WAAW,IAAI;AAAA,UACrB,CAAC;AAGD,iBAAO,MAAM;AAAA,QACf;AAAA,QACA,OAAO,MAAM;AACX,oBAAU,SAAS,MAAM;AAAA,QAC3B;AAAA,QACA,SAAS,MAAM;AACb,cAAI,OAAO;AACX,oBAAU,SAAS,eAAe,EAAE,KAAK,MAAM;AAC7C,uBAAO,0BAAS,EAAE,eAAe;AAAA,UACnC,CAAC;AACD,iBAAO;AAAA,QACT;AAAA,QACA,gBAAgB,CAAC,cAAsB,aAAa,QAAS;AAE3D,0BAAgB;AAEhB,qBAAW,UAAU;AAErB,0BAAgB,YAAY;AAG5B,wBAAc,UAAU,WAAW,MAAM;AACvC,4BAAgB;AAAA,UAClB,GAAG,UAAU;AAIb,gBAAM,SAAS,UAAU;AACzB,cAAI,QAAQ;AACV,8BAAkB,UAAU,OAAO;AAAA,cACjC,CAAC,EAAE,KAAK,MAAM;AACZ,oBAAI,KAAK,IAAI,gBAAgB,GAAG;AAE9B,sBAAI,WAAW,SAAS;AACtB;AAAA,sBAAsB,MACpB,gBAAgB,WAAW,OAAQ;AAAA,oBACrC;AAAA,kBACF;AACA;AAAA,gBACF;AAEA,gCAAgB;AAAA,cAClB;AAAA,YACF;AAAA,UACF;AAAA,QACF;AAAA,QACA,YAAY,CAAC,QAAgB,gBAAgC;AAC3D,gBAAM,SAAS,UAAU;AACzB,cAAI,CAAC,UAAU,CAAC,OAAQ,QAAO;AAE/B,cAAI,QAAQ;AACZ,iBAAO,OAAO,MAAM;AAClB,kBAAM,WAAO,0BAAS;AACtB,kBAAM,WAAW,KAAK,eAAe;AAGrC,gBAAI,MAAM;AACV,oBAAQ,MAAM,SAAS,QAAQ,QAAQ,GAAG,OAAO,IAAI;AACnD;AACA,qBAAO,OAAO;AAAA,YAChB;AAEA,gBAAI,UAAU,EAAG;AAGjB,kBAAM,UAAU,SAAS,MAAM,MAAM,EAAE,KAAK,WAAW;AACvD,iBAAK,MAAM;AACX,kBAAM,QAAQ,QAAQ,MAAM,IAAI;AAChC,uBAAW,QAAQ,OAAO;AACxB,oBAAM,QAAI,sCAAqB;AAC/B,gBAAE,WAAO,iCAAgB,IAAI,CAAC;AAC9B,mBAAK,OAAO,CAAC;AAAA,YACf;AAAA,UACF,CAAC;AAED,iBAAO;AAAA,QACT;AAAA,QACA,YAAY,MAAM;AAChB,0BAAgB;AAAA,QAClB;AAAA,MACF;AAAA,MACA,CAAC,iBAAiB,eAAe;AAAA,IACnC;AAEA,UAAM,oBAAgB;AAAA,MACpB,OAAO;AAAA,QACL,WAAW;AAAA,QACX,OAAO;AAAA,UACL,MAAM;AAAA,UACN,WAAW;AAAA,UACX,MAAM;AAAA,YACJ,MAAM;AAAA,UACR;AAAA,QACF;AAAA,QACA,OAAO,CAAC,eAAe,WAAW;AAAA;AAAA;AAAA,QAGlC,UAAU,cAAc;AAAA,QACxB,SAAS,CAAC,UAAiB;AACzB,kBAAQ,MAAM,4BAA4B,KAAK;AAAA,QACjD;AAAA,QACA,aAAa,MAAM;AACjB,gBAAM,WAAO,0BAAS;AACtB,gBAAM,QAAQ,YAAY,MAAM,IAAI;AACpC,qBAAW,QAAQ,OAAO;AACxB,kBAAM,QAAI,sCAAqB;AAC/B,cAAE,WAAO,iCAAgB,IAAI,CAAC;AAC9B,iBAAK,OAAO,CAAC;AAAA,UACf;AAAA,QACF;AAAA,MACF;AAAA,MACA,CAAC;AAAA;AAAA,IACH;AAGA,UAAM,yBAAqB,sBAAO,KAAK;AACvC,UAAM,uBAAmB,sBAAO,KAAK;AAErC,UAAM,mBAAe,2BAAY,MAAM;AACrC,UAAI,gBAAgB,QAAS,cAAa,gBAAgB,OAAO;AACjE,sBAAgB,UAAU,WAAW,MAAM;AACzC,YAAI,CAAC,mBAAmB,WAAW,CAAC,iBAAiB,SAAS;AAC5D,0BAAgB,CAAC,UAAU,EAAE,GAAG,MAAM,SAAS,MAAM,EAAE;AAAA,QACzD;AAAA,MACF,GAAG,GAAG;AAAA,IACR,GAAG,CAAC,CAAC;AAEL,UAAM,iBAAa,2BAAY,MAAM;AACnC,UAAI,gBAAgB,SAAS;AAC3B,qBAAa,gBAAgB,OAAO;AACpC,wBAAgB,UAAU;AAAA,MAC5B;AAAA,IACF,GAAG,CAAC,CAAC;AAGL,iCAAU,MAAM;AACd,YAAM,YAAY,aAAa;AAC/B,UAAI,CAAC,UAAW;AAEhB,YAAM,kBAAkB,CAAC,MAAkB;AACzC,cAAM,SAAU,EAAE,OAAuB,QAAQ,gBAAgB;AACjE,YAAI,CAAC,QAAQ;AACX,cAAI,mBAAmB,SAAS;AAC9B,+BAAmB,UAAU;AAC7B,yBAAa;AAAA,UACf;AACA;AAAA,QACF;AAEA,cAAM,cAAc,OAAO,aAAa,eAAe;AACvD,YAAI,CAAC,YAAa;AAElB,cAAM,UAAU;AAAA,UACd,GAAG,IAAI;AAAA,YACL,YACG,MAAM,GAAG,EACT;AAAA,cAAI,CAAC,OACJ,GAAG,WAAW,gBAAgB,IAC1B,GAAG,MAAM,iBAAiB,MAAM,IAChC;AAAA,YACN;AAAA,UACJ;AAAA,QACF;AAEA,2BAAmB,UAAU;AAC7B,mBAAW;AAEX,cAAM,OAAO,OAAO,sBAAsB;AAC1C,wBAAgB;AAAA,UACd,SAAS;AAAA,UACT,GAAG,KAAK;AAAA,UACR,GAAG,KAAK;AAAA,UACR,YAAY;AAAA,YACV,KAAK,KAAK;AAAA,YACV,MAAM,KAAK;AAAA,YACX,QAAQ,KAAK;AAAA,YACb,OAAO,KAAK;AAAA,YACZ,OAAO,KAAK;AAAA,YACZ,QAAQ,KAAK;AAAA,UACf;AAAA,UACA;AAAA,QACF,CAAC;AAAA,MACH;AAEA,YAAM,iBAAiB,CAAC,MAAkB;AACxC,cAAM,UAAU,EAAE;AAClB,YAAI,SAAS,QAAQ,gBAAgB,EAAG;AACxC,2BAAmB,UAAU;AAC7B,qBAAa;AAAA,MACf;AAEA,gBAAU,iBAAiB,aAAa,eAAe;AACvD,gBAAU,iBAAiB,YAAY,cAAc;AAErD,aAAO,MAAM;AACX,kBAAU,oBAAoB,aAAa,eAAe;AAC1D,kBAAU,oBAAoB,YAAY,cAAc;AACxD,YAAI,gBAAgB,QAAS,cAAa,gBAAgB,OAAO;AAAA,MACnE;AAAA,IACF,GAAG,CAAC,cAAc,UAAU,CAAC;AAG7B,iCAAU,MAAM;AACd,YAAM,YAAY,aAAa;AAC/B,UAAI,CAAC,UAAW;AAEhB,YAAM,cAAc,CAAC,MAAkB;AAErC,YAAI,kBAAkB;AACpB,gBAAM,kBAAmB,EAAE,OAAuB;AAAA,YAChD;AAAA,UACF;AACA,cAAI,iBAAiB;AACnB,kBAAM,cAAc,gBAAgB,aAAa,eAAe;AAChE,gBAAI,aAAa;AACf,oBAAM,MAAM,YAAY,MAAM,GAAG;AACjC,yBAAW,MAAM,KAAK;AACpB,sBAAM,MAAM,iBAAiB,QAAQ,IAAI,EAAE;AAC3C,oBAAI,CAAC,IAAK;AAEV,oBAAI,IAAI,SAAS,QAAQ;AACvB,oBAAE,eAAe;AACjB,sBAAI,aAAa;AACf,gCAAY,IAAI,KAAK,GAAG;AAAA,kBAC1B,OAAO;AACL,2BAAO,KAAK,IAAI,KAAK,KAAK,UAAU,qBAAqB;AAAA,kBAC3D;AACA;AAAA,gBACF;AAAA,cACF;AAAA,YACF;AAAA,UACF;AAAA,QACF;AAGA,cAAM,gBAAiB,EAAE,OAAuB;AAAA,UAC9C;AAAA,QACF;AACA,YAAI,eAAe;AACjB,gBAAM,YAAY,cAAc,aAAa,iBAAiB;AAC9D,gBAAM,cAAc,cAAc,aAAa,mBAAmB;AAClE,cAAI,aAAa,aAAa;AAC5B,cAAE,eAAe;AACjB,6BAAiB,WAAW,WAAW;AACvC;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAEA,gBAAU,iBAAiB,SAAS,WAAW;AAC/C,aAAO,MAAM;AACX,kBAAU,oBAAoB,SAAS,WAAW;AAAA,MACpD;AAAA,IACF,GAAG,CAAC,aAAa,kBAAkB,cAAc,CAAC;AAGlD,UAAM,4BAAwB;AAAA,MAC5B,CAAC,YAAoB,WAAmB;AACtC,cAAM,SAAS,UAAU;AACzB,YAAI,CAAC,OAAQ;AAEb,YAAI;AAIJ,eAAO,OAAO,MAAM;AAClB,gBAAM,WAAO,0BAAS;AACtB,gBAAM,WAAW,KAAK,gBAAgB;AACtC,qBAAW,QAAQ,UAAU;AAC3B,gBACE,iBAAiB,IAAI,KACrB,KAAK,UAAU,MAAM,GAAG,EAAE,SAAS,MAAM,GACzC;AACA,oBAAM,eAAe,qBAAqB,KAAK,OAAO,GAAG,CAAC;AAC1D,oBAAM,kBAAkB,KAAK,eAAe;AAC5C,8BAAgB;AAAA,gBACd,OAAO;AAAA,gBACP,KAAK,eAAe,gBAAgB;AAAA,gBACpC,SAAS;AAAA,cACX;AACA,oBAAM,eAAW,iCAAgB,UAAU;AAC3C,mBAAK,QAAQ,QAAQ;AACrB;AAAA,YACF;AAAA,UACF;AAAA,QACF,CAAC;AAED,wBAAgB,CAAC,UAAU,EAAE,GAAG,MAAM,SAAS,MAAM,EAAE;AACvD,YAAI,kBAAkB,QAAW;AAC/B,gBAAM,aAAa,iBAAiB,QAAQ,IAAI,MAAM;AACtD,cAAI,YAAY;AACd;AAAA,cACE;AAAA,cACA;AAAA,cACA;AAAA,cACA,WAAW;AAAA,YACb;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,MACA,CAAC,iBAAiB;AAAA,IACpB;AAEA,UAAM,mBAAe;AAAA,MACnB,CAAC,gBAAoD;AACnD,YAAI,CAAC,SAAU;AACf,oBAAY,KAAK,MAAM;AACrB,gBAAM,WAAO,0BAAS;AACtB,mBAAS,KAAK,eAAe,CAAC;AAAA,QAChC,CAAC;AAAA,MACH;AAAA,MACA,CAAC,QAAQ;AAAA,IACX;AAEA,WACE;AAAA,MAAC;AAAA;AAAA,QACC,KAAK;AAAA,QACL,WAAW;AAAA,UACT;AAAA,UACA,UAAU;AAAA,UACV;AAAA,QACF;AAAA,QACA;AAAA,QAEA;AAAA,uDAAC,0CAAgB,eACf,wDAAC,SAAI,WAAU,oBACb;AAAA;AAAA,cAAC;AAAA;AAAA,gBACC,iBACE;AAAA,kBAAC;AAAA;AAAA,oBACC,WAAW;AAAA,sBACT;AAAA,sBACA,CAAC,cAAc,CAAC,sBAAsB;AAAA,sBACtC,CAAC,cACC,sBACA;AAAA,oBACJ;AAAA;AAAA,gBACF;AAAA,gBAEF,aACE,6CAAC,SAAI,WAAU,0BAA0B,uBAAY;AAAA,gBAEvD,eAAe;AAAA;AAAA,YACjB;AAAA,YACA,6CAAC,6CAAc;AAAA,YACf,6CAAC,+CAAe,UAAU,cAAc;AAAA,YACxC;AAAA,cAAC;AAAA;AAAA,gBACC;AAAA,gBACA;AAAA,gBACA;AAAA;AAAA,YACF;AAAA,YACA;AAAA,cAAC;AAAA;AAAA,gBACC;AAAA,gBACA;AAAA;AAAA,YACF;AAAA,YACA,6CAAC,4BAAyB;AAAA,YAEzB,CAAC,cAAc,sBAAsB,6CAAC,4BAAyB;AAAA,YAE/D,iBAAiB,6CAAC,iBAAc,WAAW,eAAe;AAAA,YAE1D,OAAO,QAAQ,UAAU,6CAAC,mBAAgB,KAAU;AAAA,YAEpD,MACE,OAAO,CAAC,MAAyB,EAAE,SAAS,SAAS,EACrD,IAAI,CAAC,aAAa,MACjB;AAAA,cAAC;AAAA;AAAA,gBAEC,OAAO,YAAY;AAAA,gBACnB,SAAS,YAAY;AAAA,gBACrB;AAAA;AAAA,cAHK,WAAW,CAAC;AAAA,YAInB,CACD;AAAA,aACL,GACF;AAAA,UAEA;AAAA,YAAC;AAAA;AAAA,cACC,OAAO;AAAA,cACP,eAAe,iBAAiB;AAAA,cAChC,mBAAmB;AAAA,cACnB,YAAY;AAAA,cACZ,WAAW,MAAM;AACf,iCAAiB,UAAU;AAC3B,6BAAa;AAAA,cACf;AAAA,cACA,gBAAgB,MAAM;AACpB,iCAAiB,UAAU;AAC3B,2BAAW;AAAA,cACb;AAAA,cACA;AAAA,cACA,KAAK,mBAAmB,YAAY,MAAM;AAAA;AAAA,UAC5C;AAAA;AAAA;AAAA,IACF;AAAA,EAEJ;AACF;AASA,SAAS,2BAA2B;AAClC,QAAM,CAAC,MAAM,QAAI,0DAA0B;AAE3C,+BAAU,MAAM;AACd,WAAO,OAAO;AAAA,MACZ;AAAA,MACA,CAAC,UAAyB;AAExB,YACE,MAAM,IAAI,WAAW,OAAO,KAC5B,MAAM,QAAQ,UACd,MAAM,QAAQ,SACd,MAAM,QAAQ,YACd,MAAM,QAAQ,cACd,MAAM,QAAQ,WACd,MAAM,QAAQ,aACd,MAAM,QAAQ,SACd,MAAM,QAAQ,UACd,MAAM,QAAQ,SACd,MAAM,QAAQ,YACd,MAAM,QAAQ,QACd,MAAM,QAAQ;AAAA,SAEZ,MAAM,WAAW,MAAM,aACtB,MAAM,QAAQ,OACb,MAAM,QAAQ,OACd,MAAM,QAAQ,OACd,MAAM,QAAQ,MAClB;AACA,iBAAO;AAAA,QACT;AAEA,cAAM,eAAe;AACrB,eAAO;AAAA,MACT;AAAA,MACA;AAAA,IACF;AAAA,EACF,GAAG,CAAC,MAAM,CAAC;AAGX,+BAAU,MAAM;AACd,UAAM,OAAO,OAAO,eAAe;AACnC,QAAI,CAAC,KAAM;AACX,UAAM,QAAQ,CAAC,MAAa,EAAE,eAAe;AAC7C,SAAK,iBAAiB,SAAS,KAAK;AACpC,SAAK,iBAAiB,OAAO,KAAK;AAClC,SAAK,iBAAiB,QAAQ,KAAK;AACnC,WAAO,MAAM;AACX,WAAK,oBAAoB,SAAS,KAAK;AACvC,WAAK,oBAAoB,OAAO,KAAK;AACrC,WAAK,oBAAoB,QAAQ,KAAK;AAAA,IACxC;AAAA,EACF,GAAG,CAAC,MAAM,CAAC;AAEX,SAAO;AACT;AAKA,SAAS,cAAc;AAAA,EACrB;AACF,GAEG;AACD,QAAM,CAAC,MAAM,QAAI,0DAA0B;AAE3C,+BAAU,MAAM;AACd,WAAO,OAAO;AAAA,MACZ;AAAA,MACA,CAAC,UAAyB;AACxB,eAAO,UAAU,KAAK;AAAA,MACxB;AAAA,MACA;AAAA,IACF;AAAA,EACF,GAAG,CAAC,QAAQ,SAAS,CAAC;AAEtB,SAAO;AACT;AAKA,SAAS,gBAAgB,EAAE,IAAI,GAA2B;AACxD,QAAM,CAAC,MAAM,QAAI,0DAA0B;AAE3C,+BAAU,MAAM;AACd,WAAO,OAAO,MAAM;AAClB,oCAAS,EAAE,aAAa,GAAG;AAAA,IAC7B,CAAC;AACD,WAAO,MAAM;AAEX,aAAO,OAAO,MAAM;AAClB,sCAAS,EAAE,aAAa,IAAI;AAAA,MAC9B,CAAC;AAAA,IACH;AAAA,EACF,GAAG,CAAC,QAAQ,GAAG,CAAC;AAEhB,SAAO;AACT;","names":["allSelected","import_react","import_lexical","import_LexicalComposerContext","import_lexical","import_react","import_react_virtual","import_lexical","import_jsx_runtime","isSelected","import_LexicalComposerContext","import_lexical","import_react","import_lexical","p","paragraph","React","import_react","import_core","import_jsx_runtime","import_jsx_runtime","CATEditor","sel"]}