@opencxh/ui-kit 3.175.2 → 3.176.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +2 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +788 -674
- package/dist/index.js.map +1 -1
- package/dist/src/content/activity/ActivityRow.d.ts +82 -0
- package/dist/src/content/activity/TimelineEvent.d.ts +22 -0
- package/dist/src/index.d.ts +2 -0
- package/package.json +1 -1
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","sources":["../src/utils/cn.ts","../src/action/Button.tsx","../src/action/ButtonGroup.tsx","../src/content/Icon.tsx","../src/utils/useAnchoredPosition.ts","../src/overlays/Popover.tsx","../src/action/FilterChip.tsx","../src/action/Link.tsx","../src/action/SegmentedToggle.tsx","../src/action/SplitButton.tsx","../src/command-palette/CommandPalette.tsx","../src/content/RichText.tsx","../src/content/ArtifactView.tsx","../src/content/AssistantCard.tsx","../src/utils/identity.ts","../src/content/Avatar.tsx","../src/content/AvatarStack.tsx","../src/content/ChannelBadge.tsx","../src/content/Image.tsx","../src/content/KpiCard.tsx","../src/input/Checkbox.tsx","../src/content/List.tsx","../src/content/MessageBubble.tsx","../src/content/MetaValue.tsx","../src/feedback/ContentLoading.tsx","../src/content/PageHeader.tsx","../src/content/Page.tsx","../src/content/PageToolbar.tsx","../src/content/SectionCaption.tsx","../src/content/SectionDivider.tsx","../src/content/SettingsPage.tsx","../src/content/SettingsRow.tsx","../src/content/SourceChip.tsx","../src/input/DatePicker.tsx","../src/input/SearchField.tsx","../src/overlays/Dropdown.tsx","../src/content/Table.tsx","../src/feedback/Badge.tsx","../src/content/tableColumns.tsx","../src/typography/Text.tsx","../src/content/View.tsx","../src/error-boundary/index.tsx","../src/federated-resource/index.tsx","../src/feedback/EmptyState.tsx","../src/feedback/Kbd.tsx","../src/feedback/ProgressBar.tsx","../src/feedback/Spinner.tsx","../src/feedback/StatusDot.tsx","../src/feedback/StepIndicator.tsx","../src/forms/autofill.ts","../src/utils/text.ts","../src/input/Select.tsx","../src/input/TextField.tsx","../src/input/FolderSelect.tsx","../src/navigation/Tabs.tsx","../src/overlays/Modal.tsx","../src/input/StorageInput.tsx","../src/input/TextArea.tsx","../src/forms/Form.tsx","../src/input/ImageField.tsx","../src/input/SearchableTextField.tsx","../src/input/Switch.tsx","../src/meeting/MeetingGrid.tsx","../src/meeting/ParticipantTile.tsx","../src/meeting/VideoSurface.tsx","../src/navigation/Sidebar.tsx","../src/overlays/ConfirmDialog.tsx","../src/primitives/Box.tsx","../src/primitives/Card.tsx","../src/typography/Heading.tsx","../src/utils/catTone.ts"],"sourcesContent":["import { type ClassValue, clsx } from \"clsx\";\n\n/**\n * Utility function for combining class names\n * Combines clsx for conditional classes with Tailwind merge for deduplication\n */\nexport function cn(...inputs: ClassValue[]) {\n return clsx(inputs);\n}\n\n/**\n * Utility for creating variant-based class combinations\n */\nexport function createVariants<T extends Record<string, Record<string, string>>>(variants: T) {\n return (variant: keyof T, value: keyof T[keyof T]) => {\n return variants[variant]?.[value] || \"\";\n };\n}\n\n/**\n * Utility for creating size-based class combinations\n */\nexport function createSizes<T extends Record<string, string>>(sizes: T) {\n return (size: keyof T) => sizes[size] || \"\";\n}\n","import type React from \"react\";\nimport { forwardRef } from \"react\";\nimport { cn } from \"../utils/cn\";\n\nexport interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {\n /**\n * The visual style variant of the button\n */\n variant?:\n | \"primary\"\n | \"secondary\"\n | \"outline\"\n | \"ghost\"\n | \"destructive\"\n | \"success\"\n | \"warning\"\n | \"danger-solid\";\n\n /**\n * The size of the button\n */\n size?: \"xs\" | \"sm\" | \"md\" | \"lg\" | \"xl\" | \"2xl\" | \"full\";\n\n /**\n * Whether the button should take the full width of its container\n */\n fullWidth?: boolean;\n\n /**\n * Whether the button is in a loading state\n */\n loading?: boolean;\n\n /**\n * Icon to display before the button text\n */\n leftIcon?: React.ReactNode;\n\n /**\n * Icon to display after the button text\n */\n rightIcon?: React.ReactNode;\n\n /**\n * Whether the button should only display an icon (no text)\n */\n iconOnly?: boolean;\n\n /** `circle` is for a control that is only ever an icon: call answer/reject. */\n shape?: \"default\" | \"circle\";\n}\n\n/**\n * Variant styles — semantic tokens only, so dark mode needs no extra classes.\n * One primary button per view; everything else is secondary, ghost or a Link.\n */\nconst buttonVariants = {\n primary: \"bg-accent text-accent-fg hover:bg-accent-hover disabled:bg-surface-hover\",\n secondary: \"bg-surface-sunk text-text-strong hover:bg-surface-hover disabled:bg-surface-hover\",\n // No resting fill, so no disabled fill either — a greyed-out plate where\n // there was nothing reads as a different control, not as a disabled one.\n outline: \"border border-border text-text-strong hover:bg-surface-hover\",\n ghost: \"text-text-muted hover:bg-surface-hover hover:text-text\",\n destructive:\n \"border border-danger-border bg-danger-soft text-danger-fg hover:border-danger disabled:bg-surface-hover\",\n // Solid status fills. Reserved for a control whose colour IS the answer —\n // an RSVP, answering or rejecting a call — not for ordinary emphasis.\n success: \"bg-success text-text-inverse hover:opacity-90 disabled:bg-surface-hover\",\n warning: \"bg-warning text-text-inverse hover:opacity-90 disabled:bg-surface-hover\",\n \"danger-solid\": \"bg-danger text-text-inverse hover:opacity-90 disabled:bg-surface-hover\",\n};\n\n/** Height comes from the shared control tokens so buttons line up with inputs. */\nconst buttonSizes = {\n xs: \"h-control-xs px-2 text-xs\",\n sm: \"h-control-sm px-3 text-sm\",\n md: \"h-control-md px-4 text-sm\",\n lg: \"h-control-lg px-4 text-sm\",\n xl: \"h-control-xl px-6 text-base\",\n \"2xl\": \"h-control-2xl px-6 text-base\",\n full: \"h-control-md px-4 text-sm\",\n};\n\nconst iconOnlySizes = {\n xs: \"h-control-xs w-6\",\n sm: \"h-control-sm w-7\",\n md: \"h-control-md w-8\",\n lg: \"h-control-lg w-9\",\n xl: \"h-control-xl w-10\",\n \"2xl\": \"h-control-2xl w-12\",\n full: \"h-control-md w-8\",\n};\n\n/**\n * Button component with theme integration and multiple variants\n *\n * @example\n * ```tsx\n * <Button variant=\"primary\" size=\"md\">Click me</Button>\n * <Button variant=\"outline\" leftIcon={<Icon name={Plus} />}>Add item</Button>\n * <Button iconOnly variant=\"ghost\" aria-label=\"Zoeken\">\n * <Icon name={Search} />\n * </Button>\n * ```\n */\nexport const Button = forwardRef<HTMLButtonElement, ButtonProps>(\n (\n {\n variant = \"primary\",\n size = \"md\",\n fullWidth = false,\n loading = false,\n leftIcon,\n rightIcon,\n iconOnly = false,\n shape = \"default\",\n className,\n children,\n disabled,\n ...props\n },\n ref,\n ) => {\n const isDisabled = disabled || loading;\n\n return (\n <button\n ref={ref}\n type=\"button\"\n className={cn(\n // font-medium, not semibold: in the design a button's label carries\n // the same weight as an emphasised nav item, and 600 at 12–13px\n // reads as a heavier control than the surrounding chrome.\n \"inline-flex shrink-0 items-center justify-center gap-2 cursor-pointer font-medium\",\n shape === \"circle\" ? \"rounded-full\" : \"rounded-md\",\n \"transition-colors duration-fast ease-out\",\n \"focus-visible:outline-none focus-visible:focus-ring\",\n \"disabled:cursor-not-allowed disabled:border-border-subtle\",\n \"disabled:text-text-disabled\",\n\n buttonVariants[variant],\n iconOnly ? iconOnlySizes[size] : buttonSizes[size],\n\n (fullWidth || size === \"full\") && \"w-full\",\n loading && \"cursor-wait\",\n\n className,\n )}\n disabled={isDisabled}\n aria-busy={loading || undefined}\n {...props}\n >\n {loading && (\n <span\n aria-hidden\n className=\"size-icon-md shrink-0 animate-spin rounded-full border-2 border-current border-t-transparent\"\n />\n )}\n\n {!loading && leftIcon && (\n <span className=\"flex items-center justify-center shrink-0\">{leftIcon}</span>\n )}\n\n {!iconOnly && children}\n\n {!loading && rightIcon && (\n <span className=\"flex items-center justify-center shrink-0\">{rightIcon}</span>\n )}\n\n {iconOnly && !loading && !leftIcon && !rightIcon && children}\n </button>\n );\n },\n);\n\nButton.displayName = \"Button\";\n","import type React from \"react\";\nimport { cn } from \"../utils/cn\";\n\nexport interface ButtonGroupProps {\n /** Child buttons to group */\n children: React.ReactNode;\n /** Size of the button group */\n size?: \"xs\" | \"sm\" | \"md\" | \"lg\" | \"xl\";\n /** Orientation of the button group */\n orientation?: \"horizontal\" | \"vertical\";\n /** Whether buttons should be attached (no gap) */\n attached?: boolean;\n /** Additional CSS classes */\n className?: string;\n}\n\n/**\n * ButtonGroup component for grouping related buttons\n */\nexport const ButtonGroup: React.FC<ButtonGroupProps> = ({\n children,\n size = \"md\",\n orientation = \"horizontal\",\n attached = true,\n className,\n}) => {\n const baseClasses = cn(\n \"inline-flex\",\n {\n // Orientation\n \"flex-row\": orientation === \"horizontal\",\n \"flex-col\": orientation === \"vertical\",\n\n // Attached styling\n \"[&>*:not(:first-child):not(:last-child)]:rounded-none\": attached,\n \"[&>*:first-child]:rounded-r-none\": attached && orientation === \"horizontal\",\n \"[&>*:last-child]:rounded-l-none\": attached && orientation === \"horizontal\",\n \"[&>*:first-child]:rounded-b-none\": attached && orientation === \"vertical\",\n \"[&>*:last-child]:rounded-t-none\": attached && orientation === \"vertical\",\n\n // Borders for attached buttons\n \"[&>*:not(:first-child)]:-ml-px\": attached && orientation === \"horizontal\",\n \"[&>*:not(:first-child)]:-mt-px\": attached && orientation === \"vertical\",\n\n // Spacing for non-attached buttons\n \"gap-1\": !attached && size === \"xs\",\n \"gap-2\": !attached && (size === \"sm\" || size === \"md\"),\n \"gap-3\": !attached && (size === \"lg\" || size === \"xl\"),\n },\n className,\n );\n\n return (\n <div className={baseClasses} role=\"group\">\n {children}\n </div>\n );\n};\n","import * as LucideIcons from \"lucide-react\";\nimport type React from \"react\";\nimport { cn } from \"../utils/cn\";\n\n/**\n * Lucide icon by name, for icons that arrive as data rather than as an import —\n * a provider declaring `icon: \"MessageCircle\"` in its description, a menu entry\n * from a manifest. Accepts kebab-case, snake_case, spaced or PascalCase.\n *\n * Returns `undefined` for an unknown name so the caller can fall back; an app\n * naming an icon that does not exist should not blank out the surface.\n */\nexport function resolveLucideIcon(name: string | undefined): React.ComponentType<any> | undefined {\n if (!name) return undefined;\n const pascal = name\n .split(/[-_ ]/)\n .filter(Boolean)\n .map((part) => part.charAt(0).toUpperCase() + part.slice(1))\n .join(\"\");\n const found = (LucideIcons as unknown as Record<string, unknown>)[pascal];\n return typeof found === \"function\" || (typeof found === \"object\" && found !== null)\n ? (found as React.ComponentType<any>)\n : undefined;\n}\n\nexport interface IconProps {\n /** Icon component from lucide-react or custom icon */\n icon: React.ComponentType<any>;\n /** Icon size */\n size?: \"xs\" | \"sm\" | \"md\" | \"lg\" | \"xl\" | number;\n /** Icon color */\n color?:\n | \"primary\"\n | \"secondary\"\n | \"accent\"\n | \"success\"\n | \"warning\"\n | \"error\"\n | \"info\"\n | \"neutral\"\n | \"current\";\n /** Whether the icon should be clickable */\n clickable?: boolean;\n /** Click handler */\n onClick?: () => void;\n /** Additional CSS classes */\n className?: string;\n /** Accessibility label */\n \"aria-label\"?: string;\n}\n\n/**\n * Icon — the only way icons enter the product. Always a lucide-react component;\n * never a unicode glyph, an emoji or an inline <svg> in feature code.\n *\n * Sizes follow the icon tokens: xs 12 · sm 14 (default in controls) · md 16 ·\n * lg 20 · xl 24.\n */\nconst colorMap: Record<NonNullable<IconProps[\"color\"]>, string> = {\n primary: \"text-text\",\n secondary: \"text-text-muted\",\n accent: \"text-accent\",\n success: \"text-success-fg\",\n warning: \"text-warning-fg\",\n error: \"text-danger-fg\",\n info: \"text-info-fg\",\n neutral: \"text-text-subtle\",\n current: \"text-current\",\n};\n\nexport const Icon: React.FC<IconProps> = ({\n icon: IconComponent,\n size = \"md\",\n color = \"current\",\n clickable = false,\n onClick,\n className,\n \"aria-label\": ariaLabel,\n ...props\n}) => {\n const sizeValue = typeof size === \"number\" ? size : getSizeValue(size);\n\n const iconClasses = cn(\n \"inline-block shrink-0\",\n colorMap[color],\n clickable && [\n \"cursor-pointer rounded-sm transition-opacity duration-fast ease-out hover:opacity-80\",\n \"focus-visible:outline-none focus-visible:focus-ring\",\n ],\n className,\n );\n\n const iconProps = {\n size: sizeValue,\n strokeWidth: 2,\n className: iconClasses,\n onClick: clickable ? onClick : undefined,\n \"aria-label\": ariaLabel,\n \"aria-hidden\": ariaLabel ? undefined : true,\n role: clickable ? \"button\" : undefined,\n tabIndex: clickable ? 0 : undefined,\n onKeyDown: clickable\n ? (e: React.KeyboardEvent) => {\n if (e.key === \"Enter\" || e.key === \" \") {\n e.preventDefault();\n onClick?.();\n }\n }\n : undefined,\n ...props,\n };\n\n return <IconComponent {...iconProps} />;\n};\n\nfunction getSizeValue(size: \"xs\" | \"sm\" | \"md\" | \"lg\" | \"xl\"): number {\n const sizeMap = {\n xs: 12,\n sm: 14,\n md: 16,\n lg: 20,\n xl: 24,\n };\n\n return sizeMap[size];\n}\n","import { type CSSProperties, type RefObject, useLayoutEffect, useState } from \"react\";\n\nexport interface AnchoredPositionOptions {\n /** Gap between the anchor and the panel, in px. */\n gap?: number;\n /** Match the panel's width to the anchor — what a select wants, a menu does not. */\n matchWidth?: boolean;\n /** Preferred side. Flips automatically when there is no room. */\n placement?: \"bottom-start\" | \"bottom-end\" | \"top-start\" | \"top-end\";\n}\n\n/**\n * Position a floating panel against its trigger, in viewport coordinates.\n *\n * Two problems this solves, both of which a plain `absolute` panel has:\n * an ancestor with `overflow-hidden` (a bordered form panel, a scroll area)\n * clips the panel, and a trigger near the bottom of the window opens a panel\n * that runs off-screen. Fixed coordinates escape every ancestor, and the\n * placement flips upward when the space below is too small.\n *\n * Recomputes on scroll and resize while open, so the panel tracks its trigger\n * instead of detaching when the page moves under it.\n */\nexport function useAnchoredPosition(\n open: boolean,\n anchorRef: RefObject<HTMLElement | null>,\n { gap = 6, matchWidth = false, placement = \"bottom-start\" }: AnchoredPositionOptions = {},\n): CSSProperties {\n const [style, setStyle] = useState<CSSProperties>({});\n\n // Layout effect, not effect: measure and place before the browser paints,\n // otherwise the panel is visible at the wrong spot for a frame.\n useLayoutEffect(() => {\n if (!open) return;\n\n const place = () => {\n const anchor = anchorRef.current;\n if (!anchor) return;\n\n const rect = anchor.getBoundingClientRect();\n const spaceBelow = window.innerHeight - rect.bottom;\n const spaceAbove = rect.top;\n\n // Flip up only when below genuinely cannot hold a usable panel and above\n // has more room — flipping into an equally cramped space helps nobody.\n const wantsTop = placement.startsWith(\"top\");\n const flip = wantsTop\n ? spaceAbove < 200 && spaceBelow > spaceAbove\n : spaceBelow < 200 && spaceAbove > spaceBelow;\n const onTop = wantsTop !== flip;\n\n const next: CSSProperties = { position: \"fixed\" };\n\n if (onTop) {\n next.bottom = window.innerHeight - rect.top + gap;\n next.maxHeight = Math.max(120, spaceAbove - gap * 2);\n } else {\n next.top = rect.bottom + gap;\n next.maxHeight = Math.max(120, spaceBelow - gap * 2);\n }\n\n if (placement.endsWith(\"end\")) next.right = window.innerWidth - rect.right;\n else next.left = rect.left;\n\n if (matchWidth) next.width = rect.width;\n\n setStyle(next);\n };\n\n place();\n // `true` captures scrolls on any ancestor, not just the window.\n window.addEventListener(\"scroll\", place, true);\n window.addEventListener(\"resize\", place);\n return () => {\n window.removeEventListener(\"scroll\", place, true);\n window.removeEventListener(\"resize\", place);\n };\n }, [open, anchorRef, gap, matchWidth, placement]);\n\n return style;\n}\n","import type React from \"react\";\nimport { useEffect, useRef, useState } from \"react\";\nimport { cn } from \"../utils/cn\";\nimport { useAnchoredPosition } from \"../utils/useAnchoredPosition\";\n\nexport interface PopoverProps {\n /** Element that toggles the popover */\n trigger: React.ReactNode;\n /**\n * Panel content. Pass a function to receive a `close` callback so rows can\n * dismiss the popover after acting.\n */\n children: React.ReactNode | ((close: () => void) => React.ReactNode);\n /** Panel placement relative to the trigger. Flips when there is no room. */\n placement?: \"bottom-start\" | \"bottom-end\" | \"top-start\" | \"top-end\";\n className?: string;\n /**\n * Layout classes for the wrapper around the trigger. Needed when the trigger\n * has to fill a positioned box — a calendar event in a week grid, say —\n * because the wrapper is `inline-flex` by default.\n */\n rootClassName?: string;\n /** Layout classes for the trigger button itself. */\n triggerClassName?: string;\n disabled?: boolean;\n /**\n * Accessible name for the trigger button. Needed when the trigger is\n * icon-only, since the panel content is not announced by the trigger.\n */\n triggerLabel?: string;\n}\n\n/**\n * Generic popover with click-outside / Escape dismissal. The panel is\n * `position: fixed` (computed from the trigger) so it escapes any\n * overflow-hidden or rounded-card ancestor instead of being clipped.\n */\nexport const Popover: React.FC<PopoverProps> = ({\n trigger,\n children,\n placement = \"bottom-start\",\n className,\n rootClassName,\n triggerClassName,\n disabled = false,\n triggerLabel,\n}) => {\n const [isOpen, setIsOpen] = useState(false);\n const rootRef = useRef<HTMLDivElement>(null);\n const triggerRef = useRef<HTMLButtonElement>(null);\n // Shared with Select, DatePicker and Dropdown: flips up when the trigger sits\n // near the bottom, caps the height to the space available, and re-measures on\n // scroll so the panel keeps tracking its trigger.\n const panelStyle = useAnchoredPosition(isOpen, triggerRef, { placement });\n\n useEffect(() => {\n if (!isOpen) return;\n const onDown = (event: MouseEvent) => {\n if (rootRef.current && !rootRef.current.contains(event.target as Node)) {\n setIsOpen(false);\n }\n };\n const onEsc = (event: KeyboardEvent) => {\n if (event.key === \"Escape\") setIsOpen(false);\n };\n document.addEventListener(\"mousedown\", onDown);\n document.addEventListener(\"keydown\", onEsc);\n return () => {\n document.removeEventListener(\"mousedown\", onDown);\n document.removeEventListener(\"keydown\", onEsc);\n };\n }, [isOpen]);\n\n const handleToggle = (event: React.MouseEvent<HTMLButtonElement>) => {\n event.stopPropagation();\n if (disabled) return;\n setIsOpen((open) => !open);\n };\n\n const close = () => setIsOpen(false);\n\n return (\n <div ref={rootRef} className={cn(\"relative inline-flex\", rootClassName)}>\n <button\n ref={triggerRef}\n type=\"button\"\n onClick={handleToggle}\n disabled={disabled}\n aria-label={triggerLabel}\n aria-expanded={isOpen}\n className={cn(\n \"inline-flex items-center\",\n disabled && \"cursor-not-allowed opacity-50\",\n triggerClassName,\n )}\n >\n {trigger}\n </button>\n\n {isOpen && (\n <div\n className={cn(\n // overflow-y-auto pairs with the hook's max-height: without it a\n // panel taller than the space available is simply cut off.\n \"fixed z-overlay overflow-y-auto rounded-lg border border-border bg-surface shadow-overlay\",\n className,\n )}\n style={panelStyle}\n >\n {typeof children === \"function\" ? children(close) : children}\n </div>\n )}\n </div>\n );\n};\n","import { ChevronDown, X } from \"lucide-react\";\nimport type React from \"react\";\nimport { Icon } from \"../content/Icon\";\nimport { Popover } from \"../overlays/Popover\";\nimport { cn } from \"../utils/cn\";\n\nexport interface FilterChipProps {\n /** Chip text (e.g. \"Status\", or \"Status: Open\" when a value is set). */\n label: React.ReactNode;\n /** Leading icon, before the label. */\n icon?: React.ReactNode;\n /**\n * Meaning carried by the chip's own value — a priority, a state. Applies to\n * the resting chip; `active` still wins, because \"you picked this\" outranks\n * \"this is urgent\".\n */\n tone?: \"none\" | \"default\" | \"muted\" | \"outline\" | \"warning\" | \"danger\";\n /**\n * `rect` is the filter-bar shape — the same 9px corner as a button, so a\n * chip and a button in one toolbar line up. `pill` is for a chip that reads\n * as a tag rather than a control.\n */\n shape?: \"pill\" | \"rect\";\n /** `md` is the default control height; `sm` for a dense toolbar. */\n size?: \"sm\" | \"md\";\n /** Selected/active — renders the filled dark treatment. */\n active?: boolean;\n /** Show a trailing caret (opens a menu the consumer wires with Dropdown/Popover). */\n caret?: boolean;\n /**\n * Menu panel for a chip that opens its own popover. Receives a `close`\n * callback so a row can dismiss the panel after picking. Without it the chip\n * is purely presentational and `onClick` is yours to wire.\n */\n menu?: (close: () => void) => React.ReactNode;\n /** Panel classes, when `menu` is set. */\n menuClassName?: string;\n /** Open a value clear affordance (✕) — only shown when `active`. */\n onClear?: () => void;\n /** Accessible name for the clear affordance. */\n clearLabel?: string;\n onClick?: () => void;\n /** Native tooltip, for a chip whose label is truncated or abbreviated. */\n title?: string;\n className?: string;\n}\n\n/**\n * Resting fills. A filter bar on the white card uses sunk plates — an outlined\n * row of chips reads as a row of empty inputs at this size. `outline` is the\n * exception, for a bar that already contains a sunk plate (the table toolbar,\n * where the search field is the sunk one): there a second sunk fill beside it\n * makes the two read as one smeared control.\n */\nconst tones: Record<NonNullable<FilterChipProps[\"tone\"]>, string> = {\n none: \"hover:bg-surface-sunk hover:text-text\",\n default: \"bg-surface-sunk text-text-muted hover:text-text\",\n muted: \"bg-surface-sunk text-text-subtle hover:text-text\",\n outline:\n \"text-text-muted ring-1 ring-inset ring-border-strong hover:bg-surface-hover hover:text-text\",\n warning: \"bg-warning-soft text-warning-fg\",\n danger: \"bg-danger-soft text-danger-fg\",\n};\n\ntype ChipShape = NonNullable<FilterChipProps[\"shape\"]>;\n\nconst chipShell = (opts: {\n active?: boolean;\n tone?: NonNullable<FilterChipProps[\"tone\"]>;\n shape?: ChipShape;\n size?: NonNullable<FilterChipProps[\"size\"]>;\n className?: string;\n}) =>\n cn(\n // A button's default cursor is an arrow, so it has to be asked for.\n \"inline-flex cursor-pointer select-none items-center text-sm font-medium\",\n opts.size === \"sm\" ? \"h-control-sm\" : \"h-control-md\",\n \"transition-colors duration-fast ease-out\",\n opts.shape === \"pill\" ? \"rounded-full\" : \"rounded-md\",\n // Picking a value outranks whatever the value happens to mean.\n opts.active ? \"bg-info-soft text-info-fg\" : tones[opts.tone ?? \"default\"],\n opts.className,\n );\n\nconst chipBody = (opts: { shape?: ChipShape; withClear?: boolean }) =>\n cn(\n \"inline-flex items-center gap-1.5 pl-3\",\n opts.shape === \"pill\" ? \"rounded-full\" : \"rounded-md\",\n opts.withClear ? \"pr-1.5\" : \"pr-3\",\n \"focus-visible:outline-none focus-visible:focus-ring\",\n );\n\n/**\n * Filter chip — the inbox filter bar, the table toolbar, and the thread\n * priority/assign/inbox/tags menus. Presentational by default (the consumer\n * wires the menu with `Dropdown`/`Popover` and `onClick`); pass `menu` and the\n * chip opens its own popover instead, which is the only way to get a clear\n * button on a menu chip — a `<FilterChip>` used as someone else's trigger\n * would nest a button inside a button.\n *\n * @example\n * ```tsx\n * <FilterChip label={status ? `Status: ${status}` : \"Status\"} caret\n * active={!!status} onClear={() => setStatus(null)}\n * menu={(close) => <StatusList onPick={(s) => { setStatus(s); close(); }} />} />\n * ```\n */\nexport function FilterChip({\n label,\n icon,\n tone = \"default\",\n shape = \"rect\",\n size = \"md\",\n active = false,\n caret = false,\n menu,\n menuClassName,\n onClear,\n clearLabel = \"Filter wissen\",\n onClick,\n title,\n className,\n}: FilterChipProps) {\n const showClear = active && Boolean(onClear);\n\n // The chip and its clear affordance are two separate controls, so the shell is\n // a plain span: a <button> may not contain another <button>. Without a clear,\n // there is only one control and the shell collapses onto it.\n const radius = shape === \"pill\" ? \"rounded-full\" : \"rounded-md\";\n\n const shell = chipShell({ active, tone, shape, size, className });\n\n const body = (\n <>\n {icon}\n {label}\n {caret && <Icon icon={ChevronDown} size=\"xs\" />}\n </>\n );\n\n const bodyClasses = chipBody({ shape, withClear: showClear });\n\n const clearButton = showClear && (\n <button\n type=\"button\"\n aria-label={clearLabel}\n onClick={onClear}\n className={cn(\n \"inline-flex h-full items-center pr-3 focus-visible:outline-none focus-visible:focus-ring\",\n radius,\n )}\n >\n <Icon icon={X} size=\"xs\" />\n </button>\n );\n\n if (menu) {\n return (\n <span className={shell} title={title}>\n <Popover\n trigger={body}\n // Both heights are needed: the shell has the definite height, so the\n // trigger only fills the plate if the wrapper passes it down.\n rootClassName=\"h-full\"\n triggerClassName={cn(bodyClasses, \"h-full\")}\n className={cn(\"min-w-menu p-1.5\", menuClassName)}\n >\n {menu}\n </Popover>\n {clearButton}\n </span>\n );\n }\n\n if (!showClear) {\n return (\n <button type=\"button\" title={title} onClick={onClick} className={cn(shell, bodyClasses)}>\n {body}\n </button>\n );\n }\n\n return (\n <span className={shell} title={title}>\n <button type=\"button\" onClick={onClick} className={bodyClasses}>\n {body}\n </button>\n {clearButton}\n </span>\n );\n}\n","import { ExternalLink } from \"lucide-react\";\nimport type React from \"react\";\nimport { cn } from \"../utils/cn\";\n\nexport interface LinkProps extends React.AnchorHTMLAttributes<HTMLAnchorElement> {\n /** Link variant */\n variant?: \"default\" | \"subtle\" | \"underline\" | \"button\";\n /** Link size */\n size?: \"xs\" | \"sm\" | \"md\" | \"lg\" | \"xl\";\n /** Color theme */\n color?: \"primary\" | \"secondary\" | \"accent\" | \"success\" | \"warning\" | \"error\" | \"info\" | \"neutral\";\n /** Whether the link is disabled */\n disabled?: boolean;\n /** Whether to show external link icon */\n external?: boolean;\n /** Additional CSS classes */\n className?: string;\n /** Child content */\n children: React.ReactNode;\n}\n\n/** Same steps as Text: xs 11 · sm 12 · md 13 · lg 14 · xl 16. */\nconst sizeMap: Record<NonNullable<LinkProps[\"size\"]>, string> = {\n xs: \"text-xs\",\n sm: \"text-sm\",\n md: \"text-base\",\n lg: \"text-md\",\n xl: \"text-lg\",\n};\n\nconst colorMap: Record<NonNullable<LinkProps[\"color\"]>, string> = {\n primary: \"text-text hover:text-info-fg hover:underline\",\n info: \"text-info-fg hover:underline\",\n secondary: \"text-text-muted hover:text-text\",\n neutral: \"text-text-muted hover:text-text\",\n accent: \"text-accent hover:underline\",\n success: \"text-success-fg hover:underline\",\n warning: \"text-warning-fg hover:underline\",\n error: \"text-danger-fg hover:underline\",\n};\n\n/** `variant=\"button\"` uses the soft surface of the matching status token. */\nconst buttonBackgroundMap: Record<NonNullable<LinkProps[\"color\"]>, string> = {\n primary: \"bg-accent-soft\",\n accent: \"bg-accent-soft\",\n secondary: \"bg-surface-hover\",\n neutral: \"bg-surface-hover\",\n success: \"bg-success-soft\",\n warning: \"bg-warning-soft\",\n error: \"bg-danger-soft\",\n info: \"bg-info-soft\",\n};\n\n/**\n * Link component with theme integration and accessibility features.\n * Use for navigation; use Button for actions.\n */\nexport const Link: React.FC<LinkProps> = ({\n variant = \"default\",\n size = \"md\",\n color = \"primary\",\n disabled = false,\n external = false,\n className,\n children,\n href,\n target,\n rel,\n ...props\n}) => {\n const isExternal = external || (href && (href.startsWith(\"http\") || href.startsWith(\"mailto:\")));\n\n const baseClasses = cn(\n \"inline-flex items-center gap-1 underline-offset-2\",\n \"transition-colors duration-fast ease-out\",\n \"focus-visible:outline-none focus-visible:focus-ring\",\n\n sizeMap[size],\n !disabled && colorMap[color],\n\n variant === \"subtle\" && \"no-underline opacity-70 hover:opacity-100\",\n variant === \"underline\" && \"underline decoration-1 hover:decoration-2\",\n variant === \"button\" && [\n \"h-control-md rounded-md px-3 font-semibold no-underline hover:no-underline\",\n buttonBackgroundMap[color],\n ],\n\n disabled && \"pointer-events-none cursor-not-allowed text-text-disabled opacity-50\",\n\n className,\n );\n\n const linkProps = {\n ...props,\n href: disabled ? undefined : href,\n target: isExternal ? \"_blank\" : target,\n rel: isExternal ? \"noopener noreferrer\" : rel,\n \"aria-disabled\": disabled || undefined,\n };\n\n return (\n <a className={baseClasses} {...linkProps}>\n {children}\n {isExternal && <ExternalLink className=\"size-icon-sm shrink-0\" aria-hidden />}\n </a>\n );\n};\n","import type React from \"react\";\nimport { cn } from \"../utils/cn\";\n\nexport interface SegmentedOption<T extends string> {\n value: T;\n label: React.ReactNode;\n /**\n * Accessible name and tooltip, for a segment whose label is an icon. Without\n * it an icon-only segment announces as an empty tab.\n */\n title?: string;\n}\n\nexport interface SegmentedToggleProps<T extends string> {\n options: SegmentedOption<T>[];\n value: T;\n onChange: (value: T) => void;\n /** Control height. */\n size?: \"sm\" | \"md\";\n /**\n * Track fill. `sunk` reads against the white card; use `track` when the\n * toggle itself sits on a sunk plane, where `sunk` on `sunk` is invisible.\n */\n tone?: \"sunk\" | \"track\";\n /** Fill the container and split it evenly — a toggle that heads a column. */\n fullWidth?: boolean;\n className?: string;\n /** Accessible group label. */\n \"aria-label\"?: string;\n}\n\n/** Track height stays one step below the equivalent Button so it nests in toolbars. */\nconst sizes = {\n sm: \"h-control-sm px-3 text-sm\",\n md: \"h-control-md px-3 text-sm\",\n};\n\n/**\n * Segmented toggle (focus \"Feed / Eén tegelijk\", tasks \"Mijn / Mijn teams\").\n * Track = surface-sunk, active segment = a raised white plate — the same\n * \"lifted off the plane behind it\" language as the active nav item.\n *\n * @example\n * ```tsx\n * <SegmentedToggle\n * value={layout}\n * onChange={setLayout}\n * options={[{ value: \"feed\", label: \"Feed\" }, { value: \"one\", label: \"Eén tegelijk\" }]}\n * />\n * ```\n */\nexport function SegmentedToggle<T extends string>({\n options,\n value,\n onChange,\n size = \"sm\",\n tone = \"sunk\",\n fullWidth = false,\n className,\n \"aria-label\": ariaLabel,\n}: SegmentedToggleProps<T>) {\n return (\n <div\n role=\"tablist\"\n aria-label={ariaLabel}\n className={cn(\n \"gap-0.5 rounded-md p-0.5\",\n tone === \"track\" ? \"bg-surface-track\" : \"bg-surface-sunk\",\n fullWidth ? \"flex w-full\" : \"inline-flex\",\n className,\n )}\n >\n {options.map((opt) => {\n const active = opt.value === value;\n return (\n <button\n key={opt.value}\n role=\"tab\"\n aria-selected={active}\n aria-label={opt.title}\n title={opt.title}\n type=\"button\"\n onClick={() => onChange(opt.value)}\n className={cn(\n \"inline-flex cursor-pointer items-center justify-center gap-1.5 rounded-sm\",\n \"transition-colors duration-fast ease-out\",\n \"focus-visible:outline-none focus-visible:focus-ring\",\n fullWidth && \"min-w-0 flex-1\",\n sizes[size],\n active\n ? \"bg-surface text-text font-medium shadow-sm\"\n : \"text-text-muted hover:text-text\",\n )}\n >\n {opt.label}\n </button>\n );\n })}\n </div>\n );\n}\n","import { ChevronDown } from \"lucide-react\";\nimport type React from \"react\";\nimport { Popover } from \"../overlays/Popover\";\nimport { cn } from \"../utils/cn\";\nimport { Button } from \"./Button\";\n\nexport interface SplitButtonOption {\n id: string;\n label: string;\n onClick: () => void;\n variant?: \"default\" | \"destructive\";\n /** Optional icon before the label in the menu. */\n icon?: React.ReactNode;\n}\n\nexport interface SplitButtonProps {\n label: string;\n onClick: () => void;\n variant?: \"primary\" | \"secondary\" | \"outline\";\n size?: \"sm\" | \"md\";\n icon?: React.ReactNode;\n options: SplitButtonOption[];\n disabled?: boolean;\n /** Spinner in the primary half; blocks both halves. */\n loading?: boolean;\n /**\n * Where the options menu opens. Use a `top-*` placement when the button sits\n * at the bottom of its container (e.g. a sticky send bar).\n */\n menuPlacement?: \"bottom-end\" | \"bottom-start\" | \"top-end\" | \"top-start\";\n /** Accessible name for the chevron button. */\n menuLabel?: string;\n}\n\n/** Same variant tokens as Button, so both halves look like one button. */\nconst variantStyles = {\n primary: \"bg-accent text-accent-fg hover:bg-accent-hover\",\n secondary: \"border border-border bg-surface text-text hover:bg-surface-hover\",\n outline: \"border border-accent-border text-accent hover:bg-accent-soft\",\n};\n\nconst chevronSizes = {\n sm: \"h-control-sm w-7\",\n md: \"h-control-md w-8\",\n};\n\n/**\n * Button with a primary action plus a chevron menu of variants. The primary half\n * is a plain `Button` (same variants/loading behavior); the menu goes through\n * `Popover`, so it is `position: fixed` and never clipped by a scrolling or\n * overflow-hidden ancestor.\n *\n * Without options this renders exactly a `Button` — no chevron.\n */\nexport function SplitButton({\n label,\n onClick,\n variant = \"primary\",\n size = \"md\",\n icon,\n options,\n disabled = false,\n loading = false,\n menuPlacement = \"bottom-end\",\n menuLabel = \"More options\",\n}: SplitButtonProps) {\n const isDisabled = disabled || loading;\n const hasMenu = options.length > 0;\n\n return (\n // The split between halves is a gap in the fill, not a line: on an ink-filled\n // button 2px reads as a crack; 1px is enough to show two targets. Outlined\n // variants already have their own borders and keep the halves touching.\n <div className={cn(\"inline-flex items-center\", variant === \"primary\" && \"gap-px\")}>\n <Button\n variant={variant}\n size={size}\n onClick={onClick}\n disabled={disabled}\n loading={loading}\n leftIcon={icon}\n className={cn(hasMenu && \"rounded-r-none\")}\n >\n {label}\n </Button>\n {hasMenu && (\n <Popover\n placement={menuPlacement}\n disabled={isDisabled}\n triggerLabel={menuLabel}\n className=\"min-w-menu py-1\"\n trigger={\n <span\n aria-hidden=\"true\"\n className={cn(\n \"inline-flex items-center justify-center rounded-md rounded-l-none\",\n \"transition-colors duration-fast ease-out\",\n variantStyles[variant],\n chevronSizes[size],\n isDisabled && \"cursor-not-allowed opacity-50\",\n )}\n >\n <ChevronDown className=\"size-icon-md\" />\n </span>\n }\n >\n {(close) => (\n <>\n {options.map((option) => (\n <button\n key={option.id}\n type=\"button\"\n onClick={() => {\n option.onClick();\n close();\n }}\n className={cn(\n \"flex w-full items-center gap-2 px-3 py-1.5 text-left text-sm\",\n \"transition-colors duration-fast ease-out\",\n \"focus-visible:outline-none focus-visible:focus-ring\",\n option.variant === \"destructive\"\n ? \"text-danger-fg hover:bg-danger-soft\"\n : \"text-text hover:bg-surface-hover\",\n )}\n >\n {option.icon && <span className=\"shrink-0\">{option.icon}</span>}\n {option.label}\n </button>\n ))}\n </>\n )}\n </Popover>\n )}\n </div>\n );\n}\n","import { ChevronRight, Search } from \"lucide-react\";\nimport type React from \"react\";\nimport { useEffect, useRef, useState } from \"react\";\n\n// Define the types for the command palette\nexport interface Command {\n id: string;\n label: string;\n action?: () => void;\n subCommands?: Command[];\n icon?: React.ReactNode;\n}\n\nexport interface CommandPaletteProps {\n commands: Command[];\n isOpen: boolean;\n onClose: () => void;\n labels?: {\n searchPlaceholder?: string;\n navigate?: string;\n select?: string;\n close?: string;\n noCommandsFound?: string;\n };\n initialCommandPath?: string[];\n initialActiveIndex?: number;\n}\n\nconst CommandPalette: React.FC<CommandPaletteProps> = ({\n commands,\n isOpen,\n onClose,\n labels,\n initialCommandPath,\n initialActiveIndex,\n}) => {\n const [searchTerm, setSearchTerm] = useState(\"\");\n const [activeCommandPath, setActiveCommandPath] = useState<string[]>(initialCommandPath || []);\n const [activeIndex, setActiveIndex] = useState(initialActiveIndex || 0);\n const paletteRef = useRef<HTMLDivElement>(null);\n const listRef = useRef<HTMLDivElement>(null);\n\n const currentCommands = activeCommandPath.length\n ? commands.find((c) => c.id === activeCommandPath[0])?.subCommands || []\n : commands;\n\n const filteredCommands = currentCommands.filter((command) =>\n command.label.toLowerCase().includes(searchTerm.toLowerCase()),\n );\n\n // Reset state when the palette is closed\n useEffect(() => {\n if (!isOpen) {\n setSearchTerm(\"\");\n setActiveCommandPath(initialCommandPath || []);\n setActiveIndex(initialActiveIndex || 0);\n }\n }, [isOpen]);\n\n useEffect(() => {\n const handleKeyDown = (event: KeyboardEvent) => {\n if (!isOpen) return;\n\n if (event.key === \"Escape\") {\n onClose();\n } else if (event.key === \"ArrowUp\") {\n setActiveIndex((prevIndex) =>\n prevIndex > 0 ? prevIndex - 1 : filteredCommands.length - 1,\n );\n } else if (event.key === \"ArrowDown\") {\n setActiveIndex((prevIndex) =>\n prevIndex < filteredCommands.length - 1 ? prevIndex + 1 : 0,\n );\n } else if (event.key === \"Enter\") {\n const command = filteredCommands[activeIndex];\n if (command) {\n if (command.subCommands) {\n setActiveCommandPath([...activeCommandPath, command.id]);\n setActiveIndex(0);\n } else if (command.action) {\n command.action();\n onClose();\n }\n }\n }\n };\n\n document.addEventListener(\"keydown\", handleKeyDown);\n return () => document.removeEventListener(\"keydown\", handleKeyDown);\n }, [isOpen, onClose, filteredCommands, activeIndex, activeCommandPath]);\n\n useEffect(() => {\n const handleClickOutside = (event: MouseEvent) => {\n if (paletteRef.current && !paletteRef.current.contains(event.target as Node)) {\n onClose();\n }\n };\n\n if (isOpen) {\n document.addEventListener(\"mousedown\", handleClickOutside);\n }\n return () => document.removeEventListener(\"mousedown\", handleClickOutside);\n }, [isOpen, onClose]);\n\n // Keep active item visible while scrolling via keyboard\n useEffect(() => {\n if (!isOpen) return;\n const el = listRef.current?.querySelector<HTMLElement>(`[data-index=\"${activeIndex}\"]`);\n el?.scrollIntoView({ block: \"nearest\" });\n }, [activeIndex, isOpen]);\n\n if (!isOpen) {\n return null;\n }\n\n return (\n <div className=\"fixed inset-0 z-modal flex items-center justify-center bg-scrim\">\n <div\n className=\"flex flex-col w-full max-w-lg max-h-dialog p-2 mx-auto rounded-lg border border-border shadow-modal bg-surface\"\n ref={paletteRef}\n >\n <div className=\"flex items-center gap-2 px-3 flex-shrink-0\">\n <Search className=\"w-4 h-4 text-text-muted shrink-0\" />\n <input\n type=\"text\"\n autoFocus\n placeholder={labels?.searchPlaceholder || \"Search commands...\"}\n value={searchTerm}\n onChange={(e) => setSearchTerm(e.target.value)}\n className=\"w-full border-0 bg-transparent py-2 text-text focus:outline-none\"\n />\n </div>\n <div\n ref={listRef}\n className=\"mt-2 pt-2 text-sm border-t border-border max-h-dialog-list overflow-y-auto\"\n >\n {filteredCommands.length === 0 && (\n <div className=\"text-text-muted text-center text-sm\">\n {labels?.noCommandsFound || \"No commands found\"}\n </div>\n )}\n {filteredCommands.map((command, index) => (\n <div\n key={command.id}\n data-index={index}\n className={`flex items-center p-2 cursor-pointer rounded-md ${\n index === activeIndex ? \"bg-accent-soft text-accent\" : \"text-text\"\n }`}\n onClick={() => {\n if (command.subCommands) {\n setActiveCommandPath([...activeCommandPath, command.id]);\n setActiveIndex(0);\n } else if (command.action) {\n command.action();\n onClose();\n }\n }}\n onMouseEnter={() => setActiveIndex(index)}\n >\n {command.icon && <span className=\"mr-3\">{command.icon}</span>}\n <span className=\"flex-grow\">{command.label}</span>\n {command.subCommands && (\n <span className=\"text-text-muted\">\n <ChevronRight className=\"w-4 h-4\" />\n </span>\n )}\n </div>\n ))}\n </div>\n <div className=\"flex items-center gap-4 px-3 pt-2 mt-2 text-xs text-text-muted border-t border-border\">\n <span className=\"flex items-center gap-1.5\">\n <kbd className=\"inline-flex items-center justify-center min-w-5 px-1 py-0.5 rounded border border-border bg-surface-sunk text-xs font-medium not-italic\">\n ↑↓\n </kbd>\n {labels?.navigate || \"to navigate\"}\n </span>\n <span className=\"flex items-center gap-1.5\">\n <kbd className=\"inline-flex items-center justify-center min-w-5 px-1 py-0.5 rounded border border-border bg-surface-sunk text-xs font-medium not-italic\">\n ↵\n </kbd>\n {labels?.select || \"to select\"}\n </span>\n <span className=\"flex items-center gap-1.5\">\n <kbd className=\"inline-flex items-center justify-center min-w-5 px-1 py-0.5 rounded border border-border bg-surface-sunk text-xs font-medium not-italic\">\n Esc\n </kbd>\n {labels?.close || \"to close\"}\n </span>\n </div>\n </div>\n </div>\n );\n};\n\nexport default CommandPalette;\n","import DOMPurify from \"dompurify\";\nimport type { Components } from \"react-markdown\";\nimport Markdown from \"react-markdown\";\nimport remarkGfm from \"remark-gfm\";\nimport { cn } from \"../utils/cn\";\n\n/**\n * Renders text that may be markdown or HTML — the same fields carry rich HTML\n * from the mention-composer but markdown from an AI tool or playbook, and\n * rendering only one leaves the other as raw characters (`**5MB**`). HTML is\n * DOMPurify-sanitized, never unfiltered — it can come from a model or an\n * external provider. Markdown (react-markdown + remark-gfm) emits no raw HTML,\n * so that path cannot inject. Both branches are deliberately styled alike via\n * semantic tokens; the HTML branch needs descendant classes because Tailwind's\n * preflight strips `ul`/`ol` defaults.\n */\n\nconst HTML_TAG = /<[a-z][\\s\\S]*>/i;\n\n/** Styling for the HTML branch; mirrors the `components` map below. */\nconst HTML_PROSE = [\n \"[&_p]:my-1.5 [&_p:first-child]:mt-0 [&_p:last-child]:mb-0 [&_p]:leading-relaxed\",\n \"[&_a]:text-accent [&_a]:underline [&_a]:underline-offset-2\",\n \"[&_strong]:font-semibold [&_em]:italic\",\n \"[&_ul]:my-1.5 [&_ul]:list-disc [&_ul]:pl-5 [&_ol]:my-1.5 [&_ol]:list-decimal [&_ol]:pl-5\",\n \"[&_li]:leading-relaxed\",\n \"[&_h1]:mb-1 [&_h1]:mt-2 [&_h1]:font-semibold [&_h2]:mb-1 [&_h2]:mt-2 [&_h2]:font-semibold [&_h3]:mb-1 [&_h3]:mt-2 [&_h3]:font-semibold\",\n \"[&_blockquote]:my-1.5 [&_blockquote]:border-l-2 [&_blockquote]:border-border [&_blockquote]:pl-3 [&_blockquote]:text-text-muted\",\n \"[&_hr]:my-2 [&_hr]:border-border\",\n \"[&_pre]:my-1.5 [&_pre]:overflow-x-auto [&_pre]:rounded-md [&_pre]:bg-surface-sunk [&_pre]:p-2 [&_pre]:text-xs\",\n \"[&_code]:rounded-sm [&_code]:bg-surface-sunk [&_code]:px-1 [&_code]:py-0.5 [&_code]:font-mono [&_code]:text-xs\",\n \"[&_pre_code]:bg-transparent [&_pre_code]:p-0\",\n \"[&_table]:my-1.5 [&_table]:w-full [&_table]:border-collapse [&_table]:text-xs\",\n \"[&_th]:border [&_th]:border-border [&_th]:px-2 [&_th]:py-1 [&_th]:text-left [&_th]:font-semibold\",\n \"[&_td]:border [&_td]:border-border [&_td]:px-2 [&_td]:py-1\",\n].join(\" \");\n\nconst components: Components = {\n p: (props) => <p className=\"my-1.5 leading-relaxed first:mt-0 last:mb-0\" {...props} />,\n a: (props) => (\n <a\n className=\"text-accent underline underline-offset-2 hover:opacity-80\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n {...props}\n />\n ),\n strong: (props) => <strong className=\"font-semibold\" {...props} />,\n em: (props) => <em className=\"italic\" {...props} />,\n ul: (props) => <ul className=\"my-1.5 list-disc space-y-0.5 pl-5\" {...props} />,\n ol: (props) => <ol className=\"my-1.5 list-decimal space-y-0.5 pl-5\" {...props} />,\n li: (props) => <li className=\"leading-relaxed\" {...props} />,\n h1: (props) => <h1 className=\"mb-1 mt-2 text-md font-semibold first:mt-0\" {...props} />,\n h2: (props) => <h2 className=\"mb-1 mt-2 text-base font-semibold first:mt-0\" {...props} />,\n h3: (props) => <h3 className=\"mb-1 mt-2 text-base font-semibold first:mt-0\" {...props} />,\n blockquote: (props) => (\n <blockquote className=\"my-1.5 border-l-2 border-border pl-3 text-text-muted\" {...props} />\n ),\n hr: (props) => <hr className=\"my-2 border-border\" {...props} />,\n pre: (props) => (\n <pre className=\"my-1.5 overflow-x-auto rounded-md bg-surface-sunk p-2 text-xs\" {...props} />\n ),\n code: ({ className, children, ...props }) => {\n const content = String(children ?? \"\");\n const isBlock = /language-/.test(className ?? \"\") || content.includes(\"\\n\");\n return isBlock ? (\n <code className=\"font-mono text-xs\" {...props}>\n {children}\n </code>\n ) : (\n <code className=\"rounded-sm bg-surface-sunk px-1 py-0.5 font-mono text-xs\" {...props}>\n {children}\n </code>\n );\n },\n table: (props) => (\n <div className=\"my-1.5 overflow-x-auto\">\n <table className=\"w-full border-collapse text-xs\" {...props} />\n </div>\n ),\n th: (props) => (\n <th className=\"border border-border px-2 py-1 text-left font-semibold\" {...props} />\n ),\n td: (props) => <td className=\"border border-border px-2 py-1\" {...props} />,\n};\n\nexport interface RichTextProps {\n /** Markdown or HTML; which of the two is auto-detected. */\n text: string | null | undefined;\n /** Extra classes on the wrapper (e.g. `text-sm` or a line-clamp). */\n className?: string;\n /** Force a branch instead of detecting — only when the heuristic gets in\n * the way (e.g. plain text that happens to look like a tag). */\n as?: \"markdown\" | \"html\";\n}\n\n/** Safely rendered markdown-or-HTML text. */\nexport function RichText({ text, className, as }: RichTextProps) {\n const src = text ?? \"\";\n const isHtml = as ? as === \"html\" : HTML_TAG.test(src);\n\n if (isHtml) {\n return (\n <div\n className={cn(\"break-words\", HTML_PROSE, className)}\n dangerouslySetInnerHTML={{\n __html: DOMPurify.sanitize(src, { ADD_ATTR: [\"target\", \"rel\"] }),\n }}\n />\n );\n }\n\n return (\n <div className={cn(\"break-words\", className)}>\n <Markdown remarkPlugins={[remarkGfm]} components={components}>\n {src}\n </Markdown>\n </div>\n );\n}\n","import type { ArtifactBlock, ArtifactRuntime, ArtifactTone } from \"@opencxh/domain\";\nimport { cn } from \"../utils/cn\";\nimport { RichText } from \"./RichText\";\n\nexport interface ArtifactViewProps {\n blocks: ArtifactBlock[];\n /** Everything except `\"inert\"` is rejected. See the class note above. */\n runtime?: ArtifactRuntime;\n className?: string;\n}\n\n/** Statisch, want `text-${tone}-fg` genereert niets (Tailwind scant letterlijk). */\nconst KPI_TONE: Record<ArtifactTone, string> = {\n info: \"text-info-fg\",\n success: \"text-success-fg\",\n warning: \"text-warning-fg\",\n destructive: \"text-danger-fg\",\n};\n\nconst CALLOUT_TONE: Record<ArtifactTone, string> = {\n info: \"bg-info-soft border-info-border\",\n success: \"bg-success-soft border-success-border\",\n warning: \"bg-warning-soft border-warning-border\",\n destructive: \"bg-danger-soft border-danger-border\",\n};\n\n/** One to four tiles in a row; static too, for the same reason. */\nconst KPI_COLUMNS: Record<number, string> = {\n 1: \"grid-cols-1\",\n 2: \"grid-cols-2\",\n 3: \"grid-cols-3\",\n 4: \"grid-cols-4\",\n};\n\nfunction Block({ block }: { block: ArtifactBlock }) {\n switch (block.type) {\n case \"heading\":\n if (block.level === 1) {\n return <h1 className=\"text-2xl font-semibold tracking-tight text-text\">{block.text}</h1>;\n }\n if (block.level === 2) {\n return (\n <h2 className=\"pt-2 text-base font-semibold tracking-tight text-text\">{block.text}</h2>\n );\n }\n return <h3 className=\"pt-1 text-sm font-semibold text-text\">{block.text}</h3>;\n\n case \"paragraph\":\n return <RichText as=\"markdown\" text={block.text} className=\"text-sm text-text\" />;\n\n case \"quote\":\n return (\n <blockquote className=\"border-l-2 border-border pl-3.5\">\n <RichText as=\"markdown\" text={block.text} className=\"text-sm text-text-muted\" />\n </blockquote>\n );\n\n case \"code\":\n return (\n <pre className=\"overflow-x-auto rounded-lg bg-surface-sunk p-3 text-xs\">\n <code className=\"font-mono\">{block.text}</code>\n </pre>\n );\n\n case \"divider\":\n return <hr className=\"border-border-subtle\" />;\n\n case \"list\": {\n const List = block.style === \"numbered\" ? \"ol\" : \"ul\";\n return (\n <List\n className={cn(\n \"flex flex-col gap-2 pl-5 text-sm text-text\",\n block.style === \"numbered\" ? \"list-decimal\" : \"list-disc\",\n )}\n >\n {block.items.map((item, index) => (\n <li key={index} className=\"leading-relaxed marker:text-text-subtle\">\n {item.lead && <strong className=\"font-semibold\">{item.lead} </strong>}\n <RichText as=\"markdown\" text={item.text} className=\"inline\" />\n </li>\n ))}\n </List>\n );\n }\n\n case \"table\":\n return (\n <figure className=\"m-0\">\n {/* Brede tabellen scrollen in hun eigen doos; het document zelf mag\n nooit horizontaal schuiven. */}\n <div className=\"overflow-x-auto rounded-lg ring-1 ring-border\">\n <table className=\"w-full border-collapse text-sm\">\n <thead>\n <tr className=\"bg-surface-sunk\">\n {block.columns.map((column, index) => (\n <th\n key={index}\n className={cn(\n \"px-4 py-2.5 text-xs font-semibold text-text-muted\",\n column.align === \"right\" ? \"text-right\" : \"text-left\",\n )}\n >\n {column.label}\n </th>\n ))}\n </tr>\n </thead>\n <tbody>\n {block.rows.map((row, rowIndex) => (\n <tr key={rowIndex} className=\"border-t border-border-subtle\">\n {row.map((value, cellIndex) => (\n <td\n key={cellIndex}\n className={cn(\n \"px-4 py-2.5 tabular-nums text-text\",\n block.columns[cellIndex]?.align === \"right\" ? \"text-right\" : \"text-left\",\n )}\n >\n {value}\n </td>\n ))}\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n {block.caption && (\n <figcaption className=\"pt-2 text-xs italic text-text-subtle\">\n {block.caption}\n </figcaption>\n )}\n </figure>\n );\n\n case \"kpi\":\n return (\n <div className={cn(\"grid gap-3\", KPI_COLUMNS[block.items.length] ?? \"grid-cols-4\")}>\n {block.items.map((item, index) => (\n <div key={index} className=\"rounded-lg bg-surface-sunk px-4 py-3.5\">\n <div\n className={cn(\n \"text-2xl font-semibold tracking-tight\",\n item.tone ? KPI_TONE[item.tone] : \"text-text\",\n )}\n >\n {item.value}\n </div>\n <div className=\"pt-0.5 text-xs text-text-muted\">{item.label}</div>\n </div>\n ))}\n </div>\n );\n\n case \"callout\":\n return (\n <div className={cn(\"rounded-lg border px-4 py-3\", CALLOUT_TONE[block.tone])}>\n {block.title && (\n <div className=\"pb-0.5 text-sm font-semibold text-text\">{block.title}</div>\n )}\n <RichText as=\"markdown\" text={block.text} className=\"text-sm text-text\" />\n </div>\n );\n\n default:\n // Unknown block: skip, don't crash.\n return null;\n }\n}\n\nexport function ArtifactView({ blocks, runtime = \"inert\", className }: ArtifactViewProps) {\n if (runtime !== \"inert\") {\n return (\n <div\n className={cn(\n \"rounded-lg border border-warning-border bg-warning-soft px-4 py-3\",\n className,\n )}\n >\n <div className=\"text-sm font-semibold text-text\">\n Deze versie kan hier niet getoond worden\n </div>\n <div className=\"pt-0.5 text-xs text-text-muted\">\n Hij is opgeslagen als <code className=\"font-mono\">{runtime}</code>, en deze weergave\n tekent alleen inerte inhoud.\n </div>\n </div>\n );\n }\n\n return (\n <article className={cn(\"flex flex-col gap-4\", className)}>\n {blocks.map((block, index) => (\n <Block key={block.block_id ?? index} block={block} />\n ))}\n </article>\n );\n}\n","import { Sparkles } from \"lucide-react\";\nimport type React from \"react\";\nimport { cn } from \"../utils/cn\";\n\nexport interface AssistantCardProps extends Omit<React.HTMLAttributes<HTMLDivElement>, \"title\"> {\n /** Card heading (e.g. \"Briefing van je assistent\", \"Voorstel van de assistent\"). */\n title: React.ReactNode;\n /** Leading icon; defaults to the lucide Sparkles icon. */\n icon?: React.ReactNode;\n /** Optional action rendered on the header's right (e.g. a \"Gebruiken\" button). */\n action?: React.ReactNode;\n /** Body content. */\n children?: React.ReactNode;\n}\n\n/**\n * AI-assistant card shell — the tinted \"Briefing / Voorstel van de assistent\"\n * surface used on Mijn dag and in the Focus queue. Uses the `--assistant-*`\n * design-system tokens so the tint is mode-aware.\n *\n * @example\n * ```tsx\n * <AssistantCard title=\"Voorstel van de assistent\" action={<Button>Gebruiken</Button>}>\n * Marc wacht 12 min op WhatsApp — concept staat klaar.\n * </AssistantCard>\n * ```\n */\nexport function AssistantCard({\n title,\n icon = <Sparkles className=\"size-icon-md\" aria-hidden />,\n action,\n children,\n className,\n ...props\n}: AssistantCardProps) {\n return (\n <div\n className={cn(\n \"flex flex-col gap-2 rounded-xl border p-4\",\n \"border-assistant-border bg-assistant-soft\",\n className,\n )}\n {...props}\n >\n <div className=\"flex items-center gap-2 text-assistant\">\n <span aria-hidden>{icon}</span>\n <span className=\"text-sm font-semibold\">{title}</span>\n {action && <span className=\"ml-auto\">{action}</span>}\n </div>\n {children && <div className=\"text-sm leading-relaxed text-text\">{children}</div>}\n </div>\n );\n}\n","/**\n * Deterministic colour for a thing that has a name but no status.\n *\n * The rule this exists to enforce: the same name gets the same colour\n * everywhere in the product. Every surface that used to pick a plate colour did\n * it locally — an index in a list, a hash of its own, or a fixed neutral — so\n * one person could be blue in the inbox and grey on a task, and the colour\n * carried no information at all.\n *\n * The palette is the `--color-cat-*` categorical set, which the design system\n * defines for exactly this: equal chroma and lightness, so no entry outranks\n * another, and dark mode is handled by the tokens rather than here.\n */\n\n/** How many `--color-cat-N` entries the design system defines. */\nexport const IDENTITY_TONE_COUNT = 5;\n\nexport type IdentityTone = 1 | 2 | 3 | 4 | 5;\n\n/**\n * FNV-1a. Small, stable across runs and platforms, and well spread for short\n * strings — `Math.random` would break the whole point, and summing char codes\n * collides badly on names that are anagrams (\"Bram\"/\"Marb\") or differ only in\n * order (\"Team A\"/\"A Team\").\n */\nfunction hash(seed: string): number {\n let h = 0x811c9dc5;\n for (let i = 0; i < seed.length; i++) {\n h ^= seed.charCodeAt(i);\n h = Math.imul(h, 0x01000193);\n }\n return h >>> 0;\n}\n\n/**\n * Normalised so that the same identity written differently still lands on the\n * same colour: casing and surrounding whitespace never distinguish two names.\n */\nexport function identityTone(seed: string | undefined | null): IdentityTone {\n const key = (seed ?? \"\").trim().toLowerCase();\n if (!key) return 1;\n return ((hash(key) % IDENTITY_TONE_COUNT) + 1) as IdentityTone;\n}\n\n/**\n * Whole class names, not interpolated fragments: Tailwind scans source text, so\n * `bg-cat-${n}-soft` would compile to nothing and the plate would come out\n * transparent.\n */\nconst PLATE: Record<IdentityTone, string> = {\n 1: \"bg-cat-1-soft text-cat-1-fg\",\n 2: \"bg-cat-2-soft text-cat-2-fg\",\n 3: \"bg-cat-3-soft text-cat-3-fg\",\n 4: \"bg-cat-4-soft text-cat-4-fg\",\n 5: \"bg-cat-5-soft text-cat-5-fg\",\n};\n\nconst SOLID: Record<IdentityTone, string> = {\n 1: \"bg-cat-1\",\n 2: \"bg-cat-2\",\n 3: \"bg-cat-3\",\n 4: \"bg-cat-4\",\n 5: \"bg-cat-5\",\n};\n\n/** Wash + readable mark, for a plate that holds a monogram or a glyph. */\nexport function identityPlateClass(seed: string | undefined | null): string {\n return PLATE[identityTone(seed)];\n}\n\n/** The solid colour, for a dot or a rule. */\nexport function identityDotClass(seed: string | undefined | null): string {\n return SOLID[identityTone(seed)];\n}\n","import React from \"react\";\nimport { cn } from \"../utils/cn\";\nimport { identityPlateClass } from \"../utils/identity\";\n\n/**\n * Plate colour. `default` is the neutral disc for a lone monogram; `identity`\n * derives the colour from `name` so the same person or team is the same colour\n * on every surface (see {@link identityPlateClass}) — use it in a list of many\n * people. The other three are for a message thread, where the plate answers\n * \"which side is this from\": `in` is the other party, `out` is us (ink, so it\n * reads as ours in both themes), `note` an internal remark. Each is a fill with\n * its own foreground — the ramp does not change between light and dark.\n */\nexport type AvatarTone = \"default\" | \"identity\" | \"in\" | \"out\" | \"note\";\n\nconst toneClasses: Record<Exclude<AvatarTone, \"identity\">, string> = {\n default: \"bg-avatar text-avatar-fg\",\n in: \"bg-avatar-in text-avatar-in-fg\",\n out: \"bg-accent text-accent-fg\",\n note: \"bg-avatar-note text-avatar-note-fg\",\n};\n\nexport interface AvatarProps {\n /** Full name; used for the initials fallback and alt/title text */\n name?: string;\n /** Optional image URL; falls back to initials when absent or on load error */\n src?: string;\n /** Plate colour; see {@link AvatarTone}. */\n tone?: AvatarTone;\n size?: \"xs\" | \"sm\" | \"md\" | \"lg\";\n className?: string;\n /** Override the title attribute (defaults to `name`) */\n title?: string;\n}\n\nconst sizeClasses: Record<NonNullable<AvatarProps[\"size\"]>, string> = {\n xs: \"w-5 h-5 text-xs\",\n sm: \"w-6 h-6 text-xs\",\n md: \"w-7 h-7 text-base\",\n lg: \"w-9 h-9 text-sm\",\n};\n\nfunction initials(name?: string): string {\n if (!name) return \"?\";\n // Split on anything that is not a letter/digit (space, @, ., -, _, …) so\n // addresses like \"mail-team@x.nl\" give clean initials (\"MT\"), not \"M-\".\n const parts = name\n .trim()\n .split(/[^\\p{L}\\p{N}]+/u)\n .filter(Boolean);\n if (parts.length >= 2) return (parts[0][0] + parts[1][0]).toUpperCase();\n if (parts.length === 1) return parts[0].slice(0, 2).toUpperCase();\n return \"?\";\n}\n\n/**\n * Circular avatar: renders the photo when `src` is set (falling back to\n * initials on error), otherwise a monogram derived from `name`.\n */\nexport const Avatar: React.FC<AvatarProps> = ({\n name,\n src,\n tone = \"default\",\n size = \"sm\",\n className,\n title,\n}) => {\n const [errored, setErrored] = React.useState(false);\n const base = cn(\n // Its own token and not `surface-hover`: at 0.972 the plate was within a few\n // percent of every surface it sits on, so the monogram floated instead of\n // sitting in a disc.\n \"inline-flex items-center justify-center rounded-full overflow-hidden shrink-0 font-semibold select-none\",\n // `identity` is seeded on the name, so the same person keeps their colour\n // wherever they appear; the fixed tones stay as they were.\n tone === \"identity\" ? identityPlateClass(name) : toneClasses[tone],\n sizeClasses[size],\n className,\n );\n\n if (src && !errored) {\n return (\n <img\n src={src}\n alt={name ?? \"\"}\n title={title ?? name}\n className={cn(base, \"object-cover\")}\n onError={() => setErrored(true)}\n />\n );\n }\n\n return (\n <span className={base} title={title ?? name} aria-label={name}>\n {initials(name)}\n </span>\n );\n};\n","import type React from \"react\";\nimport { cn } from \"../utils/cn\";\nimport { Avatar, type AvatarProps } from \"./Avatar\";\n\nexport interface AvatarStackPerson {\n id?: string;\n name?: string;\n src?: string;\n /** Owner / assignee — rendered with an accent ring */\n owner?: boolean;\n /** Whether this person has seen the item; unseen renders dimmed */\n seen?: boolean;\n}\n\nexport interface AvatarStackProps {\n people: AvatarStackPerson[];\n /** Max avatars to show before collapsing the rest into a \"+N\" chip */\n max?: number;\n size?: AvatarProps[\"size\"];\n className?: string;\n}\n\n// Panel-coloured ring so overlapping avatars stay visually separated; the\n// owner gets an extra accent halo on top of it.\nconst RING = \"ring-surface\";\nconst OWNER_RING = \"ring-surface-accent\";\n\n/**\n * Overlapping row of avatars with an optional \"+N\" overflow chip. Unseen\n * people are dimmed; the owner carries an accent ring.\n */\nexport const AvatarStack: React.FC<AvatarStackProps> = ({\n people,\n max = 4,\n size = \"sm\",\n className,\n}) => {\n if (people.length === 0) return null;\n const shown = people.slice(0, max);\n const overflow = people.length - shown.length;\n\n return (\n <span className={cn(\"inline-flex items-center\", className)}>\n {shown.map((p, i) => (\n <Avatar\n key={p.id ?? `${p.name}-${i}`}\n name={p.name}\n src={p.src}\n size={size}\n title={p.name ? (p.seen === false ? `${p.name} — nog niet gelezen` : p.name) : undefined}\n className={cn(\n i > 0 && \"-ml-2\",\n p.owner ? OWNER_RING : RING,\n p.seen === false && \"opacity-40\",\n )}\n />\n ))}\n {overflow > 0 && (\n <span\n className={cn(\n // Same fill as an Avatar plate, so the \"+3\" reads as one more disc\n // in the row rather than a gap at the end of it.\n \"inline-flex items-center justify-center rounded-full shrink-0 -ml-2 font-semibold bg-avatar text-avatar-fg\",\n size === \"xs\"\n ? \"w-5 h-5 text-xs\"\n : size === \"md\"\n ? \"w-7 h-7 text-xs\"\n : size === \"lg\"\n ? \"w-9 h-9 text-xs\"\n : \"w-6 h-6 text-xs\",\n RING,\n )}\n >\n +{overflow}\n </span>\n )}\n </span>\n );\n};\n","import type React from \"react\";\nimport { cn } from \"../utils/cn\";\nimport { identityPlateClass } from \"../utils/identity\";\n\n/** Communication channel keys the redesign colour-codes. */\nexport type ChannelKey = \"mail\" | \"wa\" | \"chat\" | \"tel\" | \"note\";\n\nexport interface ChannelBadgeProps {\n /**\n * Channel — drives the colour. A known key uses that channel's tokens; any\n * other string (a provider that brought its own channel along) gets a stable\n * colour derived from the string itself, so ten custom channels stay ten\n * distinguishable colours instead of ten identical grey tiles.\n */\n channel?: ChannelKey | string;\n /** Glyph or short label inside the tile (e.g. an icon, or an initial). */\n children?: React.ReactNode;\n /** Tile size. */\n size?: \"sm\" | \"md\" | \"lg\";\n className?: string;\n title?: string;\n}\n\n/**\n * Channel key → semantic token pair. The keys are the redesign's shorthand\n * (`wa`, `tel`); the tokens spell the channel out (`whatsapp`, `phone`).\n * Written as whole class names so Tailwind can find them.\n */\nconst CH: Record<ChannelKey, { fg: string; bg: string }> = {\n mail: { fg: \"text-channel-mail-fg\", bg: \"bg-channel-mail-soft\" },\n wa: { fg: \"text-channel-whatsapp-fg\", bg: \"bg-channel-whatsapp-soft\" },\n chat: { fg: \"text-channel-chat-fg\", bg: \"bg-channel-chat-soft\" },\n tel: { fg: \"text-channel-phone-fg\", bg: \"bg-channel-phone-soft\" },\n note: { fg: \"text-channel-note-fg\", bg: \"bg-channel-note-soft\" },\n};\n\nconst sizes: Record<NonNullable<ChannelBadgeProps[\"size\"]>, string> = {\n sm: \"size-avatar-xs rounded-sm text-xs\",\n md: \"size-avatar-sm rounded-md text-xs\",\n lg: \"size-avatar-lg rounded-md text-base\",\n};\n\n/**\n * Coloured channel tile (the glyph-in-rounded-square used across inbox rows,\n * the focus queue, the attention list and Recent tabs). Colours come from the\n * `--color-channel-*` design-system tokens, so it is mode-aware.\n *\n * @example\n * ```tsx\n * <ChannelBadge channel=\"wa\">W</ChannelBadge>\n * <ChannelBadge channel=\"mail\" size=\"lg\"><MailIcon /></ChannelBadge>\n * ```\n */\nexport function ChannelBadge({\n channel,\n children,\n size = \"md\",\n className,\n title,\n}: ChannelBadgeProps) {\n const known = (channel as ChannelKey) in CH ? CH[channel as ChannelKey] : undefined;\n return (\n <span\n title={title}\n className={cn(\n \"inline-flex shrink-0 items-center justify-center font-bold\",\n sizes[size],\n // An undeclared channel used to collapse onto the neutral \"note\" tone,\n // which is also what a real internal note looks like. Deriving from the\n // key keeps it distinguishable and keeps `note` meaning `note`.\n known\n ? cn(known.fg, known.bg)\n : channel\n ? identityPlateClass(channel)\n : cn(CH.note.fg, CH.note.bg),\n className,\n )}\n >\n {children}\n </span>\n );\n}\n","import { ImageOff } from \"lucide-react\";\nimport type React from \"react\";\nimport { useState } from \"react\";\nimport { cn } from \"../utils/cn\";\nimport { Icon } from \"./Icon\";\n\nexport interface ImageProps extends React.ImgHTMLAttributes<HTMLImageElement> {\n /** Image source URL */\n src: string;\n /** Alt text for accessibility */\n alt: string;\n /** Image size preset */\n size?: \"xs\" | \"sm\" | \"md\" | \"lg\" | \"xl\" | \"full\";\n /** Aspect ratio */\n aspectRatio?: \"square\" | \"video\" | \"portrait\" | \"landscape\" | \"auto\";\n /** Border radius */\n radius?: \"none\" | \"sm\" | \"md\" | \"lg\" | \"xl\" | \"full\";\n /** Whether to show loading state */\n showLoading?: boolean;\n /** Whether to show error state */\n showError?: boolean;\n /** Fallback image URL */\n fallback?: string;\n /** Loading placeholder content */\n loadingContent?: React.ReactNode;\n /** Error placeholder content */\n errorContent?: React.ReactNode;\n /** Additional CSS classes */\n className?: string;\n}\n\n/**\n * Image component with loading states, error handling, and theme integration\n */\nexport const Image: React.FC<ImageProps> = ({\n src,\n alt,\n size = \"md\",\n aspectRatio = \"auto\",\n radius = \"md\",\n showLoading = true,\n showError = true,\n fallback,\n loadingContent,\n errorContent,\n className,\n onLoad,\n onError,\n ...props\n}) => {\n const [isLoading, setIsLoading] = useState(true);\n const [hasError, setHasError] = useState(false);\n const [currentSrc, setCurrentSrc] = useState(src);\n\n const handleLoad = (event: React.SyntheticEvent<HTMLImageElement>) => {\n setIsLoading(false);\n setHasError(false);\n onLoad?.(event);\n };\n\n const handleError = (event: React.SyntheticEvent<HTMLImageElement>) => {\n setIsLoading(false);\n setHasError(true);\n\n // Try fallback if available and not already using it\n if (fallback && currentSrc !== fallback) {\n setCurrentSrc(fallback);\n setHasError(false);\n setIsLoading(true);\n return;\n }\n\n onError?.(event);\n };\n\n const containerClasses = cn(\n \"relative overflow-hidden bg-surface-hover\",\n\n // Size variants\n {\n \"w-8 h-8\": size === \"xs\",\n \"w-12 h-12\": size === \"sm\",\n \"w-16 h-16\": size === \"md\",\n \"w-24 h-24\": size === \"lg\",\n \"w-32 h-32\": size === \"xl\",\n \"w-full h-full\": size === \"full\",\n },\n\n // Aspect ratio variants\n {\n \"aspect-square\": aspectRatio === \"square\",\n \"aspect-video\": aspectRatio === \"video\",\n \"aspect-portrait\": aspectRatio === \"portrait\",\n \"aspect-landscape\": aspectRatio === \"landscape\",\n },\n\n // Border radius variants\n {\n \"rounded-none\": radius === \"none\",\n \"rounded-sm\": radius === \"sm\",\n \"rounded-md\": radius === \"md\",\n \"rounded-lg\": radius === \"lg\",\n \"rounded-xl\": radius === \"xl\",\n \"rounded-full\": radius === \"full\",\n },\n\n className,\n );\n\n const imageClasses = cn(\"w-full h-full object-cover transition-opacity duration-fast\", {\n \"opacity-0\": isLoading || hasError,\n \"opacity-100\": !isLoading && !hasError,\n });\n\n const placeholderClasses = cn(\n \"absolute inset-0 flex items-center justify-center\",\n \"text-text-muted\",\n );\n\n const defaultLoadingContent = (\n <div className=\"animate-pulse\">\n <div className=\"w-6 h-6 bg-surface-hover rounded\"></div>\n </div>\n );\n\n const defaultErrorContent = (\n <div className=\"text-center\">\n <Icon icon={ImageOff} size=\"lg\" className=\"mx-auto mb-1\" />\n <span className=\"text-xs\">Failed to load</span>\n </div>\n );\n\n return (\n <div className={containerClasses}>\n <img\n {...props}\n src={currentSrc}\n alt={alt}\n className={imageClasses}\n onLoad={handleLoad}\n onError={handleError}\n />\n\n {/* Loading state */}\n {isLoading && showLoading && (\n <div className={placeholderClasses}>{loadingContent || defaultLoadingContent}</div>\n )}\n\n {/* Error state */}\n {hasError && showError && (\n <div className={placeholderClasses}>{errorContent || defaultErrorContent}</div>\n )}\n </div>\n );\n};\n","import type React from \"react\";\nimport { cn } from \"../utils/cn\";\n\nexport interface KpiCardProps extends React.HTMLAttributes<HTMLDivElement> {\n /** The big number / metric. */\n value: React.ReactNode;\n /** Caption under the value. */\n label: React.ReactNode;\n /** Tone — `urgent` tints the number + border, `success` tints the number. */\n tone?: \"default\" | \"urgent\" | \"success\";\n}\n\nconst valueTone: Record<NonNullable<KpiCardProps[\"tone\"]>, string> = {\n default: \"text-text\",\n urgent: \"text-danger-fg\",\n success: \"text-success-fg\",\n};\n\n/**\n * KPI / stat card (the Mijn dag metric row). Token-driven, mode-aware.\n *\n * @example\n * ```tsx\n * <KpiCard value={3} label=\"wachten op jou · gem. 1u12\" />\n * <KpiCard value={1} label=\"dreigt SLA te missen\" tone=\"urgent\" />\n * ```\n */\nexport function KpiCard({ value, label, tone = \"default\", className, ...props }: KpiCardProps) {\n return (\n <div className={cn(\"rounded-xl bg-editor px-4 py-3.5\", className)} {...props}>\n <div className={cn(\"text-2xl font-semibold\", valueTone[tone])}>{value}</div>\n <div className=\"pt-0.5 text-xs text-text-muted\">{label}</div>\n </div>\n );\n}\n","import React, { forwardRef } from \"react\";\nimport { cn } from \"../utils/cn\";\n\nexport interface CheckboxProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, \"size\"> {\n label?: string;\n\n helperText?: string;\n\n error?: string;\n\n size?: \"sm\" | \"md\" | \"lg\" | \"full\";\n\n indeterminate?: boolean;\n\n /** Additional class name for the container */\n containerClassName?: string;\n\n /** Additional class name for the label */\n labelClassName?: string;\n}\n\n/** The box grows with the field size, so a checkbox row reads as the same\n * weight as the inputs beside it instead of a small mark floating next to them. */\nconst checkboxSizes = {\n sm: \"size-checkbox-sm\",\n md: \"size-checkbox\",\n lg: \"size-checkbox-lg\",\n full: \"size-checkbox\",\n};\n\n/** Row height matches the control tokens so a checkbox lines up in a form. */\nconst rowSizes = {\n sm: \"min-h-control-sm\",\n md: \"min-h-control-md\",\n lg: \"min-h-control-lg\",\n full: \"min-h-control-md\",\n};\n\n/**\n * `full` means full width, not large: it mirrors `md` here the same way\n * `checkboxSizes` and `rowSizes` do, and the same way every other control in\n * the kit keeps `text-base` at `full`. It used to read `text-md`, which made\n * the label a step bigger than every field beside it — every Form passes\n * `size=\"full\"`, so that hit each checkbox in every form in the product.\n */\nconst labelSizes = {\n sm: \"text-sm\",\n md: \"text-base\",\n lg: \"text-md\",\n full: \"text-base\",\n};\n\n/**\n * Native checkbox styled via `accent-color`: the checked fill is the accent\n * token and dark mode needs no extra classes.\n */\nexport const Checkbox = forwardRef<HTMLInputElement, CheckboxProps>(\n (\n {\n label,\n helperText,\n error,\n size = \"md\",\n indeterminate = false,\n containerClassName,\n labelClassName,\n className,\n id,\n ...props\n },\n ref,\n ) => {\n const reactId = React.useId();\n const checkboxId = id || `checkbox-${reactId}`;\n const hasError = Boolean(error);\n const innerRef = React.useRef<HTMLInputElement | null>(null);\n\n // Keep the forwarded ref and the internal one in sync so `indeterminate`\n // works whether or not the consumer passes a ref.\n const setRefs = React.useCallback(\n (node: HTMLInputElement | null) => {\n innerRef.current = node;\n if (typeof ref === \"function\") ref(node);\n else if (ref && typeof ref === \"object\") {\n (ref as React.MutableRefObject<HTMLInputElement | null>).current = node;\n }\n },\n [ref],\n );\n\n React.useEffect(() => {\n if (innerRef.current) innerRef.current.indeterminate = indeterminate;\n }, [indeterminate]);\n\n return (\n <div className={cn(\"flex flex-col\", containerClassName)}>\n <div className={cn(\"flex items-center gap-3\", rowSizes[size])}>\n <input\n ref={setRefs}\n id={checkboxId}\n type=\"checkbox\"\n aria-invalid={hasError || undefined}\n className={cn(\n // radius-xs (5px): its own step on the scale. At 16px the 9px\n // button radius reads as a circle and the 7px chip radius still\n // rounds the corners away — a checkbox has to stay a square you\n // can aim at, with the corner just knocked off.\n \"shrink-0 rounded-xs border accent-accent\",\n \"transition-colors duration-fast ease-out\",\n \"focus-visible:outline-none focus-visible:focus-ring\",\n \"disabled:cursor-not-allowed disabled:bg-surface-hover\",\n\n checkboxSizes[size],\n hasError ? \"border-danger-border\" : \"border-border-strong\",\n\n className,\n )}\n {...props}\n />\n\n {label && (\n <div className=\"flex-1\">\n <label\n htmlFor={checkboxId}\n className={cn(\n \"cursor-pointer\",\n hasError ? \"text-danger-fg\" : \"text-text\",\n labelSizes[size],\n labelClassName,\n )}\n >\n {label}\n </label>\n\n {helperText && !error && (\n <p className=\"mt-0.5 text-xs text-text-subtle\">{helperText}</p>\n )}\n </div>\n )}\n </div>\n\n {error && <p className=\"mt-1 text-xs text-danger-fg\">{error}</p>}\n </div>\n );\n },\n);\n\nCheckbox.displayName = \"Checkbox\";\n","import { ChevronRight, X } from \"lucide-react\";\nimport React from \"react\";\nimport { Checkbox } from \"../input/Checkbox\";\nimport { cn } from \"../utils/cn\";\n\nexport interface ListGroup {\n /** Group key — must match what `groupBy` returns */\n id: string;\n /** Header label; defaults to `id` */\n title?: string;\n /** Override the row count shown on the right */\n count?: number;\n /** Start collapsed (only with `collapsible`) */\n defaultCollapsed?: boolean;\n /**\n * `danger` tints the label and its count — for a section that is a problem\n * by definition (overdue, failed), where the rows themselves carry no\n * marker that the group is the bad one.\n */\n tone?: \"default\" | \"danger\" | \"muted\";\n}\n\nexport interface ListProps<T> {\n /** Rows to render */\n items: T[];\n /** Stable key per row */\n getRowKey?: (item: T, index: number) => string | number;\n /**\n * Row separation. `inset` gives each row a rounded hover plate held off the\n * edge instead of a rule — the treatment for a list of things you tick off\n * (tasks) rather than a table you scan across (the inbox).\n */\n variant?: \"plain\" | \"divided\" | \"bulleted\" | \"inset\";\n /** Leading slot — channel tile, avatar, status dot */\n leading?: (item: T, index: number) => React.ReactNode;\n /** Main content; defaults to `String(item)` */\n renderItem?: (item: T, index: number) => React.ReactNode;\n /** Trailing slot — timestamp, badge, counter */\n trailing?: (item: T, index: number) => React.ReactNode;\n /** Row click */\n onSelect?: (item: T, index: number) => void;\n /**\n * Fired when keyboard focus lands on a row. Lets a consumer mirror the\n * roving focus into its own \"active\" state (what a hand-rolled `useArrowNav`\n * used to provide) without owning the key handling.\n */\n onActiveIndexChange?: (index: number) => void;\n /** Highlighted row (accent wash) */\n isActive?: (item: T, index: number) => boolean;\n /** Bold + accent dot */\n unread?: (item: T, index: number) => boolean;\n /** Deprioritised noise (opacity) */\n dimmed?: (item: T, index: number) => boolean;\n\n /** Group rows under headers. Return the group id for each row. */\n groupBy?: (item: T, index: number) => string;\n /**\n * Group order and labels. Groups missing here are appended in order of first\n * appearance; groups listed here but empty are skipped.\n */\n groups?: ListGroup[];\n /** Header renderer; defaults to label + count */\n renderGroupHeader?: (group: ListGroup, items: T[]) => React.ReactNode;\n /** Headers stick to the top of the scroll container */\n stickyGroupHeaders?: boolean;\n /** Headers become a toggle that folds the group */\n collapsibleGroups?: boolean;\n /** Hide the count on the right of the header */\n hideGroupCount?: boolean;\n\n /** Checkbox column + bulk bar */\n selectable?: boolean;\n /** Selected rows (controlled) */\n selectedItems?: T[];\n /** Selection handler */\n onSelectionChange?: (selected: T[]) => void;\n /** Actions shown in the bulk bar when a selection exists */\n bulkActions?: React.ReactNode;\n\n /**\n * Column-label strip above the rows (\"Van · Gesprek · tijd\"), like the inbox.\n * Align it with the row by reusing the same widths as `renderItem`.\n */\n header?: React.ReactNode;\n /** Keep the column-label strip visible while scrolling */\n stickyHeader?: boolean;\n\n /** Wrap the list in a bordered container */\n bordered?: boolean;\n /** Empty state content */\n emptyContent?: React.ReactNode;\n /** Loading state */\n loading?: boolean;\n /** Number of skeleton rows while loading */\n loadingRows?: number;\n className?: string;\n \"aria-label\"?: string;\n}\n\n/**\n * One action in the bulk dock. Its own component rather than a `Button`\n * variant: the dock is an ink surface, so every ordinary button variant is a\n * light plate on a dark bar. Use it for the `bulkActions` slot.\n *\n * @example\n * ```tsx\n * bulkActions={<>\n * <ListBulkAction icon={<Icon icon={UserPlus} size=\"sm\" />} onClick={assign}>Toewijzen</ListBulkAction>\n * <ListBulkAction icon={<Icon icon={Archive} size=\"sm\" />} onClick={archive}>Archiveren</ListBulkAction>\n * </>}\n * ```\n */\nexport function ListBulkAction({\n icon,\n onClick,\n children,\n}: {\n icon?: React.ReactNode;\n onClick?: () => void;\n children: React.ReactNode;\n}) {\n return (\n <button\n type=\"button\"\n onClick={onClick}\n className={cn(\n \"inline-flex h-control-sm shrink-0 cursor-pointer items-center gap-1.5 rounded-md px-2.5\",\n \"text-sm font-medium whitespace-nowrap\",\n \"transition-colors duration-fast ease-out hover:bg-white/15\",\n \"focus-visible:outline-none focus-visible:focus-ring\",\n )}\n >\n {icon}\n {children}\n </button>\n );\n}\n\n/**\n * List — one row is one object you open, with content of varying length\n * (inbox conversations, notifications, search results). No column headers:\n * hierarchy lives inside the row.\n *\n * Use `Table` instead when you compare values between rows (sortable columns,\n * aligned amounts, select-all in a header).\n *\n * Grouped, like the inbox and task views:\n *\n * @example\n * ```tsx\n * <List\n * items={tasks}\n * getRowKey={(t) => t.id}\n * bordered\n * groupBy={(t) => t.bucket} // \"today\" | \"tomorrow\" | \"later\"\n * groups={[\n * { id: \"today\", title: \"Vandaag\" },\n * { id: \"tomorrow\", title: \"Morgen\" },\n * { id: \"later\", title: \"Later\" },\n * ]}\n * stickyGroupHeaders\n * collapsibleGroups\n * leading={(t) => <ChannelBadge channel={t.channel} size=\"sm\" />}\n * renderItem={(t) => <span className=\"truncate\">{t.title}</span>}\n * trailing={(t) => t.due}\n * unread={(t) => !t.read}\n * />\n * ```\n */\nexport function List<T>({\n items,\n getRowKey,\n variant = \"divided\",\n leading,\n renderItem,\n trailing,\n onSelect,\n onActiveIndexChange,\n isActive,\n unread,\n dimmed,\n groupBy,\n groups,\n renderGroupHeader,\n stickyGroupHeaders = false,\n collapsibleGroups = false,\n hideGroupCount = false,\n selectable = false,\n selectedItems = [],\n onSelectionChange,\n bulkActions,\n header,\n stickyHeader = false,\n bordered = false,\n emptyContent,\n loading = false,\n loadingRows = 4,\n className,\n \"aria-label\": ariaLabel,\n}: ListProps<T>) {\n const [collapsed, setCollapsed] = React.useState<Record<string, boolean>>(() =>\n Object.fromEntries((groups ?? []).filter((g) => g.defaultCollapsed).map((g) => [g.id, true])),\n );\n\n const rootRef = React.useRef<HTMLDivElement>(null);\n\n /**\n * Roving focus across the rows. Rows are focusable in DOM order, so this walks\n * them directly rather than tracking an index — grouping and collapsing cannot\n * put it out of sync that way.\n */\n const handleKeyDown = (event: React.KeyboardEvent<HTMLDivElement>) => {\n const keys = [\"ArrowDown\", \"ArrowUp\", \"Home\", \"End\"];\n if (!keys.includes(event.key)) return;\n\n const rows = Array.from(\n rootRef.current?.querySelectorAll<HTMLLIElement>(\"li[data-list-row]\") ?? [],\n );\n if (rows.length === 0) return;\n event.preventDefault();\n\n const current = rows.indexOf(document.activeElement as HTMLLIElement);\n const next =\n event.key === \"Home\"\n ? 0\n : event.key === \"End\"\n ? rows.length - 1\n : event.key === \"ArrowDown\"\n ? (current + 1) % rows.length\n : (current - 1 + rows.length) % rows.length;\n\n rows[next]?.focus();\n };\n\n const keyOf = (item: T, index: number) => (getRowKey ? getRowKey(item, index) : index);\n\n const isSelected = (item: T) => selectedItems.includes(item);\n\n const toggle = (item: T, checked: boolean) => {\n if (!onSelectionChange) return;\n onSelectionChange(checked ? [...selectedItems, item] : selectedItems.filter((i) => i !== item));\n };\n\n const toggleAll = (checked: boolean) => onSelectionChange?.(checked ? [...items] : []);\n\n const container = cn(\n \"flex flex-col\",\n // With a selection open the list has a dock at its foot. Sticky can only\n // park at the bottom of its own box, so on a list shorter than the window\n // the dock hung right under the last row instead of at the bottom of the\n // screen. Growing the box to the height of its parent puts it where it\n // belongs — and only while a selection exists, so an ordinary short list\n // keeps its natural height.\n selectable && selectedItems.length > 0 && \"min-h-full\",\n bordered && \"overflow-hidden rounded-lg border border-border-subtle\",\n className,\n );\n\n if (loading) {\n return (\n <div className={container}>\n {Array.from({ length: loadingRows }).map((_, i) => (\n <div\n key={`skeleton-${i}`}\n className={cn(\n \"flex animate-pulse items-center gap-3 px-4 py-2\",\n variant === \"divided\" && i > 0 && \"border-t border-border-subtle\",\n )}\n >\n <span className=\"size-tile-sm shrink-0 rounded-md bg-surface-hover\" />\n <span className=\"h-3 w-30 shrink-0 rounded-sm bg-surface-hover\" />\n <span className=\"h-3 flex-1 rounded-sm bg-surface-hover\" />\n <span className=\"h-3 w-8 shrink-0 rounded-sm bg-surface-hover\" />\n </div>\n ))}\n </div>\n );\n }\n\n if (items.length === 0 && emptyContent) {\n return <div className={container}>{emptyContent}</div>;\n }\n\n const allSelected = items.length > 0 && selectedItems.length === items.length;\n const someSelected = selectedItems.length > 0 && !allSelected;\n\n // Rows in one flat pass, so `index` keeps pointing at the source item.\n const renderRow = (item: T, index: number, firstInBlock: boolean) => {\n const active = isActive?.(item, index) ?? false;\n const isUnread = unread?.(item, index) ?? false;\n const isDimmed = dimmed?.(item, index) ?? false;\n const selected = selectable && isSelected(item);\n\n return (\n <li\n key={keyOf(item, index)}\n // A clickable row cannot be a <button>: it holds a checkbox and may hold\n // trailing actions, and buttons may not nest. Button semantics are put on\n // the row instead, so it is reachable, operable and announced correctly.\n {...(onSelect\n ? {\n role: \"button\" as const,\n tabIndex: 0,\n \"data-list-row\": \"\",\n \"aria-current\": active || undefined,\n onClick: () => onSelect(item, index),\n onFocus: () => onActiveIndexChange?.(index),\n onKeyDown: (event: React.KeyboardEvent<HTMLLIElement>) => {\n if (event.key !== \"Enter\" && event.key !== \" \") return;\n // Space scrolls the page by default.\n event.preventDefault();\n onSelect(item, index);\n },\n }\n : {})}\n className={cn(\n \"flex items-center gap-3 text-base\",\n \"transition-colors duration-fast ease-out\",\n variant === \"inset\" ? \"mx-2 rounded-lg px-3 py-2.5\" : \"px-5 py-2.5\",\n variant === \"divided\" && !firstInBlock && \"border-t border-border-subtle\",\n onSelect && \"cursor-pointer focus-visible:outline-none focus-visible:focus-ring\",\n // An unread row is tinted, not bolded: at five columns a bold row\n // shouts across all of them, while the tint marks the row and leaves\n // the consumer free to weight only the sender and the subject.\n active || selected\n ? \"bg-accent-soft text-text\"\n : isUnread\n ? cn(\"bg-surface-unread\", onSelect && \"hover:bg-surface-unread-hover\")\n : onSelect && \"hover:bg-surface-hover\",\n // Read rows stay at full opacity — fading them also fades the avatars\n // and team dots, which is exactly the identity you scan by. They read\n // as secondary through weight and text colour instead.\n isDimmed && \"text-text-muted\",\n )}\n >\n {selectable && (\n <span onClick={(e) => e.stopPropagation()}>\n <Checkbox\n checked={isSelected(item)}\n onChange={(e) => toggle(item, e.target.checked)}\n aria-label=\"Rij selecteren\"\n />\n </span>\n )}\n\n {variant === \"bulleted\" && (\n <span\n aria-hidden\n className=\"mt-2 size-1.5 shrink-0 self-start rounded-full bg-border-strong\"\n />\n )}\n\n {leading && <span className=\"shrink-0\">{leading(item, index)}</span>}\n\n <div className=\"flex min-w-0 flex-1 items-center gap-2\">\n {renderItem ? renderItem(item, index) : String(item)}\n </div>\n\n {trailing && (\n <span className=\"shrink-0 text-xs text-text-subtle\">{trailing(item, index)}</span>\n )}\n </li>\n );\n };\n\n const body = (() => {\n if (!groupBy) {\n return items.map((item, index) => renderRow(item, index, index === 0));\n }\n\n // group id -> [{ item, index }], first-appearance order\n const buckets = new Map<string, { item: T; index: number }[]>();\n items.forEach((item, index) => {\n const id = groupBy(item, index);\n if (!buckets.has(id)) buckets.set(id, []);\n buckets.get(id)!.push({ item, index });\n });\n\n const declared = groups ?? [];\n const order: ListGroup[] = [\n ...declared.filter((g) => buckets.has(g.id)),\n ...[...buckets.keys()]\n .filter((id) => !declared.some((g) => g.id === id))\n .map((id) => ({ id })),\n ];\n\n return order.flatMap((group, groupIndex) => {\n const rows = buckets.get(group.id) ?? [];\n const isCollapsed = collapsibleGroups && collapsed[group.id];\n const label = group.title ?? group.id;\n const count = group.count ?? rows.length;\n\n const header = (\n <li\n key={`group-${group.id}`}\n onClick={\n collapsibleGroups\n ? () => setCollapsed((prev) => ({ ...prev, [group.id]: !prev[group.id] }))\n : undefined\n }\n className={cn(\n // A caption over its rows, not a band across the table: same\n // surface as the rows, no rules, and the count as a quiet pill.\n // The old grey uppercase strip read as a second column header.\n \"flex items-center gap-2.5 bg-surface px-5 pb-1.5 text-base font-semibold\",\n groupIndex > 0 ? \"pt-5\" : \"pt-3\",\n group.tone === \"danger\"\n ? \"text-danger-fg\"\n : group.tone === \"muted\"\n ? \"text-text-subtle\"\n : \"text-text\",\n stickyGroupHeaders && \"sticky z-10\",\n collapsibleGroups && \"cursor-pointer select-none\",\n )}\n aria-expanded={collapsibleGroups ? !isCollapsed : undefined}\n // the column strip is 28px tall, so grouped headers park underneath it\n style={stickyGroupHeaders ? { top: stickyHeader ? 28 : 0 } : undefined}\n >\n {renderGroupHeader ? (\n renderGroupHeader(\n group,\n rows.map((r) => r.item),\n )\n ) : (\n <>\n {collapsibleGroups && (\n <ChevronRight\n aria-hidden\n className={cn(\n \"size-icon-md shrink-0 text-text-subtle transition-transform duration-fast ease-out\",\n !isCollapsed && \"rotate-90\",\n )}\n />\n )}\n <span>{label}</span>\n {!hideGroupCount && (\n <span\n className={cn(\n \"rounded-full px-2 text-xs font-normal tabular-nums\",\n group.tone === \"danger\"\n ? \"bg-danger-soft text-danger-fg\"\n : \"bg-surface-sunk text-text-subtle\",\n )}\n >\n {count}\n </span>\n )}\n </>\n )}\n </li>\n );\n\n if (isCollapsed) return [header];\n\n return [\n header,\n ...rows.map(({ item, index }, rowIndex) => renderRow(item, index, rowIndex === 0)),\n ];\n });\n })();\n\n return (\n <div ref={rootRef} className={container} onKeyDown={onSelect ? handleKeyDown : undefined}>\n {header && (\n <div\n className={cn(\n // h-row-sm keeps the strip exactly 28px, which is the offset the\n // sticky group headers park against.\n \"flex h-row-sm items-end gap-3 border-b border-border bg-surface px-5 pb-2\",\n \"text-xs font-semibold uppercase tracking-wider text-text-disabled\",\n stickyHeader && \"sticky top-0 z-20\",\n )}\n >\n {selectable && (\n <span className=\"flex items-center\">\n <Checkbox\n checked={allSelected}\n indeterminate={someSelected}\n onChange={(e) => toggleAll(e.target.checked)}\n aria-label=\"Alles selecteren\"\n />\n </span>\n )}\n {header}\n </div>\n )}\n\n <ul aria-label={ariaLabel} className=\"flex flex-col\">\n {body}\n </ul>\n\n {/* Bulk bar — a dock that floats over the rows rather than a strip that\n pushes them down, so acting on a selection never moves the rows you\n are selecting. Sticky, so it rides the bottom of the scroll area. */}\n {selectable && selectedItems.length > 0 && (\n <div className=\"pointer-events-none sticky bottom-0 z-10 flex justify-center px-5 pb-4\">\n <div className=\"pointer-events-auto flex items-center gap-1.5 rounded-lg bg-accent py-1.5 pl-3.5 pr-1.5 text-accent-fg shadow-overlay\">\n <span className=\"text-sm font-medium\">{selectedItems.length} geselecteerd</span>\n <span aria-hidden className=\"mx-1.5 h-4 w-px bg-current opacity-25\" />\n {bulkActions}\n <span aria-hidden className=\"mx-1.5 h-4 w-px bg-current opacity-25\" />\n <button\n type=\"button\"\n onClick={() => onSelectionChange?.([])}\n aria-label=\"Selectie wissen\"\n className=\"grid size-tile-md shrink-0 cursor-pointer place-items-center rounded-md transition-colors duration-fast ease-out hover:bg-white/15 focus-visible:outline-none focus-visible:focus-ring\"\n >\n <X className=\"size-icon-lg\" />\n </button>\n </div>\n </div>\n )}\n </div>\n );\n}\n","import type React from \"react\";\nimport { cn } from \"../utils/cn\";\n\nexport interface MessageBubbleProps {\n /** Direction. `in` is received, `out` is sent by us. */\n side: \"in\" | \"out\";\n /**\n * Part of a run from the same sender. The bubble keeps its shape; callers use\n * this to drop the repeated header and avatar.\n */\n continued?: boolean;\n /**\n * `warning` for a message that is not settled yet, e.g. a pending draft.\n * `note` is the internal-note surface — amber, never customer-visible.\n */\n tone?: \"default\" | \"warning\" | \"note\";\n /**\n * Layout only — padding, flex behaviour, margins.\n *\n * Not width: the bubble caps its own, because `cn` is plain clsx with no\n * tailwind-merge, so a `max-w-*` passed here would not reliably beat the\n * default — both land in the class list and CSS order decides. A message\n * that runs the full width of the thread stops reading as a message.\n */\n className?: string;\n children: React.ReactNode;\n}\n\n/**\n * A single message in a conversation: chat, email and call transcript all use\n * the same shape. The corner facing the sender is tight (`rounded-sm`), the\n * other three `rounded-lg` — that is what makes direction readable without a\n * tail. Sent messages sit on `bg-bubble-out-soft`, a 4% ink wash — deliberately\n * lighter than `accent-soft`, which reads as heavy on every sent message.\n */\nexport function MessageBubble({\n side,\n continued = false,\n tone = \"default\",\n className,\n children,\n}: MessageBubbleProps) {\n return (\n <div\n className={cn(\n // A bubble never spans the whole column; it narrows further from md,\n // where the thread itself is wide enough for the difference to matter.\n // No shadow: a bubble is an inner card, and inner cards separate\n // themselves with a fill. The shadow was doing the separating while both\n // sides were white.\n \"min-w-0 max-w-bubble md:max-w-bubble-md\",\n // No outline on an ordinary message: its fill already separates it\n // from the thread, and a border on every bubble turns a conversation\n // into a stack of boxes.\n // The tight corner points back at the sender.\n side === \"out\" ? \"rounded-lg rounded-tr-sm\" : \"rounded-lg rounded-tl-sm\",\n // Sent is a pale blue, received the neutral sunk grey — the same pair\n // the full-width feed cards use, so a chat thread and a mail thread say\n // \"ours\" and \"theirs\" the same way.\n tone === \"note\"\n ? \"bg-note-soft\"\n : side === \"out\"\n ? \"bg-bubble-out-soft\"\n : \"bg-surface-sunk\",\n continued && \"mt-1\",\n className,\n )}\n >\n {children}\n </div>\n );\n}\n","import type { ReactNode } from \"react\";\n\nimport { cn } from \"../utils/cn\";\n\n/**\n * One value in a record's property line.\n *\n * The properties of a record — sender, inbox, priority, topic, folder, status,\n * assignee — belong in the sentence under the title, not in a row of chips\n * underneath it. Six chips read as six buttons for something you change maybe\n * once; the same six values as a line read as *what this record is*.\n *\n * The dashed underline is the whole affordance: it says \"you can change this\"\n * without spending a control. Wrap it in a `Popover` to make it do so.\n *\n * An unset property stays **visible** in the dimmer pair rather than\n * disappearing, so you can see that it is empty — a property that vanishes when\n * unset is a property you never discover.\n */\nexport interface MetaValueProps {\n label: string;\n icon?: ReactNode;\n /** No value chosen yet. Renders dimmer, never hidden. */\n unset?: boolean;\n className?: string;\n}\n\nexport function MetaValue({ icon, label, unset, className }: MetaValueProps) {\n return (\n <span\n className={cn(\n \"inline-flex min-w-0 items-center gap-1.5 border-b border-dashed pb-px transition-colors duration-fast ease-out\",\n unset\n ? \"border-border text-text-subtle hover:border-border-strong hover:text-text-muted\"\n : \"border-border-strong text-text-muted hover:text-text\",\n className,\n )}\n >\n {icon}\n <span className=\"truncate\">{label}</span>\n </span>\n );\n}\n\n/**\n * Separator between two properties in a meta line.\n *\n * Decorative and hidden from assistive tech — the values carry the meaning, and\n * a screen reader announcing \"middle dot\" between every property is noise.\n */\nexport function MetaSeparator() {\n return (\n <span aria-hidden className=\"shrink-0 text-text-disabled\">\n ·\n </span>\n );\n}\n","import type { ReactNode } from \"react\";\nimport { cn } from \"../utils/cn\";\n\n/**\n * What is about to appear here. A placeholder only does its job if it has the\n * geometry of the thing it stands in for: the default `text` shape under a form\n * or a list announces a heading and two paragraphs, and the real content then\n * shoves everything sideways when it lands.\n */\nexport type ContentLoadingShape = \"text\" | \"table\" | \"list\" | \"form\" | \"menu\" | \"cards\";\n\nexport interface ContentLoadingProps {\n /**\n * `page` (default) fills the route with its own `<main>` shell and padding.\n * `inline` drops both, so the skeleton can sit inside an existing panel,\n * card or scroll container without nesting a second `<main>` landmark.\n */\n variant?: \"page\" | \"inline\";\n\n /** The shape of what is coming. Defaults to `text`. */\n shape?: ContentLoadingShape;\n\n /** Rows/items/fields/cards to draw, depending on `shape`. */\n rows?: number;\n\n /** Columns, for `table` and `cards`. */\n columns?: number;\n\n /**\n * Optional custom skeleton content (overrides default)\n */\n children?: ReactNode;\n\n className?: string;\n\n /** @deprecated Pass `shape=\"table\"`. */\n showTableSkeleton?: boolean;\n /** @deprecated Pass `columns`. */\n tableColumns?: number;\n /** @deprecated Pass `rows`. */\n tableRows?: number;\n}\n\n/** One placeholder bar. Uneven widths read as text rather than as a progress bar. */\nconst Bar = ({ className }: { className?: string }) => (\n <span className={cn(\"block rounded-xs bg-surface-hover\", className)} />\n);\n\nconst WIDTHS = [\"62%\", \"44%\", \"74%\", \"52%\", \"68%\", \"48%\", \"80%\", \"56%\"];\n\nfunction Shape({\n shape,\n rows,\n columns,\n}: {\n shape: ContentLoadingShape;\n rows: number;\n columns: number;\n}) {\n switch (shape) {\n case \"table\":\n return (\n <div className=\"overflow-hidden rounded-lg ring-1 ring-border\">\n <div\n className=\"grid gap-2 bg-surface-hover p-3\"\n style={{ gridTemplateColumns: `repeat(${columns}, minmax(0, 1fr))` }}\n >\n {Array.from({ length: columns }).map((_, i) => (\n <Bar key={`h-${i}`} className=\"h-4 w-2/3 bg-border-strong\" />\n ))}\n </div>\n {Array.from({ length: rows }).map((_, r) => (\n <div\n key={`r-${r}`}\n className=\"grid gap-2 border-t border-border-subtle p-3\"\n style={{ gridTemplateColumns: `repeat(${columns}, minmax(0, 1fr))` }}\n >\n {Array.from({ length: columns }).map((_, c) => (\n <Bar key={`c-${r}-${c}`} className=\"h-4 w-full\" />\n ))}\n </div>\n ))}\n </div>\n );\n\n case \"list\":\n return (\n <div className=\"flex flex-col divide-y divide-border-subtle rounded-lg ring-1 ring-border\">\n {Array.from({ length: rows }).map((_, i) => (\n <div key={i} className=\"flex items-center gap-3 p-3\">\n <Bar className=\"size-avatar-sm shrink-0 rounded-full\" />\n <div className=\"flex min-w-0 flex-1 flex-col gap-1.5\">\n <Bar className=\"h-3\" />\n <Bar className=\"h-2.5 opacity-70\" />\n </div>\n </div>\n ))}\n </div>\n );\n\n case \"form\":\n // Label above, field below — the pair a form actually renders, so the\n // fields do not jump when they replace this.\n return (\n <div className=\"flex flex-col gap-5\">\n {Array.from({ length: rows }).map((_, i) => (\n <div key={i} className=\"flex flex-col gap-2\">\n <Bar className=\"h-2.5 w-28\" />\n <Bar className=\"h-control-md w-full rounded-md\" />\n </div>\n ))}\n </div>\n );\n\n case \"menu\":\n // The same geometry the sidebar menu draws: a caption, then rows at item\n // height with room for the icon.\n return (\n <div className=\"flex flex-col gap-px\">\n <div className=\"px-2.5 pb-2 pt-5\">\n <Bar className=\"h-2 w-16\" />\n </div>\n {Array.from({ length: rows }).map((_, i) => (\n <div key={i} className=\"flex h-row-md items-center gap-2.5 px-2.5\">\n <Bar className=\"size-4 shrink-0\" />\n <Bar className=\"h-2\" />\n </div>\n ))}\n </div>\n );\n\n case \"cards\":\n return (\n <div\n className=\"grid gap-4\"\n style={{ gridTemplateColumns: `repeat(${columns}, minmax(0, 1fr))` }}\n >\n {Array.from({ length: rows * columns }).map((_, i) => (\n <div key={i} className=\"flex flex-col gap-2 rounded-lg p-4 ring-1 ring-border\">\n <Bar className=\"h-3 w-1/2\" />\n <Bar className=\"h-2.5\" />\n <Bar className=\"h-2.5 w-3/4 opacity-70\" />\n </div>\n ))}\n </div>\n );\n\n case \"text\":\n default:\n return (\n <div className=\"flex flex-col gap-6\">\n <Bar className=\"h-6 w-1/4\" />\n <div className=\"flex flex-col gap-2\">\n {Array.from({ length: rows }).map((_, i) => (\n <Bar key={i} className=\"h-4\" />\n ))}\n </div>\n </div>\n );\n }\n}\n\n/** Sensible counts per shape, so a call site only names the shape. */\nconst DEFAULT_ROWS: Record<ContentLoadingShape, number> = {\n text: 2,\n table: 8,\n list: 6,\n form: 5,\n menu: 4,\n cards: 2,\n};\n\nexport function ContentLoading({\n variant = \"page\",\n shape,\n rows,\n columns,\n children,\n className,\n showTableSkeleton = false,\n tableColumns,\n tableRows,\n}: ContentLoadingProps) {\n // The deprecated table props still decide the shape when no `shape` is given,\n // so existing call sites keep rendering what they always did.\n const resolvedShape: ContentLoadingShape = shape ?? (showTableSkeleton ? \"table\" : \"text\");\n const resolvedRows = rows ?? tableRows ?? DEFAULT_ROWS[resolvedShape];\n const resolvedColumns = columns ?? tableColumns ?? (resolvedShape === \"cards\" ? 3 : 6);\n\n // `inline` renders the skeleton bare; `page` wraps it in the route shell.\n const inner = (\n <div className={cn(\"w-full animate-pulse\", className)} aria-hidden>\n {children ?? <Shape shape={resolvedShape} rows={resolvedRows} columns={resolvedColumns} />}\n </div>\n );\n\n if (variant === \"inline\") return inner;\n\n return <main className=\"flex-1 bg-surface p-6\">{inner}</main>;\n}\n","import { ChevronLeft, Pencil } from \"lucide-react\";\nimport React, { useEffect, useRef, useState } from \"react\";\nimport { Button, type ButtonProps } from \"../action/Button\";\nimport { SplitButton, type SplitButtonOption } from \"../action/SplitButton\";\nimport { cn } from \"../utils/cn\";\nimport { Icon } from \"./Icon\";\n\nexport interface PageHeaderAction {\n id: string;\n label: string;\n icon?: React.ReactNode;\n onClick: () => void;\n /** Action variant. Tracks Button, so the two cannot drift apart. */\n variant?: ButtonProps[\"variant\"];\n disabled?: boolean;\n splitOptions?: SplitButtonOption[];\n /**\n * Render this action yourself instead of as a Button — a filter chip, a\n * segmented toggle, an avatar stack. Position follows the array, so put it\n * before the buttons to have it sit to their left. When set, every other\n * field except `id` is ignored.\n */\n render?: () => React.ReactNode;\n}\n\nexport interface PageHeaderProps {\n /**\n * `plain` sits directly on the page. `surface` is the app-bar treatment:\n * its own background, full-bleed, closed off with a bottom border.\n */\n surface?: boolean;\n title: string;\n /** Page subtitle/description. Sits beside the title. */\n subtitle?: string;\n /**\n * Meta line under the title — a contact, a channel, a timestamp. Distinct\n * from `subtitle`, which sits beside the title, and from `children`, which\n * is the full-width row at the bottom of the header.\n *\n * A string is kept to one line and ellipsised; a node is allowed to wrap, so\n * an inline row of properties survives a narrow window.\n */\n meta?: React.ReactNode;\n breadcrumbs?: Array<{ label: string; href?: string }>;\n actions?: PageHeaderAction[];\n children?: React.ReactNode;\n className?: string;\n backLabel?: string;\n onBack?: () => void;\n backClassName?: string;\n editable?: boolean;\n onTitleChange?: (newTitle: string) => void;\n}\n\nexport const ChevronLeftIcon = () => <Icon icon={ChevronLeft} size=\"md\" color=\"current\" />;\n\nexport const PageHeader: React.FC<PageHeaderProps> = ({\n surface = false,\n title,\n subtitle,\n meta,\n actions = [],\n children,\n className,\n backLabel = \"Back\",\n backClassName,\n onBack,\n editable = false,\n onTitleChange,\n}) => {\n const [isEditing, setIsEditing] = useState(false);\n const [editValue, setEditValue] = useState(title);\n const inputRef = useRef<HTMLInputElement>(null);\n\n useEffect(() => {\n setEditValue(title);\n }, [title]);\n\n useEffect(() => {\n if (isEditing && inputRef.current) {\n inputRef.current.focus();\n inputRef.current.select();\n }\n }, [isEditing]);\n\n const handleTitleClick = () => {\n if (editable) {\n setIsEditing(true);\n }\n };\n\n const handleBlur = () => {\n setIsEditing(false);\n if (editValue.trim() !== \"\" && editValue !== title && onTitleChange) {\n onTitleChange(editValue.trim());\n } else {\n setEditValue(title);\n }\n };\n\n const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {\n if (e.key === \"Enter\") {\n inputRef.current?.blur();\n } else if (e.key === \"Escape\") {\n setEditValue(title);\n setIsEditing(false);\n }\n };\n\n return (\n <div\n className={cn(\n \"px-5 pt-4 pb-2.5\",\n // Inside the content card the header shares the card's surface; the\n // rule is the only thing that still closes it off. `surface` therefore\n // only draws the rule — the background it used to paint is now the\n // card itself.\n surface && \"border-b border-border\",\n className,\n )}\n >\n <div className=\"flex flex-row sm:items-start sm:justify-between items-center gap-4\">\n <div className=\"min-w-0 flex-1\">\n {/* Back sits beside the title block, not above the meta line, so the\n meta line starts under the title rather than under the button. */}\n <div className=\"flex items-start gap-4 text-text\">\n {onBack && (\n // Ghost: a framed box around a back chevron makes the way out of a\n // page look like the page's primary control.\n <Button\n variant=\"ghost\"\n onClick={onBack}\n leftIcon={<Icon icon={ChevronLeft} size=\"md\" />}\n iconOnly\n />\n )}\n\n <div className=\"min-w-0 flex-1\">\n {isEditing ? (\n <input\n ref={inputRef}\n type=\"text\"\n value={editValue}\n onChange={(e) => setEditValue(e.target.value)}\n onBlur={handleBlur}\n onKeyDown={handleKeyDown}\n className=\"text-1xl font-semibold bg-transparent border-b-1 border-border rounded-lg outline-none focus:border-none px-1 -mx-1 min-w-0 w-full\"\n />\n ) : (\n <div className=\"group flex items-baseline gap-2.5 min-w-0\">\n <h2\n onClick={handleTitleClick}\n className={cn(\n \"text-lg font-semibold tracking-title truncate\",\n editable && \"cursor-pointer\",\n )}\n title={editable ? \"Click to edit\" : undefined}\n >\n {title}\n </h2>\n {subtitle && (\n <span className=\"shrink-0 text-base text-text-muted truncate\">{subtitle}</span>\n )}\n {editable && (\n <Pencil\n size={15}\n onClick={handleTitleClick}\n className=\"shrink-0 cursor-pointer text-text-muted opacity-0 group-hover:opacity-100 transition-opacity\"\n />\n )}\n </div>\n )}\n\n {meta && (\n // A string meta is one line and ellipsises; a node may be a row\n // of controls (the interaction header's property line) that has\n // to be allowed to wrap — `truncate` would clip it to one line\n // and hide the last properties.\n <div\n className={cn(\n \"mt-1 min-w-0 text-sm text-text-subtle\",\n typeof meta === \"string\" && \"truncate\",\n )}\n >\n {meta}\n </div>\n )}\n </div>\n </div>\n </div>\n\n {actions.length > 0 && (\n <div className=\"flex flex-wrap sm:flex-nowrap gap-2 sm:gap-3\">\n {actions.map((action) =>\n action.render ? (\n <React.Fragment key={action.id}>{action.render()}</React.Fragment>\n ) : action.splitOptions && action.splitOptions.length > 0 ? (\n <SplitButton\n key={action.id}\n label={action.label}\n onClick={action.onClick}\n icon={action.icon}\n variant={action.variant === \"primary\" ? \"primary\" : \"outline\"}\n options={action.splitOptions}\n disabled={action.disabled}\n />\n ) : (\n <Button\n key={action.id}\n variant={action.variant ?? \"secondary\"}\n onClick={(e) => {\n e.stopPropagation();\n (e.target as HTMLButtonElement).blur();\n action.onClick();\n }}\n disabled={action.disabled}\n leftIcon={action.icon}\n >\n {action.label}\n </Button>\n ),\n )}\n </div>\n )}\n </div>\n\n {children && <div className=\"mt-4\">{children}</div>}\n </div>\n );\n};\n","import type React from \"react\";\nimport type { ReactNode } from \"react\";\nimport { ContentLoading, type ContentLoadingShape } from \"../feedback/ContentLoading\";\nimport { cn } from \"../utils/cn\";\nimport { PageHeader, type PageHeaderAction } from \"./PageHeader\";\n\n/**\n * The `md` gutter matches `PageHeader`'s own horizontal padding, so the table\n * or form below lines up with the title above it. The top value is small\n * because the header already supplies most of that gap (`pt-4 pb-2.5`).\n */\nconst paddingClasses = {\n none: \"\",\n sm: \"px-3 pb-3 pt-1\",\n md: \"px-5 pb-5 pt-1.5\",\n} as const;\n\nexport interface PageProps {\n /** Page title, rendered by `PageHeader`. */\n title: string;\n /** Short description beside the title. */\n subtitle?: string;\n /** Line under the title — a count, a record, a timestamp. */\n meta?: ReactNode;\n /** Toolbar actions, right-aligned in the header. */\n actions?: PageHeaderAction[];\n /** Renders the back chevron beside the title. */\n onBack?: () => void;\n /** Lets the user rename the record from the header: the title becomes\n * clickable and grows a pencil beside it. Forwarded to `PageHeader`. */\n editable?: boolean;\n /** Called with the trimmed new title. Only fires when it actually changed. */\n onTitleChange?: (title: string) => void;\n /**\n * Draws the rule under the header. Off by default: inside the content card\n * the header shares the card's surface, and the card's own edge already ends\n * the page — a second line right under the title reads as a divider between\n * two panes that are not there.\n *\n * Turn it on for a header that has to stay legible over content moving\n * underneath it: a dense grid, a feed, anything where the first row would\n * otherwise scroll up flush against the title.\n */\n surface?: boolean;\n /** Full-width row at the bottom of the header — a filter strip, a tab bar. */\n headerContent?: ReactNode;\n /** Swaps the body for a skeleton. */\n loading?: boolean;\n /**\n * What the body is about to render, so the placeholder has the right shape.\n * Most app pages are a table; a detail page is usually a form.\n */\n loadingShape?: ContentLoadingShape;\n /**\n * Rendered instead of `children` when there is nothing to show — pass an\n * `EmptyState`. Ignored while `loading`, so an empty list does not flash its\n * \"nothing here\" message during the first fetch.\n */\n empty?: ReactNode;\n padding?: keyof typeof paddingClasses;\n /**\n * Whether the page owns its scroll. `true` (default) pins the header and\n * scrolls the body — the right behaviour for a table you page through.\n * Set `false` when the page is rendered inside something that already\n * scrolls and should flow with it.\n */\n scroll?: boolean;\n className?: string;\n contentClassName?: string;\n children?: ReactNode;\n}\n\n/**\n * One app page: the header, the content gutter, the scroll container, and the\n * loading and empty states. These four were re-invented per page — 24 times in\n * `eylo-voip` alone, each with its own gutter — which is how `p-4` there and\n * `px-5` everywhere else ended up side by side. Reach for this instead of\n * pairing a `PageHeader` with a padded `div`. Settings panels are the\n * exception: they live inside the settings frame, which draws its own title\n * and scrolls itself, so they use `SettingsPage`.\n */\nexport const Page: React.FC<PageProps> = ({\n title,\n subtitle,\n meta,\n actions,\n onBack,\n editable,\n onTitleChange,\n surface = false,\n headerContent,\n loading = false,\n loadingShape = \"table\",\n empty,\n padding = \"md\",\n scroll = true,\n className,\n contentClassName,\n children,\n}) => {\n const body = loading ? (\n <ContentLoading variant=\"inline\" shape={loadingShape} />\n ) : empty ? (\n <div className=\"flex flex-col items-center justify-center py-12\">{empty}</div>\n ) : (\n children\n );\n\n return (\n <div\n className={cn(\n // `h-full` resolves against the shell's content card, which is the\n // element that scrolls today. Taking the scroll over here is what pins\n // the header; without `min-h-0` the body would grow past the card\n // instead of scrolling inside it.\n scroll ? \"flex h-full min-h-0 flex-col\" : \"flex flex-col\",\n className,\n )}\n >\n <PageHeader\n surface={surface}\n title={title}\n subtitle={subtitle}\n meta={meta}\n actions={actions}\n onBack={onBack}\n editable={editable}\n onTitleChange={onTitleChange}\n className={scroll ? \"shrink-0\" : undefined}\n >\n {headerContent}\n </PageHeader>\n\n <div\n className={cn(\n scroll && \"min-h-0 flex-1 overflow-y-auto\",\n paddingClasses[padding],\n contentClassName,\n )}\n >\n {body}\n </div>\n </div>\n );\n};\n","import { ChevronLeft } from \"lucide-react\";\nimport React from \"react\";\nimport { Button, type ButtonProps } from \"../action/Button\";\nimport { SplitButton } from \"../action/SplitButton\";\nimport { cn } from \"../utils/cn\";\nimport { Icon } from \"./Icon\";\nimport type { PageHeaderAction } from \"./PageHeader\";\n\nexport interface PageToolbarActionsProps {\n actions: PageHeaderAction[];\n /**\n * Variant for actions that do not name one. `secondary` (a sunk plate) suits\n * a detail page's Cancel/Save pair; a list toolbar passes `ghost`, because\n * there the plate would collide with the sunk search field beside it.\n */\n defaultVariant?: ButtonProps[\"variant\"];\n className?: string;\n}\n\n/**\n * The action cluster shared by `PageToolbar` and the table toolbar, so a\n * \"New …\" button looks the same wherever a view decides to put it.\n */\nexport const PageToolbarActions: React.FC<PageToolbarActionsProps> = ({\n actions,\n defaultVariant = \"secondary\",\n className,\n}) => {\n if (actions.length === 0) return null;\n\n return (\n <div className={cn(\"flex flex-wrap items-center gap-2\", className)}>\n {actions.map((action) =>\n action.render ? (\n <React.Fragment key={action.id}>{action.render()}</React.Fragment>\n ) : action.splitOptions && action.splitOptions.length > 0 ? (\n <SplitButton\n key={action.id}\n label={action.label}\n onClick={action.onClick}\n icon={action.icon}\n variant={action.variant === \"primary\" ? \"primary\" : \"secondary\"}\n options={action.splitOptions}\n disabled={action.disabled}\n />\n ) : (\n <Button\n key={action.id}\n variant={action.variant ?? defaultVariant}\n onClick={(e) => {\n e.stopPropagation();\n (e.target as HTMLButtonElement).blur();\n action.onClick();\n }}\n disabled={action.disabled}\n leftIcon={action.icon}\n >\n {action.label}\n </Button>\n ),\n )}\n </div>\n );\n};\n\nexport interface PageToolbarProps {\n /** Right-aligned action buttons */\n actions?: PageHeaderAction[];\n /** Renders a ghost back button on the left */\n onBack?: () => void;\n /** Back button label (visually hidden on small screens) */\n backLabel?: string;\n className?: string;\n padding?: \"none\" | \"sm\" | \"md\" | \"lg\";\n /** Optional extra content placed between back button and actions */\n children?: React.ReactNode;\n}\n\nconst paddingClasses = {\n none: \"p-0\",\n sm: \"p-2\",\n md: \"px-5 py-4\",\n lg: \"p-6\",\n};\n\n/**\n * Thin action-bar for views that already have a host header (e.g. a settings\n * page or a detail view). Renders an optional back button on the left and\n * action buttons on the right. Use `PageHeader` when the view needs its own\n * title/breadcrumbs — and for a list, put the actions on the `Table` toolbar\n * instead, next to the search and filters they apply to.\n */\nexport const PageToolbar: React.FC<PageToolbarProps> = ({\n actions = [],\n onBack,\n backLabel = \"Back\",\n className,\n padding = \"md\",\n children,\n}) => {\n return (\n <div className={cn(\"flex items-center gap-2\", paddingClasses[padding], className)}>\n {onBack && (\n <Button\n variant=\"secondary\"\n onClick={onBack}\n aria-label={backLabel}\n leftIcon={<Icon icon={ChevronLeft} size=\"sm\" />}\n iconOnly\n />\n )}\n\n {children && <div className=\"min-w-0 flex-1\">{children}</div>}\n\n <PageToolbarActions actions={actions} className={cn(!children && \"ml-auto\")} />\n </div>\n );\n};\n","import type React from \"react\";\nimport { cn } from \"../utils/cn\";\n\nexport interface SectionCaptionProps {\n /** Uppercase label. Short — this is a caption, not a heading. */\n label: string;\n /**\n * The right-hand slot: a count, a progress (\"1/3 done\") or a summary\n * (\"2 on a call · 1 away\"). A caption with nothing to its right is a caption\n * that could have been a heading.\n */\n meta?: string;\n /** Far right — a toggle or an icon button. Sits after `meta` when both are set. */\n action?: React.ReactNode;\n /** Layout only (margins, alignment). Not padding: see the note below. */\n className?: string;\n}\n\n/**\n * The section header over rounded plates and cards — the counterpart of the\n * sticky sunk band, which belongs to full-bleed divided rows.\n *\n * The top padding is generous and self-cancelling (`pt-7 first:pt-0`): a caption\n * is what separates two blocks in a scrolling panel, so it carries the gap\n * rather than the blocks around it, and the first one in a panel does not push\n * the content away from the panel header.\n *\n * `className` cannot override that padding — `cn` is plain clsx with no\n * tailwind-merge, so both classes would land in the list and CSS order would\n * decide. Use it for margins and alignment only.\n */\nexport const SectionCaption: React.FC<SectionCaptionProps> = ({\n label,\n meta,\n action,\n className,\n}) => (\n <div className={cn(\"flex items-center gap-2 px-1 pb-2 pt-7 first:pt-0\", className)}>\n <span className=\"shrink-0 text-2xs font-semibold uppercase tracking-label text-text-subtle\">\n {label}\n </span>\n {meta && (\n <span className=\"min-w-0 flex-1 truncate text-right text-xs tabular-nums text-text-subtle\">\n {meta}\n </span>\n )}\n {action && <span className=\"ml-auto shrink-0\">{action}</span>}\n </div>\n);\n","import type React from \"react\";\nimport { cn } from \"../utils/cn\";\n\nexport interface SectionDividerProps {\n /** Centred label. Without one the divider is a plain rule. */\n label?: React.ReactNode;\n /** Right-aligned slot after the rule, e.g. a count or a status. */\n trailing?: React.ReactNode;\n /** Push the label to the start instead of centring it. */\n align?: \"center\" | \"start\";\n className?: string;\n}\n\n/**\n * A hairline that separates two runs of content, optionally naming the run that\n * follows: day separators in a feed, group headings in search results.\n *\n * @example\n * ```tsx\n * <SectionDivider label=\"Gisteren\" />\n * <SectionDivider label=\"Gesprekken\" align=\"start\" trailing={<Badge>12</Badge>} />\n * ```\n */\nexport function SectionDivider({\n label,\n trailing,\n align = \"center\",\n className,\n}: SectionDividerProps) {\n const rule = <span className=\"h-px flex-1 bg-border-subtle\" aria-hidden />;\n\n if (!label && !trailing) {\n return <div className={cn(\"flex items-center py-2\", className)}>{rule}</div>;\n }\n\n return (\n <div className={cn(\"flex items-center gap-3 py-2\", className)}>\n {align === \"center\" && rule}\n <span className=\"shrink-0 text-xs font-medium text-text-muted\">{label}</span>\n {rule}\n {trailing && <span className=\"shrink-0\">{trailing}</span>}\n </div>\n );\n}\n","import { ChevronLeft } from \"lucide-react\";\nimport React, {\n createContext,\n type ReactNode,\n useContext,\n useEffect,\n useId,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport { Button } from \"../action/Button\";\nimport { ContentLoading, type ContentLoadingShape } from \"../feedback/ContentLoading\";\nimport { cn } from \"../utils/cn\";\nimport { Icon } from \"./Icon\";\nimport type { PageHeaderAction } from \"./PageHeader\";\nimport { PageToolbarActions } from \"./PageToolbar\";\n\n/**\n * What a panel adds to the frame it is rendered in: the record it drilled into,\n * and the way back out. The settings frame already draws the app breadcrumb and\n * the page title, so a panel that repeats them produces the double bar this\n * component exists to remove.\n */\nexport interface SettingsTrailEntry {\n /** Appended to the frame breadcrumb; usually the detail record's name. */\n title?: string;\n /** When set, the frame renders the back affordance and calls this. */\n onBack?: () => void;\n /**\n * Primary actions to draw in the frame header instead of in the panel's own\n * sticky footer — set by `SettingsPage` when it is passed\n * `actionsPlacement=\"header\"`. See that prop for when to reach for it.\n */\n actions?: PageHeaderAction[];\n /** Left-aligned counterpart of {@link actions}, drawn before them. */\n secondaryActions?: PageHeaderAction[];\n}\n\nexport interface SettingsFrameApi {\n /**\n * `ownerId` scopes the write to one panel instance. Stable across renders,\n * so it is safe to use as a hook dependency.\n */\n setTrail: (ownerId: string, entry: SettingsTrailEntry | null) => void;\n}\n\nconst SettingsFrameContext = createContext<SettingsFrameApi | null>(null);\n\n/**\n * Provided by the settings frame (the shell overlay), consumed by panels that\n * are federated remotes. Safe across the module-federation boundary because\n * React and `@opencxh/ui-kit` are both shared singletons — a second copy of\n * either would hand panels a different context object and the trail would\n * silently never arrive.\n */\nexport const SettingsFrameProvider = SettingsFrameContext.Provider;\n\nexport function useSettingsFrame(): SettingsFrameApi | null {\n return useContext(SettingsFrameContext);\n}\n\n/**\n * State half of the frame contract, for the host to own. Returns the current\n * trail plus the stable api to hand down through `SettingsFrameProvider`.\n */\nexport function useSettingsFrameHost() {\n const [state, setState] = useState<{ ownerId: string; entry: SettingsTrailEntry } | null>(null);\n\n const api = useMemo<SettingsFrameApi>(\n () => ({\n setTrail: (ownerId, entry) =>\n setState((prev) => {\n if (entry) return { ownerId, entry };\n // A panel that unmounts *after* its replacement has already mounted\n // must not wipe the replacement's trail. Only the current owner is\n // allowed to clear.\n return prev && prev.ownerId !== ownerId ? prev : null;\n }),\n }),\n [],\n );\n\n return { trail: state?.entry ?? null, api };\n}\n\nconst paddingClasses = {\n none: \"\",\n sm: \"px-3 py-3\",\n // Matches the frame header's own horizontal padding, so content lines up\n // with the title above it.\n md: \"px-5 py-4\",\n} as const;\n\nexport interface SettingsPageProps {\n children: ReactNode;\n /**\n * Primary actions, right-aligned in the sticky footer — the Cancel/Save pair\n * of a form. Omit entirely on a read-only or list page and no footer renders.\n */\n actions?: PageHeaderAction[];\n /** Left-aligned footer actions, set apart from the primary pair (e.g. Delete). */\n secondaryActions?: PageHeaderAction[];\n /**\n * Where the actions go: `footer` (default) is the sticky bar at the bottom\n * of the panel; `header` hands them to the frame beside the title.\n *\n * Reach for `header` when the body contains an anchored overlay (a `Select`,\n * a `Popover`) inside a `sticky` or `z-index`-bearing element:\n * `position: sticky` creates a stacking context even at `z-index: auto`, so\n * the menu paints behind the footer's own `sticky … z-10`. Removing the\n * footer removes the competitor rather than starting a z-index race.\n */\n actionsPlacement?: \"footer\" | \"header\";\n /** Renders the back affordance in the frame header (or inline when unframed). */\n onBack?: () => void;\n backLabel?: string;\n /** The record's own name, appended to the frame breadcrumb. */\n title?: string;\n /** Swaps the body for a skeleton while keeping the footer in place. */\n loading?: boolean;\n /**\n * What the body is about to render. A settings panel is nearly always a form\n * or a table, and saying which keeps the placeholder from announcing a\n * heading and two paragraphs that never arrive.\n */\n loadingShape?: ContentLoadingShape;\n padding?: keyof typeof paddingClasses;\n className?: string;\n contentClassName?: string;\n}\n\n/**\n * The body of one settings panel. Owns the three things every panel used to\n * re-invent: the scroll container, the content padding, and where the actions\n * live. Title, description and breadcrumb belong to the frame — pass `title`\n * and `onBack` to extend them rather than drawing a second header here.\n */\nexport const SettingsPage: React.FC<SettingsPageProps> = ({\n children,\n actions = [],\n secondaryActions = [],\n actionsPlacement = \"header\",\n onBack,\n backLabel = \"Back\",\n title,\n loading = false,\n loadingShape = \"form\",\n padding = \"md\",\n className,\n contentClassName,\n}) => {\n const frame = useSettingsFrame();\n const ownerId = useId();\n\n // The handler is re-created on every render of the panel; keeping it in a ref\n // means the effect below depends on *whether* there is a back action, not on\n // its identity — otherwise every render would re-publish the trail and the\n // resulting frame re-render would loop.\n const backRef = useRef(onBack);\n backRef.current = onBack;\n\n const hasBack = Boolean(onBack);\n\n // Same reason as `backRef`: the action objects are re-created on every render of\n // the panel, so publishing them directly would re-publish the trail each time and\n // the resulting frame re-render would loop.\n const actionsRef = useRef(actions);\n actionsRef.current = actions;\n const secondaryRef = useRef(secondaryActions);\n secondaryRef.current = secondaryActions;\n\n const inHeader = actionsPlacement === \"header\";\n const hasHeaderActions = inHeader && (actions.length > 0 || secondaryActions.length > 0);\n\n /**\n * What the frame header would *look* like. This is the dependency the effect\n * watches, so a label or a disabled flag that changes still reaches the header\n * while a new closure identity alone does not.\n *\n * `icon` and `render` are deliberately absent: they are React nodes, so they\n * cannot be compared cheaply. A header action whose icon changes without any\n * other change is not a case that exists here — Cancel/Save carry no icon.\n */\n const headerActionsKey = hasHeaderActions\n ? JSON.stringify(\n [...secondaryActions, ...actions].map((a) => [a.id, a.label, a.variant, a.disabled]),\n )\n : \"\";\n\n /**\n * Stable proxies. The published object only changes when the signature above\n * does, but `onClick` reaches through the ref, so it always runs the handler of\n * the latest render — a Save in the frame header must write what is on screen\n * now, not what was there when the trail was published.\n */\n const headerActions = useMemo(() => {\n if (!hasHeaderActions) return undefined;\n const proxy = (which: \"primary\" | \"secondary\") => (a: PageHeaderAction, i: number) => ({\n ...a,\n onClick: () => {\n const list = which === \"primary\" ? actionsRef.current : secondaryRef.current;\n list[i]?.onClick();\n },\n });\n return {\n actions: actionsRef.current.map(proxy(\"primary\")),\n secondaryActions: secondaryRef.current.map(proxy(\"secondary\")),\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [hasHeaderActions, headerActionsKey]);\n\n useEffect(() => {\n if (!frame) return;\n if (!hasBack && !title && !headerActions) return;\n frame.setTrail(ownerId, {\n title,\n onBack: hasBack ? () => backRef.current?.() : undefined,\n actions: headerActions?.actions,\n secondaryActions: headerActions?.secondaryActions,\n });\n return () => frame.setTrail(ownerId, null);\n }, [frame, ownerId, title, hasBack, headerActions]);\n\n // Header placement moves the bar out of the panel entirely; there is no\n // fallback footer, because two homes for the same pair is the double bar this\n // component exists to remove.\n const showFooter = !inHeader && (actions.length > 0 || secondaryActions.length > 0);\n\n return (\n /**\n * `min-h-full` + a sticky footer, rather than a nested scroll container.\n * The frame already scrolls, and taking that over here would clip every\n * panel that has not been migrated yet. This way a short page still drops\n * its footer to the bottom, and a long one keeps it pinned while scrolling.\n */\n <div className={cn(\"flex min-h-full flex-col\", className)}>\n {/**\n * Unframed usage — the same panel opened as a modal or a plain route.\n * There is no frame to hand the title and the back action to, so this\n * draws them itself. Without this the record's name simply disappears\n * outside the settings overlay.\n */}\n {!frame && (hasBack || title || hasHeaderActions) && (\n <div className=\"flex shrink-0 items-center gap-2 px-5 pt-4\">\n {hasBack && (\n <Button\n variant=\"ghost\"\n onClick={() => backRef.current?.()}\n aria-label={backLabel}\n leftIcon={<Icon icon={ChevronLeft} size=\"sm\" />}\n iconOnly\n />\n )}\n {title && (\n <h2 className=\"min-w-0 truncate text-md font-semibold tracking-title text-text\">\n {title}\n </h2>\n )}\n {/* Header placement with no frame to hand them to: draw the pair here,\n or the panel would have no way to save at all. */}\n {inHeader && (\n <div className=\"ml-auto flex items-center gap-2\">\n {secondaryActions.length > 0 && (\n <PageToolbarActions actions={secondaryActions} defaultVariant=\"ghost\" />\n )}\n {actions.length > 0 && <PageToolbarActions actions={actions} />}\n </div>\n )}\n </div>\n )}\n\n <div className={cn(\"flex-1\", paddingClasses[padding], contentClassName)}>\n {loading ? <ContentLoading variant=\"inline\" shape={loadingShape} /> : children}\n </div>\n\n {showFooter && (\n <div className=\"sticky bottom-0 z-10 flex shrink-0 items-center gap-2 border-t border-border bg-surface px-5 py-3\">\n {secondaryActions.length > 0 && (\n <PageToolbarActions actions={secondaryActions} defaultVariant=\"ghost\" />\n )}\n {actions.length > 0 && <PageToolbarActions actions={actions} className=\"ml-auto\" />}\n </div>\n )}\n </div>\n );\n};\n","import type React from \"react\";\nimport { cn } from \"../utils/cn\";\nimport { Icon } from \"./Icon\";\n\nexport interface SettingsRowProps {\n /** What the setting is called. */\n label: React.ReactNode;\n /** One line on what it does or what turning it on means. */\n description?: React.ReactNode;\n /** The control itself — a `Switch`, `Select`, `Button`… */\n control: React.ReactNode;\n /**\n * Draw the row as a bordered pill. Use it for a flat list of independent\n * toggles; leave it off inside a `SettingsSection`, which already groups.\n */\n bordered?: boolean;\n disabled?: boolean;\n className?: string;\n}\n\n/**\n * One setting: label (+ description) on the left, its control on the right.\n *\n * @example\n * ```tsx\n * <SettingsRow\n * label=\"Agenda synchroniseren\"\n * description=\"Afspraken uit dit account verschijnen in je agenda.\"\n * control={<Switch checked={on} onChange={setOn} aria-label=\"Agenda synchroniseren\" />}\n * />\n * ```\n */\nexport function SettingsRow({\n label,\n description,\n control,\n bordered = false,\n disabled = false,\n className,\n}: SettingsRowProps) {\n return (\n <div\n className={cn(\n \"flex items-center justify-between gap-4 py-2\",\n bordered && \"rounded-lg border border-border px-3\",\n disabled && \"opacity-50\",\n className,\n )}\n >\n <div className=\"min-w-0\">\n <div className=\"text-sm text-text\">{label}</div>\n {description && <div className=\"text-xs text-text-muted\">{description}</div>}\n </div>\n <div className=\"shrink-0\">{control}</div>\n </div>\n );\n}\n\nexport interface SettingsSectionProps {\n /** Lucide icon component, rendered in the section heading. */\n icon?: React.ComponentType<Record<string, unknown>>;\n title: React.ReactNode;\n description?: React.ReactNode;\n /** Right-aligned slot in the heading, e.g. a \"Reset\" button. */\n actions?: React.ReactNode;\n children: React.ReactNode;\n className?: string;\n}\n\n/**\n * A titled group of `SettingsRow`s. The body is indented to line up under the\n * title text rather than the icon.\n */\nexport function SettingsSection({\n icon,\n title,\n description,\n actions,\n children,\n className,\n}: SettingsSectionProps) {\n return (\n <section className={cn(\"flex flex-col gap-2\", className)}>\n <div className=\"flex items-start gap-2\">\n {icon && <Icon icon={icon} size=\"md\" color=\"secondary\" className=\"mt-0.5\" />}\n <div className=\"min-w-0 flex-1\">\n <h3 className=\"text-sm font-semibold text-text\">{title}</h3>\n {description && <p className=\"text-xs text-text-muted\">{description}</p>}\n </div>\n {actions && <div className=\"shrink-0\">{actions}</div>}\n </div>\n <div className={cn(\"flex flex-col\", icon && \"pl-6\")}>{children}</div>\n </section>\n );\n}\n","import { ArrowUpRight } from \"lucide-react\";\nimport type React from \"react\";\nimport { cn } from \"../utils/cn\";\nimport { Icon } from \"./Icon\";\n\nexport interface SourceChipProps {\n /** Label — the linked conversation subject. */\n label: React.ReactNode;\n /** Optional leading dot colour (a `--color-cat-*` or channel CSS var). */\n dotColor?: string;\n /** Click opens the linked conversation. */\n onClick?: () => void;\n className?: string;\n}\n\n/**\n * Source-conversation chip — the \"open brongesprek / gekoppeld gesprek ↗\" pill\n * that links a task or focus item back to its conversation.\n *\n * @example\n * ```tsx\n * <SourceChip label=\"Vraag over factuur maart\" dotColor=\"var(--color-cat-2)\" onClick={openThread} />\n * ```\n */\nexport function SourceChip({ label, dotColor, onClick, className }: SourceChipProps) {\n return (\n <button\n type=\"button\"\n onClick={onClick}\n className={cn(\n \"inline-flex max-w-full items-center gap-1.5 rounded-full bg-surface-sunk px-2.5 py-1 text-xs text-text-muted transition-colors duration-fast ease-out hover:bg-surface-hover hover:text-text\",\n className,\n )}\n >\n {dotColor && (\n <span\n className=\"h-1.5 w-1.5 shrink-0 rounded-full\"\n style={{ backgroundColor: dotColor }}\n aria-hidden\n />\n )}\n <span className=\"min-w-0 truncate\">{label}</span>\n <Icon icon={ArrowUpRight} size=\"xs\" className=\"opacity-70\" />\n </button>\n );\n}\n","import { Calendar, ChevronLeft, ChevronRight } from \"lucide-react\";\nimport { forwardRef, useEffect, useRef, useState } from \"react\";\nimport { Icon } from \"../content/Icon\";\nimport { cn } from \"../utils/cn\";\nimport { useAnchoredPosition } from \"../utils/useAnchoredPosition\";\n\nexport interface DatePickerProps {\n value?: Date | null;\n onChange?: (date: Date | null) => void;\n placeholder?: string;\n disabled?: boolean;\n required?: boolean;\n size?: \"sm\" | \"md\" | \"lg\" | \"full\";\n className?: string;\n minDate?: Date;\n maxDate?: Date;\n format?: \"MM/dd/yyyy\" | \"dd/MM/yyyy\" | \"yyyy-MM-dd\";\n /**\n * Also pick a time. The display gains `HH:mm`, the panel gains a time field,\n * and choosing a day keeps the time already on the value instead of resetting\n * it to midnight. Replaces a native `datetime-local`.\n */\n withTime?: boolean;\n}\n\nconst CalendarIcon = () => <Icon icon={Calendar} size=\"md\" color=\"current\" />;\nconst ChevronLeftIcon = () => <Icon icon={ChevronLeft} size=\"md\" color=\"current\" />;\nconst ChevronRightIcon = () => <Icon icon={ChevronRight} size=\"md\" color=\"current\" />;\n\nexport const DatePicker = forwardRef<HTMLInputElement, DatePickerProps>(\n (\n {\n value,\n onChange,\n placeholder = \"Select date\",\n disabled = false,\n required = false,\n size = \"md\",\n className,\n minDate,\n maxDate,\n format = \"MM/dd/yyyy\",\n withTime = false,\n },\n ref,\n ) => {\n const [isOpen, setIsOpen] = useState(false);\n const [currentMonth, setCurrentMonth] = useState(() => value || new Date());\n const containerRef = useRef<HTMLDivElement>(null);\n const fieldRef = useRef<HTMLDivElement>(null);\n // Fixed against the field: an absolute panel is clipped by the form panel's\n // overflow and opens below the fold when the field sits near the bottom.\n //\n // Anchored on the field, not on containerRef — the panel is a child of the\n // container, so on the first open (before the fixed position lands) it counted\n // towards the container's own height and the panel placed itself a panel's\n // length too low. Closing and reopening looked fine because the measurement\n // from the previous open was still in state.\n const panelStyle = useAnchoredPosition(isOpen, fieldRef);\n\n useEffect(() => {\n const handleClickOutside = (event: MouseEvent) => {\n if (containerRef.current && !containerRef.current.contains(event.target as Node)) {\n setIsOpen(false);\n }\n };\n\n if (isOpen) {\n document.addEventListener(\"mousedown\", handleClickOutside);\n }\n\n return () => {\n document.removeEventListener(\"mousedown\", handleClickOutside);\n };\n }, [isOpen]);\n\n const formatDate = (date: Date | null): string => {\n if (!date) return \"\";\n\n const day = date.getDate().toString().padStart(2, \"0\");\n const month = (date.getMonth() + 1).toString().padStart(2, \"0\");\n const year = date.getFullYear();\n\n const datePart =\n format === \"dd/MM/yyyy\"\n ? `${day}/${month}/${year}`\n : format === \"yyyy-MM-dd\"\n ? `${year}-${month}-${day}`\n : `${month}/${day}/${year}`;\n\n return withTime ? `${datePart} ${formatTime(date)}` : datePart;\n };\n\n /** `HH:mm`, the value shape a native time input expects. */\n const formatTime = (date: Date) =>\n `${date.getHours().toString().padStart(2, \"0\")}:${date.getMinutes().toString().padStart(2, \"0\")}`;\n\n const handleTimeChange = (next: string) => {\n const [hours, minutes] = next.split(\":\").map(Number);\n if (Number.isNaN(hours) || Number.isNaN(minutes)) return;\n // Time can be set before a day is picked; fall back to today.\n const base = value ? new Date(value) : new Date();\n base.setHours(hours, minutes, 0, 0);\n onChange?.(base);\n };\n\n const getCalendarDays = () => {\n const year = currentMonth.getFullYear();\n const month = currentMonth.getMonth();\n\n const firstDay = new Date(year, month, 1);\n const lastDay = new Date(year, month + 1, 0);\n const startDate = new Date(firstDay);\n startDate.setDate(startDate.getDate() - firstDay.getDay());\n\n const days = [];\n const current = new Date(startDate);\n\n for (let i = 0; i < 42; i++) {\n days.push(new Date(current));\n current.setDate(current.getDate() + 1);\n }\n\n return days;\n };\n\n const handleDateSelect = (date: Date) => {\n const isInCurrentMonth = date.getMonth() === currentMonth.getMonth();\n if (!isInCurrentMonth) return;\n\n if (minDate && date < minDate) return;\n if (maxDate && date > maxDate) return;\n\n if (withTime) {\n // Carry the time across, otherwise picking a day silently resets it to 00:00.\n const picked = new Date(date);\n picked.setHours(value?.getHours() ?? 0, value?.getMinutes() ?? 0, 0, 0);\n onChange?.(picked);\n // Stay open: the time still has to be set.\n return;\n }\n\n onChange?.(date);\n setIsOpen(false);\n };\n\n const handlePrevMonth = () => {\n setCurrentMonth(new Date(currentMonth.getFullYear(), currentMonth.getMonth() - 1, 1));\n };\n\n const handleNextMonth = () => {\n setCurrentMonth(new Date(currentMonth.getFullYear(), currentMonth.getMonth() + 1, 1));\n };\n\n const handleClear = () => {\n onChange?.(null);\n setIsOpen(false);\n };\n\n const inputClasses = cn(\n \"w-full border border-border-strong rounded-tile transition-colors duration-fast\",\n \"bg-surface text-text\",\n \"placeholder:text-text-muted\",\n \"focus-visible:outline-none focus-visible:focus-ring\",\n \"disabled:cursor-not-allowed disabled:bg-surface-hover disabled:text-text-disabled\",\n {\n \"h-control-sm px-3 text-sm\": size === \"sm\",\n \"h-control-md px-3 text-base\": size === \"md\",\n \"h-control-lg px-3 text-base\": size === \"lg\",\n \"h-control-md w-full px-3 text-base\": size === \"full\",\n },\n className,\n );\n\n const calendarDays = getCalendarDays();\n const monthNames = [\n \"January\",\n \"February\",\n \"March\",\n \"April\",\n \"May\",\n \"June\",\n \"July\",\n \"August\",\n \"September\",\n \"October\",\n \"November\",\n \"December\",\n ];\n\n return (\n <div ref={containerRef} className=\"relative\">\n <div ref={fieldRef} className=\"relative\">\n <input\n ref={ref}\n type=\"text\"\n value={formatDate(value ?? null)}\n placeholder={placeholder}\n disabled={disabled}\n required={required}\n readOnly\n onClick={() => !disabled && setIsOpen(!isOpen)}\n className={cn(inputClasses, \"pr-10 cursor-pointer\")}\n />\n <button\n type=\"button\"\n onClick={() => !disabled && setIsOpen(!isOpen)}\n disabled={disabled}\n className=\"absolute inset-y-0 right-0 flex items-center pr-3 text-text-muted hover:text-text-muted\"\n >\n <CalendarIcon />\n </button>\n </div>\n\n {isOpen && (\n <div\n style={panelStyle}\n className=\"fixed z-overlay overflow-auto rounded-lg border border-border bg-surface p-4 shadow-overlay min-w-panel\"\n >\n <div className=\"flex items-center justify-between mb-4\">\n <button onClick={handlePrevMonth} className=\"p-1 hover:bg-surface-hover rounded\">\n <ChevronLeftIcon />\n </button>\n <h3 className=\"text-sm font-medium text-text\">\n {monthNames[currentMonth.getMonth()]} {currentMonth.getFullYear()}\n </h3>\n <button onClick={handleNextMonth} className=\"p-1 hover:bg-surface-hover rounded\">\n <ChevronRightIcon />\n </button>\n </div>\n\n <div className=\"grid grid-cols-7 gap-1 mb-2\">\n {[\"Su\", \"Mo\", \"Tu\", \"We\", \"Th\", \"Fr\", \"Sa\"].map((day) => (\n <div key={day} className=\"text-xs font-medium text-text-muted text-center py-1\">\n {day}\n </div>\n ))}\n </div>\n\n <div className=\"grid grid-cols-7 gap-1\">\n {calendarDays.map((date, index) => {\n const isCurrentMonth = date.getMonth() === currentMonth.getMonth();\n const isSelected = value && date.toDateString() === value.toDateString();\n const isToday = date.toDateString() === new Date().toDateString();\n const isDisabled =\n !isCurrentMonth || (minDate && date < minDate) || (maxDate && date > maxDate);\n\n return (\n <button\n key={index}\n onClick={() => !isDisabled && handleDateSelect(date)}\n disabled={isDisabled}\n className={cn(\"w-8 h-8 text-sm rounded transition-colors duration-fast\", {\n \"text-text hover:bg-surface-hover\":\n isCurrentMonth && !isSelected && !isDisabled,\n \"bg-accent text-accent-fg\": isSelected,\n \"bg-surface-hover text-text\": isToday && !isSelected,\n \"text-text-muted cursor-not-allowed\": isDisabled,\n })}\n >\n {date.getDate()}\n </button>\n );\n })}\n </div>\n\n {withTime && (\n <div className=\"mt-3 flex items-center gap-2 border-t border-border pt-3\">\n <label htmlFor=\"datepicker-time\" className=\"text-sm text-text-muted\">\n Tijd\n </label>\n <input\n id=\"datepicker-time\"\n type=\"time\"\n value={value ? formatTime(value) : \"\"}\n onChange={(e) => handleTimeChange(e.target.value)}\n className=\"h-control-sm rounded-md border border-border bg-surface-input px-2 text-sm text-text focus-visible:outline-none focus-visible:focus-ring\"\n />\n </div>\n )}\n\n <div className=\"flex justify-between items-center mt-4 pt-3 border-t border-border\">\n <button onClick={handleClear} className=\"text-sm text-text-muted hover:text-text\">\n Clear\n </button>\n <button\n onClick={() => setIsOpen(false)}\n className=\"text-sm text-info-fg hover:underline\"\n >\n Close\n </button>\n </div>\n </div>\n )}\n </div>\n );\n },\n);\n","import { Search, X } from \"lucide-react\";\nimport type React from \"react\";\nimport { forwardRef } from \"react\";\nimport { Icon } from \"../content/Icon\";\nimport { cn } from \"../utils/cn\";\n\nexport interface SearchFieldProps\n extends Omit<\n React.InputHTMLAttributes<HTMLInputElement>,\n \"size\" | \"value\" | \"onChange\" | \"type\"\n > {\n /** Current query. Controlled — this field has no internal state. */\n value: string;\n /** Called with the new query text (not the event). */\n onValueChange: (value: string) => void;\n /**\n * Keyboard hint shown at the trailing edge while the field is empty —\n * `⌘K`, `⌘F`. Replaced by the clear button once there is a query.\n */\n hint?: string;\n /** `md` is the default control height; `sm` for a dense bar. */\n size?: \"sm\" | \"md\";\n /** Accessible name for the clear button. */\n clearLabel?: string;\n /** Classes for the plate around the input. */\n containerClassName?: string;\n}\n\n/**\n * The sunk, frameless search plate: a magnifier, the query, and either a\n * keyboard hint or a clear button. It is deliberately *not* a `TextField` —\n * a bordered box would read as one more empty input in a toolbar that already\n * has filter chips and buttons, where this is the thing you type into.\n *\n * Three places had grown their own copy of it (the nav search, the settings\n * search, the table toolbar); they share this one now.\n */\nexport const SearchField = forwardRef<HTMLInputElement, SearchFieldProps>(\n (\n {\n value,\n onValueChange,\n hint,\n size = \"md\",\n clearLabel = \"Clear search\",\n containerClassName,\n className,\n disabled,\n ...props\n },\n ref,\n ) => (\n <div\n className={cn(\n // No focus ring: the plate is a container, not the control, and a ring\n // around a frameless search box reads as the border it deliberately\n // does not have.\n \"flex items-center gap-2 rounded-md bg-surface-sunk px-2.5 text-text-muted\",\n \"transition-colors duration-fast ease-out\",\n size === \"sm\" ? \"h-control-sm\" : \"h-control-md\",\n disabled && \"opacity-60\",\n containerClassName,\n )}\n >\n <Icon icon={Search} size=\"md\" color=\"current\" className=\"shrink-0\" />\n\n <input\n ref={ref}\n type=\"search\"\n value={value}\n disabled={disabled}\n onChange={(event) => onValueChange(event.target.value)}\n className={cn(\n // The forms base layer hands every input a white fill, a 1px border\n // and a blue focus ring; a field that is only a caret inside a plate\n // has to switch all three off explicitly. Padding too — the base rule\n // sets 8/12px, which would push the text off the plate.\n \"min-w-0 flex-1 border-0 bg-transparent p-0 text-sm text-text shadow-none\",\n \"placeholder:text-text-subtle\",\n \"focus:border-0 focus:shadow-none focus:outline-none focus:ring-0\",\n // The UA search decorations (the WebKit cancel button) would sit\n // beside our own clear button.\n \"[&::-webkit-search-cancel-button]:appearance-none\",\n className,\n )}\n {...props}\n />\n\n {value ? (\n <button\n type=\"button\"\n aria-label={clearLabel}\n onClick={() => onValueChange(\"\")}\n className=\"grid size-4 shrink-0 cursor-pointer place-items-center rounded-xs text-text-subtle transition-colors duration-fast ease-out hover:text-text focus-visible:outline-none focus-visible:focus-ring\"\n >\n <Icon icon={X} size=\"sm\" color=\"current\" />\n </button>\n ) : (\n hint && <span className=\"shrink-0 text-xs text-text-subtle\">{hint}</span>\n )}\n </div>\n ),\n);\n\nSearchField.displayName = \"SearchField\";\n","import { Check } from \"lucide-react\";\nimport type React from \"react\";\nimport { useEffect, useRef, useState } from \"react\";\nimport { cn } from \"../utils/cn\";\nimport { useAnchoredPosition } from \"../utils/useAnchoredPosition\";\n\nexport interface DropdownOption {\n value: string;\n label: string;\n icon?: React.ReactNode;\n disabled?: boolean;\n divider?: boolean;\n description?: string;\n children?: DropdownOption[];\n}\n\nexport interface DropdownProps {\n trigger: React.ReactNode;\n options: DropdownOption[];\n value?: string | Record<string, string | string[]>;\n onSelect?: (value: string, parentValue?: string) => void;\n placement?: \"bottom-start\" | \"bottom-end\" | \"top-start\" | \"top-end\";\n disabled?: boolean;\n className?: string;\n showCheck?: boolean;\n header?: string | React.ReactNode;\n}\n\n/** Menu dropdown with optional nested submenus and roving keyboard focus. */\nexport const Dropdown: React.FC<DropdownProps> = ({\n trigger,\n options,\n value,\n onSelect,\n placement = \"bottom-start\",\n disabled = false,\n className,\n showCheck = true,\n header,\n}) => {\n const [isOpen, setIsOpen] = useState(false);\n const [hoveredOption, setHoveredOption] = useState<\n string | Record<string, string | string[]> | null\n >(null);\n const [submenuPosition, setSubmenuPosition] = useState({ top: 0, left: 0 });\n // Menu is position:fixed (computed from the trigger) so it escapes any\n // overflow-hidden / rounded-card ancestor instead of being clipped.\n const dropdownRef = useRef<HTMLDivElement>(null);\n const triggerRef = useRef<HTMLButtonElement>(null);\n const submenuRef = useRef<HTMLDivElement>(null);\n const menuRef = useRef<HTMLDivElement>(null);\n const menuPos = useAnchoredPosition(isOpen, triggerRef, { placement });\n const hideTimeoutRef = useRef<NodeJS.Timeout | null>(null);\n\n /** Enabled option buttons in the open menu, in DOM order. */\n const menuItems = () =>\n Array.from(\n menuRef.current?.querySelectorAll<HTMLButtonElement>(\n \"button[data-option-value]:not(:disabled)\",\n ) ?? [],\n );\n\n // Move focus into the menu when it opens, so the keyboard lands somewhere\n // useful instead of on the page behind it.\n useEffect(() => {\n if (isOpen) menuItems()[0]?.focus();\n }, [isOpen]);\n\n /** Roving focus: ↓/↑ wrap through the options, Home/End jump to the ends. */\n const handleMenuKeyDown = (event: React.KeyboardEvent<HTMLDivElement>) => {\n const keys = [\"ArrowDown\", \"ArrowUp\", \"Home\", \"End\"];\n if (!keys.includes(event.key)) return;\n\n const items = menuItems();\n if (items.length === 0) return;\n event.preventDefault();\n\n const current = items.indexOf(document.activeElement as HTMLButtonElement);\n const next =\n event.key === \"Home\"\n ? 0\n : event.key === \"End\"\n ? items.length - 1\n : event.key === \"ArrowDown\"\n ? (current + 1) % items.length\n : (current - 1 + items.length) % items.length;\n\n items[next]?.focus();\n };\n\n useEffect(() => {\n const handleClickOutside = (event: MouseEvent) => {\n if (\n dropdownRef.current &&\n (!dropdownRef.current.contains(event.target as Node) ||\n (submenuRef.current && !submenuRef.current.contains(event.target as Node)))\n ) {\n setIsOpen(false);\n setHoveredOption(null);\n if (hideTimeoutRef.current) {\n clearTimeout(hideTimeoutRef.current);\n hideTimeoutRef.current = null;\n }\n }\n };\n\n if (isOpen) {\n document.addEventListener(\"mousedown\", handleClickOutside);\n }\n\n return () => {\n document.removeEventListener(\"mousedown\", handleClickOutside);\n };\n }, [isOpen]);\n\n useEffect(() => {\n const handleEscape = (event: KeyboardEvent) => {\n if (event.key === \"Escape\") {\n setIsOpen(false);\n setHoveredOption(null);\n // Hand focus back to the trigger; otherwise it falls to <body> and the\n // keyboard user loses their place.\n triggerRef.current?.focus();\n if (hideTimeoutRef.current) {\n clearTimeout(hideTimeoutRef.current);\n hideTimeoutRef.current = null;\n }\n }\n };\n\n if (isOpen) {\n document.addEventListener(\"keydown\", handleEscape);\n }\n\n return () => {\n document.removeEventListener(\"keydown\", handleEscape);\n };\n }, [isOpen]);\n\n useEffect(() => {\n if (hoveredOption && dropdownRef.current) {\n const hoveredElement = dropdownRef.current.querySelector(\n `[data-option-value=\"${hoveredOption}\"]`,\n );\n if (hoveredElement) {\n const rect = hoveredElement.getBoundingClientRect();\n setSubmenuPosition({\n top: rect.top,\n left: rect.right + 8,\n });\n }\n }\n }, [hoveredOption]);\n\n useEffect(() => {\n return () => {\n if (hideTimeoutRef.current) {\n clearTimeout(hideTimeoutRef.current);\n }\n };\n }, []);\n\n const handleTriggerClick = (event: React.MouseEvent<HTMLButtonElement>) => {\n event.stopPropagation();\n if (disabled) return;\n setIsOpen(!isOpen);\n setHoveredOption(null);\n if (hideTimeoutRef.current) {\n clearTimeout(hideTimeoutRef.current);\n hideTimeoutRef.current = null;\n }\n };\n\n const handleOptionClick = (\n event: React.MouseEvent<HTMLButtonElement>,\n option: DropdownOption,\n parentValue?: string,\n ) => {\n event.stopPropagation();\n if (!option.disabled && !option.divider) {\n if (!option.children || option.children.length === 0) {\n onSelect?.(option.value, parentValue);\n setIsOpen(false);\n setHoveredOption(null);\n }\n }\n };\n\n const handleOptionHover = (option: DropdownOption) => {\n if (hideTimeoutRef.current) {\n clearTimeout(hideTimeoutRef.current);\n hideTimeoutRef.current = null;\n }\n\n if (option.children && option.children.length > 0) {\n setHoveredOption(option.value);\n } else {\n setHoveredOption(null);\n }\n };\n\n const handleOptionLeave = (option: DropdownOption) => {\n if (option.children && option.children.length > 0) {\n hideTimeoutRef.current = setTimeout(() => {\n setHoveredOption(null);\n }, 150);\n } else {\n setHoveredOption(null);\n }\n };\n\n const handleSubmenuOptionClick = (\n event: React.MouseEvent<HTMLButtonElement>,\n option: DropdownOption,\n parentValue?: string,\n ) => {\n event.stopPropagation();\n if (!option.disabled && !option.divider) {\n onSelect?.(option.value, parentValue);\n setIsOpen(false);\n setHoveredOption(null);\n }\n };\n\n const dropdownClasses = cn(\n \"fixed z-overlay min-w-menu bg-surface rounded-lg shadow-overlay border border-border\",\n \"max-h-60 overflow-auto\",\n );\n\n const submenuClasses = cn(\n \"fixed z-overlay min-w-menu bg-surface rounded-lg shadow-overlay border border-border\",\n \"max-h-60 overflow-auto\",\n );\n\n return (\n <div\n ref={dropdownRef}\n className={cn(\n \"relative flex flex-row items-center justify-center rounded-lg hover:bg-surface-hover\",\n className,\n )}\n >\n <button\n ref={triggerRef}\n onClick={handleTriggerClick}\n disabled={disabled}\n aria-haspopup=\"menu\"\n aria-expanded={isOpen}\n className={cn(\"inline-flex items-center justify-center\", {\n \"opacity-50 cursor-not-allowed\": disabled,\n })}\n >\n {trigger}\n </button>\n\n {isOpen && (\n <div\n ref={menuRef}\n className={dropdownClasses}\n style={menuPos}\n onKeyDown={handleMenuKeyDown}\n >\n <div className=\"p-2\" role=\"menu\">\n {header &&\n (typeof header === \"string\" ? (\n <div className=\"text-xs font-medium text-text-muted uppercase tracking-label px-3 py-2\">\n {header}\n </div>\n ) : (\n header\n ))}\n\n {options.map((option, index) => {\n if (option.divider) {\n return <div key={`divider-${index}`} className=\"border-t border-border my-2\" />;\n }\n\n const hasChildren = option.children && option.children.length > 0;\n const isHovered = hoveredOption === option.value;\n\n return (\n <div key={option.value} className=\"relative group\">\n <button\n data-option-value={option.value}\n role=\"menuitem\"\n aria-haspopup={hasChildren ? \"menu\" : undefined}\n onClick={(event) => handleOptionClick(event, option)}\n onMouseEnter={() => handleOptionHover(option)}\n onMouseLeave={() => handleOptionLeave(option)}\n disabled={option.disabled}\n className={cn(\n \"flex w-full items-center justify-between rounded-md px-3 py-1.5 text-sm text-text transition-colors duration-fast ease-out hover:bg-surface-hover focus-visible:outline-none focus-visible:focus-ring\",\n {\n \"opacity-50 cursor-not-allowed\": option.disabled,\n \"bg-surface-hover\": isHovered && hasChildren,\n },\n )}\n >\n <div className=\"flex items-center space-x-2 min-w-0\">\n {option.icon && <span className=\"flex-shrink-0\">{option.icon}</span>}\n <div className=\"flex flex-col items-start min-w-0\">\n <span className=\"font-base truncate\">{option.label}</span>\n {option.description && (\n <span className=\"text-xs text-text-muted\">{option.description}</span>\n )}\n </div>\n </div>\n\n <div className=\"flex items-center space-x-2\">\n {showCheck && value === option.value && (\n <Check className=\"size-icon-md shrink-0 text-success-fg\" aria-hidden />\n )}\n {hasChildren && <span className=\"text-text-muted text-xs\">▶</span>}\n </div>\n </button>\n\n {hasChildren && isHovered && (\n <div\n ref={submenuRef}\n className={submenuClasses}\n style={{\n top: `${submenuPosition.top}px`,\n left: `${submenuPosition.left}px`,\n }}\n onMouseEnter={() => {\n if (hideTimeoutRef.current) {\n clearTimeout(hideTimeoutRef.current);\n hideTimeoutRef.current = null;\n }\n setHoveredOption(option.value);\n }}\n onMouseLeave={() => {\n hideTimeoutRef.current = setTimeout(() => {\n setHoveredOption(null);\n }, 150);\n }}\n >\n <div className=\"p-2\">\n {option.children!.map((childOption, childIndex) => {\n if (childOption.divider) {\n return (\n <div\n key={`divider-${childIndex}`}\n className=\"border-t border-border my-2\"\n />\n );\n }\n\n const isSelected =\n (typeof value === \"object\" &&\n value[option.value] &&\n value[option.value].includes(childOption.value)) ||\n value === childOption.value;\n\n return (\n <button\n key={childOption.value}\n onClick={(event) =>\n handleSubmenuOptionClick(event, childOption, option.value)\n }\n disabled={childOption.disabled}\n className={cn(\n \"flex w-full items-center justify-between rounded-md px-3 py-1.5 text-sm text-text transition-colors duration-fast ease-out hover:bg-surface-hover\",\n {\n \"opacity-50 cursor-not-allowed\": childOption.disabled,\n },\n )}\n >\n <div className=\"flex items-center space-x-2 min-w-0\">\n {childOption.icon && (\n <span className=\"flex-shrink-0\">{childOption.icon}</span>\n )}\n <div className=\"flex flex-col items-start min-w-0\">\n <span className=\"font-base truncate\">{childOption.label}</span>\n {childOption.description && (\n <span className=\"text-xs text-text-muted\">\n {childOption.description}\n </span>\n )}\n </div>\n </div>\n\n {showCheck && isSelected && (\n <Check\n className=\"size-icon-md shrink-0 text-success-fg\"\n aria-hidden\n />\n )}\n </button>\n );\n })}\n </div>\n </div>\n )}\n </div>\n );\n })}\n\n {(() => {\n const totalOptions = options.reduce((count, option) => {\n if (option.divider) return count;\n const childCount = option.children\n ? option.children.filter((child) => !child.divider).length\n : 0;\n return count + 1 + childCount;\n }, 0);\n\n return (\n totalOptions > 1 && (\n <div className=\"border-t border-border mt-2 pt-2\">\n <div className=\"text-xs text-text-muted px-3 py-1\">\n {totalOptions} options available\n </div>\n </div>\n )\n );\n })()}\n </div>\n </div>\n )}\n </div>\n );\n};\n","import { ChevronDown, ChevronLeft, ChevronRight, ChevronUp, Ellipsis } from \"lucide-react\";\nimport type React from \"react\";\nimport { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport { Button } from \"../action/Button\";\nimport { FilterChip } from \"../action/FilterChip\";\nimport { Checkbox } from \"../input/Checkbox\";\nimport { DatePicker } from \"../input/DatePicker\";\nimport { SearchField } from \"../input/SearchField\";\nimport { Dropdown } from \"../overlays/Dropdown\";\nimport { cn } from \"../utils/cn\";\nimport { Icon } from \"./Icon\";\nimport type { ListGroup } from \"./List\";\nimport type { PageHeaderAction } from \"./PageHeader\";\nimport { PageToolbarActions } from \"./PageToolbar\";\n\nexport interface TableColumn<T = any> {\n /** Unique column identifier */\n id: string;\n /** Column header text */\n header: string;\n /** Data accessor - can be string key or function */\n accessor: keyof T | ((row: T) => any);\n /** Custom cell renderer */\n cell?: (value: any, row: T, index: number) => React.ReactNode;\n /** Column width */\n width?: string | number;\n /** Whether column is sortable */\n sortable?: boolean;\n /** Whether column is searchable */\n searchable?: boolean;\n /** Column alignment */\n align?: \"left\" | \"center\" | \"right\";\n /** Whether column is sticky */\n sticky?: \"left\" | \"right\";\n /** Custom header renderer */\n headerCell?: () => React.ReactNode;\n}\n\nexport interface TableAction<T = any> {\n /** Action identifier */\n id: string;\n /** Action label */\n label: string;\n /** Action icon */\n icon?: React.ReactNode;\n /** Action handler */\n onClick: (row: T, index: number) => void;\n /** Whether action is disabled for this row */\n disabled?: (row: T) => boolean;\n /** Action variant */\n variant?: \"primary\" | \"secondary\" | \"outline\" | \"ghost\" | \"destructive\";\n}\n\nexport interface TableFilter {\n /** Filter identifier */\n id: string;\n /** Filter label */\n label: string;\n /** Filter type */\n type?: \"select\" | \"date\" | \"dateRange\";\n /** Leading icon on the chip — what the filter is about, at a glance. */\n icon?: React.ReactNode;\n /** Filter options (for select type) */\n options?: Array<{ value: string; label: string }>;\n /** Current filter value */\n value?: string | Date | null | string[];\n /** Filter change handler */\n onChange: (value: string | Date | null | string[]) => void;\n /** Placeholder text */\n placeholder?: string;\n /** Whether filter is multi-select */\n multiSelect?: boolean;\n}\n\n/**\n * `TableFilter.onChange` covers every filter `type` in one signature, so a\n * handler written for the type it is actually attached to does not fit it\n * (parameters are contravariant). These adapters bridge that without pushing a\n * cast into every call site.\n *\n * The fallbacks are unreachable in practice — a `type: \"date\"` filter only ever\n * emits `Date | null`, a single `type: \"select\"` only ever a string — but they\n * keep the coercion explicit instead of asserting it away.\n */\nexport const dateFilterHandler =\n (fn: (value: Date | null) => void): TableFilter[\"onChange\"] =>\n (value) =>\n fn(value instanceof Date ? value : null);\n\nexport const selectFilterHandler =\n (fn: (value: string) => void): TableFilter[\"onChange\"] =>\n (value) =>\n fn(typeof value === \"string\" ? value : \"\");\n\n/**\n * The `multiSelect: true` counterpart. It emits `string[]`, which neither adapter\n * above accepts, so without this every multi-select call site pushed a cast — the\n * exact thing these adapters exist to avoid.\n *\n * A single string is folded into a one-element array rather than dropped: a filter\n * that silently clears itself when the menu emits the narrower shape is worse than\n * one that over-selects.\n */\nexport const multiSelectFilterHandler =\n (fn: (value: string[]) => void): TableFilter[\"onChange\"] =>\n (value) =>\n fn(Array.isArray(value) ? value : typeof value === \"string\" && value ? [value] : []);\n\nexport interface TableProps<T = any> {\n /** Table data */\n data: T[];\n /** Column definitions */\n columns: TableColumn<T>[];\n /** Loading state */\n loading?: boolean;\n /** Whether table is searchable */\n searchable?: boolean;\n /** Search placeholder text */\n searchPlaceholder?: string;\n /**\n * Take over the query. Pass this pair when the page already filters its own\n * data — across fields no column exposes, or together with filters of its\n * own — and only wants the search plate to sit in the toolbar where the\n * design puts it. The built-in column search then stays out of the way;\n * without them the table owns both the box and the filtering.\n */\n searchValue?: string;\n onSearchChange?: (value: string) => void;\n /** Table filters */\n filters?: TableFilter[];\n /**\n * Buttons for the toolbar above the table — \"New …\", import, export. This is\n * where a list's own actions belong: the page header names the page, the\n * table owns what you do to it.\n */\n toolbarActions?: PageHeaderAction[];\n /**\n * Extra controls in the toolbar, rendered **after the filter chips and before**\n * the right-hand action cluster.\n *\n * This is where anything that belongs *with the filters* goes: an avatar stack\n * you filter by, a \"clear filters\" chip, a group-by control. `toolbarActions`\n * is pushed right with `ml-auto`, so putting a filter there parks it beside the\n * list's own buttons and it stops reading as a filter at all.\n */\n toolbarContent?: React.ReactNode;\n /**\n * Split the rows into sections. Returns the group id a row belongs to; the\n * same shape `List` uses, so a screen that has both does not learn two APIs.\n *\n * **Grouping turns pagination off.** Paging *across* sections is meaningless —\n * \"1–10 of 40\" under a section holding three rows is worse than no paginator —\n * so a grouped table shows every row it was given.\n */\n groupBy?: (item: T, index: number) => string;\n /**\n * Declared section order, titles and tone. Ids missing from the data are\n * skipped; ids in the data that are not declared come after, in the order\n * they first appear.\n */\n groups?: ListGroup[];\n /** Whether table has pagination */\n paginated?: boolean;\n /** Rows per page. Fixed — the footer is a summary and a page list, nothing to set. */\n defaultPageSize?: number;\n /** Row actions */\n actions?: TableAction<T>[];\n /** Row click handler */\n onRowClick?: (row: T, index: number) => void;\n /** Row selection */\n selectable?: boolean;\n /** Selected rows */\n selectedRows?: T[];\n /** Selection change handler */\n onSelectionChange?: (selectedRows: T[]) => void;\n /** Row key accessor */\n getRowKey?: (row: T, index: number) => string | number;\n /** Empty state content */\n emptyContent?: React.ReactNode;\n /**\n * Left-hand line under the table. Defaults to the range/total count on a\n * paginated table; pass a node to say it in the app's own words (\"9 of 1,284\n * contacts\"), which also gives an unpaginated table that one line.\n */\n footerSummary?: React.ReactNode;\n /** Row density. `md` is the default data row; `sm` for a dense inline list. */\n size?: \"sm\" | \"md\" | \"lg\";\n /** Whether to show row hover */\n hoverable?: boolean;\n /** Additional CSS classes */\n className?: string;\n /** Custom row className */\n rowClassName?: (row: T, index: number) => string;\n /**\n * Tint the header row so it reads as a column strip rather than a first row.\n * On by default. Turn it off for a table that already sits on a tinted\n * surface, where a second wash just muddies the edge.\n */\n headerBackground?: boolean;\n /** Horizontal padding inside the table box. */\n padding?: \"none\" | \"sm\" | \"md\" | \"lg\";\n}\n\n/** Cell padding — uniform across columns, so the grid reads as a grid. */\nconst cellPadding = {\n none: \"px-0\",\n sm: \"px-2\",\n md: \"px-3.5\",\n lg: \"px-5\",\n};\n\nconst rowHeights = {\n sm: \"h-row-lg\",\n md: \"h-row-xl\",\n lg: \"h-14\",\n};\n\n/** How many placeholder rows to draw while the data is on its way. */\nconst SKELETON_ROWS = 8;\n\n/** Uneven cell widths, so the placeholder reads as text and not as a bar chart. */\nconst SKELETON_WIDTHS = [\"62%\", \"44%\", \"74%\", \"52%\", \"68%\", \"48%\", \"80%\", \"56%\"];\n\ninterface SortState {\n column: string | null;\n direction: \"asc\" | \"desc\" | null;\n}\n\ninterface PaginationState {\n page: number;\n pageSize: number;\n}\n\n/**\n * Page buttons around the current page: first, last, a window of neighbours,\n * and an ellipsis wherever that skips something.\n */\nfunction pageWindow(current: number, total: number): Array<number | \"gap\"> {\n if (total <= 7) return Array.from({ length: total }, (_, i) => i);\n\n const pages = new Set([0, total - 1, current - 1, current, current + 1]);\n const sorted = [...pages].filter((p) => p >= 0 && p < total).sort((a, b) => a - b);\n\n const out: Array<number | \"gap\"> = [];\n sorted.forEach((page, index) => {\n if (index > 0 && page - (sorted[index - 1] as number) > 1) out.push(\"gap\");\n out.push(page);\n });\n return out;\n}\n\n/** One filter, as a chip that opens its own value list. */\nconst FilterMenu = (props: TableFilter) => {\n const values = Array.isArray(props.value)\n ? props.value\n : props.value != null && props.value !== \"\" && props.value !== \"all\"\n ? [props.value]\n : [];\n\n if (props.type === \"date\") {\n const active = props.value instanceof Date;\n return (\n <DatePicker\n value={props.value as Date | null}\n onChange={(date) => props.onChange(date)}\n placeholder={props.placeholder || props.label}\n // Active is a soft fill, like every other filter — an outline in the\n // accent hue was left over from the blue-accent design and read as a\n // focus ring on a field nobody had focused.\n className={cn(\"min-w-40\", active && \"bg-info-soft text-info-fg\")}\n />\n );\n }\n\n const labelOf = (value: unknown) =>\n props.options?.find((option) => option.value === value)?.label ?? String(value);\n\n const isSelected = (value: string) =>\n Array.isArray(props.value) ? props.value.includes(value) : props.value === value;\n\n const pick = (value: string) => {\n if (!props.multiSelect) return props.onChange(value);\n const current = Array.isArray(props.value) ? props.value : [];\n props.onChange(\n current.includes(value) ? current.filter((v) => v !== value) : [...current, value],\n );\n };\n\n const active = values.length > 0;\n\n return (\n <FilterChip\n tone=\"outline\"\n caret\n icon={props.icon}\n active={active}\n label={active ? `${props.label}: ${values.map(labelOf).join(\", \")}` : props.label}\n title={props.label}\n onClear={() => props.onChange(props.multiSelect ? [] : \"all\")}\n menu={(close) => (\n <div role=\"listbox\" className=\"flex flex-col\">\n {props.options?.map((option) => (\n <button\n key={option.value}\n type=\"button\"\n role=\"option\"\n aria-selected={isSelected(option.value)}\n onClick={() => {\n pick(option.value);\n if (!props.multiSelect) close();\n }}\n className={cn(\n \"flex cursor-pointer items-center gap-2 rounded-md px-2.5 py-1.5 text-left text-sm\",\n \"transition-colors duration-fast ease-out hover:bg-surface-hover\",\n \"focus-visible:outline-none focus-visible:focus-ring\",\n isSelected(option.value) ? \"font-medium text-text\" : \"text-text-muted\",\n )}\n >\n {option.label}\n </button>\n ))}\n </div>\n )}\n />\n );\n};\n\n/**\n * Data table: a bordered box with a tinted column strip, a toolbar above it\n * (search, filters, the list's own actions) and a summary + paginator below.\n */\nexport const Table = <T extends Record<string, any>>({\n data,\n columns,\n loading = false,\n searchable = false,\n searchPlaceholder = \"Search...\",\n searchValue,\n onSearchChange,\n filters = [],\n toolbarActions = [],\n toolbarContent,\n groupBy,\n groups,\n paginated = false,\n defaultPageSize = 10,\n actions = [],\n onRowClick,\n selectable = false,\n selectedRows = [],\n onSelectionChange,\n getRowKey = (row, index) => index,\n emptyContent,\n footerSummary,\n size = \"md\",\n hoverable = true,\n padding = \"md\",\n className,\n rowClassName,\n headerBackground = true,\n}: TableProps<T>) => {\n const [internalSearch, setInternalSearch] = useState(\"\");\n const controlledSearch = searchValue !== undefined;\n const searchTerm = controlledSearch ? searchValue : internalSearch;\n const setSearchTerm = onSearchChange ?? setInternalSearch;\n const [sortState, setSortState] = useState<SortState>({\n column: null,\n direction: null,\n });\n const [pagination, setPagination] = useState<PaginationState>({\n page: 0,\n pageSize: defaultPageSize,\n });\n\n const tableRef = useRef<HTMLTableElement>(null);\n\n const filteredData = useMemo(() => {\n let result = data;\n\n // Apply search filter — unless the owner took over the query, in which case\n // the data arriving here is already filtered and doing it again would\n // narrow it to whatever the columns happen to expose.\n if (searchable && !controlledSearch && searchTerm.trim()) {\n const searchableColumns = columns.filter((col) => col.searchable !== false);\n const lowerSearchTerm = searchTerm.toLowerCase();\n\n result = result.filter((row) => {\n return searchableColumns.some((column) => {\n const value =\n typeof column.accessor === \"function\" ? column.accessor(row) : row[column.accessor];\n\n return String(value || \"\")\n .toLowerCase()\n .includes(lowerSearchTerm);\n });\n });\n }\n\n /**\n * Column filters are rendered here but applied by the page: `TableFilter`\n * requires both `value` and `onChange`, so filter state lives outside and\n * `data` arrives pre-narrowed. The in-table pass that used to run on top\n * of that compared labels against keys and strings against multi-select\n * arrays, silently emptying the table; every consumer already pre-filters,\n * so removing it changed nothing for them and unbroke those cases.\n */\n\n return result;\n }, [data, searchTerm, controlledSearch, columns, searchable]);\n\n const sortedData = useMemo(() => {\n if (!sortState.column || !sortState.direction) return filteredData;\n\n const column = columns.find((col) => col.id === sortState.column);\n if (!column) return filteredData;\n\n return [...filteredData].sort((a, b) => {\n const aValue =\n typeof column.accessor === \"function\" ? column.accessor(a) : a[column.accessor];\n const bValue =\n typeof column.accessor === \"function\" ? column.accessor(b) : b[column.accessor];\n\n let comparison = 0;\n\n if (aValue < bValue) comparison = -1;\n else if (aValue > bValue) comparison = 1;\n\n return sortState.direction === \"desc\" ? -comparison : comparison;\n });\n }, [filteredData, sortState, columns]);\n\n const paginatedData = useMemo(() => {\n // Grouping wins over pagination: see the `groupBy` prop.\n if (!paginated || groupBy) return sortedData;\n\n const startIndex = pagination.page * pagination.pageSize;\n const endIndex = startIndex + pagination.pageSize;\n return sortedData.slice(startIndex, endIndex);\n }, [sortedData, pagination, paginated, groupBy]);\n\n const handleSort = useCallback(\n (columnId: string) => {\n const column = columns.find((col) => col.id === columnId);\n if (!column?.sortable) return;\n\n setSortState((prev) => {\n if (prev.column !== columnId) {\n return { column: columnId, direction: \"asc\" };\n }\n if (prev.direction === \"asc\") {\n return { column: columnId, direction: \"desc\" };\n }\n return { column: null, direction: null };\n });\n },\n [columns],\n );\n\n const handlePageChange = useCallback((newPage: number) => {\n setPagination((prev) => ({ ...prev, page: newPage }));\n }, []);\n\n const handleRowSelection = useCallback(\n (row: T, checked: boolean) => {\n if (!onSelectionChange) return;\n\n const rowKey = getRowKey(row, 0);\n if (checked) {\n onSelectionChange([...selectedRows, row]);\n } else {\n onSelectionChange(selectedRows.filter((_, i) => getRowKey(selectedRows[i], i) !== rowKey));\n }\n },\n [selectedRows, onSelectionChange, getRowKey],\n );\n\n const handleSelectAll = useCallback(\n (checked: boolean) => {\n if (!onSelectionChange) return;\n onSelectionChange(checked ? [...paginatedData] : []);\n },\n [paginatedData, onSelectionChange],\n );\n\n useEffect(() => {\n setPagination((prev) => ({ ...prev, page: 0 }));\n }, [searchTerm, sortState]);\n\n // Separate effect for filters to avoid infinite loops\n useEffect(() => {\n setPagination((prev) => ({ ...prev, page: 0 }));\n }, [filters.map((f) => f.value).join(\",\")]);\n\n const totalItems = sortedData.length;\n const totalPages = Math.ceil(totalItems / pagination.pageSize);\n const startItem = pagination.page * pagination.pageSize + 1;\n const endItem = Math.min(startItem + pagination.pageSize - 1, totalItems);\n\n const allVisibleSelected =\n paginatedData.length > 0 &&\n paginatedData.every((row) => {\n const rowKey = getRowKey(row, 0);\n return selectedRows.some((selectedRow, i) => getRowKey(selectedRow, i) === rowKey);\n });\n\n const someVisibleSelected = paginatedData.some((row) => {\n const rowKey = getRowKey(row, 0);\n return selectedRows.some((selectedRow, i) => getRowKey(selectedRow, i) === rowKey);\n });\n\n const pad = cellPadding[padding];\n\n // Sticky cells need the same fill as the strip they sit in, otherwise the\n // frozen column shows the page through while the rest of the row is tinted.\n const headerFill = headerBackground ? \"bg-surface-sunk\" : \"bg-surface\";\n\n const cellClasses = cn(\"text-text\", pad);\n const showToolbar =\n searchable || filters.length > 0 || toolbarActions.length > 0 || !!toolbarContent;\n /**\n * The rows to render, as sections. Without `groupBy` that is one nameless\n * section holding everything, so the body below has a single shape.\n *\n * Bucketed in first-appearance order and then reordered by `groups`, matching\n * `List` exactly — a screen that groups a list and a table should not get two\n * different orderings out of the same data.\n */\n const sections = useMemo(() => {\n const rows = paginatedData.map((row, index) => ({ row, index }));\n if (!groupBy) return [{ group: null as ListGroup | null, rows }];\n\n const buckets = new Map<string, typeof rows>();\n for (const entry of rows) {\n const id = groupBy(entry.row, entry.index);\n const bucket = buckets.get(id);\n if (bucket) bucket.push(entry);\n else buckets.set(id, [entry]);\n }\n\n const declared = groups ?? [];\n const ordered: ListGroup[] = [\n ...declared.filter((group) => buckets.has(group.id)),\n ...[...buckets.keys()]\n .filter((id) => !declared.some((group) => group.id === id))\n .map((id) => ({ id })),\n ];\n return ordered.map((group) => ({ group, rows: buckets.get(group.id) ?? [] }));\n }, [paginatedData, groupBy, groups]);\n\n /** Every column the body spans, so a section header can stretch across it. */\n const bodyColumnCount = columns.length + (selectable ? 1 : 0) + (actions.length > 0 ? 1 : 0);\n\n const showPaginator = paginated && totalItems > 0;\n const showFooter = showPaginator || footerSummary !== undefined;\n\n return (\n <div className={cn(\"flex min-w-0 flex-col\", className)}>\n {showToolbar && (\n <div className=\"flex flex-wrap items-center gap-2 pb-3\">\n {searchable && (\n <SearchField\n value={searchTerm}\n onValueChange={setSearchTerm}\n placeholder={searchPlaceholder}\n containerClassName=\"w-full sm:w-66\"\n />\n )}\n\n {filters.map((filter) => (\n <FilterMenu key={filter.id} {...filter} />\n ))}\n\n {toolbarContent}\n\n {toolbarActions.length > 0 && (\n <PageToolbarActions actions={toolbarActions} className=\"ml-auto\" />\n )}\n </div>\n )}\n\n {/* A bordered box, not a bare grid: the table has to hold its own edge\n now that the page around it is one undivided card. */}\n <div className=\"relative w-full overflow-x-auto rounded-lg ring-1 ring-border\">\n {/**\n * The skeleton lives in the tbody, not above the table: headers are\n * known before the data is, so they stay, and only the rows are drawn\n * as placeholders — at row height, in the real columns. Rendering a\n * `ContentLoading` here used to put a page-padded `<main>` landmark\n * inside this box, and the real rows then shoved everything into place.\n */}\n <table ref={tableRef} className=\"min-w-full border-collapse text-base text-text\">\n <thead className={cn(headerFill, \"h-control-lg\")}>\n <tr>\n {selectable && (\n <th className={cn(cellClasses, \"w-10\")}>\n <Checkbox\n size=\"sm\"\n checked={allVisibleSelected}\n indeterminate={someVisibleSelected && !allVisibleSelected}\n onChange={(e) => handleSelectAll(e.target.checked)}\n aria-label=\"Select all rows\"\n />\n </th>\n )}\n\n {columns.map((column) => (\n <th\n key={column.id}\n className={cn(\n cellClasses,\n \"text-xs font-semibold uppercase tracking-label text-text-muted\",\n \"group\",\n {\n \"text-left\": column.align === \"left\" || !column.align,\n \"text-center\": column.align === \"center\",\n \"text-right\": column.align === \"right\",\n \"cursor-pointer select-none\": column.sortable,\n [`sticky left-0 ${headerFill}`]: column.sticky === \"left\",\n [`sticky right-0 ${headerFill}`]: column.sticky === \"right\",\n },\n )}\n style={{ width: column.width }}\n onClick={() => column.sortable && handleSort(column.id)}\n >\n <div className=\"flex items-center gap-1.5\">\n {column.headerCell ? column.headerCell() : column.header}\n {column.sortable && (\n <Icon\n icon={\n sortState.column === column.id && sortState.direction === \"desc\"\n ? ChevronDown\n : ChevronUp\n }\n size=\"xs\"\n className={cn(\n \"transition-opacity duration-fast ease-out\",\n sortState.column === column.id\n ? \"text-text opacity-100\"\n : \"text-text-subtle opacity-0 group-hover:opacity-100\",\n )}\n />\n )}\n </div>\n </th>\n ))}\n\n {actions.length > 0 && (\n <th className={cn(cellClasses, \"w-10\")}>\n <span className=\"sr-only\">Actions</span>\n </th>\n )}\n </tr>\n </thead>\n\n <tbody>\n {loading ? (\n Array.from({ length: SKELETON_ROWS }).map((_, rowIndex) => (\n <tr\n key={`skeleton-${rowIndex}`}\n className=\"animate-pulse border-t border-border-subtle\"\n aria-hidden\n >\n {selectable && (\n <td className={cn(cellClasses, \"h-row-lg\")}>\n <span className=\"block size-4 rounded-xs bg-surface-hover\" />\n </td>\n )}\n {columns.map((column, columnIndex) => (\n <td\n key={column.id}\n className={cn(cellClasses, \"h-row-lg\")}\n style={{ width: column.width }}\n >\n <span\n className=\"block h-3 rounded-xs bg-surface-hover\"\n // Uneven widths, so a column of placeholders reads as\n // text rather than as a set of progress bars.\n style={{\n width: SKELETON_WIDTHS[(rowIndex + columnIndex) % SKELETON_WIDTHS.length],\n }}\n />\n </td>\n ))}\n {actions.length > 0 && <td className={cn(cellClasses, \"h-row-lg\")} />}\n </tr>\n ))\n ) : paginatedData.length === 0 ? (\n <tr>\n <td\n colSpan={columns.length + (selectable ? 1 : 0) + (actions.length > 0 ? 1 : 0)}\n className={cn(\n cellClasses,\n \"border-t border-border-subtle py-10 text-center text-text-muted\",\n )}\n >\n {emptyContent || \"No data available\"}\n </td>\n </tr>\n ) : (\n sections.flatMap((section) => [\n ...(section.group\n ? [\n <tr key={`group-${section.group.id}`}>\n <td\n colSpan={bodyColumnCount}\n className={cn(\n // A caption over its rows, not a second column strip:\n // same surface as the rows, no rules, count on the right.\n \"border-t border-border-subtle bg-surface px-4 pb-1.5 pt-4 text-sm font-semibold\",\n section.group.tone === \"danger\"\n ? \"text-danger-fg\"\n : section.group.tone === \"muted\"\n ? \"text-text-subtle\"\n : \"text-text\",\n )}\n >\n <span className=\"flex items-center gap-2.5\">\n {section.group.title ?? section.group.id}\n <span className=\"rounded-full bg-surface-sunk px-1.5 py-0.5 text-xs font-medium text-text-muted tabular-nums\">\n {section.group.count ?? section.rows.length}\n </span>\n </span>\n </td>\n </tr>,\n ]\n : []),\n ...section.rows.map(({ row, index }) => {\n const rowKey = getRowKey(row, index);\n const isSelected = selectedRows.some(\n (selectedRow, i) => getRowKey(selectedRow, i) === rowKey,\n );\n\n return (\n <tr\n key={rowKey}\n className={cn(\n rowHeights[size],\n // Hairline above every row, including the first: it is\n // what separates the body from the column strip.\n \"border-t border-border-subtle\",\n hoverable &&\n \"transition-colors duration-fast ease-out hover:bg-surface-hover\",\n onRowClick && \"cursor-pointer\",\n isSelected && \"bg-accent-soft\",\n rowClassName?.(row, index),\n )}\n onClick={(e) => {\n e.stopPropagation();\n onRowClick?.(row, index);\n }}\n >\n {selectable && (\n <td className={cellClasses}>\n <Checkbox\n size=\"sm\"\n checked={isSelected}\n onClick={(e) => e.stopPropagation()}\n onChange={(e) => {\n e.stopPropagation();\n handleRowSelection(row, e.target.checked);\n }}\n aria-label=\"Select row\"\n />\n </td>\n )}\n\n {columns.map((column) => {\n const value =\n typeof column.accessor === \"function\"\n ? column.accessor(row)\n : row[column.accessor];\n\n return (\n <td\n key={column.id}\n className={cn(cellClasses, {\n \"text-left\": column.align === \"left\" || !column.align,\n \"text-center\": column.align === \"center\",\n \"text-right\": column.align === \"right\",\n \"sticky left-0 bg-surface\": column.sticky === \"left\",\n \"sticky right-0 bg-surface\": column.sticky === \"right\",\n })}\n >\n {column.cell ? column.cell(value, row, index) : String(value || \"\")}\n </td>\n );\n })}\n\n {actions.length > 0 && (\n <td className={cellClasses}>\n <Dropdown\n className=\"rounded-md\"\n trigger={\n <span className=\"grid size-7 place-items-center text-text-subtle\">\n <Icon icon={Ellipsis} size=\"md\" color=\"current\" />\n </span>\n }\n options={actions.map((action) => ({\n value: action.id,\n label: action.label,\n icon: action.icon,\n disabled: action.disabled?.(row),\n }))}\n onSelect={(actionId) => {\n const action = actions.find((a) => a.id === actionId);\n if (action) {\n action.onClick(row, index);\n }\n }}\n placement=\"bottom-end\"\n />\n </td>\n )}\n </tr>\n );\n }),\n ])\n )}\n </tbody>\n </table>\n </div>\n\n {showFooter && (\n <div className=\"flex flex-wrap items-center gap-3 px-1 pt-3 text-xs text-text-muted\">\n <span className=\"min-w-0 flex-1\">\n {footerSummary ?? `Showing ${startItem}–${endItem} of ${totalItems}`}\n </span>\n\n {showPaginator && (\n <div className=\"flex items-center gap-0.5\">\n <Button\n size=\"xs\"\n variant=\"ghost\"\n iconOnly\n aria-label=\"Previous page\"\n onClick={() => handlePageChange(pagination.page - 1)}\n disabled={pagination.page === 0}\n >\n <Icon icon={ChevronLeft} size=\"md\" color=\"current\" />\n </Button>\n\n {pageWindow(pagination.page, totalPages).map((entry, index) =>\n entry === \"gap\" ? (\n <span key={`gap-${index}`} className=\"px-1 text-text-subtle\">\n …\n </span>\n ) : (\n <button\n key={entry}\n type=\"button\"\n aria-current={entry === pagination.page ? \"page\" : undefined}\n onClick={() => handlePageChange(entry)}\n className={cn(\n \"h-control-xs min-w-6 cursor-pointer rounded-sm px-1.5 tabular-nums\",\n \"transition-colors duration-fast ease-out\",\n \"focus-visible:outline-none focus-visible:focus-ring\",\n entry === pagination.page\n ? \"bg-surface-sunk font-semibold text-text\"\n : \"text-text-muted hover:bg-surface-hover hover:text-text\",\n )}\n >\n {entry + 1}\n </button>\n ),\n )}\n\n <Button\n size=\"xs\"\n variant=\"ghost\"\n iconOnly\n aria-label=\"Next page\"\n onClick={() => handlePageChange(pagination.page + 1)}\n disabled={pagination.page >= totalPages - 1}\n >\n <Icon icon={ChevronRight} size=\"md\" color=\"current\" />\n </Button>\n </div>\n )}\n </div>\n )}\n </div>\n );\n};\n","import { X } from \"lucide-react\";\nimport type React from \"react\";\nimport { Icon } from \"../content/Icon\";\nimport { cn } from \"../utils/cn\";\n\n/**\n * `color` is deliberately omitted from the inherited HTML attributes. It is a\n * legacy HTML attribute, so `<Badge color=\"success\">` used to type-check while\n * doing nothing at all — the badge silently rendered as `default`. Omitting it\n * turns that mistake into a compile error pointing at `variant`.\n */\nexport interface BadgeProps extends Omit<React.HTMLAttributes<HTMLSpanElement>, \"color\"> {\n variant?: \"default\" | \"primary\" | \"secondary\" | \"success\" | \"warning\" | \"error\" | \"info\" | \"note\";\n\n size?: \"sm\" | \"md\" | \"lg\";\n\n dot?: boolean;\n\n icon?: React.ReactNode;\n\n dismissible?: boolean;\n\n onDismiss?: () => void;\n}\n\n// Semantic, token-driven (mode-aware via CSS custom properties — no dark: overrides needed)\nconst badgeVariants = {\n default: \"bg-surface-hover text-text\",\n primary: \"bg-accent-soft text-accent\",\n secondary: \"bg-surface-hover text-text-muted\",\n success: \"bg-success-soft text-success-fg\",\n warning: \"bg-warning-soft text-warning-fg\",\n error: \"bg-danger-soft text-danger-fg\",\n info: \"bg-info-soft text-info-fg\",\n // Internal-note amber. Never for customer-visible content.\n note: \"bg-note-soft text-note\",\n};\n\nconst badgeSizes = {\n sm: \"px-2 py-0.5 text-xs\",\n md: \"px-2.5 py-1 text-sm\",\n lg: \"px-3 py-1.5 text-base\",\n};\n\nconst dotColors = {\n default: \"bg-surface-hover\",\n primary: \"bg-accent\",\n secondary: \"bg-surface-hover\",\n success: \"bg-success\",\n warning: \"bg-warning\",\n error: \"bg-danger\",\n info: \"bg-info\",\n note: \"bg-note\",\n};\n\n/** Badge for status indicators and labels. */\nexport function Badge({\n variant = \"default\",\n size = \"md\",\n dot = false,\n icon,\n dismissible = false,\n onDismiss,\n className,\n children,\n ...props\n}: BadgeProps) {\n return (\n <span\n className={cn(\n \"inline-flex items-center font-medium rounded-full\",\n badgeVariants[variant],\n badgeSizes[size],\n className,\n )}\n {...props}\n >\n {dot && <span className={cn(\"w-2 h-2 rounded-full mr-1.5\", dotColors[variant])} />}\n\n {icon && <span className={cn(\"flex-shrink-0\", children && \"mr-1\")}>{icon}</span>}\n\n {children}\n\n {dismissible && onDismiss && (\n <button\n onClick={onDismiss}\n className=\"ml-1.5 flex-shrink-0 hover:opacity-70 transition-opacity\"\n aria-label=\"Remove badge\"\n >\n <Icon icon={X} size=\"xs\" color=\"current\" />\n </button>\n )}\n </span>\n );\n}\n","import React from \"react\";\nimport { Badge, type BadgeProps } from \"../feedback/Badge\";\nimport type { TableColumn } from \"./Table\";\n\n/**\n * Column builders for the three things table configs kept getting wrong across\n * apps: a state rendered as prose, a stored code shown raw, and an id shown\n * instead of the name it points at.\n *\n * All keep the *label* in `accessor` and render only the decoration in `cell`,\n * because `Table` searches and sorts on the accessor value — so a search for\n * \"Active\" or a team's name matches what the row actually shows.\n */\n\nexport interface BadgeColumnConfig<T, V = unknown> {\n id: string;\n header: string;\n accessor: (row: T) => V;\n /** The text in the badge — and the text this column is searched on. */\n label: (value: V, row: T) => string;\n /** Defaults to `default`; give states their own tone (success/error/…). */\n tone?: (value: V, row: T) => BadgeProps[\"variant\"];\n /** Return false to render nothing at all for this row. */\n visible?: (value: V, row: T) => boolean;\n width?: string | number;\n sortable?: boolean;\n align?: TableColumn<T>[\"align\"];\n}\n\n/** A value that is a state rather than prose — render it as a pill. */\nexport function badgeColumn<T, V = unknown>(config: BadgeColumnConfig<T, V>): TableColumn<T> {\n const { id, header, accessor, label, tone, visible, width, sortable, align } = config;\n return {\n id,\n header,\n accessor: (row: T) => label(accessor(row), row),\n cell: (_value: unknown, row: T) => {\n const raw = accessor(row);\n if (visible && !visible(raw, row)) return null;\n return (\n <Badge variant={tone ? tone(raw, row) : \"default\"} size=\"sm\">\n {label(raw, row)}\n </Badge>\n );\n },\n width,\n sortable,\n align,\n };\n}\n\nexport interface BooleanBadgeColumnConfig<T> {\n id: string;\n header: string;\n accessor: (row: T) => boolean | undefined;\n onLabel: string;\n offLabel: string;\n /** Defaults to success/secondary — on reads as healthy, off as merely quiet. */\n onTone?: BadgeProps[\"variant\"];\n offTone?: BadgeProps[\"variant\"];\n width?: string | number;\n sortable?: boolean;\n}\n\n/** The on/off case of `badgeColumn`, which is most of them. */\nexport function booleanBadgeColumn<T>(config: BooleanBadgeColumnConfig<T>): TableColumn<T> {\n const {\n id,\n header,\n accessor,\n onLabel,\n offLabel,\n onTone = \"success\",\n offTone = \"secondary\",\n width,\n sortable,\n } = config;\n\n return badgeColumn<T, boolean>({\n id,\n header,\n accessor: (row) => !!accessor(row),\n label: (on) => (on ? onLabel : offLabel),\n tone: (on) => (on ? onTone : offTone),\n width,\n sortable,\n });\n}\n\nexport interface LabelsColumnConfig<T> {\n id: string;\n header: string;\n /** One code, a list of them, or nothing. */\n accessor: (row: T) => string | string[] | undefined | null;\n /**\n * Code to readable text. Return `undefined` to fall back to the code itself,\n * so an unknown value still shows something instead of vanishing.\n */\n label: (code: string) => string | undefined;\n /** Shown when there is nothing at all. Defaults to an em dash. */\n empty?: string;\n separator?: string;\n width?: string | number;\n searchable?: boolean;\n sortable?: boolean;\n}\n\n/**\n * Stored codes rendered as the words the user picked them by. Covers both\n * localising an enum (`interactions:created` → \"Interaction created\") and\n * resolving a foreign key (a `teamId` → that team's name — pass a lookup as\n * `label`), because from the table's side those are the same problem.\n */\nexport function labelsColumn<T>(config: LabelsColumnConfig<T>): TableColumn<T> {\n const {\n id,\n header,\n accessor,\n label,\n empty = \"—\",\n separator = \", \",\n width,\n searchable,\n sortable,\n } = config;\n\n return {\n id,\n header,\n accessor: (row: T) => {\n const raw = accessor(row);\n const codes = raw == null ? [] : Array.isArray(raw) ? raw : [raw];\n const labelled = codes.filter(Boolean).map((code) => label(code) ?? code);\n return labelled.length ? labelled.join(separator) : empty;\n },\n width,\n searchable,\n sortable,\n };\n}\n","import type React from \"react\";\nimport { cn } from \"../utils/cn\";\n\nexport interface TextProps {\n /** Text variant */\n variant?: \"body\" | \"caption\" | \"label\" | \"code\";\n /** Text size */\n size?: \"xs\" | \"sm\" | \"md\" | \"lg\" | \"xl\";\n /** Font weight */\n weight?: \"light\" | \"normal\" | \"medium\" | \"semibold\" | \"bold\";\n /** Text color */\n color?:\n | \"primary\"\n | \"secondary\"\n | \"accent\"\n | \"success\"\n | \"warning\"\n | \"error\"\n | \"info\"\n | \"neutral\"\n | \"current\"\n | \"muted\";\n /** Text alignment */\n align?: \"left\" | \"center\" | \"right\" | \"justify\";\n /** Whether to truncate text with ellipsis */\n truncate?: boolean;\n /** Whether text should be italic */\n italic?: boolean;\n /** Whether text should be underlined */\n underline?: boolean;\n /** Line height */\n lineHeight?: \"tight\" | \"normal\" | \"relaxed\";\n /** HTML element to render */\n as?: \"p\" | \"span\" | \"div\" | \"code\" | \"pre\";\n /** Additional CSS classes */\n className?: string;\n /** Child content */\n children: React.ReactNode;\n}\n\n/**\n * Text — every copy string in the product goes through this component.\n *\n * Size maps onto the token type scale (tokens.css):\n * xs 11 · sm 12 · md 13 (body) · lg 14 (long-form) · xl 16\n * Anything above 16px is a Heading, not Text.\n */\nconst sizeMap: Record<NonNullable<TextProps[\"size\"]>, string> = {\n xs: \"text-xs\",\n sm: \"text-sm\",\n md: \"text-base\",\n lg: \"text-md\",\n xl: \"text-lg\",\n};\n\nconst weightMap: Record<NonNullable<TextProps[\"weight\"]>, string> = {\n light: \"font-normal\",\n normal: \"font-normal\",\n medium: \"font-medium\",\n semibold: \"font-semibold\",\n bold: \"font-bold\",\n};\n\n/** Semantic colour tokens — these flip in dark mode, so no `dark:` variants. */\nconst colorMap: Record<NonNullable<TextProps[\"color\"]>, string> = {\n primary: \"text-text\",\n secondary: \"text-text-muted\",\n accent: \"text-accent\",\n success: \"text-success-fg\",\n warning: \"text-warning-fg\",\n error: \"text-danger-fg\",\n info: \"text-info-fg\",\n neutral: \"text-text\",\n current: \"text-current\",\n muted: \"text-text-subtle\",\n};\n\nconst alignMap: Record<NonNullable<TextProps[\"align\"]>, string> = {\n left: \"text-left\",\n center: \"text-center\",\n right: \"text-right\",\n justify: \"text-justify\",\n};\n\nconst lineHeightMap: Record<NonNullable<TextProps[\"lineHeight\"]>, string> = {\n tight: \"leading-tight\",\n normal: \"leading-normal\",\n relaxed: \"leading-relaxed\",\n};\n\nexport const Text: React.FC<TextProps> = ({\n variant = \"body\",\n size = \"md\",\n weight = \"normal\",\n color = \"current\",\n align = \"left\",\n truncate = false,\n italic = false,\n underline = false,\n lineHeight = \"normal\",\n as,\n className,\n children,\n ...props\n}) => {\n const Tag = (as || getDefaultElement(variant)) as keyof React.JSX.IntrinsicElements;\n\n const isCaption = variant === \"caption\";\n const isLabel = variant === \"label\";\n const isCode = variant === \"code\";\n\n const textClasses = cn(\n \"font-sans\",\n\n // Variant — caption and label pin their own size/weight\n isCaption && \"text-xs text-text-subtle\",\n isLabel && \"text-xs font-semibold uppercase tracking-label text-text-subtle\",\n isCode && \"font-mono text-sm rounded-sm bg-surface-hover px-1 py-0.5\",\n\n // Size — only where the variant does not pin it\n !isCaption && !isLabel && !isCode && sizeMap[size],\n\n // Weight — label owns its weight\n !isLabel && weightMap[weight],\n\n colorMap[color],\n alignMap[align],\n\n // Line height only overrides the body variant\n variant === \"body\" && lineHeightMap[lineHeight],\n\n italic && \"italic\",\n underline && \"underline\",\n truncate && \"truncate\",\n\n className,\n );\n\n return (\n <Tag className={textClasses} {...props}>\n {children}\n </Tag>\n );\n};\n\nfunction getDefaultElement(variant: TextProps[\"variant\"]): string {\n const elementMap: Record<NonNullable<TextProps[\"variant\"]>, string> = {\n body: \"p\",\n caption: \"span\",\n label: \"span\",\n code: \"code\",\n };\n\n return elementMap[variant!] || \"p\";\n}\n","import type React from \"react\";\nimport { useCallback } from \"react\";\nimport { Text } from \"../typography/Text\";\nimport { cn } from \"../utils/cn\";\n\nexport type ViewFieldType =\n | \"text\"\n | \"email\"\n | \"password\"\n | \"number\"\n | \"tel\"\n | \"url\"\n | \"textarea\"\n | \"select\"\n | \"checkbox\"\n | \"radio\"\n | \"date\"\n | \"custom\"\n | \"array\";\n\ntype NestedKeys<T> = {\n [K in keyof T]: T[K] extends object ? `${K & string}.${NestedKeys<T[K]>}` : K & string;\n}[keyof T];\n\nfunction getValueByPath<T>(obj: T, path: string): unknown {\n return path\n .split(\".\")\n .reduce(\n (acc, key) => (acc && typeof acc === \"object\" && key in acc ? (acc as any)[key] : undefined),\n obj,\n );\n}\n\nexport interface ViewGroupItem<T = any> {\n /** Field name (key in data) */\n name: NestedKeys<T>;\n /** Field label */\n label: string;\n /** Field type */\n type: ViewFieldType;\n /** Field width (CSS width value or grid columns) */\n width?: string | number;\n /** Options for select/radio/checkbox types */\n options?: Array<{\n value: string | number;\n label: string;\n }>;\n /** Conditional function to show/hide field */\n conditional?: (data: T) => boolean;\n /** Whether field is hidden */\n hidden?: boolean;\n /** Custom component renderer */\n customComponent?: (props: { value: any }) => React.ReactNode;\n /** Format function for display value */\n format?: (value: any, data: T) => string | React.ReactNode;\n\n /** For 'array' type, defines the fields for each item in the array */\n arrayFields?: ViewGroupItem<any>[];\n}\n\nexport interface ViewGroup<T = any> {\n /** Group identifier */\n id: string;\n /** Group title */\n title: string;\n /** Group description */\n description?: string;\n /** Group items */\n items: ViewGroupItem<T>[];\n /** Conditional function to show/hide group */\n conditional?: (data: T) => boolean;\n /** Group layout */\n layout?: \"grid\" | \"flex\";\n /** Number of columns for grid layout */\n columns?: number;\n /** Additional CSS classes */\n className?: string;\n}\n\nexport interface ViewProps<T = Record<string, any>> {\n /** View groups */\n groups: ViewGroup<T>[];\n /** Data to display */\n data: T;\n /** View size */\n size?: \"sm\" | \"md\" | \"lg\" | \"full\";\n /** Additional CSS classes */\n className?: string;\n}\n\n/**\n * Generic View component with groups and conditional rendering\n * Displays data in a read-only format matching the Form component layout\n */\nexport const View = <T extends Record<string, any>>({\n groups,\n data,\n size = \"md\",\n className,\n}: ViewProps<T>) => {\n const formatDate = (value: any): string => {\n if (!value) return \"-\";\n try {\n const date = new Date(value);\n return date.toLocaleDateString(\"nl-NL\", {\n year: \"numeric\",\n month: \"long\",\n day: \"numeric\",\n });\n } catch {\n return String(value);\n }\n };\n\n const formatCheckbox = (value: any): string => {\n return value ? \"Ja\" : \"Nee\";\n };\n\n const getOptionLabel = (\n value: any,\n options?: Array<{ value: string | number; label: string }>,\n ): string => {\n if (!options) return String(value || \"-\");\n const option = options.find((opt) => opt.value === value);\n return option ? option.label : String(value || \"-\");\n };\n\n const renderField = useCallback(\n (item: ViewGroupItem<T>) => {\n const value = getValueByPath(data, item.name as string);\n\n if (item.type === \"array\") {\n const arrayValue = value as any[] | undefined;\n if (!arrayValue || arrayValue.length === 0) {\n return (\n <Text variant=\"body\" size=\"sm\" className=\"text-text-muted\">\n Geen items\n </Text>\n );\n }\n\n return (\n <div className=\"space-y-3\">\n {arrayValue.map((row, index) => (\n <div key={index} className=\"p-4 border border-border rounded-xl space-y-3\">\n <div className=\"flex items-center justify-between\">\n <Text variant=\"label\" size=\"sm\" weight=\"medium\" className=\"text-text-muted\">\n {item.label} {index + 1}\n </Text>\n </div>\n <div className=\"grid grid-cols-1 sm:grid-cols-2 gap-3\">\n {item.arrayFields?.map((subField) => {\n const subValue = row?.[subField.name];\n let displayValue: React.ReactNode = \"-\";\n\n switch (subField.type) {\n case \"text\":\n case \"email\":\n case \"number\":\n case \"tel\":\n case \"url\":\n case \"textarea\":\n displayValue = subValue || \"-\";\n break;\n case \"select\":\n case \"radio\":\n displayValue = getOptionLabel(subValue, subField.options);\n break;\n case \"checkbox\":\n displayValue = formatCheckbox(subValue);\n break;\n case \"date\":\n displayValue = formatDate(subValue);\n break;\n case \"custom\":\n displayValue = subField.customComponent?.({\n value: subValue,\n });\n break;\n default:\n displayValue = String(subValue || \"-\");\n }\n\n if (subField.format) {\n displayValue = subField.format(subValue, row);\n }\n\n return (\n <div key={subField.name as string} className=\"space-y-1\">\n <Text variant=\"label\" size=\"xs\" className=\"text-text-muted\">\n {subField.label}\n </Text>\n <Text variant=\"body\" size=\"sm\" className=\"text-text\">\n {displayValue}\n </Text>\n </div>\n );\n })}\n </div>\n </div>\n ))}\n </div>\n );\n }\n\n if (item.type === \"custom\" && item.customComponent) {\n return item.customComponent({ value });\n }\n\n if (item.format) {\n const formatted = item.format(value, data);\n return (\n <Text variant=\"body\" size=\"sm\" className=\"text-text\">\n {formatted}\n </Text>\n );\n }\n\n let displayValue: React.ReactNode = \"-\";\n\n switch (item.type) {\n case \"text\":\n case \"email\":\n case \"password\":\n case \"number\":\n case \"tel\":\n case \"url\":\n displayValue = value ? String(value) : \"-\";\n break;\n\n case \"textarea\":\n displayValue = value ? (\n <div className=\"whitespace-pre-wrap\">{String(value || \"-\")}</div>\n ) : (\n \"-\"\n );\n break;\n\n case \"select\":\n case \"radio\":\n displayValue = getOptionLabel(value, item.options);\n break;\n\n case \"checkbox\":\n displayValue = formatCheckbox(value);\n break;\n\n case \"date\":\n displayValue = formatDate(value);\n break;\n\n default:\n displayValue = value ? String(value) : \"-\";\n }\n\n return (\n <Text variant=\"body\" size=\"sm\" className=\"text-text\">\n {displayValue}\n </Text>\n );\n },\n [data],\n );\n\n const renderGroup = useCallback(\n (group: ViewGroup<T>) => {\n if (group.conditional && !group.conditional(data)) {\n return null;\n }\n\n const visibleItems = group.items.filter((item) => {\n if (item.hidden) return false;\n if (item.conditional && !item.conditional(data)) return false;\n return true;\n });\n\n if (visibleItems.length === 0) return null;\n\n const groupClasses = cn(\n \"space-y-4\",\n {\n \"grid gap-4\": group.layout === \"grid\",\n \"flex flex-wrap gap-4\": group.layout === \"flex\",\n },\n group.layout === \"grid\" && {\n \"grid-cols-1\": !group.columns || group.columns === 1,\n \"grid-cols-2\": group.columns === 2,\n \"grid-cols-3\": group.columns === 3,\n \"grid-cols-4\": group.columns === 4,\n },\n group.className,\n );\n\n return (\n <div\n key={group.id}\n className=\"grid grid-cols-1 md:grid-cols-3 gap-8 p-6 border border-border rounded-xl\"\n >\n <div className=\"md:col-span-1\">\n <Text variant=\"label\" size=\"lg\" weight=\"semibold\" className=\"text-text\">\n {group.title}\n </Text>\n {group.description && (\n <Text variant=\"body\" size=\"sm\" className=\"text-text-muted mt-1\">\n {group.description}\n </Text>\n )}\n </div>\n\n <div className=\"md:col-span-2\">\n <div className={groupClasses}>\n {visibleItems.map((item) => {\n return (\n <div\n key={item.name as string}\n className=\"space-y-1\"\n // Inline, not `w-[…]`/`col-span-…`: those are built at runtime,\n // so Tailwind never sees them and `width` silently did nothing.\n // Same fix Form.tsx already carries.\n style={{\n width: typeof item.width === \"string\" ? item.width : undefined,\n gridColumn:\n typeof item.width === \"number\"\n ? `span ${item.width} / span ${item.width}`\n : undefined,\n }}\n >\n {item.type !== \"custom\" && (\n <Text variant=\"label\" size=\"sm\" className=\"font-medium text-text-muted\">\n {item.label}\n </Text>\n )}\n\n {renderField(item)}\n </div>\n );\n })}\n </div>\n </div>\n </div>\n );\n },\n [data, renderField],\n );\n\n const viewClasses = cn(\n \"space-y-6\",\n {\n \"max-w-md\": size === \"sm\",\n \"max-w-2xl\": size === \"md\",\n \"max-w-4xl\": size === \"lg\",\n \"max-w-full\": size === \"full\",\n },\n className,\n );\n\n return (\n <div className={viewClasses}>\n <div className=\"space-y-8\">{groups.map(renderGroup)}</div>\n </div>\n );\n};\n","import type { ErrorInfo, ReactNode } from \"react\";\nimport { Component } from \"react\";\n\ninterface Props {\n children: ReactNode;\n fallback?: ReactNode;\n name?: string;\n}\n\ninterface State {\n hasError: boolean;\n error: Error | null;\n}\n\nexport class ErrorBoundary extends Component<Props, State> {\n public state: State = {\n hasError: false,\n error: null,\n };\n\n public static getDerivedStateFromError(error: Error): State {\n return { hasError: true, error };\n }\n\n public componentDidCatch(error: Error, errorInfo: ErrorInfo) {\n console.error(`[ErrorBoundary] Error in ${this.props.name || \"Component\"}:`, error, errorInfo);\n }\n\n public render() {\n if (this.state.hasError) {\n if (this.props.fallback) {\n return this.props.fallback;\n }\n\n return (\n <div className=\"p-4 m-4 border border-danger-border bg-danger-soft rounded-lg\">\n <h2 className=\"text-lg font-semibold text-danger-fg\">Something went wrong</h2>\n <p className=\"text-sm text-danger-fg mt-1\">\n {this.props.name ? `Error in ${this.props.name}` : \"The component failed to render.\"}\n </p>\n <button\n className=\"mt-4 px-4 py-2 bg-danger text-danger-fg rounded hover:opacity-90 transition-opacity text-sm\"\n onClick={() => this.setState({ hasError: false, error: null })}\n >\n Try again\n </button>\n </div>\n );\n }\n\n return this.props.children;\n }\n}\n","import type React from \"react\";\nimport { useEffect, useRef, useState } from \"react\";\n\ninterface FederatedResourceProps {\n identifier: string;\n resourceLoader: () => Promise<any>;\n framework?: \"react\" | \"svelte\" | \"vanilla\";\n /** Shown while the remote loads. */\n fallback?: React.ReactNode;\n /**\n * Shown when the remote cannot load — app disabled, bundle broken, wrong key.\n * Without it a failure renders nothing, and the spot where something should have\n * been disappears without a trace in the UI.\n */\n errorFallback?: React.ReactNode;\n componentProps?: Record<string, any>;\n className?: string;\n}\n\n/**\n * Remotes already fetched, by identifier. Without this every identifier switch showed\n * the fallback for a frame even when the bundle was in memory — a full-screen flash per\n * click on pages that switch resource while navigating. Module Federation already caches\n * the import itself; this removes the render round, not a network round.\n */\nconst loadedRemotes = new Map<string, any>();\n\nexport function FederatedResource({\n identifier,\n resourceLoader,\n framework = \"react\",\n fallback,\n errorFallback,\n componentProps,\n className,\n}: FederatedResourceProps) {\n const containerRef = useRef<HTMLDivElement>(null);\n const [failedId, setFailedId] = useState<string | null>(null);\n const [, bump] = useState(0);\n\n const svelteInstanceRef = useRef<any>(null);\n\n /**\n * The loader lives in a ref and the effect depends only on `identifier`: callers pass\n * inline arrows, which get a new identity every render — with the loader in the deps\n * the bundle reloaded on every render. `identifier` is the resource's real identity.\n * `componentProps` are unaffected: they spread in the render, so prop changes reach the\n * remote without refetching the bundle (svelte prop updates go via `$set` below).\n */\n const loaderRef = useRef(resourceLoader);\n loaderRef.current = resourceLoader;\n\n useEffect(() => {\n if (loadedRemotes.has(identifier)) return;\n\n let isMounted = true;\n\n const load = async () => {\n try {\n const module = await loaderRef.current();\n if (!isMounted) return;\n loadedRemotes.set(identifier, module);\n bump((n) => n + 1);\n } catch (err) {\n console.error(`[FederatedResource] Load failed: ${identifier}`, err);\n if (isMounted) setFailedId(identifier);\n }\n };\n\n void load();\n return () => {\n isMounted = false;\n };\n }, [identifier]);\n\n /*\n * Everything derives from `identifier` in the render, never from state: effects run\n * after paint, so on a switch there is one frame where the old remote would get the\n * new remote's props — exactly when a page crashes on a prop it never expected.\n */\n const module = loadedRemotes.get(identifier) ?? null;\n // `!module` first: switching away from a failed remote and back retries the load; if\n // that succeeds the stale error must not linger next to the loaded component.\n const failed = !module && failedId === identifier;\n const isLoading = !module && !failed;\n\n const ExternalComponent = module\n ? framework === \"react\"\n ? module.component || module.default || module\n : module\n : null;\n\n useEffect(() => {\n if (!ExternalComponent || framework === \"react\") return;\n\n const target = containerRef.current;\n if (!target) return;\n\n if (framework === \"svelte\") {\n const Component = ExternalComponent.default || ExternalComponent;\n const instance = new Component({ target, props: componentProps || {} });\n svelteInstanceRef.current = instance;\n return () => instance.$destroy();\n }\n\n if (ExternalComponent.mount) {\n const cleanup = ExternalComponent.mount({ container: target, props: componentProps || {} });\n return () => {\n if (typeof cleanup === \"function\") cleanup();\n else ExternalComponent.unmount?.(target);\n };\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [ExternalComponent, framework]);\n\n useEffect(() => {\n if (framework === \"svelte\" && svelteInstanceRef.current) {\n svelteInstanceRef.current.$set?.(componentProps);\n }\n }, [componentProps, framework]);\n\n return (\n <div ref={containerRef} className={className} style={{ display: \"contents\" }}>\n {isLoading && fallback}\n {failed && errorFallback}\n\n {framework === \"react\" && ExternalComponent && <ExternalComponent {...componentProps} />}\n </div>\n );\n}\n","import type React from \"react\";\nimport { cn } from \"../utils/cn\";\n\nexport interface EmptyStateProps extends Omit<React.HTMLAttributes<HTMLDivElement>, \"title\"> {\n /** Leading glyph or icon (e.g. a check for the focus \"queue empty\" state). */\n icon?: React.ReactNode;\n /** Tint of the icon disc. */\n tone?: \"neutral\" | \"success\";\n /** Headline. */\n title: React.ReactNode;\n /** Supporting copy. */\n description?: React.ReactNode;\n /** Action buttons row. */\n actions?: React.ReactNode;\n /** Render inside a raised card (focus \"done\" state) vs. plain centered block. */\n card?: boolean;\n}\n\nconst toneDisc: Record<NonNullable<EmptyStateProps[\"tone\"]>, string> = {\n neutral: \"bg-surface-hover text-text-muted\",\n success: \"bg-success-soft text-success-fg\",\n};\n\n/** Centered empty / completion state (focus queue done, no-results lists). */\nexport function EmptyState({\n icon,\n tone = \"neutral\",\n title,\n description,\n actions,\n card = false,\n className,\n ...props\n}: EmptyStateProps) {\n return (\n <div\n className={cn(\n \"flex flex-col items-center gap-2.5 text-center\",\n card && \"rounded-lg border border-border bg-surface px-11 py-9 shadow-overlay\",\n className,\n )}\n {...props}\n >\n {icon != null && (\n <span\n className={cn(\n \"flex size-disc items-center justify-center rounded-full text-2xl\",\n toneDisc[tone],\n )}\n >\n {icon}\n </span>\n )}\n <div className=\"text-lg font-semibold text-text\">{title}</div>\n {description && (\n <div className=\"max-w-sm text-sm leading-relaxed text-text-muted\">{description}</div>\n )}\n {actions && <div className=\"flex gap-2 pt-1.5\">{actions}</div>}\n </div>\n );\n}\n","import type React from \"react\";\nimport { cn } from \"../utils/cn\";\n\nexport interface KbdProps extends React.HTMLAttributes<HTMLElement> {\n /** Key label, e.g. \"⌘K\", \"E\", \"↵\". */\n children: React.ReactNode;\n}\n\n/** Keyboard-shortcut chip (the `⌘K` / `E` / `↵` hints). */\nexport function Kbd({ children, className, ...props }: KbdProps) {\n return (\n <kbd\n className={cn(\n \"inline-flex items-center rounded border border-border px-1.5 py-px text-xs font-medium leading-none text-text-muted\",\n className,\n )}\n {...props}\n >\n {children}\n </kbd>\n );\n}\n","import type React from \"react\";\nimport { cn } from \"../utils/cn\";\n\nexport interface ProgressBarProps extends React.HTMLAttributes<HTMLDivElement> {\n /** Completion 0–100 (clamped). */\n value: number;\n /** Fill tone. */\n variant?: \"accent\" | \"success\";\n /** Track height. */\n size?: \"sm\" | \"md\";\n}\n\nconst trackSizes: Record<NonNullable<ProgressBarProps[\"size\"]>, string> = {\n sm: \"h-1\",\n md: \"h-1.5\",\n};\n\nconst fillVariants: Record<NonNullable<ProgressBarProps[\"variant\"]>, string> = {\n accent: \"bg-accent\",\n success: \"bg-success\",\n};\n\n/**\n * Slim determinate progress bar (focus queue progress, subtask completion).\n * Token-driven and mode-aware.\n */\nexport function ProgressBar({\n value,\n variant = \"accent\",\n size = \"md\",\n className,\n ...props\n}: ProgressBarProps) {\n const pct = Math.max(0, Math.min(100, value));\n return (\n <div\n role=\"progressbar\"\n aria-valuenow={Math.round(pct)}\n aria-valuemin={0}\n aria-valuemax={100}\n className={cn(\n \"w-full overflow-hidden rounded-full bg-surface-hover\",\n trackSizes[size],\n className,\n )}\n {...props}\n >\n <div\n className={cn(\n \"h-full rounded-full transition-[width] duration-300 ease-out\",\n fillVariants[variant],\n )}\n style={{ width: `${pct}%` }}\n />\n </div>\n );\n}\n","import type React from \"react\";\nimport { cn } from \"../utils/cn\";\n\nexport interface SpinnerProps extends React.HTMLAttributes<HTMLDivElement> {\n size?: \"xs\" | \"sm\" | \"md\" | \"lg\" | \"xl\";\n\n variant?: \"primary\" | \"secondary\" | \"white\" | \"current\";\n\n /** Accessible label; rendered visually only with `showLabel`. */\n label?: string;\n\n showLabel?: boolean;\n}\n\nconst spinnerSizes = {\n xs: \"h-3 w-3\",\n sm: \"h-4 w-4\",\n md: \"h-6 w-6\",\n lg: \"h-8 w-8\",\n xl: \"h-12 w-12\",\n};\n\nconst spinnerColors = {\n primary: \"text-accent\",\n secondary: \"text-text-muted\",\n white: \"text-white\",\n current: \"text-current\",\n};\n\nconst labelSizes = {\n xs: \"text-xs\",\n sm: \"text-sm\",\n md: \"text-sm\",\n lg: \"text-base\",\n xl: \"text-lg\",\n};\n\n/** Spinner for loading states. */\nexport function Spinner({\n size = \"md\",\n variant = \"primary\",\n label = \"Loading...\",\n showLabel = false,\n className,\n ...props\n}: SpinnerProps) {\n return (\n <div\n className={cn(\"inline-flex items-center\", showLabel ? \"flex-col space-y-2\" : \"\", className)}\n {...props}\n >\n <svg\n className={cn(\"animate-spin\", spinnerSizes[size], spinnerColors[variant])}\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n aria-hidden={!showLabel}\n role={showLabel ? \"status\" : undefined}\n >\n <circle\n className=\"opacity-25\"\n cx=\"12\"\n cy=\"12\"\n r=\"10\"\n stroke=\"currentColor\"\n strokeWidth=\"4\"\n />\n <path\n className=\"opacity-75\"\n fill=\"currentColor\"\n d=\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z\"\n />\n </svg>\n\n {showLabel && <span className={cn(\"text-text-muted\", labelSizes[size])}>{label}</span>}\n\n {!showLabel && <span className=\"sr-only\">{label}</span>}\n </div>\n );\n}\n","import React from \"react\";\nimport { cn } from \"../utils/cn\";\n\nexport interface StatusDotProps {\n /**\n * Meaning, not colour. `neutral` is the off/idle state; `accent` marks\n * unread; the rest follow the status tokens.\n */\n tone?:\n | \"neutral\"\n | \"accent\"\n | \"success\"\n | \"warning\"\n | \"danger\"\n | \"info\"\n | \"note\"\n /** Identity without status — an inbox, a speaker. See --color-cat-*. */\n | \"cat-1\"\n | \"cat-2\"\n | \"cat-3\"\n | \"cat-4\"\n | \"cat-5\";\n /** Square instead of round — the marker style used for inbox identity. */\n shape?: \"round\" | \"square\";\n /** Slow pulse for something live: an active call, a running recording. */\n pulse?: boolean;\n /**\n * Accessible name. Without one the dot is decorative and hidden from\n * assistive tech, which is right when adjacent text already says the state.\n */\n label?: string;\n className?: string;\n}\n\nconst tones: Record<NonNullable<StatusDotProps[\"tone\"]>, string> = {\n neutral: \"bg-text-disabled\",\n accent: \"bg-accent\",\n success: \"bg-success\",\n warning: \"bg-warning\",\n danger: \"bg-danger\",\n info: \"bg-info\",\n note: \"bg-note\",\n \"cat-1\": \"bg-cat-1\",\n \"cat-2\": \"bg-cat-2\",\n \"cat-3\": \"bg-cat-3\",\n \"cat-4\": \"bg-cat-4\",\n \"cat-5\": \"bg-cat-5\",\n};\n\n/** Small state marker: presence, session state, unread, live recording. */\nexport function StatusDot({\n tone = \"neutral\",\n shape = \"round\",\n pulse = false,\n label,\n className,\n}: StatusDotProps) {\n return (\n <span\n role={label ? \"img\" : undefined}\n aria-label={label}\n aria-hidden={label ? undefined : true}\n className={cn(\n \"inline-block size-dot shrink-0\",\n shape === \"round\" ? \"rounded-full\" : \"rounded-sm\",\n tones[tone],\n pulse && \"animate-pulse\",\n className,\n )}\n />\n );\n}\n","import React from \"react\";\nimport { cn } from \"../utils/cn\";\n\nexport interface StepIndicatorProps {\n /** Step ids in order. Only the position matters; the ids identify `current`. */\n steps: string[];\n /** Id of the step being shown. Unknown ids render as \"not started\". */\n current: string;\n /**\n * `numbered` draws counted circles joined by a rule — use it when the user\n * needs to know how many steps are left. `dots` is the quieter bar, for a\n * flow whose heading already says where you are.\n */\n variant?: \"numbered\" | \"dots\";\n /**\n * Accessible name. Without one the indicator is treated as decorative, which\n * is right when a heading beside it already announces the current step.\n */\n label?: string;\n className?: string;\n}\n\n/** Progress through a short, linear flow. */\nexport function StepIndicator({\n steps,\n current,\n variant = \"numbered\",\n label,\n className,\n}: StepIndicatorProps) {\n const activeIndex = steps.indexOf(current);\n const shared = {\n role: label ? \"group\" : undefined,\n \"aria-label\": label,\n \"aria-hidden\": label ? undefined : true,\n };\n\n if (variant === \"dots\") {\n return (\n <div {...shared} className={cn(\"flex items-center gap-1.5\", className)}>\n {steps.map((step, i) => (\n <span\n key={step}\n className={cn(\n \"h-step-bar rounded-full transition-all duration-fast ease-out\",\n i === activeIndex ? \"w-5 bg-accent\" : \"w-3.5\",\n i < activeIndex && \"bg-border-strong\",\n i > activeIndex && \"bg-border\",\n )}\n />\n ))}\n </div>\n );\n }\n\n return (\n <div {...shared} className={cn(\"flex items-center gap-2\", className)}>\n {steps.map((step, i) => (\n <div key={step} className=\"flex items-center gap-2\">\n <span\n aria-current={i === activeIndex ? \"step\" : undefined}\n className={cn(\n \"flex size-tile-sm items-center justify-center rounded-full text-xs font-medium\",\n i === activeIndex && \"bg-accent text-accent-fg\",\n i < activeIndex && \"bg-accent-soft text-accent\",\n i > activeIndex && \"bg-surface-hover text-text-muted\",\n )}\n >\n {i + 1}\n </span>\n {i < steps.length - 1 && <span className=\"h-px w-8 bg-border-strong\" />}\n </div>\n ))}\n </div>\n );\n}\n","/**\n * Keeping password managers out of fields that are not credentials.\n *\n * The problem was not that autofill was switched on — it was that most fields\n * asked for it by accident. A form field passed things like `\"sip-password\"`,\n * `\"api-key\"`, `\"record-pin\"` or `\"system-prompt\"` as its autocomplete value.\n * None of those are autocomplete tokens, and the HTML spec says an unrecognised\n * token behaves as `on`. So the fields most likely to be mistaken for a login\n * were the ones most loudly inviting Bitwarden, LastPass and Dashlane to fill\n * them in — and to offer to save them.\n *\n * `autocomplete=\"off\"` alone does not settle it: the major managers ignore it on\n * purpose, because sites used to abuse it. Each honours its own opt-out\n * attribute instead, so a field that means it has to say so in four dialects.\n */\n\n/**\n * The autocomplete tokens we actually want honoured. Anything outside this set\n * is treated as \"someone wrote a note to themselves in this field\", not as a\n * browser instruction.\n */\nconst REAL_TOKENS = new Set([\n \"name\",\n \"given-name\",\n \"family-name\",\n \"additional-name\",\n \"nickname\",\n \"honorific-prefix\",\n \"honorific-suffix\",\n \"email\",\n \"username\",\n \"organization\",\n \"organization-title\",\n \"tel\",\n \"tel-country-code\",\n \"tel-national\",\n \"tel-extension\",\n \"url\",\n \"photo\",\n \"language\",\n \"bday\",\n \"sex\",\n \"street-address\",\n \"address-line1\",\n \"address-line2\",\n \"address-line3\",\n \"address-level1\",\n \"address-level2\",\n \"country\",\n \"country-name\",\n \"postal-code\",\n \"current-password\",\n \"new-password\",\n \"one-time-code\",\n]);\n\nexport interface AutofillProps {\n autoComplete: string;\n \"data-1p-ignore\"?: string;\n \"data-lpignore\"?: string;\n \"data-bwignore\"?: string;\n \"data-form-type\"?: string;\n}\n\n/**\n * Props that switch a field's autofill behaviour on or off deliberately.\n *\n * Pass the field's declared autocomplete value; a genuine token is honoured, and\n * anything else — including nothing at all — turns autofill off and tells each\n * manager so in the attribute it listens to.\n */\nexport function autofillProps(token?: string): AutofillProps {\n if (token && REAL_TOKENS.has(token)) return { autoComplete: token };\n\n return {\n autoComplete: \"off\",\n // 1Password\n \"data-1p-ignore\": \"true\",\n // LastPass\n \"data-lpignore\": \"true\",\n // Bitwarden\n \"data-bwignore\": \"true\",\n // Dashlane — \"other\" means \"not a login field\".\n \"data-form-type\": \"other\",\n };\n}\n","/**\n * Coerce an unknown value to text.\n *\n * Every input component in this kit declares its `value` as `string`, and every\n * one of them is fed straight out of form data, which is `any`. A field whose\n * entity type is numeric therefore arrives as a number — `eylo-voip`'s time\n * rules do exactly that (`interval?: number`, `days?: number[]`) — and the two\n * failure modes are both silent from the type system's side:\n *\n * - a hard `TypeError: (…).trim is not a function` the moment anything calls a\n * string method on it;\n * - a comparison that simply never matches, because `1 === \"1\"` is false, so a\n * stored option renders as unselected with no error at all.\n *\n * `null` and `undefined` become `\"\"` so callers can treat \"absent\" and \"empty\"\n * alike, which is what a text input does anyway.\n */\nexport const text = (value: unknown): string => (value == null ? \"\" : String(value));\n\n/** {@link text} folded to a comparable key: trimmed and lower-cased. */\nexport const normalizeText = (value: unknown): string => text(value).trim().toLowerCase();\n","import { ChevronDownIcon } from \"lucide-react\";\nimport React, { forwardRef } from \"react\";\nimport { cn } from \"../utils/cn\";\nimport { normalizeText, text } from \"../utils/text\";\nimport { useAnchoredPosition } from \"../utils/useAnchoredPosition\";\n\nexport interface SelectOption {\n value: string;\n label: string;\n disabled?: boolean;\n}\n\nexport interface SelectProps extends Omit<React.HTMLAttributes<HTMLDivElement>, \"onChange\"> {\n value?: string | string[];\n onChange?: (value: string | string[]) => void;\n label?: string;\n helperText?: string;\n error?: string;\n size?: \"sm\" | \"md\" | \"lg\" | \"full\";\n fullWidth?: boolean;\n /**\n * Blocks opening the menu. The trigger is a button inside the container, so\n * this has to be forwarded explicitly — spreading it with the rest of the\n * props lands it on the wrapping div, where it does nothing.\n */\n disabled?: boolean;\n options: SelectOption[];\n placeholder?: string;\n containerClassName?: string;\n labelClassName?: string;\n searchable?: boolean;\n multiple?: boolean;\n\n /** Allow free-text entry (Enter, or clicking the create row). */\n allowCreate?: boolean;\n\n /** Optionally shape the option created from free-text input. */\n onCreateOption?: (label: string) => SelectOption;\n}\n\nconst selectSizes = {\n sm: \"h-control-sm px-3 text-sm\",\n md: \"h-control-md px-3 text-sm\",\n lg: \"h-control-lg px-3 text-base\",\n full: \"h-control-md w-full px-3 text-sm\",\n};\n\n/**\n * The props below say `string`, but this component is fed straight out of form\n * data, which is `any`. See {@link text} for why that has to be coerced here.\n */\nconst norm = normalizeText;\n\nexport const Select = forwardRef<HTMLDivElement, SelectProps>(\n (\n {\n label,\n helperText,\n error,\n size = \"md\",\n fullWidth = false,\n disabled = false,\n options,\n placeholder,\n containerClassName,\n labelClassName,\n className,\n id,\n searchable,\n multiple,\n value,\n onChange,\n allowCreate,\n onCreateOption,\n ...props\n },\n ref,\n ) => {\n const selectId = id || `select-${Math.random().toString(36).substr(2, 9)}`;\n const hasError = Boolean(error);\n const [isOpen, setIsOpen] = React.useState(false);\n const [searchTerm, setSearchTerm] = React.useState(\"\");\n const containerRef = React.useRef<HTMLDivElement>(null);\n // Anchored to the control itself, not the container — the container also\n // holds the label, which would push the panel down by its height.\n const triggerRef = React.useRef<HTMLButtonElement>(null);\n // Fixed, not absolute: an absolute panel is clipped by the form panel's\n // overflow and runs off-screen when the field sits near the bottom.\n const panelStyle = useAnchoredPosition(isOpen, triggerRef, { matchWidth: true });\n\n // Local copy of the options so free-text values can be mixed in.\n const [localOptions, setLocalOptions] = React.useState<SelectOption[]>(options);\n\n React.useImperativeHandle(ref, () => containerRef.current!);\n\n React.useEffect(() => {\n const handleClickOutside = (event: MouseEvent) => {\n if (containerRef.current && !containerRef.current.contains(event.target as Node)) {\n setIsOpen(false);\n }\n };\n document.addEventListener(\"mousedown\", handleClickOutside);\n return () => document.removeEventListener(\"mousedown\", handleClickOutside);\n }, []);\n\n // Matches on either value or label.\n const includesOption = React.useCallback(\n (opts: SelectOption[], needle: string) =>\n opts.some((o) => norm(o.value) === norm(needle) || norm(o.label) === norm(needle)),\n [],\n );\n\n const ensureValuesInOptions = React.useCallback(\n (baseOptions: SelectOption[], currentValue?: unknown) => {\n const out = [...baseOptions];\n\n const addIfMissing = (v?: unknown) => {\n const val = text(v).trim();\n if (!val) return;\n if (!includesOption(out, val)) {\n out.push({ value: val, label: val });\n }\n };\n\n if (Array.isArray(currentValue)) {\n currentValue.forEach(addIfMissing);\n } else {\n addIfMissing(currentValue);\n }\n\n return out;\n },\n [includesOption],\n );\n\n // On an options change: merge, keeping locally created options and the current value(s).\n React.useEffect(() => {\n setLocalOptions((prev) => {\n const next = [...options];\n\n prev.forEach((opt) => {\n if (!includesOption(next, opt.value) && !includesOption(next, opt.label)) {\n next.push(opt);\n }\n });\n\n return ensureValuesInOptions(next, value);\n });\n }, [options, value, ensureValuesInOptions, includesOption]);\n\n /**\n * The incoming value as text, so every comparison below is string-to-string.\n * Option values are strings by contract; the value comes from form data and\n * may not be — see {@link text}.\n */\n const selected = React.useMemo(\n () => (Array.isArray(value) ? value.map(text) : value == null ? undefined : text(value)),\n [value],\n );\n\n const handleSelect = (optionValue: string) => {\n if (multiple) {\n const currentValues = Array.isArray(selected) ? selected : [];\n const newValues = currentValues.includes(optionValue)\n ? currentValues.filter((v) => v !== optionValue)\n : [...currentValues, optionValue];\n onChange?.(newValues);\n } else {\n onChange?.(optionValue);\n setIsOpen(false);\n setSearchTerm(\"\");\n }\n };\n\n const selectedOption = multiple ? null : localOptions.find((o) => o.value === selected);\n\n const selectedOptions = multiple\n ? localOptions.filter((o) => Array.isArray(selected) && selected.includes(o.value))\n : [];\n\n const filteredOptions =\n searchable && searchTerm\n ? localOptions.filter((option) =>\n option.label.toLowerCase().includes(searchTerm.toLowerCase()),\n )\n : localOptions;\n\n const canCreate =\n Boolean(allowCreate) &&\n Boolean(searchable) &&\n Boolean(searchTerm.trim()) &&\n !includesOption(localOptions, searchTerm);\n\n const createOption = (labelToCreate: string) => {\n const clean = labelToCreate.trim();\n if (!clean) return;\n\n const newOption: SelectOption = onCreateOption?.(clean) ?? {\n value: clean,\n label: clean,\n };\n\n if (\n includesOption(localOptions, newOption.value) ||\n includesOption(localOptions, newOption.label)\n ) {\n // Already present; just select it.\n handleSelect(newOption.value);\n setSearchTerm(\"\");\n if (!multiple) setIsOpen(false);\n return;\n }\n\n setLocalOptions((prev) => [...prev, newOption]);\n handleSelect(newOption.value);\n\n setSearchTerm(\"\");\n if (!multiple) setIsOpen(false);\n };\n\n const getDisplayValue = () => {\n if (multiple) {\n if (selectedOptions.length > 0) {\n return selectedOptions.map((o) => o.label).join(\", \");\n }\n return placeholder || \"Select options\";\n }\n return selectedOption?.label || placeholder || \"Select an option\";\n };\n\n return (\n <div\n ref={containerRef}\n className={cn(\"relative flex flex-col\", fullWidth && \"w-full\", containerClassName)}\n {...props}\n >\n {label && (\n <label\n htmlFor={selectId}\n onClick={() => setIsOpen(!isOpen)}\n className={cn(\n \"block text-xs font-semibold uppercase tracking-label mb-1.5\",\n hasError ? \"text-danger-fg\" : \"text-text-muted\",\n labelClassName,\n )}\n >\n {label}\n </label>\n )}\n\n <div className=\"relative\">\n <button\n ref={triggerRef}\n type=\"button\"\n id={selectId}\n disabled={disabled}\n onClick={() => setIsOpen(!isOpen)}\n className={cn(\n \"flex w-full items-center justify-between gap-2 rounded-tile border transition-colors duration-fast text-left\",\n \"focus-visible:outline-none focus-visible:focus-ring\",\n \"disabled:cursor-not-allowed disabled:bg-surface-hover disabled:text-text-disabled\",\n \"bg-surface\",\n selectSizes[size],\n hasError\n ? \"border-danger-border text-danger-fg focus-visible:focus-ring\"\n : \"border-border-strong text-text\",\n className,\n )}\n >\n <span className=\"truncate flex-1 min-w-0\">{getDisplayValue()}</span>\n <ChevronDownIcon\n size={16}\n className={cn(\n \"shrink-0 opacity-60 transition-transform duration-fast\",\n isOpen && \"rotate-180\",\n )}\n />\n </button>\n\n {isOpen && (\n <div\n style={panelStyle}\n className=\"fixed z-overlay flex flex-col overflow-hidden rounded-lg border border-border bg-surface shadow-overlay\"\n >\n {searchable && (\n <div className=\"p-2\">\n <input\n type=\"text\"\n placeholder=\"Search...\"\n value={searchTerm}\n onChange={(e) => setSearchTerm(e.target.value)}\n onKeyDown={(e) => {\n if (e.key === \"Enter\" && canCreate) {\n e.preventDefault();\n createOption(searchTerm);\n }\n }}\n className={cn(\n \"w-full px-3 py-2 text-sm rounded-md border\",\n \"text-text border-border focus-visible:outline-none focus-visible:focus-ring\",\n )}\n />\n </div>\n )}\n\n {canCreate && (\n <div\n className={cn(\n \"cursor-pointer px-3 py-1.5 text-sm\",\n \"text-info-fg hover:bg-surface-hover\",\n )}\n onClick={() => createOption(searchTerm)}\n >\n +: “{searchTerm.trim()}”\n </div>\n )}\n\n <ul className=\"min-h-0 flex-1 overflow-auto py-1\">\n {placeholder && !multiple && (\n <li\n className=\"cursor-pointer px-3 py-1.5 text-sm text-text-muted hover:bg-surface-hover\"\n onClick={() => {\n onChange?.(\"\");\n setIsOpen(false);\n }}\n >\n {placeholder}\n </li>\n )}\n {filteredOptions.map((option) => {\n const isSelected = multiple\n ? Array.isArray(selected) && selected.includes(option.value)\n : selected === option.value;\n return (\n <li\n key={option.value}\n onClick={() => !option.disabled && handleSelect(option.value)}\n className={cn(\n \"cursor-pointer px-3 py-1.5 text-sm\",\n \"text-text\",\n option.disabled\n ? \"opacity-50 cursor-not-allowed\"\n : \"hover:bg-surface-hover\",\n isSelected && \"bg-accent-soft text-accent font-semibold\",\n )}\n >\n <div className=\"flex items-center\">\n {multiple && (\n <input\n type=\"checkbox\"\n checked={isSelected}\n readOnly\n className=\"mr-3 h-4 w-4 rounded border-border [&:not(:checked)]:bg-surface-input text-accent \"\n />\n )}\n <span>{option.label}</span>\n </div>\n </li>\n );\n })}\n </ul>\n </div>\n )}\n </div>\n\n {(error || helperText) && (\n <p className={cn(\"mt-1 text-xs\", hasError ? \"text-danger-fg\" : \"text-text-muted\")}>\n {error || helperText}\n </p>\n )}\n </div>\n );\n },\n);\n\nSelect.displayName = \"Select\";\n","import React, { forwardRef } from \"react\";\nimport { cn } from \"../utils/cn\";\n\nexport interface TextFieldProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, \"size\"> {\n label?: string;\n\n helperText?: string;\n\n error?: string;\n\n size?: \"sm\" | \"md\" | \"lg\" | \"full\";\n\n fullWidth?: boolean;\n\n startIcon?: React.ReactNode;\n\n endIcon?: React.ReactNode;\n\n loading?: boolean;\n\n /** Additional class name for the container */\n containerClassName?: string;\n\n /** Additional class name for the label */\n labelClassName?: string;\n}\n\n/** Heights come from the control tokens — inputs, buttons and selects align. */\nconst inputSizes = {\n sm: \"h-control-sm px-3 text-sm\",\n md: \"h-control-md px-3 text-base\",\n lg: \"h-control-lg px-3 text-base\",\n full: \"h-control-md px-3 text-base\",\n};\n\n/**\n * Shared with TextArea / Select / DatePicker.\n *\n * Sentence case, not the uppercase small-caps it used to be: a form is a page\n * now, and every field label shouting in caps competes with the section titles\n * beside them. Uppercase is still the section-label treatment (`tracking-label`),\n * one level up.\n */\nexport const fieldLabelClasses = \"mb-1.5 block text-sm font-medium\";\n\n/**\n * Shared field frame. A resting 1px edge in `border-strong` — a form field has\n * to read as an empty box you can type in, which the lighter `border` used for\n * structural rules does not do at this size.\n */\nexport const fieldFrameClasses =\n \"block w-full rounded-tile border bg-surface-input text-text \" +\n \"transition-colors duration-fast ease-out \" +\n \"placeholder:text-text-subtle \" +\n \"focus-visible:outline-none focus-visible:focus-ring \" +\n \"disabled:cursor-not-allowed disabled:bg-surface-hover disabled:text-text-disabled\";\n\n/** Resting / error edge, shared so every control agrees on what \"invalid\" looks like. */\nexport const fieldEdgeClasses = (hasError: boolean) =>\n hasError ? \"border-danger-border bg-danger-soft\" : \"border-border-strong\";\n\n/** Themed text input; shares its frame and label classes with TextArea, Select and DatePicker. */\nexport const TextField = forwardRef<HTMLInputElement, TextFieldProps>(\n (\n {\n label,\n helperText,\n error,\n size = \"md\",\n fullWidth = false,\n startIcon,\n endIcon,\n loading = false,\n containerClassName,\n labelClassName,\n className,\n id,\n ...props\n },\n ref,\n ) => {\n const reactId = React.useId();\n const inputId = id || `textfield-${reactId}`;\n const hasError = Boolean(error);\n const describedBy = error || helperText ? `${inputId}-description` : undefined;\n\n return (\n <div className={cn(\"flex flex-col\", fullWidth && \"w-full\", containerClassName)}>\n {label && (\n <label\n htmlFor={inputId}\n className={cn(\n fieldLabelClasses,\n hasError ? \"text-danger-fg\" : \"text-text-muted\",\n labelClassName,\n )}\n >\n {label}\n </label>\n )}\n\n <div className=\"relative\">\n {startIcon && (\n <span className=\"pointer-events-none absolute inset-y-0 left-0 flex items-center pl-3 text-text-muted\">\n {startIcon}\n </span>\n )}\n\n <input\n ref={ref}\n id={inputId}\n aria-invalid={hasError || undefined}\n aria-describedby={describedBy}\n className={cn(\n fieldFrameClasses,\n\n inputSizes[size],\n\n startIcon && \"pl-9\",\n (endIcon || loading) && \"pr-9\",\n\n fieldEdgeClasses(hasError),\n\n className,\n )}\n {...props}\n />\n\n {(endIcon || loading) && (\n <span className=\"absolute inset-y-0 right-0 flex items-center pr-3 text-text-muted\">\n {loading ? (\n <span\n aria-hidden\n className=\"size-icon-md animate-spin rounded-full border-2 border-border border-t-transparent\"\n />\n ) : (\n endIcon\n )}\n </span>\n )}\n </div>\n\n {(error || helperText) && (\n <p\n id={describedBy}\n className={cn(\"mt-1 text-xs\", hasError ? \"text-danger-fg\" : \"text-text-subtle\")}\n >\n {error || helperText}\n </p>\n )}\n </div>\n );\n },\n);\n\nTextField.displayName = \"TextField\";\n","import type { VirtualMount } from \"@opencxh/domain\";\nimport { useEffect, useState } from \"react\";\nimport { Select } from \"./Select\";\nimport { TextField } from \"./TextField\";\n\nexport interface FolderDestination {\n /** Storage folder (mount) id. */\n folderId?: string;\n /** Optional sub-path template, e.g. \"{yyyy}/{mm}/{dd}\". */\n pathTemplate?: string;\n}\n\nexport interface FolderSelectProps {\n value?: FolderDestination;\n onChange?: (value: FolderDestination) => void;\n /** Loads the selectable storage folders (the shell wires this to storage.mount). */\n onListFolders?: () => Promise<{ data: VirtualMount[] }> | undefined;\n disabled?: boolean;\n size?: \"sm\" | \"md\" | \"lg\" | \"full\";\n placeholder?: string;\n templatePlaceholder?: string;\n /** Show the sub-path template input (default true). */\n showTemplate?: boolean;\n}\n\n/**\n * Destination picker for a storage folder (+ optional sub-path template).\n * Unlike StorageInput (which picks/uploads a file), this selects *where* things\n * should be written. Used by the `type: \"folder\"` form field.\n */\nexport function FolderSelect({\n value,\n onChange,\n onListFolders,\n disabled,\n size = \"md\",\n placeholder,\n templatePlaceholder,\n showTemplate = true,\n}: FolderSelectProps) {\n const [folders, setFolders] = useState<VirtualMount[]>([]);\n\n useEffect(() => {\n let active = true;\n Promise.resolve(onListFolders?.())\n .then((res) => {\n if (active && res) setFolders(res.data ?? []);\n })\n .catch(() => {\n /* folder list unavailable */\n });\n return () => {\n active = false;\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n const v = value ?? {};\n\n return (\n <div className=\"flex flex-col gap-2\">\n <Select\n size={size}\n fullWidth\n value={v.folderId ?? \"\"}\n options={folders.map((f) => ({ value: f.id, label: f.name }))}\n placeholder={placeholder ?? \"Select a folder\"}\n onChange={(val) => onChange?.({ ...v, folderId: val as string })}\n />\n {showTemplate && (\n <TextField\n size={size}\n disabled={disabled ?? false}\n value={v.pathTemplate ?? \"\"}\n placeholder={templatePlaceholder ?? \"Sub-path e.g. {yyyy}/{mm}/{dd} (optional)\"}\n onChange={(e) => onChange?.({ ...v, pathTemplate: e.target.value })}\n />\n )}\n </div>\n );\n}\n","import type React from \"react\";\nimport { createContext, useContext, useState } from \"react\";\nimport { cn } from \"../utils/cn\";\n\nexport interface TabItem {\n /** Tab identifier */\n id: string;\n /** Tab label */\n label: string;\n /** Tab content */\n content?: React.ReactNode;\n /** Whether tab is disabled */\n disabled?: boolean;\n /** Badge/count to show next to label */\n badge?: string | number;\n}\n\nexport interface TabsProps {\n /** Tab items */\n items: TabItem[];\n /** Default active tab */\n defaultTab?: string;\n /** Active tab (controlled) */\n activeTab?: string;\n /** Tab change handler */\n onTabChange?: (tabId: string) => void;\n /** Tabs variant */\n variant?: \"default\" | \"pills\" | \"underline\";\n /** Tabs size */\n size?: \"sm\" | \"md\" | \"lg\";\n /** Additional CSS classes */\n className?: string;\n}\n\nexport interface TabBarProps {\n /** Tab items (simplified for bar style) */\n items: Array<{\n id: string;\n label: string;\n badge?: string | number;\n disabled?: boolean;\n }>;\n /** Active tab */\n activeTab?: string;\n /** Tab change handler */\n onTabChange?: (tabId: string) => void;\n /** Additional CSS classes */\n className?: string;\n}\n\nconst TabsContext = createContext<{\n activeTab: string;\n setActiveTab: (tab: string) => void;\n} | null>(null);\n\n/**\n * TabBar component for simple tab navigation (like in the screenshot)\n */\nexport const TabBar: React.FC<TabBarProps> = ({ items, activeTab, onTabChange, className }) => {\n const [internalActiveTab, setInternalActiveTab] = useState(items[0]?.id || \"\");\n const currentActiveTab = activeTab || internalActiveTab;\n\n const handleTabClick = (tabId: string) => {\n if (onTabChange) {\n onTabChange(tabId);\n } else {\n setInternalActiveTab(tabId);\n }\n };\n\n return (\n <div className={cn(\"border-b border-border\", className)}>\n <nav className=\"-mb-px flex space-x-8\">\n {items.map((item) => (\n <button\n key={item.id}\n onClick={() => !item.disabled && handleTabClick(item.id)}\n disabled={item.disabled}\n className={cn(\n \"border-b-2 py-2 px-1 text-sm font-medium transition-colors duration-fast\",\n {\n \"border-accent-border text-text\": currentActiveTab === item.id,\n \"border-transparent text-text-muted hover:text-text\":\n currentActiveTab !== item.id && !item.disabled,\n \"border-transparent text-text-disabled cursor-not-allowed\": item.disabled,\n },\n )}\n >\n <span className=\"flex items-center gap-2\">\n {item.label}\n {item.badge && (\n <span\n className={cn(\n \"inline-flex items-center px-2 py-0.5 rounded-full text-xs font-medium\",\n currentActiveTab === item.id\n ? \"bg-accent-soft text-accent\"\n : \"bg-surface-hover text-text-muted\",\n )}\n >\n {item.badge}\n </span>\n )}\n </span>\n </button>\n ))}\n </nav>\n </div>\n );\n};\n\n/**\n * Full Tabs component with content panels\n */\nexport const Tabs: React.FC<TabsProps> = ({\n items,\n defaultTab,\n activeTab,\n onTabChange,\n variant = \"default\",\n size = \"md\",\n className,\n}) => {\n const [internalActiveTab, setInternalActiveTab] = useState(\n defaultTab || activeTab || items[0]?.id || \"\",\n );\n\n const currentActiveTab = activeTab || internalActiveTab;\n\n const handleTabChange = (tabId: string) => {\n if (onTabChange) {\n onTabChange(tabId);\n } else {\n setInternalActiveTab(tabId);\n }\n };\n\n const contextValue = {\n activeTab: currentActiveTab,\n setActiveTab: handleTabChange,\n };\n\n const activeTabItem = items.find((item) => item.id === currentActiveTab);\n\n const tabListClasses = cn(\n \"flex\",\n {\n \"border-b border-border px-2\": variant === \"default\" || variant === \"underline\",\n \"bg-surface-hover p-1 rounded-lg\": variant === \"pills\",\n \"space-x-1\": variant === \"pills\",\n \"space-x-8\": variant === \"default\" || variant === \"underline\",\n },\n className,\n );\n\n const tabClasses = (item: TabItem, isActive: boolean) => {\n const baseClasses = \"transition-colors duration-fast font-medium\";\n\n const sizeClasses = {\n \"text-xs px-2 py-1\": size === \"sm\",\n \"text-sm px-3 py-2\": size === \"md\",\n \"text-base px-4 py-3\": size === \"lg\",\n };\n\n const variantClasses = {\n // Default variant\n \"border-b-2 -mb-px\": variant === \"default\",\n // Pills variant\n \"rounded-md\": variant === \"pills\",\n // Underline variant\n \"border-b-2 pb-2\": variant === \"underline\",\n };\n\n const stateClasses = {\n \"opacity-50 cursor-not-allowed\": item.disabled,\n \"cursor-pointer\": !item.disabled,\n };\n\n // Active state classes\n const activeClasses = isActive\n ? {\n \"border-accent-border text-text\": variant === \"default\" || variant === \"underline\",\n \"bg-surface text-text shadow-none\": variant === \"pills\",\n }\n : {};\n\n // Non-active, non-disabled state classes\n const inactiveClasses =\n !isActive && !item.disabled\n ? {\n \"border-transparent text-text-muted hover:text-text\": variant === \"default\",\n \"text-text-muted hover:text-text\": variant === \"pills\",\n \"border-transparent text-text-muted hover:text-text hover:border-border\":\n variant === \"underline\",\n }\n : {};\n\n return cn(\n baseClasses,\n sizeClasses,\n variantClasses,\n stateClasses,\n activeClasses,\n inactiveClasses,\n );\n };\n\n return (\n <TabsContext.Provider value={contextValue}>\n <div>\n {/* Tab List */}\n <div className={tabListClasses} role=\"tablist\">\n {items.map((item) => (\n <button\n key={item.id}\n type=\"button\"\n role=\"tab\"\n aria-selected={currentActiveTab === item.id}\n aria-controls={`tabpanel-${item.id}`}\n disabled={item.disabled}\n onClick={() => !item.disabled && handleTabChange(item.id)}\n className={tabClasses(item, currentActiveTab === item.id)}\n >\n <span className=\"flex items-center gap-2\">\n {item.label}\n {item.badge && (\n <span className=\"inline-flex items-center px-2 py-0.5 rounded-full text-xs font-medium bg-surface-hover text-text-muted\">\n {item.badge}\n </span>\n )}\n </span>\n </button>\n ))}\n </div>\n\n {/* Tab Content */}\n {activeTabItem?.content && (\n <div\n role=\"tabpanel\"\n id={`tabpanel-${currentActiveTab}`}\n aria-labelledby={`tab-${currentActiveTab}`}\n className=\"mt-4\"\n >\n {activeTabItem.content}\n </div>\n )}\n </div>\n </TabsContext.Provider>\n );\n};\n\n/**\n * Hook to access tab context\n */\nexport const useTabsContext = () => {\n const context = useContext(TabsContext);\n if (!context) {\n throw new Error(\"useTabsContext must be used within a Tabs component\");\n }\n return context;\n};\n","import { X } from \"lucide-react\";\nimport type React from \"react\";\nimport { useEffect, useRef } from \"react\";\nimport { Icon } from \"../content/Icon\";\nimport { cn } from \"../utils/cn\";\n\nexport interface ModalProps {\n open: boolean;\n\n onClose: () => void;\n\n title?: string;\n\n size?: \"sm\" | \"md\" | \"lg\" | \"xl\" | \"full\";\n\n /** Close when the backdrop is clicked. */\n closeOnBackdropClick?: boolean;\n\n /** Close on Escape. */\n closeOnEscape?: boolean;\n\n className?: string;\n\n backdropClassName?: string;\n\n children: React.ReactNode;\n\n footer?: React.ReactNode;\n\n showCloseButton?: boolean;\n}\n\nconst modalSizes = {\n sm: \"max-w-md\",\n md: \"max-w-lg\",\n lg: \"max-w-2xl\",\n xl: \"max-w-4xl\",\n full: \"max-w-full mx-4\",\n};\n\n/** Dialog overlay with focus management, Escape/backdrop close, and body-scroll lock. */\nexport function Modal({\n open,\n onClose,\n title,\n size = \"md\",\n closeOnBackdropClick = true,\n closeOnEscape = true,\n className,\n backdropClassName,\n children,\n footer,\n showCloseButton = true,\n}: ModalProps) {\n const modalRef = useRef<HTMLDivElement>(null);\n const previousActiveElement = useRef<HTMLElement | null>(null);\n\n useEffect(() => {\n if (!open || !closeOnEscape) return;\n\n const handleEscape = (event: KeyboardEvent) => {\n if (event.key === \"Escape\") {\n onClose();\n }\n };\n\n document.addEventListener(\"keydown\", handleEscape);\n return () => document.removeEventListener(\"keydown\", handleEscape);\n }, [open, closeOnEscape, onClose]);\n\n useEffect(() => {\n if (open) {\n previousActiveElement.current = document.activeElement as HTMLElement;\n\n if (modalRef.current) {\n modalRef.current.focus();\n }\n\n document.body.style.overflow = \"hidden\";\n } else {\n if (previousActiveElement.current) {\n previousActiveElement.current.focus();\n }\n\n document.body.style.overflow = \"\";\n }\n\n return () => {\n document.body.style.overflow = \"\";\n };\n }, [open]);\n\n const handleBackdropClick = (event: React.MouseEvent) => {\n if (closeOnBackdropClick && event.target === event.currentTarget) {\n onClose();\n }\n };\n\n if (!open) return null;\n\n return (\n <div\n className={cn(\n \"fixed inset-0 z-modal flex items-center justify-center p-4\",\n \"bg-scrim\",\n backdropClassName,\n )}\n onClick={handleBackdropClick}\n >\n <div\n ref={modalRef}\n className={cn(\n \"relative w-full bg-surface rounded-lg shadow-modal\",\n \"focus:outline-none\",\n modalSizes[size],\n className,\n )}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby={title ? \"modal-title\" : undefined}\n tabIndex={-1}\n >\n {(title || showCloseButton) && (\n <div className=\"flex items-center justify-between p-6 pb-0\">\n {title && (\n <span id=\"modal-title\" className=\"text-lg font-semibold text-text\">\n {title}\n </span>\n )}\n\n {showCloseButton && (\n <button\n onClick={onClose}\n className=\"p-1 text-text-muted hover:text-text transition-colors\"\n aria-label=\"Close modal\"\n >\n <Icon icon={X} size=\"lg\" color=\"current\" />\n </button>\n )}\n </div>\n )}\n\n <div className=\"p-6\">{children}</div>\n\n {footer && <div className=\"px-6 py-4\">{footer}</div>}\n </div>\n </div>\n );\n}\n","import type { FilePointer, VirtualMount } from \"@opencxh/domain\";\nimport {\n ArrowLeft,\n FileText,\n Folder,\n FolderPlus,\n HardDrive,\n Search,\n Upload,\n X,\n} from \"lucide-react\";\nimport type React from \"react\";\nimport { useEffect, useMemo, useRef, useState } from \"react\";\nimport { Button } from \"../action/Button\";\nimport { Icon } from \"../content/Icon\";\nimport { Table, type TableColumn } from \"../content/Table\";\nimport { Tabs } from \"../navigation/Tabs\";\nimport { Modal } from \"../overlays/Modal\";\nimport { Text } from \"../typography/Text\";\nimport { cn } from \"../utils/cn\";\nimport { TextField } from \"./TextField\";\n\nexport interface StorageInputProps {\n value?: string | FilePointer;\n onChange: (value: FilePointer | null, content?: Uint8Array) => void;\n label?: string;\n placeholder?: string;\n error?: string;\n disabled?: boolean;\n accept?: string;\n onListFiles?: (filters?: {\n name?: string;\n mimeType?: string;\n mountId?: string;\n path?: string;\n }) => Promise<{ data: FilePointer[] }> | undefined;\n onListMounts?: () => Promise<{ data: VirtualMount[] }> | undefined;\n onUploadFile?: (payload: {\n file: Uint8Array;\n filename: string;\n mimeType: string;\n mountId?: string;\n path?: string;\n }) => Promise<{ data: FilePointer }> | undefined;\n onDownloadFile?: (fileId: string) => Promise<Uint8Array> | undefined;\n onRegisterFile?: (payload: Omit<FilePointer, \"id\">) => Promise<{ data: FilePointer }> | undefined;\n}\n\nexport const StorageInput: React.FC<StorageInputProps> = ({\n value,\n onChange,\n label,\n placeholder = \"Select or upload a file\",\n error,\n disabled,\n accept,\n onListFiles,\n onListMounts,\n onUploadFile,\n onDownloadFile,\n onRegisterFile,\n}) => {\n const [isModalOpen, setIsModalOpen] = useState(false);\n const [mounts, setMounts] = useState<VirtualMount[]>([]);\n const [selectedMount, setSelectedMount] = useState<VirtualMount | null>(null);\n const [currentPath, setCurrentPath] = useState<string>(\"/\");\n const [remoteFiles, setRemoteFiles] = useState<FilePointer[]>([]);\n const [loading, setLoading] = useState(false);\n const [searchTerm, setSearchTerm] = useState(\"\");\n const [selectedPointer, setSelectedPointer] = useState<FilePointer | null>(\n typeof value === \"object\" ? value : null,\n );\n const [isCreatingFolder, setIsCreatingFolder] = useState(false);\n const [newFolderName, setNewFolderName] = useState(\"\");\n\n const fileInputRef = useRef<HTMLInputElement>(null);\n\n // Load mounts when modal opens\n useEffect(() => {\n if (isModalOpen) {\n loadMounts();\n }\n }, [isModalOpen]);\n\n // Load files when a mount is selected or search term changes\n useEffect(() => {\n if (isModalOpen && selectedMount) {\n loadFiles();\n }\n }, [isModalOpen, selectedMount, currentPath, searchTerm]);\n\n const loadMounts = async () => {\n setLoading(true);\n try {\n const resp = await onListMounts?.();\n if (resp?.data) {\n setMounts(resp.data);\n }\n } catch (err) {\n console.error(\"Failed to load mounts\", err);\n } finally {\n setLoading(false);\n }\n };\n\n const loadFiles = async () => {\n if (!selectedMount) return;\n setLoading(true);\n try {\n const resp = await onListFiles?.({\n mountId: selectedMount.id,\n name: searchTerm,\n path: currentPath,\n });\n if (resp?.data) {\n setRemoteFiles(resp.data);\n }\n } catch (err) {\n console.error(\"Failed to load files\", err);\n } finally {\n setLoading(false);\n }\n };\n\n const handleLocalOnlySelection = async (e: React.ChangeEvent<HTMLInputElement>) => {\n const file = e.target.files?.[0];\n if (!file) return;\n\n setLoading(true);\n try {\n const buffer = await file.arrayBuffer();\n const content = new Uint8Array(buffer);\n\n const virtualPointer: FilePointer = {\n id: `local-${Date.now()}`,\n name: file.name,\n type: \"file\",\n path: \"/\",\n mimeType: file.type || \"application/octet-stream\",\n size: file.size,\n mountId: \"local\",\n providerKey: file.name,\n ownerId: \"me\",\n };\n\n setSelectedPointer(virtualPointer);\n onChange(virtualPointer, content);\n setIsModalOpen(false);\n } catch (err) {\n console.error(\"Local file selection failed\", err);\n } finally {\n setLoading(false);\n }\n };\n\n const handleUploadToStorage = async (e: React.ChangeEvent<HTMLInputElement>) => {\n const file = e.target.files?.[0];\n if (!file || !selectedMount) return;\n\n setLoading(true);\n try {\n const buffer = await file.arrayBuffer();\n const resp = await onUploadFile?.({\n file: new Uint8Array(buffer),\n filename: file.name,\n mimeType: file.type || \"application/octet-stream\",\n mountId: selectedMount.id,\n path: currentPath,\n });\n\n if (resp?.data) {\n const pointer = resp.data;\n setSelectedPointer(pointer);\n onChange(pointer, new Uint8Array(buffer));\n setIsModalOpen(false);\n }\n } catch (err) {\n console.error(\"Upload failed\", err);\n } finally {\n setLoading(false);\n }\n };\n\n const handleCreateFolder = async () => {\n if (!newFolderName || !selectedMount) return;\n\n setLoading(true);\n try {\n const fullPath = `${currentPath}${newFolderName}/`;\n\n if (onRegisterFile) {\n await onRegisterFile({\n name: newFolderName,\n type: \"folder\",\n path: currentPath,\n mimeType: \"application/x-directory\",\n size: 0,\n mountId: selectedMount.id,\n providerKey: fullPath,\n ownerId: \"me\",\n });\n await loadFiles();\n }\n\n setIsCreatingFolder(false);\n setNewFolderName(\"\");\n } catch (err) {\n console.error(\"Failed to create folder\", err);\n } finally {\n setLoading(false);\n }\n };\n\n const handleSelectRemote = async (pointer: FilePointer) => {\n setLoading(true);\n try {\n const content = await onDownloadFile?.(pointer.id);\n setSelectedPointer(pointer);\n onChange(pointer, content);\n setIsModalOpen(false);\n } catch (err) {\n console.error(\"Failed to download file\", err);\n } finally {\n setLoading(false);\n }\n };\n\n const handleClear = (e: React.MouseEvent) => {\n e.stopPropagation();\n setSelectedPointer(null);\n onChange(null);\n };\n\n // Folder & File calculation logic\n const explorerItems = useMemo(() => {\n const folders = new Set<string>();\n const files: FilePointer[] = [];\n\n remoteFiles.forEach((file) => {\n if (file.type === \"folder\") {\n if (file.path === currentPath) {\n folders.add(file.name);\n }\n return;\n }\n\n const path = file.providerKey || file.name;\n const relativePath = currentPath ? path.substring(currentPath.length) : path;\n const parts = relativePath.split(\"/\").filter((p) => p !== \"\");\n\n if (parts.length > 1) {\n folders.add(parts[0]);\n } else if (parts.length === 1) {\n files.push(file);\n }\n });\n\n return {\n folders: Array.from(folders).sort(),\n files: files.sort((a, b) => a.name.localeCompare(b.name)),\n };\n }, [remoteFiles, currentPath]);\n\n const handleFolderClick = (folderName: string) => {\n setCurrentPath((prev) => `${prev}${folderName}/`);\n };\n\n const handleGoBack = () => {\n const parts = currentPath.split(\"/\").filter((p) => p !== \"\");\n parts.pop();\n if (parts.length === 0) {\n setCurrentPath(\"/\");\n } else {\n setCurrentPath(`${parts.join(\"/\")}/`);\n }\n };\n\n const tableData = useMemo(() => {\n const data: any[] = explorerItems.folders.map((f) => ({\n id: `folder-${f}`,\n name: f,\n type: \"folder\",\n }));\n\n explorerItems.files.forEach((f) => {\n data.push({\n ...f,\n type: \"file\",\n });\n });\n\n return data;\n }, [explorerItems]);\n\n const explorerColumns: TableColumn<any>[] = [\n {\n id: \"name\",\n header: \"Name\",\n accessor: \"name\",\n cell: (val, row) => (\n <div className=\"flex items-center gap-2\">\n <Icon\n icon={row.type === \"folder\" ? Folder : FileText}\n size=\"sm\"\n color={row.type === \"folder\" ? \"warning\" : \"secondary\"}\n />\n <span className={cn(row.type === \"folder\" && \"font-medium\")}>{val}</span>\n </div>\n ),\n },\n {\n id: \"type\",\n header: \"Type\",\n accessor: (row) => (row.type === \"folder\" ? \"Folder\" : row.mimeType),\n },\n {\n id: \"size\",\n header: \"Size\",\n accessor: (row) => (row.type === \"file\" ? `${(row.size / 1024).toFixed(1)} KB` : \"-\"),\n },\n {\n id: \"actions\",\n header: \"\",\n accessor: \"id\",\n align: \"right\",\n cell: (_, row) => (\n <Button\n variant=\"outline\"\n onClick={(e) => {\n e.stopPropagation();\n row.type === \"folder\" ? handleFolderClick(row.name) : handleSelectRemote(row);\n }}\n loading={loading && row.type === \"file\" && selectedPointer?.id === row.id}\n >\n {row.type === \"folder\" ? \"Open\" : \"Select\"}\n </Button>\n ),\n },\n ];\n\n const mountColumns: TableColumn<VirtualMount>[] = [\n {\n id: \"name\",\n header: \"Mount Name\",\n accessor: \"name\",\n cell: (val) => (\n <div className=\"flex items-center gap-2 font-medium\">\n <Icon icon={HardDrive} size=\"sm\" color=\"primary\" />\n <span>{val}</span>\n </div>\n ),\n },\n {\n id: \"actions\",\n header: \"\",\n accessor: \"id\",\n align: \"right\",\n cell: (_, row) => (\n <Button variant=\"outline\" onClick={() => setSelectedMount(row)}>\n Open\n </Button>\n ),\n },\n ];\n\n return (\n <div className=\"space-y-1\">\n {label && (\n <Text variant=\"label\" size=\"sm\" weight=\"medium\">\n {label}\n </Text>\n )}\n\n <div\n onClick={() => !disabled && setIsModalOpen(true)}\n className={cn(\n \"flex items-center gap-3 px-3 py-2 border rounded-lg cursor-pointer transition-colors\",\n \"hover:border-border-strong bg-surface\",\n error ? \"border-danger-border\" : \"border-border\",\n disabled && \"opacity-50 cursor-not-allowed bg-surface-sunk\",\n )}\n >\n <div className=\"flex-shrink-0\">\n <Icon\n icon={selectedPointer ? FileText : HardDrive}\n color={selectedPointer ? \"primary\" : \"secondary\"}\n />\n </div>\n\n <div className=\"flex-grow truncate\">\n {selectedPointer ? (\n <span className=\"text-sm text-text font-medium\">{selectedPointer.name}</span>\n ) : (\n <span className=\"text-sm text-text-muted\">{placeholder}</span>\n )}\n </div>\n\n {selectedPointer && !disabled && (\n <button onClick={handleClear} className=\"p-1 hover:bg-surface-hover rounded\">\n <Icon icon={X} size=\"xs\" />\n </button>\n )}\n </div>\n\n {error && (\n <Text variant=\"body\" size=\"xs\" className=\"text-danger-fg\">\n {error}\n </Text>\n )}\n\n <Modal\n open={isModalOpen}\n onClose={() => {\n setIsModalOpen(false);\n setSelectedMount(null);\n setCurrentPath(\"/\");\n setIsCreatingFolder(false);\n }}\n title={selectedMount ? `Explorer: ${selectedMount.name}` : \"Select Storage Mount\"}\n size=\"lg\"\n >\n <Tabs\n variant=\"pills\"\n items={[\n {\n id: \"remote\",\n label: \"Platform Storage\",\n content: (\n <div className=\"space-y-4\">\n {selectedMount ? (\n <>\n <div className=\"flex flex-wrap items-center gap-2\">\n <Button\n variant=\"ghost\"\n onClick={() => {\n if (currentPath && currentPath !== \"/\") {\n handleGoBack();\n } else {\n setSelectedMount(null);\n }\n }}\n leftIcon={<Icon icon={ArrowLeft} size=\"xs\" />}\n >\n {currentPath ? \"Back\" : \"Back to Mounts\"}\n </Button>\n\n <div className=\"flex items-center gap-1 text-xs text-text-muted overflow-hidden bg-surface-hover px-2 py-1 rounded\">\n <span className=\"truncate\">{currentPath}</span>\n </div>\n\n <div className=\"flex-grow min-w-40\">\n <TextField\n placeholder=\"Search...\"\n value={searchTerm}\n onChange={(e) => setSearchTerm(e.target.value)}\n startIcon={<Search size={14} />}\n fullWidth\n />\n </div>\n\n <div className=\"flex items-center gap-1\">\n <Button\n variant=\"outline\"\n onClick={() => setIsCreatingFolder(true)}\n leftIcon={<Icon icon={FolderPlus} size=\"xs\" />}\n >\n New Folder\n </Button>\n <Button\n variant=\"primary\"\n onClick={() => fileInputRef.current?.click()}\n leftIcon={<Icon icon={Upload} size=\"xs\" />}\n loading={loading}\n >\n Upload\n </Button>\n <input\n type=\"file\"\n className=\"hidden\"\n ref={fileInputRef}\n onChange={handleUploadToStorage}\n accept={accept}\n />\n </div>\n </div>\n\n {isCreatingFolder && (\n <div className=\"flex items-center gap-2 p-3 bg-surface-sunk rounded-lg border border-border\">\n <Icon icon={Folder} size=\"sm\" color=\"warning\" />\n <TextField\n placeholder=\"Folder name\"\n value={newFolderName}\n onChange={(e) => setNewFolderName(e.target.value)}\n autoFocus\n />\n <Button onClick={handleCreateFolder} loading={loading}>\n Create\n </Button>\n <Button variant=\"ghost\" onClick={() => setIsCreatingFolder(false)}>\n Cancel\n </Button>\n </div>\n )}\n\n <div className=\"max-h-100 overflow-auto rounded-lg border border-border\">\n <Table\n data={tableData}\n columns={explorerColumns}\n loading={loading}\n emptyContent=\"This folder is empty\"\n onRowClick={(row) =>\n row.type === \"folder\"\n ? handleFolderClick(row.name)\n : handleSelectRemote(row)\n }\n />\n </div>\n </>\n ) : (\n <div className=\"max-h-100 overflow-auto rounded-lg border border-border\">\n <Table\n data={mounts}\n columns={mountColumns}\n loading={loading}\n emptyContent=\"No storage mounts configured\"\n onRowClick={(row) => setSelectedMount(row)}\n />\n </div>\n )}\n </div>\n ),\n },\n {\n id: \"local\",\n label: \"Local File\",\n content: (\n <div className=\"space-y-6\">\n <div className=\"flex flex-col items-center justify-center py-12 border-2 border-dashed border-border rounded-xl bg-surface-sunk/50\">\n <div className=\"p-4 bg-accent-soft rounded-full mb-4\">\n <Icon icon={FileText} size=\"xl\" color=\"primary\" />\n </div>\n <Text variant=\"label\" size=\"lg\" weight=\"semibold\">\n Select a file from your computer\n </Text>\n <Text\n variant=\"body\"\n size=\"sm\"\n className=\"text-text-muted mt-1 mb-6 text-center max-w-xs\"\n >\n This file will be used directly in the form and will <strong>not</strong> be\n uploaded to platform storage.\n </Text>\n\n <label className=\"cursor-pointer\">\n <Button\n variant=\"primary\"\n size=\"lg\"\n loading={loading}\n className=\"pointer-events-none\"\n >\n Browse Local File\n </Button>\n <input\n type=\"file\"\n className=\"hidden\"\n onChange={handleLocalOnlySelection}\n accept={accept}\n disabled={loading}\n />\n </label>\n </div>\n </div>\n ),\n },\n ]}\n />\n </Modal>\n </div>\n );\n};\n","import React, { forwardRef } from \"react\";\nimport { cn } from \"../utils/cn\";\nimport { fieldEdgeClasses, fieldFrameClasses, fieldLabelClasses } from \"./TextField\";\n\nexport interface TextAreaProps\n extends Omit<React.TextareaHTMLAttributes<HTMLTextAreaElement>, \"size\"> {\n label?: string;\n\n helperText?: string;\n\n error?: string;\n\n size?: \"sm\" | \"md\" | \"lg\" | \"full\";\n\n fullWidth?: boolean;\n\n loading?: boolean;\n\n /** Additional class name for the container */\n containerClassName?: string;\n\n /** Additional class name for the label */\n labelClassName?: string;\n}\n\nconst textareaSizes = {\n sm: \"px-3 py-1.5 text-sm\",\n md: \"px-3 py-2 text-base\",\n lg: \"px-3 py-2 text-md\",\n full: \"px-3 py-2 text-md\",\n};\n\nexport const TextArea = forwardRef<HTMLTextAreaElement, TextAreaProps>(\n (\n {\n label,\n helperText,\n error,\n size = \"md\",\n fullWidth = false,\n loading = false,\n containerClassName,\n labelClassName,\n className,\n id,\n rows = 4,\n ...props\n },\n ref,\n ) => {\n const reactId = React.useId();\n const textareaId = id || `textarea-${reactId}`;\n const hasError = Boolean(error);\n const describedBy = error || helperText ? `${textareaId}-description` : undefined;\n\n return (\n <div className={cn(\"flex flex-col\", fullWidth && \"w-full\", containerClassName)}>\n {label && (\n <label\n htmlFor={textareaId}\n className={cn(\n fieldLabelClasses,\n hasError ? \"text-danger-fg\" : \"text-text-muted\",\n labelClassName,\n )}\n >\n {label}\n </label>\n )}\n\n <div className=\"relative\">\n <textarea\n ref={ref}\n id={textareaId}\n rows={rows}\n aria-invalid={hasError || undefined}\n aria-describedby={describedBy}\n className={cn(\n fieldFrameClasses,\n \"resize-y leading-relaxed\",\n\n textareaSizes[size],\n\n fieldEdgeClasses(hasError),\n\n className,\n )}\n {...props}\n />\n\n {loading && (\n <span\n aria-hidden\n className=\"absolute top-2 right-3 size-icon-md animate-spin rounded-full border-2 border-border border-t-transparent\"\n />\n )}\n </div>\n\n {(error || helperText) && (\n <p\n id={describedBy}\n className={cn(\"mt-1 text-xs\", hasError ? \"text-danger-fg\" : \"text-text-subtle\")}\n >\n {error || helperText}\n </p>\n )}\n </div>\n );\n },\n);\n\nTextArea.displayName = \"TextArea\";\n","import type { FilePointer, InvokeOptions, VirtualMount } from \"@opencxh/domain\";\nimport { Plus, Trash2 } from \"lucide-react\";\nimport React, { useCallback, useEffect, useState } from \"react\";\nimport { Button } from \"../action/Button\";\nimport { SegmentedToggle } from \"../action/SegmentedToggle\";\nimport { Checkbox } from \"../input/Checkbox\";\nimport { DatePicker } from \"../input/DatePicker\";\nimport { type FolderDestination, FolderSelect } from \"../input/FolderSelect\";\nimport { Select } from \"../input/Select\";\nimport { StorageInput } from \"../input/StorageInput\";\nimport { TextArea } from \"../input/TextArea\";\nimport { TextField } from \"../input/TextField\";\nimport { cn } from \"../utils/cn\";\nimport { autofillProps } from \"./autofill\";\n\nexport type FormFieldType =\n | \"text\"\n | \"email\"\n | \"password\"\n | \"number\"\n | \"tel\"\n | \"url\"\n // A colour swatch. Its own type rather than `fieldProps: { type: \"color\" }`,\n // because the `type` prop is applied after the spread and would win.\n | \"color\"\n | \"textarea\"\n | \"select\"\n | \"segmented\"\n | \"checkbox\"\n | \"radio\"\n | \"date\"\n | \"file\"\n | \"folder\"\n | \"custom\"\n | \"array\";\n\n/**\n * `NonNullable` before the `object` test is load-bearing: form data is routinely\n * a `Partial<T>`, and `Address | undefined extends object` is false — without it\n * every nested path silently disappears and only top-level keys survive.\n * Arrays, Dates and functions are leaves; recursing into them blows the union\n * past what TypeScript will infer (see {@link FieldPath}).\n * `T extends unknown ?` distributes over unions: `keyof (A | B)` keeps only\n * shared keys, so `ownerScope.teamId` would otherwise be unspellable.\n */\ntype NestedKeys<T> = T extends unknown\n ? {\n [K in keyof T]-?: NonNullable<T[K]> extends\n | readonly unknown[]\n | Date\n | ((...args: never[]) => unknown)\n ? K & string\n : NonNullable<T[K]> extends object\n ? `${K & string}.${NestedKeys<NonNullable<T[K]>>}`\n : K & string;\n }[keyof T]\n : never;\n\n/**\n * What a field may be named: any leaf path, plus any top-level key. The second\n * half exists for `type: \"custom\"` fields, which own a whole sub-object and\n * would otherwise be unspellable, since `NestedKeys` only reaches leaves. Kept\n * as a separate union: folding it into the recursion doubles the union at every\n * level, and TypeScript quietly gives up inferring it once it gets large.\n */\ntype FieldPath<T> = NestedKeys<T> | (keyof T & string);\n\nfunction getValueByPath<T>(obj: T, path: string): unknown {\n return path\n .split(\".\")\n .reduce(\n (acc, key) => (acc && typeof acc === \"object\" && key in acc ? (acc as any)[key] : undefined),\n obj,\n );\n}\n\nfunction setValueByPath<T extends object>(obj: T, path: string, value: any): T {\n const keys = path.split(\".\");\n const clone: any = Array.isArray(obj) ? [...(obj as any)] : { ...(obj as any) };\n let cur: any = clone;\n\n for (let i = 0; i < keys.length - 1; i++) {\n const k = keys[i];\n const prev = cur[k];\n cur[k] =\n prev && typeof prev === \"object\" ? (Array.isArray(prev) ? [...prev] : { ...prev }) : {};\n cur = cur[k];\n }\n cur[keys[keys.length - 1]] = value;\n return clone;\n}\n\nfunction unsetByPath<T extends object>(obj: T, path: string): T {\n const keys = path.split(\".\");\n const clone: any = Array.isArray(obj) ? [...(obj as any)] : { ...(obj as any) };\n let cur: any = clone;\n\n for (let i = 0; i < keys.length - 1; i++) {\n const k = keys[i];\n if (!cur[k] || typeof cur[k] !== \"object\") return clone; // nothing to unset\n cur[k] = Array.isArray(cur[k]) ? [...cur[k]] : { ...cur[k] };\n cur = cur[k];\n }\n delete cur[keys[keys.length - 1]];\n return clone;\n}\n\n/**\n * One band of a page-wide form: a fixed label column with the section title and\n * its explanation, and the fields beside it. Exported because a page often has\n * a section that is not a `Form` field at all — a checklist, linked records —\n * that must line up with the bands above it; without this each re-derives the\n * column width and spacing, and they drift.\n */\nexport function FormSection({\n title,\n description,\n className,\n children,\n}: {\n title?: string;\n description?: string;\n /** Caller owns the divider: only it knows whether it is the last band. */\n className?: string;\n children: React.ReactNode;\n}) {\n if (!title && !description) {\n return <div>{children}</div>;\n }\n\n return (\n <div className={cn(\"flex flex-col gap-4 py-6 sm:flex-row sm:gap-8\", className)}>\n <div className=\"w-full shrink-0 sm:w-form-label\">\n {title && <span className=\"text-base font-semibold text-text\">{title}</span>}\n {description && (\n <p className=\"mt-1 text-sm leading-relaxed text-pretty text-text-subtle\">{description}</p>\n )}\n </div>\n <div className=\"min-w-0 flex-1\">{children}</div>\n </div>\n );\n}\n\nexport interface FormGroupItem<T = any> {\n /** Field name (key in form data) */\n name: FieldPath<T>;\n /** Field label */\n label: string;\n /** Field type */\n type: FormFieldType;\n /** Text input type (when type is 'text') */\n textType?: \"text\" | \"email\" | \"password\" | \"number\" | \"tel\" | \"url\";\n /** Field width (CSS width value or grid columns) */\n width?: string | number;\n /** Placeholder text */\n placeholder?: string;\n /**\n * Visible rows for `textarea`. Defaults to 4 — right for a note, far too small\n * for a field someone reads back while editing (a system prompt).\n */\n rows?: number;\n /**\n * Autocomplete token. Only real HTML tokens (`email`, `tel`, `name`, …) are\n * passed to the browser; anything else switches autofill *off* rather than\n * silently meaning \"on\" — see {@link autofillProps}.\n */\n autocomplete?: string;\n /** Options for select/radio/checkbox types */\n options?: Array<{\n value: string | number;\n label: string;\n disabled?: boolean;\n }>;\n /** Validation function */\n validator?: (value: any, formData: T) => string | null;\n /** Conditional function to show/hide field */\n conditional?: (formData: T) => boolean;\n /** Whether field can be empty */\n allowEmpty?: boolean;\n /** Remove field from form data when empty */\n removeIfEmpty?: boolean;\n /**\n * Explanation under the field: why it exists, or what the choice implies. For\n * fields whose label cannot fully pose the question — a group `description` is\n * too coarse once a group has several fields.\n */\n help?: string;\n /** Whether field is hidden */\n hidden?: boolean;\n /** Whether field is required */\n required?: boolean;\n /** Whether field is disabled */\n disabled?: boolean;\n /** Custom component renderer */\n customComponent?: (props: {\n value: any;\n onChange: (value: any) => void;\n error?: string;\n disabled?: boolean;\n }) => React.ReactNode;\n /** Additional props to pass to the field component */\n fieldProps?: Record<string, any>;\n\n /** For 'array' type, defines the fields for each item in the array */\n arrayFields?: FormGroupItem<any>[];\n\n /** For 'select' type, whether the select should be searchable */\n searchable?: boolean;\n\n /** For 'select' type, whether the select should be multiple */\n multiple?: boolean;\n\n allowCreate?: boolean;\n\n // todo: add support for array of objects\n}\n\nexport interface FormGroup<T = any> {\n /** Group identifier */\n id: string;\n /**\n * Group title. Optional: a form that is one unbroken block of fields has no\n * heading to give, and the label column already renders empty without it.\n */\n title?: string;\n /** Group description */\n description?: string;\n /** Group items */\n items: FormGroupItem<T>[];\n /** Conditional function to show/hide group */\n conditional?: (formData: T) => boolean;\n /** Group layout */\n layout?: \"grid\" | \"flex\";\n /** Number of columns for grid layout */\n columns?: number;\n /** Additional CSS classes */\n className?: string;\n}\n\nexport interface FormButtonProps {\n /** Button label */\n label: string;\n /** Button variant */\n variant?: \"primary\" | \"secondary\" | \"outline\" | \"ghost\" | \"destructive\";\n /** Button size */\n size?: \"sm\" | \"md\" | \"lg\";\n /** Whether button is disabled */\n disabled?: boolean;\n /** Additional CSS classes */\n className?: string;\n}\n\n/**\n * The sliver of an app SDK that `Form` needs: one call to the server. Nothing here knows\n * about the rest of the SDK, which is the point.\n */\nexport interface FormInvoker {\n http: { invoke<T = any>(options: InvokeOptions): Promise<T> };\n}\n\nexport interface FormProps<T = Record<string, any>> {\n /** Form groups */\n groups: FormGroup<T>[];\n /** Initial form data */\n data?: Partial<T>;\n /** Form submit handler */\n onSubmit?: (data: T, transformedData?: any) => void | Promise<void>;\n /** Form cancel handler */\n onCancel?: () => void;\n /** Form change handler */\n onChange?: (data: Partial<T>, changedField?: keyof T) => void;\n /** Data transformation function (called before submit) */\n transform?: (data: T) => any;\n /** Form validation function */\n validate?: (data: T) => Record<keyof T, string> | null;\n /** Submit button props */\n submitButton?: FormButtonProps;\n /** Cancel button props */\n cancelButton?: FormButtonProps;\n /** Whether to show buttons */\n showButtons?: boolean;\n /** Form layout */\n layout?: \"vertical\" | \"horizontal\";\n /** Form size */\n size?: \"sm\" | \"md\" | \"lg\" | \"full\";\n /** Whether form is loading */\n loading?: boolean;\n /** Additional CSS classes */\n className?: string;\n /**\n * Form ref. Includes `| null` because that is what React 19's\n * `useRef<HTMLFormElement>(null)` produces; without it every caller has to\n * cast at the call site.\n */\n ref?: React.RefObject<HTMLFormElement | null>;\n /**\n * A handle that can call the server, for the storage fields (`storage`, `folder`).\n * Typed as the one method used rather than the whole `InternalSDK`: that type lives\n * in `@opencxh/app-sdk`, which ui-kit does not (and should not) depend on to render\n * a form. It also keeps the storage fields' slated move to a federated resource\n * from being blocked by a type import.\n */\n sdk?: FormInvoker;\n}\n\n/**\n * Generic Form component with groups, validation, and conditional rendering\n */\nexport const Form = <T extends Record<string, any>>({\n groups,\n data: externalData,\n onSubmit,\n onCancel,\n onChange,\n transform,\n validate,\n submitButton = { label: \"Submit\", variant: \"primary\" },\n cancelButton = { label: \"Cancel\", variant: \"outline\" },\n showButtons = true,\n layout = \"vertical\",\n size = \"md\",\n loading = false,\n className,\n ref,\n sdk,\n}: FormProps<T>) => {\n const [formData, setFormData] = useState<Partial<T>>(externalData || {});\n\n /**\n * A DOM id per field, unique per rendered form. `useId` and not the field name\n * alone: two forms on one page (a detail pane beside a modal) would hand out the\n * same id twice, and then a label points at whichever came first.\n */\n const formDomId = React.useId();\n const fieldDomId = useCallback(\n (name: keyof T | string) => `${formDomId}-${String(name).replace(/\\./g, \"-\")}`,\n [formDomId],\n );\n const [errors, setErrors] = useState<Record<string, string>>({});\n const [touched, setTouched] = useState<Record<string, boolean>>({});\n\n useEffect(() => {\n if (externalData) {\n setFormData({ ...externalData });\n }\n }, [externalData]);\n\n const validateField = useCallback(\n (name: keyof T, value: any, currentData: Partial<T>) => {\n const field = groups.flatMap((group) => group.items).find((item) => item.name === name);\n\n if (!field) return null;\n\n if (field.required && (value === undefined || value === null || value === \"\")) {\n return `${field.label} is required`;\n }\n\n if (field.type === \"array\") {\n if (field.required && (!value || value.length === 0)) {\n return `${field.label} cannot be empty.`;\n }\n if (value && !Array.isArray(value)) {\n return `${field.label} must be an array.`;\n }\n\n if (Array.isArray(value) && field.arrayFields) {\n const arrayErrors: Record<string, any>[] = [];\n let hasErrors = false;\n value.forEach((row, index) => {\n const rowErrors: Record<string, string> = {};\n for (const subField of field.arrayFields!) {\n const subFieldValue = row?.[subField.name];\n if (\n subField.required &&\n (subFieldValue === undefined || subFieldValue === null || subFieldValue === \"\")\n ) {\n rowErrors[subField.name as string] = `${subField.label} is required`;\n hasErrors = true;\n } else if (subField.validator) {\n const error = subField.validator(subFieldValue, row);\n if (error) {\n rowErrors[subField.name as string] = error;\n hasErrors = true;\n }\n }\n }\n arrayErrors[index] = rowErrors;\n });\n\n if (hasErrors) {\n return JSON.stringify(arrayErrors);\n }\n }\n }\n\n if (field.validator) {\n return field.validator(value, currentData as T);\n }\n\n return null;\n },\n [groups],\n );\n\n const handleFieldChange = useCallback(\n (name: keyof T, value: any) => {\n let newData = setValueByPath(formData as object, name as string, value) as Partial<T>;\n\n const field = groups.flatMap((g) => g.items).find((i) => i.name === name);\n if (field?.removeIfEmpty && (value === undefined || value === null || value === \"\")) {\n newData = unsetByPath(newData as object, name as string) as Partial<T>;\n }\n\n setFormData(newData);\n setTouched((prev) => ({ ...prev, [name]: true }));\n\n const error = validateField(name, value, newData);\n setErrors((prev) => ({\n ...prev,\n [name]: error || \"\",\n }));\n\n onChange?.(newData, name);\n },\n [formData, groups, validateField, onChange],\n );\n\n const handleSubmit = useCallback(\n async (e: React.FormEvent) => {\n e.preventDefault();\n\n if (loading) return;\n\n const newErrors: Record<string, string> = {};\n const allFields = groups.flatMap((group) => group.items);\n\n for (const field of allFields) {\n if (field.hidden || (field.conditional && !field.conditional(formData as T))) {\n continue;\n }\n\n const error = validateField(\n field.name,\n getValueByPath<Partial<T>>(formData, field.name),\n formData,\n );\n if (error) {\n newErrors[field.name as string] = error;\n }\n }\n\n if (validate) {\n const formErrors = validate(formData as T);\n if (formErrors) {\n Object.assign(newErrors, formErrors);\n }\n }\n\n setErrors(newErrors);\n\n if (Object.keys(newErrors).some((key) => newErrors[key])) {\n return;\n }\n\n const finalData = transform ? transform(formData as T) : formData;\n\n await onSubmit?.(formData as T, finalData);\n },\n [formData, groups, validateField, validate, transform, onSubmit, loading],\n );\n\n const renderField = useCallback(\n (item: FormGroupItem<T>) => {\n const value = getValueByPath<Partial<T>>(formData, item.name) as string | undefined;\n const error = touched[item.name as string] ? errors[item.name as string] : undefined;\n const isDisabled = item.disabled || loading;\n\n const commonProps = {\n // Ties the field to the `<label>` the group wrapper renders; without it\n // labels are unassociated — screen readers announce nothing, clicking a\n // label focuses nothing, and tests cannot find a field by its label.\n id: fieldDomId(item.name),\n value: value || \"\",\n disabled: isDisabled,\n required: item.required,\n placeholder: item.placeholder,\n ...item.fieldProps,\n };\n\n switch (item.type) {\n case \"text\":\n case \"email\":\n case \"password\":\n case \"number\":\n case \"tel\":\n case \"url\":\n case \"color\":\n return (\n <TextField\n {...commonProps}\n onChange={(e) => handleFieldChange(item.name, e.target.value)}\n type={item.textType || item.type}\n {...autofillProps(item.autocomplete)}\n error={error}\n size={size}\n />\n );\n\n case \"textarea\":\n return (\n <TextArea\n {...commonProps}\n onChange={(e) => handleFieldChange(item.name, e.target.value)}\n rows={item.rows ?? 4}\n error={error}\n size={size}\n />\n );\n\n case \"select\":\n return (\n <Select\n {...commonProps}\n onChange={(e) => handleFieldChange(item.name, e)}\n options={(item.options || []).map((option) => ({\n ...option,\n value: String(option.value),\n }))}\n searchable={item.searchable}\n multiple={item.multiple}\n allowCreate={item.allowCreate}\n error={error}\n size={size}\n />\n );\n\n // A short, fixed set of mutually exclusive values — priority, a scope,\n // a status. All options visible at once beats a dropdown you have to\n // open to learn what the choices even are.\n case \"segmented\":\n return (\n <SegmentedToggle\n aria-label={item.label}\n value={value == null ? \"\" : String(value)}\n onChange={(next) => handleFieldChange(item.name, next)}\n options={(item.options || []).map((option) => ({\n value: String(option.value),\n label: option.label,\n }))}\n size={size === \"sm\" ? \"sm\" : \"md\"}\n className={item.disabled ? \"pointer-events-none opacity-50\" : undefined}\n />\n );\n\n case \"checkbox\":\n return (\n <Checkbox\n {...commonProps}\n label={item.label}\n checked={Boolean(value)}\n onChange={(e) => handleFieldChange(item.name, e.target.checked)}\n size={size}\n />\n );\n\n case \"radio\":\n return (\n <div className=\"flex flex-col gap-2\">\n {item.options?.map((option) => (\n <label\n key={option.value}\n // gap-3 like Checkbox: a radio row and a checkbox row in the\n // same form must sit the same distance from their label.\n className=\"flex cursor-pointer items-center gap-3 text-base text-text\"\n >\n <input\n type=\"radio\"\n name={item.name as string}\n value={option.value}\n checked={value === option.value}\n onChange={() => handleFieldChange(item.name, option.value)}\n disabled={isDisabled || option.disabled}\n className=\"size-checkbox shrink-0 rounded-xs border border-border-strong bg-surface-input accent-accent focus-visible:outline-none focus-visible:focus-ring\"\n />\n {option.label}\n </label>\n ))}\n </div>\n );\n\n case \"date\":\n return (\n <DatePicker\n {...commonProps}\n value={value ? new Date(value) : null}\n onChange={(date) => handleFieldChange(item.name, date)}\n size={size}\n />\n );\n\n case \"file\":\n return (\n <StorageInput\n {...commonProps}\n value={value}\n onChange={(pointer) => handleFieldChange(item.name, pointer)}\n error={error}\n onListFiles={(filters) =>\n sdk?.http.invoke<{ data: FilePointer[] }>({\n method: \"POST\",\n action: \"storage.file.list\",\n body: filters,\n })\n }\n onListMounts={() =>\n sdk?.http.invoke<{ data: VirtualMount[] }>({\n method: \"GET\",\n action: \"storage.mount\",\n })\n }\n onUploadFile={(payload) =>\n sdk?.http.invoke<{ data: FilePointer }>({\n method: \"POST\",\n action: \"storage.file.upload\",\n body: payload,\n })\n }\n onDownloadFile={(fileId) =>\n sdk?.http.invoke<Uint8Array>({\n method: \"GET\",\n action: `storage.file.${fileId}.download`,\n })\n }\n onRegisterFile={(payload) =>\n sdk?.http.invoke<{ data: FilePointer }>({\n method: \"POST\",\n action: \"storage.file.folder\",\n body: payload,\n })\n }\n />\n );\n\n case \"folder\":\n return (\n <FolderSelect\n value={(value as unknown as FolderDestination) || {}}\n onChange={(v) => handleFieldChange(item.name, v)}\n onListFolders={() =>\n sdk?.http.invoke<{ data: VirtualMount[] }>({\n method: \"GET\",\n action: \"storage.mount\",\n })\n }\n disabled={isDisabled}\n size={size}\n placeholder={item.placeholder}\n />\n );\n\n case \"custom\":\n return item.customComponent?.({\n value,\n onChange: (newValue) => handleFieldChange(item.name, newValue),\n error,\n disabled: isDisabled,\n });\n\n case \"array\": {\n const arrayValue = (value || []) as any[];\n let arrayErrors: Record<string, string>[] = [];\n if (typeof error === \"string\" && error.startsWith(\"[\")) {\n try {\n arrayErrors = JSON.parse(error);\n } catch (e) {\n // ignore parse error\n }\n }\n\n const handleAddItem = () => {\n handleFieldChange(item.name, [...arrayValue, {}]);\n };\n\n const handleRemoveItem = (index: number) => {\n handleFieldChange(\n item.name,\n arrayValue.filter((_, i) => i !== index),\n );\n };\n\n const handleSubFieldChange = (index: number, fieldName: string, fieldValue: any) => {\n const newArray = [...arrayValue];\n newArray[index] = {\n ...newArray[index],\n [fieldName]: fieldValue,\n };\n handleFieldChange(item.name, newArray);\n };\n\n return (\n <div className=\"flex flex-col gap-2\">\n {arrayValue.map((row, index) => (\n <div key={index} className=\"flex items-start gap-2\">\n <div className=\"grid flex-1 gap-2 sm:grid-cols-2\">\n {item.arrayFields?.map((subField) => {\n const subFieldError = arrayErrors?.[index]?.[subField.name as string];\n return (\n <div key={subField.name as string} className=\"flex flex-col gap-1\">\n {index === 0 && (\n <label className=\"block text-xs font-semibold uppercase tracking-label text-text-subtle\">\n {subField.label}\n {subField.required && <span className=\"ml-1 text-danger-fg\">*</span>}\n </label>\n )}\n {(() => {\n const commonSubFieldProps = {\n value: row?.[subField.name] || \"\",\n disabled: isDisabled,\n required: subField.required,\n placeholder: subField.placeholder,\n ...subField.fieldProps,\n };\n switch (subField.type) {\n case \"text\":\n case \"email\":\n case \"password\":\n case \"number\":\n case \"tel\":\n case \"url\":\n return (\n <TextField\n {...commonSubFieldProps}\n onChange={(e) =>\n handleSubFieldChange(\n index,\n subField.name as string,\n e.target.value,\n )\n }\n type={subField.textType || subField.type}\n {...autofillProps(subField.autocomplete)}\n error={subFieldError}\n size={size}\n />\n );\n case \"textarea\":\n return (\n <TextArea\n {...commonSubFieldProps}\n onChange={(e) =>\n handleSubFieldChange(\n index,\n subField.name as string,\n e.target.value,\n )\n }\n rows={subField.rows ?? 4}\n error={subFieldError}\n size={size}\n />\n );\n case \"select\":\n return (\n <Select\n {...commonSubFieldProps}\n onChange={(e) =>\n handleSubFieldChange(index, subField.name as string, e)\n }\n options={(subField.options || []).map((option) => ({\n ...option,\n value: String(option.value),\n }))}\n searchable={subField.searchable}\n multiple={subField.multiple}\n allowCreate={subField.allowCreate}\n error={subFieldError}\n size={size}\n />\n );\n case \"checkbox\":\n return (\n <Checkbox\n {...commonSubFieldProps}\n checked={Boolean(row?.[subField.name])}\n onChange={(e) =>\n handleSubFieldChange(\n index,\n subField.name as string,\n e.target.checked,\n )\n }\n size={size}\n />\n );\n case \"radio\":\n return (\n <div className=\"flex flex-col gap-2\">\n {subField.options?.map((option) => (\n <label\n key={option.value}\n className=\"flex cursor-pointer items-center gap-2 text-base text-text\"\n >\n <input\n type=\"radio\"\n name={`${item.name as string}.${index}.${\n subField.name as string\n }`}\n value={option.value}\n checked={row?.[subField.name] === option.value}\n onChange={() =>\n handleSubFieldChange(\n index,\n subField.name as string,\n option.value,\n )\n }\n disabled={isDisabled || option.disabled}\n className=\"size-checkbox shrink-0 rounded-xs border border-border-strong bg-surface-input accent-accent focus-visible:outline-none focus-visible:focus-ring\"\n />\n {option.label}\n </label>\n ))}\n </div>\n );\n case \"date\": {\n const dateValue = row?.[subField.name];\n return (\n <DatePicker\n {...commonSubFieldProps}\n value={dateValue ? new Date(dateValue) : null}\n onChange={(date) =>\n handleSubFieldChange(index, subField.name as string, date)\n }\n size={size}\n />\n );\n }\n case \"custom\":\n return subField.customComponent?.({\n value: row?.[subField.name],\n onChange: (newValue) =>\n handleSubFieldChange(index, subField.name as string, newValue),\n error: subFieldError,\n disabled: isDisabled,\n });\n default:\n return <p>Unsupported field type in array: {subField.type}</p>;\n }\n })()}\n {subFieldError && (\n <p className=\"text-xs text-danger-fg\">{subFieldError}</p>\n )}\n </div>\n );\n })}\n </div>\n <Button\n type=\"button\"\n iconOnly\n variant=\"ghost\"\n size=\"md\"\n aria-label={`${item.label} verwijderen`}\n onClick={() => handleRemoveItem(index)}\n disabled={isDisabled}\n className={cn(\"shrink-0\", index === 0 && \"mt-5\")}\n >\n <Trash2 className=\"size-icon-md\" />\n </Button>\n </div>\n ))}\n <Button\n type=\"button\"\n variant=\"secondary\"\n leftIcon={<Plus className=\"size-icon-sm\" />}\n onClick={handleAddItem}\n disabled={isDisabled}\n >\n {item.label} toevoegen\n </Button>\n </div>\n );\n }\n\n default:\n return null;\n }\n },\n [formData, touched, errors, loading, size, handleFieldChange],\n );\n\n const renderGroup = useCallback(\n (group: FormGroup<T>) => {\n if (group.conditional && !group.conditional(formData as T)) {\n return null;\n }\n\n const visibleItems = group.items.filter((item) => {\n if (item.hidden) return false;\n if (item.conditional && !item.conditional(formData as T)) return false;\n return true;\n });\n\n if (visibleItems.length === 0) return null;\n\n const groupClasses = cn(\n group.layout === \"flex\" ? \"flex flex-wrap gap-3.5\" : \"grid gap-3.5\",\n group.layout !== \"flex\" && {\n \"grid-cols-1\": !group.columns || group.columns === 1,\n \"grid-cols-2\": group.columns === 2,\n \"grid-cols-3\": group.columns === 3,\n \"grid-cols-4\": group.columns === 4,\n },\n group.className,\n );\n\n return (\n // A band across the page, closed off with a hairline — no card. The\n // form *is* the page now, so a panel around each section would be a\n // second surface on top of the one it already sits on.\n <FormSection\n key={group.id}\n title={group.title}\n description={group.description}\n className=\"not-last:border-b not-last:border-border-subtle\"\n >\n <div>\n <div className={groupClasses}>\n {visibleItems.map((item) => {\n const fieldError = touched[item.name as string]\n ? errors[item.name as string]\n : undefined;\n\n return (\n <div\n key={item.name as string}\n className={cn(\n \"flex min-w-0 flex-col gap-1.5\",\n item.type === \"checkbox\" && \"justify-center\",\n )}\n style={{\n width: typeof item.width === \"string\" ? item.width : undefined,\n gridColumn:\n typeof item.width === \"number\"\n ? `span ${item.width} / span ${item.width}`\n : undefined,\n }}\n >\n {/* Checkbox carries its own label on the right, so it gets none here. */}\n {item.type !== \"custom\" && item.type !== \"checkbox\" && (\n <label\n htmlFor={fieldDomId(item.name)}\n className=\"block text-sm font-medium text-text-muted\"\n >\n {item.label}\n {item.required && <span className=\"ml-1 text-danger-fg\">*</span>}\n </label>\n )}\n\n {renderField(item)}\n\n {/* Help text under the field; a validation error takes its place. */}\n {item.help && !fieldError && (\n <p className=\"text-xs leading-4 text-text-subtle\">{item.help}</p>\n )}\n </div>\n );\n })}\n </div>\n </div>\n </FormSection>\n );\n },\n [formData, touched, errors, renderField],\n );\n\n // No panel chrome: the page (or the card the page sits in) is the surface.\n // `size` is a reading width, nothing more — `md` is the standard form column.\n const formClasses = cn(\n {\n \"max-w-settings\": size === \"sm\",\n \"max-w-form\": size === \"md\",\n \"max-w-6xl\": size === \"lg\",\n \"max-w-full\": size === \"full\",\n },\n className,\n );\n\n const buttonSize = size === \"full\" ? \"md\" : size;\n\n return (\n <form ref={ref} onSubmit={handleSubmit} className={formClasses}>\n {/* Implicit submission. A form with several fields and no submit button\n inside it ignores Enter entirely — which is what happens whenever the\n save button lives in a page header (showButtons={false}). This hidden\n button is the form's default button, so Enter in a text field submits\n the way it does everywhere else. */}\n {!showButtons && <button type=\"submit\" className=\"hidden\" tabIndex={-1} aria-hidden />}\n\n <div className=\"flex flex-col\">{groups.map(renderGroup)}</div>\n\n {/* Footer. Only when it has something in it: without the panel around the\n form, an empty footer is a rule hanging under the last section. */}\n {showButtons && (\n <div className=\"flex items-center gap-2 border-t border-border-subtle py-4\">\n <>\n <div className=\"ml-auto\" />\n {onCancel && (\n <Button\n type=\"button\"\n variant={cancelButton.variant ?? \"ghost\"}\n size={cancelButton.size || buttonSize}\n disabled={cancelButton.disabled || loading}\n onClick={onCancel}\n className={cancelButton.className}\n >\n {cancelButton.label}\n </Button>\n )}\n\n <Button\n type=\"submit\"\n variant={submitButton.variant}\n size={submitButton.size || buttonSize}\n disabled={submitButton.disabled || loading}\n loading={loading}\n className={submitButton.className}\n >\n {submitButton.label}\n </Button>\n </>\n </div>\n )}\n </form>\n );\n};\n","import type React from \"react\";\nimport { useRef, useState } from \"react\";\nimport { Button } from \"../action/Button\";\nimport { Text } from \"../typography/Text\";\nimport { cn } from \"../utils/cn\";\n\nexport interface ImageFieldProps {\n label?: string;\n /** Current value as a base64 data URI, or undefined when empty. */\n value?: string;\n onChange: (value: string | undefined) => void;\n /** Longest side after downscaling. */\n maxDimension?: number;\n /**\n * Ceiling for the encoded result. The server checks this too — this one is\n * here so the user finds out before saving, not so the rule is enforced.\n */\n maxBytes?: number;\n /** Preview box shape. */\n aspect?: \"square\" | \"wide\";\n helperText?: React.ReactNode;\n disabled?: boolean;\n className?: string;\n chooseLabel?: string;\n removeLabel?: string;\n /** Shown when the picked file is still too large after downscaling. */\n tooLargeLabel?: string;\n}\n\n/**\n * Pick an image and keep it inline as a base64 data URI.\n *\n * For the small brand assets that live on a row rather than in the storage app\n * — an organisation logo, a help centre favicon. Not for content: an article\n * image belongs in storage, once storage can serve a public URL.\n *\n * The canvas downscale is a courtesy, not a limit. The same ceiling is enforced\n * server-side by `assertInlineImage`, because anyone can call the API without\n * going through this field.\n */\nexport function ImageField({\n label,\n value,\n onChange,\n maxDimension = 256,\n maxBytes,\n aspect = \"square\",\n helperText,\n disabled,\n className,\n chooseLabel = \"Choose\",\n removeLabel = \"Remove\",\n tooLargeLabel = \"That image is too large.\",\n}: ImageFieldProps) {\n const inputRef = useRef<HTMLInputElement>(null);\n const [error, setError] = useState<string | null>(null);\n\n const handleSelect = async (file?: File) => {\n if (!file) return;\n setError(null);\n const encoded = await downscaleToDataUri(file, maxDimension);\n if (maxBytes && approximateBytes(encoded) > maxBytes) {\n setError(tooLargeLabel);\n return;\n }\n onChange(encoded);\n };\n\n return (\n <div className={cn(\"flex flex-col gap-1.5\", className)}>\n {label && <span className=\"text-sm font-medium text-text\">{label}</span>}\n\n <div className=\"flex items-center gap-4\">\n <div\n className={cn(\n \"flex items-center justify-center overflow-hidden rounded-md border border-border bg-surface-sunk\",\n aspect === \"square\" ? \"h-16 w-16\" : \"h-16 w-28\",\n )}\n >\n {value ? (\n <img src={value} alt=\"\" className=\"h-full w-full object-contain\" />\n ) : (\n <Text variant=\"caption\" color=\"muted\">\n —\n </Text>\n )}\n </div>\n\n <div className=\"flex flex-col gap-2\">\n <div className=\"flex gap-2\">\n <Button\n type=\"button\"\n variant=\"secondary\"\n disabled={disabled}\n onClick={() => inputRef.current?.click()}\n >\n {chooseLabel}\n </Button>\n {value && (\n <Button\n type=\"button\"\n variant=\"ghost\"\n disabled={disabled}\n onClick={() => {\n setError(null);\n onChange(undefined);\n }}\n >\n {removeLabel}\n </Button>\n )}\n </div>\n {(error || helperText) && (\n <Text variant=\"caption\" color={error ? \"error\" : \"muted\"}>\n {error ?? helperText}\n </Text>\n )}\n </div>\n </div>\n\n <input\n ref={inputRef}\n type=\"file\"\n accept=\"image/png,image/jpeg,image/webp\"\n className=\"hidden\"\n onChange={(e) => void handleSelect(e.target.files?.[0])}\n />\n </div>\n );\n}\n\n/** Roughly how many bytes a data URI's payload decodes to. */\nexport function approximateBytes(dataUri: string): number {\n const base64 = dataUri.slice(dataUri.indexOf(\",\") + 1);\n const padding = base64.endsWith(\"==\") ? 2 : base64.endsWith(\"=\") ? 1 : 0;\n return Math.floor((base64.length * 3) / 4) - padding;\n}\n\n/** Read a file, shrink it to `maxDimension` on a canvas, return a PNG data URI. */\nfunction downscaleToDataUri(file: File, maxDimension: number): Promise<string> {\n return new Promise((resolve, reject) => {\n const reader = new FileReader();\n reader.onload = () => {\n const img = new Image();\n img.onload = () => {\n const scale = Math.min(1, maxDimension / Math.max(img.width, img.height));\n const width = Math.round(img.width * scale);\n const height = Math.round(img.height * scale);\n const canvas = document.createElement(\"canvas\");\n canvas.width = width;\n canvas.height = height;\n const ctx = canvas.getContext(\"2d\");\n if (!ctx) {\n // No canvas: hand back the original rather than nothing, and let the\n // size check decide whether it is usable.\n resolve(reader.result as string);\n return;\n }\n ctx.drawImage(img, 0, 0, width, height);\n resolve(canvas.toDataURL(\"image/png\"));\n };\n img.onerror = reject;\n img.src = reader.result as string;\n };\n reader.onerror = reject;\n reader.readAsDataURL(file);\n });\n}\n","import { ChevronDown, Loader2, Search } from \"lucide-react\";\nimport type React from \"react\";\nimport { forwardRef, useEffect, useMemo, useRef, useState } from \"react\";\n\nconst cn = (...classes: string[]) => classes.filter(Boolean).join(\" \");\n\nconst SearchIcon = (props: React.SVGProps<SVGSVGElement>) => (\n <Search {...props} strokeWidth={1.5} aria-hidden />\n);\n\nconst ChevronDownIcon = (props: React.SVGProps<SVGSVGElement>) => (\n <ChevronDown {...props} strokeWidth={1.5} aria-hidden />\n);\n\nconst inputSizes = {\n sm: \"h-control-sm px-3 text-sm\",\n md: \"h-control-md px-3 text-base\",\n lg: \"h-control-lg px-3 text-base\",\n full: \"h-control-md w-full px-3 text-base\",\n};\n\nconst iconSizes = {\n sm: \"h-4 w-4\",\n md: \"h-5 w-5\",\n lg: \"h-6 w-6\",\n full: \"h-6 w-6\",\n};\n\nexport interface SearchableTextFieldOption {\n value: string;\n label: string;\n}\n\nexport interface SearchableTextFieldProps\n extends Omit<React.InputHTMLAttributes<HTMLInputElement>, \"size\" | \"onSelect\"> {\n label?: string;\n helperText?: string;\n error?: string;\n size?: \"sm\" | \"md\" | \"lg\" | \"full\";\n fullWidth?: boolean;\n startIcon?: React.ReactNode;\n loading?: boolean;\n containerClassName?: string;\n labelClassName?: string;\n\n options: SearchableTextFieldOption[];\n onRemoteSearch?: (searchTerm: string) => Promise<void>;\n\n onSelect?: (value: string) => void;\n debounceTime?: number;\n /**\n * Selector mode: on open (focus/chevron) show all options, even when the current\n * value is an already-selected option; filter only once the user actually types.\n * For fields that are more picker than free search (e.g. a sender selector).\n */\n showAllOnOpen?: boolean;\n}\n\nexport const SearchableTextField = forwardRef<HTMLInputElement, SearchableTextFieldProps>(\n (\n {\n label,\n helperText,\n error,\n size = \"md\",\n fullWidth = false,\n startIcon = <SearchIcon />,\n loading: externalLoading = false,\n containerClassName,\n labelClassName,\n className,\n id,\n options,\n onRemoteSearch,\n onSelect,\n debounceTime = 500,\n showAllOnOpen = false,\n value: propValue,\n onChange,\n ...props\n },\n ref,\n ) => {\n const inputId = id || `searchfield-${Math.random().toString(36).substr(2, 9)}`;\n const hasError = Boolean(error);\n const containerRef = useRef(null);\n const inputRef = useRef(null);\n\n const [inputValue, setInputValue] = useState(propValue || \"\");\n const [isOpen, setIsOpen] = useState(false);\n const [isSearchingRemote, setIsSearchingRemote] = useState(false);\n // Selector mode: has the user actually typed since opening? If not, show the\n // full list instead of filtering on the chosen value.\n const [typedSinceOpen, setTypedSinceOpen] = useState(false);\n\n useEffect(() => {\n if (propValue !== undefined) {\n setInputValue(propValue);\n }\n }, [propValue]);\n\n const filteredOptions = useMemo(() => {\n if (showAllOnOpen && !typedSinceOpen) return options;\n if (!inputValue) return options;\n const lowerCaseInput = String(inputValue).toLowerCase();\n return options.filter((option) =>\n String(option.label).toLowerCase().includes(lowerCaseInput),\n );\n }, [inputValue, options, showAllOnOpen, typedSinceOpen]);\n\n useEffect(() => {\n if (!onRemoteSearch || !inputValue) {\n setIsSearchingRemote(false);\n return;\n }\n\n const handler = setTimeout(async () => {\n setIsSearchingRemote(true);\n try {\n await onRemoteSearch(String(inputValue));\n } catch (e) {\n console.error(\"Remote search failed:\", e);\n } finally {\n setIsSearchingRemote(false);\n }\n }, debounceTime);\n\n return () => {\n clearTimeout(handler);\n setIsSearchingRemote(false);\n };\n }, [inputValue, debounceTime, onRemoteSearch]);\n\n useEffect(() => {\n const handleClickOutside = (event: MouseEvent) => {\n if (\n containerRef.current &&\n !(containerRef.current as HTMLElement).contains(event.target as Node)\n ) {\n setIsOpen(false);\n }\n };\n document.addEventListener(\"mousedown\", handleClickOutside);\n return () => document.removeEventListener(\"mousedown\", handleClickOutside);\n }, []);\n\n const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n const newValue = e.target.value;\n setInputValue(newValue);\n setIsOpen(true);\n setTypedSinceOpen(true);\n if (onChange) {\n onChange(e);\n }\n };\n\n const handleSelect = (option: SearchableTextFieldOption) => {\n setInputValue(option.label);\n setIsOpen(false);\n setTypedSinceOpen(false);\n if (onSelect) {\n onSelect(option.value);\n }\n };\n\n const handleFocus = () => {\n // Open dropdown alleen als er opties zijn of remote search beschikbaar is\n if (options.length > 0 || onRemoteSearch) {\n setIsOpen(true);\n setTypedSinceOpen(false);\n }\n };\n\n const loading = externalLoading || isSearchingRemote;\n\n const showDropdown = isOpen && (filteredOptions.length > 0 || loading);\n\n const resolvedRef = useMemo(() => ref || inputRef, [ref]);\n\n return (\n <div\n className={cn(\n \"flex flex-col\",\n fullWidth ? \"w-full\" : \"\",\n containerClassName || \"\",\n \"relative\",\n )}\n ref={containerRef}\n >\n {label && (\n <label\n htmlFor={inputId}\n className={cn(\n \"block text-sm font-medium mb-1\",\n hasError ? \"text-danger-fg\" : \"text-text\",\n labelClassName || \"\",\n )}\n >\n {label}\n </label>\n )}\n\n <div className=\"relative\">\n {startIcon && (\n <div className=\"absolute left-0 pl-3 flex items-center pointer-events-none h-full\">\n <span className={cn(\"text-text-muted\", iconSizes[size])}>{startIcon}</span>\n </div>\n )}\n\n <input\n ref={resolvedRef}\n id={inputId}\n value={inputValue}\n onChange={handleChange}\n onFocus={handleFocus}\n className={cn(\n // Base styles\n \"block w-full rounded-tile border bg-surface-input transition-colors duration-fast ease-out\",\n \"placeholder:text-text-subtle focus-visible:outline-none focus-visible:focus-ring\",\n \"disabled:cursor-not-allowed disabled:bg-surface-hover disabled:text-text-disabled\",\n\n // Size styles\n inputSizes[size],\n\n // Icon padding\n startIcon ? \"pl-10\" : \"\",\n \"pr-10\", // room for the dropdown/loading icon\n\n // State styles\n hasError\n ? \"border-danger-border text-danger-fg focus-visible:focus-ring\"\n : \"border-border-strong text-text\",\n\n className || \"\",\n )}\n {...props}\n />\n\n {/* END ICON (Laden/Dropdown) */}\n <div className=\"absolute inset-y-0 right-0 pr-3 flex items-center\">\n {loading || externalLoading ? (\n // Loading Spinner\n <Loader2 className={cn(\"animate-spin text-info-fg\", iconSizes[size])} aria-hidden />\n ) : (\n // Dropdown Chevron\n <ChevronDownIcon\n className={cn(\n \"text-text-muted cursor-pointer transition-transform\",\n iconSizes[size],\n isOpen ? \"rotate-180\" : \"rotate-0\",\n )}\n onClick={() => {\n setIsOpen((prev) => {\n if (!prev) setTypedSinceOpen(false);\n return !prev;\n });\n }}\n />\n )}\n </div>\n </div>\n\n {/* DROPDOWN LIJST */}\n {showDropdown && (\n <ul\n className=\"absolute z-10 mt-1 w-full bg-surface border border-border rounded-lg shadow-lg max-h-60 overflow-auto top-full\"\n role=\"listbox\"\n >\n {filteredOptions.length > 0 ? (\n filteredOptions.map((option: SearchableTextFieldOption) => (\n <li\n key={option.value}\n className=\"px-4 py-2 cursor-pointer text-text hover:bg-accent-soft hover:text-accent transition-colors\"\n onClick={() => handleSelect(option)}\n role=\"option\"\n aria-selected={inputValue === option.label}\n >\n {option.label}\n </li>\n ))\n ) : (\n <li className=\"px-4 py-2 text-text-muted\">\n {loading || externalLoading ? \"Zoeken op afstand...\" : \"Geen resultaten gevonden.\"}\n </li>\n )}\n </ul>\n )}\n\n {/* HELPER/ERROR TEKST */}\n {(error || helperText) && (\n <p className={cn(\"mt-1 text-xs\", hasError ? \"text-danger-fg\" : \"text-text-muted\")}>\n {error || helperText}\n </p>\n )}\n\n {/* Simple example of the current status for demo */}\n {onRemoteSearch && (\n <p className=\"mt-2 text-xs text-success-fg\">\n Huidige zoekterm (niet-gedebounced): {String(inputValue)}\n </p>\n )}\n </div>\n );\n },\n);\n\nSearchableTextField.displayName = \"SearchableTextField\";\n","import type React from \"react\";\nimport { forwardRef } from \"react\";\nimport { cn } from \"../utils/cn\";\n\nexport interface SwitchProps\n extends Omit<\n React.InputHTMLAttributes<HTMLInputElement>,\n \"type\" | \"size\" | \"checked\" | \"onChange\"\n > {\n /**\n * Current state. Deliberately required and never held internally: several\n * call sites drive this from an optimistic update that rolls back on a failed\n * request, and internal state would silently swallow the rollback.\n */\n checked: boolean;\n onChange: (checked: boolean) => void;\n /** Text beside the switch. Omit it and pass `aria-label` instead. */\n label?: React.ReactNode;\n disabled?: boolean;\n}\n\n/**\n * On/off switch for a setting that applies immediately.\n *\n * Use `Checkbox` instead when the value is part of a form the user submits —\n * a switch says \"this is now on\", a checkbox says \"include this when I save\".\n *\n * @example\n * ```tsx\n * <Switch checked={syncEnabled} onChange={setSyncEnabled} label=\"Agenda synchroniseren\" />\n * ```\n */\nexport const Switch = forwardRef<HTMLInputElement, SwitchProps>(\n ({ checked, onChange, label, disabled = false, className, ...props }, ref) => {\n const track = (\n <span\n aria-hidden\n className={cn(\n \"relative inline-flex size-switch-track shrink-0 items-center rounded-full\",\n \"transition-colors duration-fast ease-out\",\n checked ? \"bg-accent\" : \"bg-surface-hover\",\n disabled && \"opacity-50\",\n )}\n >\n <span\n className={cn(\n \"absolute left-0.5 size-switch-knob rounded-full bg-surface shadow-overlay\",\n \"transition-transform duration-fast ease-out\",\n checked && \"translate-switch\",\n )}\n />\n </span>\n );\n\n return (\n <label\n className={cn(\n \"inline-flex items-center gap-2\",\n disabled ? \"cursor-not-allowed\" : \"cursor-pointer\",\n className,\n )}\n >\n <input\n ref={ref}\n type=\"checkbox\"\n role=\"switch\"\n checked={checked}\n disabled={disabled}\n onChange={(event) => onChange(event.target.checked)}\n className=\"peer sr-only\"\n {...props}\n />\n {/* The ring lives on the track, since the input itself is visually hidden. */}\n <span className=\"inline-flex rounded-full peer-focus-visible:focus-ring\">{track}</span>\n {label && <span className=\"text-sm text-text\">{label}</span>}\n </label>\n );\n },\n);\n\nSwitch.displayName = \"Switch\";\n","import type { ReactNode } from \"react\";\nimport { cn } from \"../utils/cn\";\n\nexport interface MeetingGridProps {\n tiles: ReactNode[];\n /**\n * The tile that gets the main area — the dominant speaker, or the other\n * party in a 1-on-1. The rest renders as a strip underneath.\n */\n pinned?: ReactNode;\n /**\n * Max strip tiles side by side; the overflow collapses into one `+N` tile.\n * Without it a twelve-person meeting squeezes into a 448px dock as twelve\n * unreadable slivers.\n */\n maxStrip?: number;\n className?: string;\n}\n\n/**\n * Columns per participant count, measured against **the pane itself**, not the\n * viewport: `md:` breakpoints look at the window, while this pane can just as\n * well be a 300px corner of a wide screen — hence the `@container` variants.\n *\n * Written out literally, not composed: Tailwind scans source for literal class\n * names, so a name that only exists at runtime does not exist in the build.\n */\nconst COLUMNS: { upTo: number; cols: string }[] = [\n { upTo: 1, cols: \"grid-cols-1\" },\n { upTo: 2, cols: \"grid-cols-1 @[22rem]:grid-cols-2\" },\n { upTo: 4, cols: \"grid-cols-1 @[18rem]:grid-cols-2\" },\n { upTo: 9, cols: \"grid-cols-2 @[26rem]:grid-cols-3\" },\n];\nconst COLUMNS_MANY = \"grid-cols-2 @[26rem]:grid-cols-3 @[40rem]:grid-cols-4\";\n\nexport function columnsFor(count: number): string {\n return COLUMNS.find((c) => count <= c.upTo)?.cols ?? COLUMNS_MANY;\n}\n\n/**\n * How many strip tiles to show, and how many collapse into `+N`.\n *\n * The counter takes a slot itself — otherwise it replaces one tile with the\n * message that one tile is missing, which is no gain. With exactly enough\n * room it stays away.\n */\nexport function splitStrip(count: number, maxStrip?: number): { shown: number; hidden: number } {\n const room = maxStrip ?? count;\n if (count <= room) return { shown: count, hidden: 0 };\n const shown = Math.max(0, room - 1);\n return { shown, hidden: count - shown };\n}\n\n/**\n * Layout primitive for meeting views. When `pinned` is provided it gets the\n * dominant area and remaining tiles render in a strip underneath; otherwise\n * tiles flow in an auto-sized grid.\n *\n * The pane sizes the tiles, not the other way around: every cell is\n * `min-w-0 min-h-0` and rows share the height (`auto-rows-fr`) so the grid\n * fits the box it gets. Give tiles `fill`: a tile that takes its height from\n * its own width (`aspect-video`, the default) pushes the grid out of bounds\n * once the pane is shorter than that ratio.\n */\nexport function MeetingGrid({ tiles, pinned, maxStrip, className }: MeetingGridProps) {\n if (pinned) {\n const { shown: room, hidden } = splitStrip(tiles.length, maxStrip);\n const shown = tiles.slice(0, room);\n\n return (\n <div className={cn(\"@container flex h-full min-h-0 flex-col gap-2\", className)}>\n <div className=\"min-h-0 flex-1\">{pinned}</div>\n {tiles.length > 0 && (\n // Fixed height as a share of the pane, with a floor so the strip\n // does not squeeze into a line in a shallow dock.\n <div className=\"flex h-[22%] min-h-14 shrink-0 gap-2 overflow-x-auto\">\n {shown.map((t, i) => (\n <div key={i} className=\"h-full min-w-0 shrink-0 aspect-video\">\n {t}\n </div>\n ))}\n {hidden > 0 && (\n <div className=\"flex h-full aspect-video shrink-0 items-center justify-center rounded-lg bg-video-surface text-sm font-semibold tabular-nums text-white/80\">\n +{hidden}\n </div>\n )}\n </div>\n )}\n </div>\n );\n }\n\n return (\n <div\n className={cn(\n \"@container grid h-full min-h-0 auto-rows-fr gap-2\",\n columnsFor(tiles.length),\n className,\n )}\n >\n {tiles.map((t, i) => (\n <div key={i} className=\"min-h-0 min-w-0\">\n {t}\n </div>\n ))}\n </div>\n );\n}\n","import { Mic, MicOff, User, VideoOff } from \"lucide-react\";\nimport type { ReactNode } from \"react\";\nimport { cn } from \"../utils/cn\";\n\nexport interface ParticipantTileProps {\n name: string;\n avatarUrl?: string;\n muted?: boolean;\n speaking?: boolean;\n hasVideo?: boolean;\n videoSlot?: ReactNode;\n className?: string;\n /**\n * Fill the parent box (height + width) instead of keeping a 16:9 aspect\n * ratio. Use for a pinned/main tile inside a bounded container so it fits the\n * available space without deriving its height from its width (which would\n * overflow a wide surface). Grid thumbnails keep the default aspect ratio.\n */\n fill?: boolean;\n}\n\n/**\n * Provider-agnostic participant tile. Shows avatar + name when no video is\n * available; otherwise renders the `videoSlot` passed by the caller (typically\n * a <VideoSurface /> wired to a provider-specific stream renderer).\n *\n * Everything inside scales with the tile, not the window: the tile is its own\n * `@container`, so the avatar is a share of the tile's width and the name only\n * appears when there is room. Tailwind's viewport breakpoints (`md:`) look at\n * the screen, while the tile can be 120px wide on a 4K monitor — fixed sizes\n * (a 64px avatar, a `max-w-35` label) overflowed a 90px strip tile.\n */\nexport function ParticipantTile({\n name,\n avatarUrl,\n muted,\n speaking,\n hasVideo,\n videoSlot,\n className,\n fill,\n}: ParticipantTileProps) {\n return (\n <div\n className={cn(\n \"@container relative flex min-h-0 min-w-0 items-center justify-center overflow-hidden rounded-lg bg-video-surface\",\n fill ? \"h-full w-full\" : \"aspect-video\",\n speaking && \"ring-2 ring-speaking\",\n className,\n )}\n >\n {hasVideo && videoSlot ? (\n <div className=\"absolute inset-0\">{videoSlot}</div>\n ) : (\n <div className=\"flex min-h-0 min-w-0 items-center justify-center text-gray-300\">\n {avatarUrl ? (\n <img\n src={avatarUrl}\n alt={name}\n className=\"aspect-square w-[38%] max-w-16 min-w-6 rounded-full object-cover\"\n />\n ) : (\n <div className=\"flex aspect-square w-[38%] max-w-16 min-w-6 items-center justify-center rounded-full bg-video-surface-strong\">\n {hasVideo ? <VideoOff className=\"w-1/2 h-1/2\" /> : <User className=\"w-1/2 h-1/2\" />}\n </div>\n )}\n </div>\n )}\n\n {/* The label must never widen the tile: `inset-x` rather than a fixed\n `max-w` keeps it inside the edge however narrow the tile gets. Below\n 9rem the name drops and the mic icon remains — the information that\n still counts in a strip tile. */}\n <div className=\"absolute inset-x-2 bottom-2 flex w-fit max-w-[calc(100%-1rem)] items-center gap-1.5 rounded bg-video-overlay px-1.5 py-1 text-xs text-white\">\n {muted ? <MicOff className=\"size-3 shrink-0\" /> : <Mic className=\"size-3 shrink-0\" />}\n <span className=\"hidden truncate @[9rem]:inline\">{name}</span>\n </div>\n </div>\n );\n}\n","import { useEffect, useRef } from \"react\";\nimport { cn } from \"../utils/cn\";\n\nexport interface VideoSurfaceProps {\n /**\n * Provider-specific mount callback. Receives the container element so\n * provider SDKs (Azure, Zoom, etc.) can attach their own video renderer.\n * Return an optional cleanup function.\n */\n attach: (el: HTMLDivElement) => void | (() => void);\n active?: boolean;\n mirrored?: boolean;\n className?: string;\n}\n\n/**\n * Provider-agnostic video tile surface. Owns the DOM slot; the caller wires\n * their SDK's renderer to it via `attach`.\n */\nexport function VideoSurface({\n attach,\n active = true,\n mirrored = false,\n className,\n}: VideoSurfaceProps) {\n const ref = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n if (!active || !ref.current) return;\n const cleanup = attach(ref.current);\n return () => {\n if (typeof cleanup === \"function\") cleanup();\n if (ref.current) ref.current.innerHTML = \"\";\n };\n }, [attach, active]);\n\n return (\n <div\n ref={ref}\n className={cn(\n \"w-full h-full bg-video-surface-strong overflow-hidden\",\n mirrored && \"[&>*]:scale-x-[-1]\",\n className,\n )}\n />\n );\n}\n","import type React from \"react\";\nimport { ContentLoading } from \"../feedback/ContentLoading\";\nimport { cn } from \"../utils/cn\";\n\nexport interface MenuItemAction {\n /** Icon on the right side of the row. */\n icon: React.ReactNode;\n /** Required: aria-label + tooltip — the button is icon-only. */\n label: string;\n onClick: () => void;\n /** \"hover\" (default) = only visible on hover/focus, \"always\" = always visible. */\n visibility?: \"hover\" | \"always\";\n}\n\nexport interface MenuItem {\n icon?: React.ReactNode;\n label: string;\n count?: number;\n /**\n * Short hint on the right of the row, where the count normally sits — for\n * rows where the number is not the interesting fact, or cannot be known\n * without loading everything. When both are set the count wins.\n */\n meta?: React.ReactNode;\n /**\n * Optional action on the right of the row — delete, pin, mute. Does not\n * navigate: the button stops propagation and only calls its own onClick.\n */\n action?: MenuItemAction;\n children?: MenuItem[];\n path?: string;\n /**\n * @deprecated Groups no longer collapse — a section with children renders as\n * a heading with its items below. Kept so existing call sites keep\n * compiling; the value is ignored.\n */\n defaultCollapsed?: boolean;\n}\n\ninterface SidebarMenuProps {\n items: MenuItem[];\n isSelectedHandler: (path: string | undefined) => boolean;\n onClick?: (path: string | undefined) => void;\n preMenuItemsComponent?: React.ReactNode;\n /**\n * The app that owns this menu has not reported in yet. Draws the menu's own\n * geometry in placeholder form rather than nothing: an empty column during\n * startup reads as \"this app has no menu\", and the items then appear and shove\n * the first click target somewhere else.\n */\n loading?: boolean;\n}\n\n/**\n * Placeholder in the shape of the menu: a caption, then rows at item height.\n * The geometry lives in `ContentLoading`'s `menu` shape so there is one menu\n * placeholder in the product rather than a copy per column that renders one.\n */\nconst SidebarMenuSkeleton: React.FC = () => <ContentLoading variant=\"inline\" shape=\"menu\" />;\n\ntype SelectedHandler = (path: string | undefined) => boolean;\ntype ClickHandler = (path: string | undefined) => void;\n\nconst SidebarMenuItem: React.FC<{\n item: MenuItem;\n isSelectedHandler: SelectedHandler;\n onClick?: ClickHandler;\n depth?: number;\n}> = ({ item, isSelectedHandler, onClick, depth = 0 }) => {\n const hasChildren = Boolean(item.children && item.children.length > 0);\n const isSelected = isSelectedHandler ? isSelectedHandler(item.path) : false;\n\n // An item with children is a section, not a destination: no hover, no\n // cursor, no selection — just a heading above its items.\n if (hasChildren) {\n return (\n <li>\n {/* A section without a name gets no heading — otherwise an empty\n `pt-5 pb-2` line promises a group without naming it. */}\n {item.label && (\n <div\n className={cn(\n \"flex items-center gap-2 px-2.5 pt-5 pb-2 text-2xs font-semibold uppercase tracking-label text-text-disabled\",\n )}\n >\n <span className=\"min-w-0 flex-1 truncate\">{item.label}</span>\n {item.count !== undefined && item.count > 0 && (\n <span className=\"font-normal tabular-nums\">{item.count}</span>\n )}\n </div>\n )}\n <ul className=\"flex flex-col gap-px\">\n {item.children?.map((child, index) => (\n <SidebarMenuItem\n key={index}\n item={child}\n isSelectedHandler={isSelectedHandler}\n onClick={onClick}\n depth={depth + 1}\n />\n ))}\n </ul>\n </li>\n );\n }\n\n // A hover action swaps places with the count instead of sliding in next to\n // it, otherwise the row shifts as soon as the mouse arrives.\n const action = item.action;\n const swapsWithCount = action?.visibility !== \"always\";\n\n return (\n <li>\n <div\n role=\"button\"\n tabIndex={0}\n onClick={() => item.path && onClick?.(item.path)}\n onKeyDown={(e) => {\n if (e.key === \"Enter\" || e.key === \" \") {\n e.preventDefault();\n item.path && onClick?.(item.path);\n }\n }}\n className={cn(\n // The column sits on the backdrop, not on a panel: the active item\n // is a raised plate, and hover is the darker backdrop hover rather\n // than the lighter surface hover.\n \"group/item flex h-row-md cursor-pointer items-center gap-2.5 rounded-md px-2.5 text-base no-underline\",\n \"transition-colors duration-fast ease-out\",\n \"focus-visible:outline-none focus-visible:focus-ring\",\n isSelected\n ? // `surface-hover`, not pure white: on the 0.93 backdrop a #fff plate\n // is a 7% jump and the current item shouts. Still a raised plate —\n // same language as the rail and the segmented toggle — just quieter.\n \"bg-surface-hover font-medium text-text shadow-sm\"\n : \"text-text-muted hover:bg-page-hover hover:text-text\",\n )}\n >\n {item.icon && (\n <span\n className={cn(\n \"flex w-5 shrink-0 items-center justify-center\",\n isSelected ? \"text-text-muted\" : \"text-text-subtle\",\n )}\n >\n {item.icon}\n </span>\n )}\n <span className=\"min-w-0 flex-1 truncate\">{item.label}</span>\n {item.count !== undefined && item.count > 0 && (\n <span\n className={cn(\n \"shrink-0 text-xs tabular-nums\",\n isSelected\n ? \"rounded-full bg-accent px-1.5 font-semibold text-accent-fg\"\n : \"text-text-subtle\",\n action && swapsWithCount && \"group-hover/item:hidden group-focus-within/item:hidden\",\n )}\n >\n {item.count}\n </span>\n )}\n {item.count === undefined && item.meta !== undefined && (\n <span\n className={cn(\n \"shrink-0 text-xs text-text-subtle\",\n action && swapsWithCount && \"group-hover/item:hidden group-focus-within/item:hidden\",\n )}\n >\n {item.meta}\n </span>\n )}\n {action && (\n <button\n type=\"button\"\n aria-label={action.label}\n title={action.label}\n onClick={(e) => {\n e.stopPropagation();\n action.onClick();\n }}\n // Enter/Space would otherwise bubble to the row and still navigate.\n onKeyDown={(e) => e.stopPropagation()}\n className={cn(\n \"flex size-5 shrink-0 items-center justify-center rounded-sm\",\n \"text-text-subtle transition-colors duration-fast ease-out\",\n \"hover:bg-page-hover hover:text-text\",\n \"focus-visible:outline-none focus-visible:focus-ring\",\n swapsWithCount && \"opacity-0 group-hover/item:opacity-100 focus-visible:opacity-100\",\n )}\n >\n {action.icon}\n </button>\n )}\n </div>\n </li>\n );\n};\n\n/**\n * A free slot (search) with the nav items below. Sections with children are\n * headings, not buttons. No title or action icons here: the app switcher and\n * user footer are shell chrome in `NavigationPanel`; this only fills the\n * middle of that column.\n */\nexport const SidebarMenu: React.FC<SidebarMenuProps> = ({\n items,\n isSelectedHandler,\n onClick,\n preMenuItemsComponent,\n loading = false,\n}) => {\n if (loading) {\n return (\n <nav className=\"flex h-full min-h-0 shrink-0 flex-col\">\n {preMenuItemsComponent && <div className=\"pb-4\">{preMenuItemsComponent}</div>}\n <SidebarMenuSkeleton />\n </nav>\n );\n }\n\n return (\n <nav className=\"flex h-full min-h-0 shrink-0 flex-col\">\n {preMenuItemsComponent && <div className=\"pb-4\">{preMenuItemsComponent}</div>}\n\n <ul className=\"flex min-h-0 flex-1 flex-col gap-px overflow-y-auto\">\n {items.map((item, index) =>\n !item.path && !item.label && !item.children ? (\n <li key={index} aria-hidden className=\"h-4\" />\n ) : (\n <SidebarMenuItem\n key={index}\n item={item}\n isSelectedHandler={isSelectedHandler}\n onClick={onClick}\n />\n ),\n )}\n </ul>\n </nav>\n );\n};\n\nexport default SidebarMenu;\n","import type React from \"react\";\nimport { Button } from \"../action/Button\";\nimport { Text } from \"../typography/Text\";\nimport { Modal } from \"./Modal\";\n\nexport interface ConfirmDialogProps {\n open: boolean;\n title: string;\n /** What is about to happen, and what it costs if it is wrong. */\n description?: React.ReactNode;\n confirmLabel?: string;\n cancelLabel?: string;\n /** `danger` for anything that destroys or detaches something. */\n tone?: \"default\" | \"danger\";\n /** Disables both buttons and puts the confirm button in its loading state. */\n loading?: boolean;\n onConfirm: () => void;\n onCancel: () => void;\n}\n\n/**\n * Confirmation before a destructive or irreversible action.\n *\n * Replaces `window.confirm`, which blocks the main thread, cannot be styled,\n * cannot be translated, and is suppressed outright in some embedded contexts.\n * Note the shape difference: `confirm()` returns a boolean inline, so callers\n * written as `if (!confirm(...)) return;` have to split into a request step\n * and a confirm step.\n */\nexport function ConfirmDialog({\n open,\n title,\n description,\n confirmLabel = \"Bevestigen\",\n cancelLabel = \"Annuleren\",\n tone = \"default\",\n loading = false,\n onConfirm,\n onCancel,\n}: ConfirmDialogProps) {\n return (\n <Modal\n open={open}\n onClose={onCancel}\n title={title}\n size=\"sm\"\n footer={\n <div className=\"flex w-full items-center justify-end gap-2\">\n <Button variant=\"ghost\" onClick={onCancel} disabled={loading}>\n {cancelLabel}\n </Button>\n <Button\n variant={tone === \"danger\" ? \"destructive\" : \"primary\"}\n onClick={onConfirm}\n loading={loading}\n >\n {confirmLabel}\n </Button>\n </div>\n }\n >\n {description && <Text color=\"secondary\">{description}</Text>}\n </Modal>\n );\n}\n","import type React from \"react\";\nimport { forwardRef } from \"react\";\nimport { cn } from \"../utils/cn\";\n\nexport interface BoxProps extends React.HTMLAttributes<HTMLDivElement> {\n /**\n * Padding size\n */\n padding?: \"none\" | \"xs\" | \"sm\" | \"md\" | \"lg\" | \"xl\" | \"2xl\";\n\n /**\n * Margin size\n */\n margin?: \"none\" | \"xs\" | \"sm\" | \"md\" | \"lg\" | \"xl\" | \"2xl\";\n\n /**\n * Background color\n */\n background?:\n | \"none\"\n | \"primary\"\n | \"secondary\"\n | \"accent\"\n | \"success\"\n | \"warning\"\n | \"error\"\n | \"info\"\n | \"neutral\"\n | \"white\"\n | \"module\"\n | \"module-subtle\"\n | \"backdrop\"\n | \"editor\"\n | \"input\";\n\n /**\n * Border radius\n */\n radius?: \"none\" | \"sm\" | \"md\" | \"lg\" | \"xl\" | \"full\";\n\n /**\n * Shadow size\n */\n shadow?: \"none\" | \"sm\" | \"md\" | \"lg\" | \"xl\" | \"2xl\";\n\n /**\n * Border width\n */\n border?: \"none\" | \"sm\" | \"md\" | \"lg\";\n\n /**\n * Border color\n */\n borderColor?:\n | \"primary\"\n | \"secondary\"\n | \"accent\"\n | \"success\"\n | \"warning\"\n | \"error\"\n | \"info\"\n | \"neutral\"\n | \"white\";\n}\n\n/** 4px spacing scale only. */\nconst paddingMap = {\n none: \"p-0\",\n xs: \"p-1\",\n sm: \"p-2\",\n md: \"p-4\",\n lg: \"p-6\",\n xl: \"p-8\",\n \"2xl\": \"p-12\",\n};\n\nconst marginMap = {\n none: \"m-0\",\n xs: \"m-1\",\n sm: \"m-2\",\n md: \"m-4\",\n lg: \"m-6\",\n xl: \"m-8\",\n \"2xl\": \"m-12\",\n};\n\n/** Semantic surfaces — the legacy `module` names stay as aliases. */\nconst backgroundMap = {\n none: \"\",\n primary: \"bg-accent text-accent-fg\",\n secondary: \"bg-surface-hover\",\n accent: \"bg-accent-soft\",\n success: \"bg-success-soft\",\n warning: \"bg-warning-soft\",\n error: \"bg-danger-soft\",\n info: \"bg-info-soft\",\n neutral: \"bg-surface-hover\",\n white: \"bg-surface\",\n module: \"bg-surface\",\n \"module-subtle\": \"bg-surface-sunk\",\n backdrop: \"bg-page\",\n editor: \"bg-surface\",\n input: \"bg-surface-input\",\n};\n\nconst radiusMap = {\n none: \"rounded-none\",\n sm: \"rounded-sm\",\n md: \"rounded-md\",\n lg: \"rounded-lg\",\n xl: \"rounded-xl\",\n full: \"rounded-full\",\n};\n\n/**\n * Elevation: cards are defined by their border, not a shadow — `sm` is\n * intentionally flat. Shadows only mean \"this floats above the page\".\n */\nconst shadowMap = {\n none: \"shadow-none\",\n sm: \"shadow-none\",\n md: \"shadow-overlay\",\n lg: \"shadow-overlay\",\n xl: \"shadow-modal\",\n \"2xl\": \"shadow-modal\",\n};\n\nconst borderMap = {\n none: \"border-0\",\n sm: \"border\",\n md: \"border-2\",\n lg: \"border-4\",\n};\n\nconst borderColorMap = {\n primary: \"border-accent-border\",\n secondary: \"border-border\",\n accent: \"border-accent-border\",\n success: \"border-success-border\",\n warning: \"border-warning-border\",\n error: \"border-danger-border\",\n info: \"border-info-border\",\n neutral: \"border-border-subtle\",\n white: \"border-border-subtle\",\n};\n\n/**\n * Box primitive component for layout and styling\n *\n * @example\n * ```tsx\n * <Box padding=\"md\" background=\"module\" radius=\"lg\" border=\"sm\" borderColor=\"neutral\">\n * Content\n * </Box>\n * ```\n */\nexport const Box = forwardRef<HTMLDivElement, BoxProps>(\n (\n {\n padding,\n margin,\n background,\n radius,\n shadow,\n border,\n borderColor,\n className,\n children,\n ...props\n },\n ref,\n ) => {\n return (\n <div\n ref={ref}\n className={cn(\n padding && paddingMap[padding],\n margin && marginMap[margin],\n background && backgroundMap[background],\n radius && radiusMap[radius],\n shadow && shadowMap[shadow],\n border && borderMap[border],\n borderColor && borderColorMap[borderColor],\n className,\n )}\n {...props}\n >\n {children}\n </div>\n );\n },\n);\n\nBox.displayName = \"Box\";\n","import { Box, type BoxProps } from \"./Box\";\n\nexport interface CardProps extends BoxProps {\n children: React.ReactNode;\n title?: string;\n}\n\n/**\n * Card — panel on a page background. Border, never a shadow (radius-lg = 12px).\n */\nexport const Card = ({ children, title, ...props }: CardProps) => {\n return (\n <Box background=\"module\" radius=\"lg\" shadow=\"none\" border=\"sm\" borderColor=\"neutral\" {...props}>\n {title && <span className=\"mb-2 block text-sm font-semibold text-text\">{title}</span>}\n {children}\n </Box>\n );\n};\n","import type React from \"react\";\nimport { cn } from \"../utils/cn\";\n\nexport interface HeadingProps {\n /** Heading level */\n level?: 1 | 2 | 3 | 4 | 5 | 6;\n /** Visual size (can be different from semantic level) */\n size?: \"xs\" | \"sm\" | \"md\" | \"lg\" | \"xl\" | \"2xl\" | \"3xl\" | \"4xl\";\n /** Font weight */\n weight?: \"light\" | \"normal\" | \"medium\" | \"semibold\" | \"bold\";\n /** Text color */\n color?:\n | \"primary\"\n | \"secondary\"\n | \"accent\"\n | \"success\"\n | \"warning\"\n | \"error\"\n | \"info\"\n | \"neutral\"\n | \"current\";\n /** Text alignment */\n align?: \"left\" | \"center\" | \"right\";\n /** Whether to truncate text with ellipsis */\n truncate?: boolean;\n /** Additional CSS classes */\n className?: string;\n /** Child content */\n children: React.ReactNode;\n}\n\n/**\n * Heading — semantic level, visual size from the token scale.\n *\n * md 13 · lg 16 (section head) · xl 20 (detail title)\n * 2xl 24 (page title) · 3xl / 4xl 30 (display)\n *\n * Default weight is semibold: the system has no 700 headings.\n */\nconst sizeMap: Record<NonNullable<HeadingProps[\"size\"]>, string> = {\n xs: \"text-xs\",\n sm: \"text-sm\",\n md: \"text-base\",\n lg: \"text-lg\",\n xl: \"text-xl\",\n \"2xl\": \"text-2xl\",\n \"3xl\": \"text-3xl\",\n \"4xl\": \"text-3xl\",\n};\n\nconst weightMap: Record<NonNullable<HeadingProps[\"weight\"]>, string> = {\n light: \"font-normal\",\n normal: \"font-normal\",\n medium: \"font-medium\",\n semibold: \"font-semibold\",\n bold: \"font-bold\",\n};\n\nconst colorMap: Record<NonNullable<HeadingProps[\"color\"]>, string> = {\n primary: \"text-text\",\n secondary: \"text-text-muted\",\n accent: \"text-accent\",\n success: \"text-success-fg\",\n warning: \"text-warning-fg\",\n error: \"text-danger-fg\",\n info: \"text-info-fg\",\n neutral: \"text-text\",\n current: \"text-current\",\n};\n\nconst alignMap: Record<NonNullable<HeadingProps[\"align\"]>, string> = {\n left: \"text-left\",\n center: \"text-center\",\n right: \"text-right\",\n};\n\nexport const Heading: React.FC<HeadingProps> = ({\n level = 1,\n size,\n weight = \"semibold\",\n color = \"current\",\n align = \"left\",\n truncate = false,\n className,\n children,\n ...props\n}) => {\n const Tag = `h${level}` as keyof React.JSX.IntrinsicElements;\n\n const headingClasses = cn(\n \"font-sans text-pretty\",\n sizeMap[size || getDefaultSize(level)!],\n weightMap[weight],\n colorMap[color],\n alignMap[align],\n truncate && \"truncate\",\n className,\n );\n\n return (\n <Tag className={headingClasses} {...props}>\n {children}\n </Tag>\n );\n};\n\nfunction getDefaultSize(level: number): HeadingProps[\"size\"] {\n const sizeMap: Record<number, HeadingProps[\"size\"]> = {\n 1: \"2xl\",\n 2: \"xl\",\n 3: \"lg\",\n 4: \"lg\",\n 5: \"md\",\n 6: \"md\",\n };\n\n return sizeMap[level] || \"md\";\n}\n","/** The categorical set — identity without status. See --color-cat-*. */\nexport type CatTone = \"cat-1\" | \"cat-2\" | \"cat-3\" | \"cat-4\" | \"cat-5\";\n\nconst TONES: CatTone[] = [\"cat-1\", \"cat-2\", \"cat-3\", \"cat-4\", \"cat-5\"];\n\n/**\n * A stable colour for a thing that has an identity but no state — a knowledge\n * base, an inbox, a team, a transcript speaker.\n *\n * Derived from the id, so the colour survives reloads and is the same for every\n * user. Storing one would mean a column per entity that wants a dot; a hash\n * gets the same result for free, and the categorical tokens are equal-weight by\n * construction so no entry can accidentally outrank another.\n */\nexport function catTone(id: string): CatTone {\n let hash = 0;\n for (let i = 0; i < id.length; i++) hash = (hash * 31 + id.charCodeAt(i)) >>> 0;\n return TONES[hash % TONES.length];\n}\n"],"names":["cn","inputs","clsx","createVariants","variants","variant","value","createSizes","sizes","size","buttonVariants","buttonSizes","iconOnlySizes","Button","forwardRef","fullWidth","loading","leftIcon","rightIcon","iconOnly","shape","className","children","disabled","props","ref","isDisabled","jsxs","jsx","ButtonGroup","orientation","attached","baseClasses","resolveLucideIcon","name","pascal","part","found","LucideIcons","colorMap","Icon","IconComponent","color","clickable","onClick","ariaLabel","sizeValue","getSizeValue","iconClasses","iconProps","e","useAnchoredPosition","open","anchorRef","gap","matchWidth","placement","style","setStyle","useState","useLayoutEffect","place","anchor","rect","spaceBelow","spaceAbove","wantsTop","flip","onTop","next","Popover","trigger","rootClassName","triggerClassName","triggerLabel","isOpen","setIsOpen","rootRef","useRef","triggerRef","panelStyle","useEffect","onDown","event","onEsc","handleToggle","close","tones","chipShell","opts","chipBody","FilterChip","label","icon","tone","active","caret","menu","menuClassName","onClear","clearLabel","title","showClear","radius","shell","body","Fragment","ChevronDown","bodyClasses","clearButton","X","sizeMap","buttonBackgroundMap","Link","external","href","target","rel","isExternal","linkProps","ExternalLink","SegmentedToggle","options","onChange","opt","variantStyles","chevronSizes","SplitButton","menuPlacement","menuLabel","hasMenu","option","CommandPalette","commands","onClose","labels","initialCommandPath","initialActiveIndex","searchTerm","setSearchTerm","activeCommandPath","setActiveCommandPath","activeIndex","setActiveIndex","paletteRef","listRef","filteredCommands","c","command","handleKeyDown","prevIndex","handleClickOutside","Search","index","ChevronRight","HTML_TAG","HTML_PROSE","components","content","RichText","text","as","src","DOMPurify","Markdown","remarkGfm","KPI_TONE","CALLOUT_TONE","KPI_COLUMNS","Block","block","List","item","column","row","rowIndex","cellIndex","ArtifactView","blocks","runtime","AssistantCard","Sparkles","action","IDENTITY_TONE_COUNT","hash","seed","h","i","identityTone","key","PLATE","SOLID","identityPlateClass","identityDotClass","toneClasses","sizeClasses","initials","parts","Avatar","errored","setErrored","React","base","RING","OWNER_RING","AvatarStack","people","max","shown","overflow","p","CH","ChannelBadge","channel","known","Image","alt","aspectRatio","showLoading","showError","fallback","loadingContent","errorContent","onLoad","onError","isLoading","setIsLoading","hasError","setHasError","currentSrc","setCurrentSrc","handleLoad","handleError","containerClasses","imageClasses","placeholderClasses","defaultLoadingContent","defaultErrorContent","ImageOff","valueTone","KpiCard","checkboxSizes","rowSizes","labelSizes","Checkbox","helperText","error","indeterminate","containerClassName","labelClassName","id","reactId","checkboxId","innerRef","setRefs","node","ListBulkAction","items","getRowKey","leading","renderItem","trailing","onSelect","onActiveIndexChange","isActive","unread","dimmed","groupBy","groups","renderGroupHeader","stickyGroupHeaders","collapsibleGroups","hideGroupCount","selectable","selectedItems","onSelectionChange","bulkActions","header","stickyHeader","bordered","emptyContent","loadingRows","collapsed","setCollapsed","g","rows","current","keyOf","isSelected","toggle","checked","toggleAll","container","_","allSelected","someSelected","renderRow","firstInBlock","isUnread","isDimmed","selected","buckets","declared","group","groupIndex","isCollapsed","count","prev","r","MessageBubble","side","continued","MetaValue","unset","MetaSeparator","Bar","Shape","columns","DEFAULT_ROWS","ContentLoading","showTableSkeleton","tableColumns","tableRows","resolvedShape","resolvedRows","resolvedColumns","inner","PageHeader","surface","subtitle","meta","actions","backLabel","backClassName","onBack","editable","onTitleChange","isEditing","setIsEditing","editValue","setEditValue","inputRef","handleTitleClick","handleBlur","ChevronLeft","Pencil","paddingClasses","Page","headerContent","loadingShape","empty","padding","scroll","contentClassName","PageToolbarActions","defaultVariant","PageToolbar","SectionCaption","SectionDivider","align","rule","SettingsFrameContext","createContext","SettingsFrameProvider","useSettingsFrame","useContext","useSettingsFrameHost","state","setState","api","useMemo","ownerId","entry","SettingsPage","secondaryActions","actionsPlacement","frame","useId","backRef","hasBack","actionsRef","secondaryRef","inHeader","hasHeaderActions","headerActionsKey","a","headerActions","proxy","which","showFooter","SettingsRow","description","control","SettingsSection","SourceChip","dotColor","ArrowUpRight","CalendarIcon","Calendar","ChevronLeftIcon","ChevronRightIcon","DatePicker","placeholder","required","minDate","maxDate","format","withTime","currentMonth","setCurrentMonth","containerRef","fieldRef","formatDate","date","day","month","year","datePart","formatTime","handleTimeChange","hours","minutes","getCalendarDays","firstDay","startDate","days","handleDateSelect","picked","handlePrevMonth","handleNextMonth","handleClear","inputClasses","calendarDays","monthNames","isCurrentMonth","isToday","SearchField","onValueChange","hint","Dropdown","showCheck","hoveredOption","setHoveredOption","submenuPosition","setSubmenuPosition","dropdownRef","submenuRef","menuRef","menuPos","hideTimeoutRef","menuItems","handleMenuKeyDown","handleEscape","hoveredElement","handleTriggerClick","handleOptionClick","parentValue","handleOptionHover","handleOptionLeave","handleSubmenuOptionClick","dropdownClasses","submenuClasses","hasChildren","isHovered","Check","childOption","childIndex","totalOptions","childCount","child","dateFilterHandler","fn","selectFilterHandler","multiSelectFilterHandler","cellPadding","rowHeights","SKELETON_ROWS","SKELETON_WIDTHS","pageWindow","total","sorted","b","out","page","FilterMenu","values","labelOf","pick","v","Table","data","searchable","searchPlaceholder","searchValue","onSearchChange","filters","toolbarActions","toolbarContent","paginated","defaultPageSize","onRowClick","selectedRows","footerSummary","hoverable","rowClassName","headerBackground","internalSearch","setInternalSearch","controlledSearch","sortState","setSortState","pagination","setPagination","tableRef","filteredData","result","searchableColumns","col","lowerSearchTerm","sortedData","aValue","bValue","comparison","paginatedData","startIndex","endIndex","handleSort","useCallback","columnId","handlePageChange","newPage","handleRowSelection","rowKey","handleSelectAll","f","totalItems","totalPages","startItem","endItem","allVisibleSelected","selectedRow","someVisibleSelected","pad","headerFill","cellClasses","showToolbar","sections","bucket","bodyColumnCount","showPaginator","filter","ChevronUp","columnIndex","section","Ellipsis","actionId","badgeVariants","badgeSizes","dotColors","Badge","dot","dismissible","onDismiss","badgeColumn","config","accessor","visible","width","sortable","_value","raw","booleanBadgeColumn","onLabel","offLabel","onTone","offTone","on","labelsColumn","separator","labelled","code","weightMap","alignMap","lineHeightMap","Text","weight","truncate","italic","underline","lineHeight","Tag","getDefaultElement","isCaption","isLabel","isCode","textClasses","getValueByPath","obj","path","acc","View","formatCheckbox","getOptionLabel","renderField","arrayValue","subField","subValue","displayValue","formatted","renderGroup","visibleItems","groupClasses","viewClasses","ErrorBoundary","Component","errorInfo","loadedRemotes","FederatedResource","identifier","resourceLoader","framework","errorFallback","componentProps","failedId","setFailedId","bump","svelteInstanceRef","loaderRef","isMounted","module","n","err","failed","ExternalComponent","instance","cleanup","toneDisc","EmptyState","card","Kbd","trackSizes","fillVariants","ProgressBar","pct","spinnerSizes","spinnerColors","Spinner","showLabel","StatusDot","pulse","StepIndicator","steps","shared","step","REAL_TOKENS","autofillProps","token","normalizeText","selectSizes","norm","Select","multiple","allowCreate","onCreateOption","selectId","localOptions","setLocalOptions","includesOption","needle","o","ensureValuesInOptions","baseOptions","currentValue","addIfMissing","val","handleSelect","optionValue","currentValues","newValues","selectedOption","selectedOptions","filteredOptions","canCreate","createOption","labelToCreate","clean","newOption","getDisplayValue","ChevronDownIcon","inputSizes","fieldLabelClasses","fieldFrameClasses","fieldEdgeClasses","TextField","startIcon","endIcon","inputId","describedBy","FolderSelect","onListFolders","templatePlaceholder","showTemplate","folders","setFolders","res","TabsContext","TabBar","activeTab","onTabChange","internalActiveTab","setInternalActiveTab","currentActiveTab","handleTabClick","tabId","Tabs","defaultTab","handleTabChange","contextValue","activeTabItem","tabListClasses","tabClasses","variantClasses","stateClasses","activeClasses","inactiveClasses","modalSizes","Modal","closeOnBackdropClick","closeOnEscape","backdropClassName","footer","showCloseButton","modalRef","previousActiveElement","handleBackdropClick","StorageInput","accept","onListFiles","onListMounts","onUploadFile","onDownloadFile","onRegisterFile","isModalOpen","setIsModalOpen","mounts","setMounts","selectedMount","setSelectedMount","currentPath","setCurrentPath","remoteFiles","setRemoteFiles","setLoading","selectedPointer","setSelectedPointer","isCreatingFolder","setIsCreatingFolder","newFolderName","setNewFolderName","fileInputRef","loadMounts","loadFiles","resp","handleLocalOnlySelection","file","buffer","virtualPointer","handleUploadToStorage","pointer","handleCreateFolder","fullPath","handleSelectRemote","explorerItems","files","handleFolderClick","folderName","handleGoBack","tableData","explorerColumns","Folder","FileText","mountColumns","HardDrive","ArrowLeft","FolderPlus","Upload","textareaSizes","TextArea","textareaId","setValueByPath","keys","clone","cur","k","unsetByPath","FormSection","Form","externalData","onSubmit","onCancel","transform","validate","submitButton","cancelButton","showButtons","layout","sdk","formData","setFormData","formDomId","fieldDomId","errors","setErrors","touched","setTouched","validateField","currentData","field","arrayErrors","hasErrors","rowErrors","subFieldValue","handleFieldChange","newData","handleSubmit","newErrors","allFields","formErrors","finalData","commonProps","payload","fileId","newValue","handleAddItem","handleRemoveItem","handleSubFieldChange","fieldName","fieldValue","newArray","subFieldError","commonSubFieldProps","dateValue","Trash2","Plus","fieldError","formClasses","buttonSize","ImageField","maxDimension","maxBytes","aspect","chooseLabel","removeLabel","tooLargeLabel","setError","encoded","downscaleToDataUri","approximateBytes","dataUri","base64","resolve","reject","reader","img","scale","height","canvas","ctx","classes","SearchIcon","iconSizes","SearchableTextField","externalLoading","onRemoteSearch","debounceTime","showAllOnOpen","propValue","inputValue","setInputValue","isSearchingRemote","setIsSearchingRemote","typedSinceOpen","setTypedSinceOpen","lowerCaseInput","handler","handleChange","handleFocus","showDropdown","resolvedRef","Loader2","Switch","track","COLUMNS","COLUMNS_MANY","columnsFor","splitStrip","maxStrip","room","MeetingGrid","tiles","pinned","hidden","t","ParticipantTile","avatarUrl","muted","speaking","hasVideo","videoSlot","fill","VideoOff","User","MicOff","Mic","VideoSurface","attach","mirrored","SidebarMenuSkeleton","SidebarMenuItem","isSelectedHandler","depth","swapsWithCount","SidebarMenu","preMenuItemsComponent","ConfirmDialog","confirmLabel","cancelLabel","onConfirm","paddingMap","marginMap","backgroundMap","radiusMap","shadowMap","borderMap","borderColorMap","Box","margin","background","shadow","border","borderColor","Card","Heading","level","headingClasses","getDefaultSize","TONES","catTone"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.cjs","sources":["../src/utils/cn.ts","../src/action/Button.tsx","../src/action/ButtonGroup.tsx","../src/content/Icon.tsx","../src/utils/useAnchoredPosition.ts","../src/overlays/Popover.tsx","../src/action/FilterChip.tsx","../src/action/Link.tsx","../src/action/SegmentedToggle.tsx","../src/action/SplitButton.tsx","../src/command-palette/CommandPalette.tsx","../src/content/RichText.tsx","../src/content/ArtifactView.tsx","../src/content/AssistantCard.tsx","../src/utils/identity.ts","../src/content/Avatar.tsx","../src/content/AvatarStack.tsx","../src/content/activity/ActivityRow.tsx","../src/content/activity/TimelineEvent.tsx","../src/content/ChannelBadge.tsx","../src/content/Image.tsx","../src/content/KpiCard.tsx","../src/input/Checkbox.tsx","../src/content/List.tsx","../src/content/MessageBubble.tsx","../src/content/MetaValue.tsx","../src/feedback/ContentLoading.tsx","../src/content/PageHeader.tsx","../src/content/Page.tsx","../src/content/PageToolbar.tsx","../src/content/SectionCaption.tsx","../src/content/SectionDivider.tsx","../src/content/SettingsPage.tsx","../src/content/SettingsRow.tsx","../src/content/SourceChip.tsx","../src/input/DatePicker.tsx","../src/input/SearchField.tsx","../src/overlays/Dropdown.tsx","../src/content/Table.tsx","../src/feedback/Badge.tsx","../src/content/tableColumns.tsx","../src/typography/Text.tsx","../src/content/View.tsx","../src/error-boundary/index.tsx","../src/federated-resource/index.tsx","../src/feedback/EmptyState.tsx","../src/feedback/Kbd.tsx","../src/feedback/ProgressBar.tsx","../src/feedback/Spinner.tsx","../src/feedback/StatusDot.tsx","../src/feedback/StepIndicator.tsx","../src/forms/autofill.ts","../src/utils/text.ts","../src/input/Select.tsx","../src/input/TextField.tsx","../src/input/FolderSelect.tsx","../src/navigation/Tabs.tsx","../src/overlays/Modal.tsx","../src/input/StorageInput.tsx","../src/input/TextArea.tsx","../src/forms/Form.tsx","../src/input/ImageField.tsx","../src/input/SearchableTextField.tsx","../src/input/Switch.tsx","../src/meeting/MeetingGrid.tsx","../src/meeting/ParticipantTile.tsx","../src/meeting/VideoSurface.tsx","../src/navigation/Sidebar.tsx","../src/overlays/ConfirmDialog.tsx","../src/primitives/Box.tsx","../src/primitives/Card.tsx","../src/typography/Heading.tsx","../src/utils/catTone.ts"],"sourcesContent":["import { type ClassValue, clsx } from \"clsx\";\n\n/**\n * Utility function for combining class names\n * Combines clsx for conditional classes with Tailwind merge for deduplication\n */\nexport function cn(...inputs: ClassValue[]) {\n return clsx(inputs);\n}\n\n/**\n * Utility for creating variant-based class combinations\n */\nexport function createVariants<T extends Record<string, Record<string, string>>>(variants: T) {\n return (variant: keyof T, value: keyof T[keyof T]) => {\n return variants[variant]?.[value] || \"\";\n };\n}\n\n/**\n * Utility for creating size-based class combinations\n */\nexport function createSizes<T extends Record<string, string>>(sizes: T) {\n return (size: keyof T) => sizes[size] || \"\";\n}\n","import type React from \"react\";\nimport { forwardRef } from \"react\";\nimport { cn } from \"../utils/cn\";\n\nexport interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {\n /**\n * The visual style variant of the button\n */\n variant?:\n | \"primary\"\n | \"secondary\"\n | \"outline\"\n | \"ghost\"\n | \"destructive\"\n | \"success\"\n | \"warning\"\n | \"danger-solid\";\n\n /**\n * The size of the button\n */\n size?: \"xs\" | \"sm\" | \"md\" | \"lg\" | \"xl\" | \"2xl\" | \"full\";\n\n /**\n * Whether the button should take the full width of its container\n */\n fullWidth?: boolean;\n\n /**\n * Whether the button is in a loading state\n */\n loading?: boolean;\n\n /**\n * Icon to display before the button text\n */\n leftIcon?: React.ReactNode;\n\n /**\n * Icon to display after the button text\n */\n rightIcon?: React.ReactNode;\n\n /**\n * Whether the button should only display an icon (no text)\n */\n iconOnly?: boolean;\n\n /** `circle` is for a control that is only ever an icon: call answer/reject. */\n shape?: \"default\" | \"circle\";\n}\n\n/**\n * Variant styles — semantic tokens only, so dark mode needs no extra classes.\n * One primary button per view; everything else is secondary, ghost or a Link.\n */\nconst buttonVariants = {\n primary: \"bg-accent text-accent-fg hover:bg-accent-hover disabled:bg-surface-hover\",\n secondary: \"bg-surface-sunk text-text-strong hover:bg-surface-hover disabled:bg-surface-hover\",\n // No resting fill, so no disabled fill either — a greyed-out plate where\n // there was nothing reads as a different control, not as a disabled one.\n outline: \"border border-border text-text-strong hover:bg-surface-hover\",\n ghost: \"text-text-muted hover:bg-surface-hover hover:text-text\",\n destructive:\n \"border border-danger-border bg-danger-soft text-danger-fg hover:border-danger disabled:bg-surface-hover\",\n // Solid status fills. Reserved for a control whose colour IS the answer —\n // an RSVP, answering or rejecting a call — not for ordinary emphasis.\n success: \"bg-success text-text-inverse hover:opacity-90 disabled:bg-surface-hover\",\n warning: \"bg-warning text-text-inverse hover:opacity-90 disabled:bg-surface-hover\",\n \"danger-solid\": \"bg-danger text-text-inverse hover:opacity-90 disabled:bg-surface-hover\",\n};\n\n/** Height comes from the shared control tokens so buttons line up with inputs. */\nconst buttonSizes = {\n xs: \"h-control-xs px-2 text-xs\",\n sm: \"h-control-sm px-3 text-sm\",\n md: \"h-control-md px-4 text-sm\",\n lg: \"h-control-lg px-4 text-sm\",\n xl: \"h-control-xl px-6 text-base\",\n \"2xl\": \"h-control-2xl px-6 text-base\",\n full: \"h-control-md px-4 text-sm\",\n};\n\nconst iconOnlySizes = {\n xs: \"h-control-xs w-6\",\n sm: \"h-control-sm w-7\",\n md: \"h-control-md w-8\",\n lg: \"h-control-lg w-9\",\n xl: \"h-control-xl w-10\",\n \"2xl\": \"h-control-2xl w-12\",\n full: \"h-control-md w-8\",\n};\n\n/**\n * Button component with theme integration and multiple variants\n *\n * @example\n * ```tsx\n * <Button variant=\"primary\" size=\"md\">Click me</Button>\n * <Button variant=\"outline\" leftIcon={<Icon name={Plus} />}>Add item</Button>\n * <Button iconOnly variant=\"ghost\" aria-label=\"Zoeken\">\n * <Icon name={Search} />\n * </Button>\n * ```\n */\nexport const Button = forwardRef<HTMLButtonElement, ButtonProps>(\n (\n {\n variant = \"primary\",\n size = \"md\",\n fullWidth = false,\n loading = false,\n leftIcon,\n rightIcon,\n iconOnly = false,\n shape = \"default\",\n className,\n children,\n disabled,\n ...props\n },\n ref,\n ) => {\n const isDisabled = disabled || loading;\n\n return (\n <button\n ref={ref}\n type=\"button\"\n className={cn(\n // font-medium, not semibold: in the design a button's label carries\n // the same weight as an emphasised nav item, and 600 at 12–13px\n // reads as a heavier control than the surrounding chrome.\n \"inline-flex shrink-0 items-center justify-center gap-2 cursor-pointer font-medium\",\n shape === \"circle\" ? \"rounded-full\" : \"rounded-md\",\n \"transition-colors duration-fast ease-out\",\n \"focus-visible:outline-none focus-visible:focus-ring\",\n \"disabled:cursor-not-allowed disabled:border-border-subtle\",\n \"disabled:text-text-disabled\",\n\n buttonVariants[variant],\n iconOnly ? iconOnlySizes[size] : buttonSizes[size],\n\n (fullWidth || size === \"full\") && \"w-full\",\n loading && \"cursor-wait\",\n\n className,\n )}\n disabled={isDisabled}\n aria-busy={loading || undefined}\n {...props}\n >\n {loading && (\n <span\n aria-hidden\n className=\"size-icon-md shrink-0 animate-spin rounded-full border-2 border-current border-t-transparent\"\n />\n )}\n\n {!loading && leftIcon && (\n <span className=\"flex items-center justify-center shrink-0\">{leftIcon}</span>\n )}\n\n {!iconOnly && children}\n\n {!loading && rightIcon && (\n <span className=\"flex items-center justify-center shrink-0\">{rightIcon}</span>\n )}\n\n {iconOnly && !loading && !leftIcon && !rightIcon && children}\n </button>\n );\n },\n);\n\nButton.displayName = \"Button\";\n","import type React from \"react\";\nimport { cn } from \"../utils/cn\";\n\nexport interface ButtonGroupProps {\n /** Child buttons to group */\n children: React.ReactNode;\n /** Size of the button group */\n size?: \"xs\" | \"sm\" | \"md\" | \"lg\" | \"xl\";\n /** Orientation of the button group */\n orientation?: \"horizontal\" | \"vertical\";\n /** Whether buttons should be attached (no gap) */\n attached?: boolean;\n /** Additional CSS classes */\n className?: string;\n}\n\n/**\n * ButtonGroup component for grouping related buttons\n */\nexport const ButtonGroup: React.FC<ButtonGroupProps> = ({\n children,\n size = \"md\",\n orientation = \"horizontal\",\n attached = true,\n className,\n}) => {\n const baseClasses = cn(\n \"inline-flex\",\n {\n // Orientation\n \"flex-row\": orientation === \"horizontal\",\n \"flex-col\": orientation === \"vertical\",\n\n // Attached styling\n \"[&>*:not(:first-child):not(:last-child)]:rounded-none\": attached,\n \"[&>*:first-child]:rounded-r-none\": attached && orientation === \"horizontal\",\n \"[&>*:last-child]:rounded-l-none\": attached && orientation === \"horizontal\",\n \"[&>*:first-child]:rounded-b-none\": attached && orientation === \"vertical\",\n \"[&>*:last-child]:rounded-t-none\": attached && orientation === \"vertical\",\n\n // Borders for attached buttons\n \"[&>*:not(:first-child)]:-ml-px\": attached && orientation === \"horizontal\",\n \"[&>*:not(:first-child)]:-mt-px\": attached && orientation === \"vertical\",\n\n // Spacing for non-attached buttons\n \"gap-1\": !attached && size === \"xs\",\n \"gap-2\": !attached && (size === \"sm\" || size === \"md\"),\n \"gap-3\": !attached && (size === \"lg\" || size === \"xl\"),\n },\n className,\n );\n\n return (\n <div className={baseClasses} role=\"group\">\n {children}\n </div>\n );\n};\n","import * as LucideIcons from \"lucide-react\";\nimport type React from \"react\";\nimport { cn } from \"../utils/cn\";\n\n/**\n * Lucide icon by name, for icons that arrive as data rather than as an import —\n * a provider declaring `icon: \"MessageCircle\"` in its description, a menu entry\n * from a manifest. Accepts kebab-case, snake_case, spaced or PascalCase.\n *\n * Returns `undefined` for an unknown name so the caller can fall back; an app\n * naming an icon that does not exist should not blank out the surface.\n */\nexport function resolveLucideIcon(name: string | undefined): React.ComponentType<any> | undefined {\n if (!name) return undefined;\n const pascal = name\n .split(/[-_ ]/)\n .filter(Boolean)\n .map((part) => part.charAt(0).toUpperCase() + part.slice(1))\n .join(\"\");\n const found = (LucideIcons as unknown as Record<string, unknown>)[pascal];\n return typeof found === \"function\" || (typeof found === \"object\" && found !== null)\n ? (found as React.ComponentType<any>)\n : undefined;\n}\n\nexport interface IconProps {\n /** Icon component from lucide-react or custom icon */\n icon: React.ComponentType<any>;\n /** Icon size */\n size?: \"xs\" | \"sm\" | \"md\" | \"lg\" | \"xl\" | number;\n /** Icon color */\n color?:\n | \"primary\"\n | \"secondary\"\n | \"accent\"\n | \"success\"\n | \"warning\"\n | \"error\"\n | \"info\"\n | \"neutral\"\n | \"current\";\n /** Whether the icon should be clickable */\n clickable?: boolean;\n /** Click handler */\n onClick?: () => void;\n /** Additional CSS classes */\n className?: string;\n /** Accessibility label */\n \"aria-label\"?: string;\n}\n\n/**\n * Icon — the only way icons enter the product. Always a lucide-react component;\n * never a unicode glyph, an emoji or an inline <svg> in feature code.\n *\n * Sizes follow the icon tokens: xs 12 · sm 14 (default in controls) · md 16 ·\n * lg 20 · xl 24.\n */\nconst colorMap: Record<NonNullable<IconProps[\"color\"]>, string> = {\n primary: \"text-text\",\n secondary: \"text-text-muted\",\n accent: \"text-accent\",\n success: \"text-success-fg\",\n warning: \"text-warning-fg\",\n error: \"text-danger-fg\",\n info: \"text-info-fg\",\n neutral: \"text-text-subtle\",\n current: \"text-current\",\n};\n\nexport const Icon: React.FC<IconProps> = ({\n icon: IconComponent,\n size = \"md\",\n color = \"current\",\n clickable = false,\n onClick,\n className,\n \"aria-label\": ariaLabel,\n ...props\n}) => {\n const sizeValue = typeof size === \"number\" ? size : getSizeValue(size);\n\n const iconClasses = cn(\n \"inline-block shrink-0\",\n colorMap[color],\n clickable && [\n \"cursor-pointer rounded-sm transition-opacity duration-fast ease-out hover:opacity-80\",\n \"focus-visible:outline-none focus-visible:focus-ring\",\n ],\n className,\n );\n\n const iconProps = {\n size: sizeValue,\n strokeWidth: 2,\n className: iconClasses,\n onClick: clickable ? onClick : undefined,\n \"aria-label\": ariaLabel,\n \"aria-hidden\": ariaLabel ? undefined : true,\n role: clickable ? \"button\" : undefined,\n tabIndex: clickable ? 0 : undefined,\n onKeyDown: clickable\n ? (e: React.KeyboardEvent) => {\n if (e.key === \"Enter\" || e.key === \" \") {\n e.preventDefault();\n onClick?.();\n }\n }\n : undefined,\n ...props,\n };\n\n return <IconComponent {...iconProps} />;\n};\n\nfunction getSizeValue(size: \"xs\" | \"sm\" | \"md\" | \"lg\" | \"xl\"): number {\n const sizeMap = {\n xs: 12,\n sm: 14,\n md: 16,\n lg: 20,\n xl: 24,\n };\n\n return sizeMap[size];\n}\n","import { type CSSProperties, type RefObject, useLayoutEffect, useState } from \"react\";\n\nexport interface AnchoredPositionOptions {\n /** Gap between the anchor and the panel, in px. */\n gap?: number;\n /** Match the panel's width to the anchor — what a select wants, a menu does not. */\n matchWidth?: boolean;\n /** Preferred side. Flips automatically when there is no room. */\n placement?: \"bottom-start\" | \"bottom-end\" | \"top-start\" | \"top-end\";\n}\n\n/**\n * Position a floating panel against its trigger, in viewport coordinates.\n *\n * Two problems this solves, both of which a plain `absolute` panel has:\n * an ancestor with `overflow-hidden` (a bordered form panel, a scroll area)\n * clips the panel, and a trigger near the bottom of the window opens a panel\n * that runs off-screen. Fixed coordinates escape every ancestor, and the\n * placement flips upward when the space below is too small.\n *\n * Recomputes on scroll and resize while open, so the panel tracks its trigger\n * instead of detaching when the page moves under it.\n */\nexport function useAnchoredPosition(\n open: boolean,\n anchorRef: RefObject<HTMLElement | null>,\n { gap = 6, matchWidth = false, placement = \"bottom-start\" }: AnchoredPositionOptions = {},\n): CSSProperties {\n const [style, setStyle] = useState<CSSProperties>({});\n\n // Layout effect, not effect: measure and place before the browser paints,\n // otherwise the panel is visible at the wrong spot for a frame.\n useLayoutEffect(() => {\n if (!open) return;\n\n const place = () => {\n const anchor = anchorRef.current;\n if (!anchor) return;\n\n const rect = anchor.getBoundingClientRect();\n const spaceBelow = window.innerHeight - rect.bottom;\n const spaceAbove = rect.top;\n\n // Flip up only when below genuinely cannot hold a usable panel and above\n // has more room — flipping into an equally cramped space helps nobody.\n const wantsTop = placement.startsWith(\"top\");\n const flip = wantsTop\n ? spaceAbove < 200 && spaceBelow > spaceAbove\n : spaceBelow < 200 && spaceAbove > spaceBelow;\n const onTop = wantsTop !== flip;\n\n const next: CSSProperties = { position: \"fixed\" };\n\n if (onTop) {\n next.bottom = window.innerHeight - rect.top + gap;\n next.maxHeight = Math.max(120, spaceAbove - gap * 2);\n } else {\n next.top = rect.bottom + gap;\n next.maxHeight = Math.max(120, spaceBelow - gap * 2);\n }\n\n if (placement.endsWith(\"end\")) next.right = window.innerWidth - rect.right;\n else next.left = rect.left;\n\n if (matchWidth) next.width = rect.width;\n\n setStyle(next);\n };\n\n place();\n // `true` captures scrolls on any ancestor, not just the window.\n window.addEventListener(\"scroll\", place, true);\n window.addEventListener(\"resize\", place);\n return () => {\n window.removeEventListener(\"scroll\", place, true);\n window.removeEventListener(\"resize\", place);\n };\n }, [open, anchorRef, gap, matchWidth, placement]);\n\n return style;\n}\n","import type React from \"react\";\nimport { useEffect, useRef, useState } from \"react\";\nimport { cn } from \"../utils/cn\";\nimport { useAnchoredPosition } from \"../utils/useAnchoredPosition\";\n\nexport interface PopoverProps {\n /** Element that toggles the popover */\n trigger: React.ReactNode;\n /**\n * Panel content. Pass a function to receive a `close` callback so rows can\n * dismiss the popover after acting.\n */\n children: React.ReactNode | ((close: () => void) => React.ReactNode);\n /** Panel placement relative to the trigger. Flips when there is no room. */\n placement?: \"bottom-start\" | \"bottom-end\" | \"top-start\" | \"top-end\";\n className?: string;\n /**\n * Layout classes for the wrapper around the trigger. Needed when the trigger\n * has to fill a positioned box — a calendar event in a week grid, say —\n * because the wrapper is `inline-flex` by default.\n */\n rootClassName?: string;\n /** Layout classes for the trigger button itself. */\n triggerClassName?: string;\n disabled?: boolean;\n /**\n * Accessible name for the trigger button. Needed when the trigger is\n * icon-only, since the panel content is not announced by the trigger.\n */\n triggerLabel?: string;\n}\n\n/**\n * Generic popover with click-outside / Escape dismissal. The panel is\n * `position: fixed` (computed from the trigger) so it escapes any\n * overflow-hidden or rounded-card ancestor instead of being clipped.\n */\nexport const Popover: React.FC<PopoverProps> = ({\n trigger,\n children,\n placement = \"bottom-start\",\n className,\n rootClassName,\n triggerClassName,\n disabled = false,\n triggerLabel,\n}) => {\n const [isOpen, setIsOpen] = useState(false);\n const rootRef = useRef<HTMLDivElement>(null);\n const triggerRef = useRef<HTMLButtonElement>(null);\n // Shared with Select, DatePicker and Dropdown: flips up when the trigger sits\n // near the bottom, caps the height to the space available, and re-measures on\n // scroll so the panel keeps tracking its trigger.\n const panelStyle = useAnchoredPosition(isOpen, triggerRef, { placement });\n\n useEffect(() => {\n if (!isOpen) return;\n const onDown = (event: MouseEvent) => {\n if (rootRef.current && !rootRef.current.contains(event.target as Node)) {\n setIsOpen(false);\n }\n };\n const onEsc = (event: KeyboardEvent) => {\n if (event.key === \"Escape\") setIsOpen(false);\n };\n document.addEventListener(\"mousedown\", onDown);\n document.addEventListener(\"keydown\", onEsc);\n return () => {\n document.removeEventListener(\"mousedown\", onDown);\n document.removeEventListener(\"keydown\", onEsc);\n };\n }, [isOpen]);\n\n const handleToggle = (event: React.MouseEvent<HTMLButtonElement>) => {\n event.stopPropagation();\n if (disabled) return;\n setIsOpen((open) => !open);\n };\n\n const close = () => setIsOpen(false);\n\n return (\n <div ref={rootRef} className={cn(\"relative inline-flex\", rootClassName)}>\n <button\n ref={triggerRef}\n type=\"button\"\n onClick={handleToggle}\n disabled={disabled}\n aria-label={triggerLabel}\n aria-expanded={isOpen}\n className={cn(\n \"inline-flex items-center\",\n disabled && \"cursor-not-allowed opacity-50\",\n triggerClassName,\n )}\n >\n {trigger}\n </button>\n\n {isOpen && (\n <div\n className={cn(\n // overflow-y-auto pairs with the hook's max-height: without it a\n // panel taller than the space available is simply cut off.\n \"fixed z-overlay overflow-y-auto rounded-lg border border-border bg-surface shadow-overlay\",\n className,\n )}\n style={panelStyle}\n >\n {typeof children === \"function\" ? children(close) : children}\n </div>\n )}\n </div>\n );\n};\n","import { ChevronDown, X } from \"lucide-react\";\nimport type React from \"react\";\nimport { Icon } from \"../content/Icon\";\nimport { Popover } from \"../overlays/Popover\";\nimport { cn } from \"../utils/cn\";\n\nexport interface FilterChipProps {\n /** Chip text (e.g. \"Status\", or \"Status: Open\" when a value is set). */\n label: React.ReactNode;\n /** Leading icon, before the label. */\n icon?: React.ReactNode;\n /**\n * Meaning carried by the chip's own value — a priority, a state. Applies to\n * the resting chip; `active` still wins, because \"you picked this\" outranks\n * \"this is urgent\".\n */\n tone?: \"none\" | \"default\" | \"muted\" | \"outline\" | \"warning\" | \"danger\";\n /**\n * `rect` is the filter-bar shape — the same 9px corner as a button, so a\n * chip and a button in one toolbar line up. `pill` is for a chip that reads\n * as a tag rather than a control.\n */\n shape?: \"pill\" | \"rect\";\n /** `md` is the default control height; `sm` for a dense toolbar. */\n size?: \"sm\" | \"md\";\n /** Selected/active — renders the filled dark treatment. */\n active?: boolean;\n /** Show a trailing caret (opens a menu the consumer wires with Dropdown/Popover). */\n caret?: boolean;\n /**\n * Menu panel for a chip that opens its own popover. Receives a `close`\n * callback so a row can dismiss the panel after picking. Without it the chip\n * is purely presentational and `onClick` is yours to wire.\n */\n menu?: (close: () => void) => React.ReactNode;\n /** Panel classes, when `menu` is set. */\n menuClassName?: string;\n /** Open a value clear affordance (✕) — only shown when `active`. */\n onClear?: () => void;\n /** Accessible name for the clear affordance. */\n clearLabel?: string;\n onClick?: () => void;\n /** Native tooltip, for a chip whose label is truncated or abbreviated. */\n title?: string;\n className?: string;\n}\n\n/**\n * Resting fills. A filter bar on the white card uses sunk plates — an outlined\n * row of chips reads as a row of empty inputs at this size. `outline` is the\n * exception, for a bar that already contains a sunk plate (the table toolbar,\n * where the search field is the sunk one): there a second sunk fill beside it\n * makes the two read as one smeared control.\n */\nconst tones: Record<NonNullable<FilterChipProps[\"tone\"]>, string> = {\n none: \"hover:bg-surface-sunk hover:text-text\",\n default: \"bg-surface-sunk text-text-muted hover:text-text\",\n muted: \"bg-surface-sunk text-text-subtle hover:text-text\",\n outline:\n \"text-text-muted ring-1 ring-inset ring-border-strong hover:bg-surface-hover hover:text-text\",\n warning: \"bg-warning-soft text-warning-fg\",\n danger: \"bg-danger-soft text-danger-fg\",\n};\n\ntype ChipShape = NonNullable<FilterChipProps[\"shape\"]>;\n\nconst chipShell = (opts: {\n active?: boolean;\n tone?: NonNullable<FilterChipProps[\"tone\"]>;\n shape?: ChipShape;\n size?: NonNullable<FilterChipProps[\"size\"]>;\n className?: string;\n}) =>\n cn(\n // A button's default cursor is an arrow, so it has to be asked for.\n \"inline-flex cursor-pointer select-none items-center text-sm font-medium\",\n opts.size === \"sm\" ? \"h-control-sm\" : \"h-control-md\",\n \"transition-colors duration-fast ease-out\",\n opts.shape === \"pill\" ? \"rounded-full\" : \"rounded-md\",\n // Picking a value outranks whatever the value happens to mean.\n opts.active ? \"bg-info-soft text-info-fg\" : tones[opts.tone ?? \"default\"],\n opts.className,\n );\n\nconst chipBody = (opts: { shape?: ChipShape; withClear?: boolean }) =>\n cn(\n \"inline-flex items-center gap-1.5 pl-3\",\n opts.shape === \"pill\" ? \"rounded-full\" : \"rounded-md\",\n opts.withClear ? \"pr-1.5\" : \"pr-3\",\n \"focus-visible:outline-none focus-visible:focus-ring\",\n );\n\n/**\n * Filter chip — the inbox filter bar, the table toolbar, and the thread\n * priority/assign/inbox/tags menus. Presentational by default (the consumer\n * wires the menu with `Dropdown`/`Popover` and `onClick`); pass `menu` and the\n * chip opens its own popover instead, which is the only way to get a clear\n * button on a menu chip — a `<FilterChip>` used as someone else's trigger\n * would nest a button inside a button.\n *\n * @example\n * ```tsx\n * <FilterChip label={status ? `Status: ${status}` : \"Status\"} caret\n * active={!!status} onClear={() => setStatus(null)}\n * menu={(close) => <StatusList onPick={(s) => { setStatus(s); close(); }} />} />\n * ```\n */\nexport function FilterChip({\n label,\n icon,\n tone = \"default\",\n shape = \"rect\",\n size = \"md\",\n active = false,\n caret = false,\n menu,\n menuClassName,\n onClear,\n clearLabel = \"Filter wissen\",\n onClick,\n title,\n className,\n}: FilterChipProps) {\n const showClear = active && Boolean(onClear);\n\n // The chip and its clear affordance are two separate controls, so the shell is\n // a plain span: a <button> may not contain another <button>. Without a clear,\n // there is only one control and the shell collapses onto it.\n const radius = shape === \"pill\" ? \"rounded-full\" : \"rounded-md\";\n\n const shell = chipShell({ active, tone, shape, size, className });\n\n const body = (\n <>\n {icon}\n {label}\n {caret && <Icon icon={ChevronDown} size=\"xs\" />}\n </>\n );\n\n const bodyClasses = chipBody({ shape, withClear: showClear });\n\n const clearButton = showClear && (\n <button\n type=\"button\"\n aria-label={clearLabel}\n onClick={onClear}\n className={cn(\n \"inline-flex h-full items-center pr-3 focus-visible:outline-none focus-visible:focus-ring\",\n radius,\n )}\n >\n <Icon icon={X} size=\"xs\" />\n </button>\n );\n\n if (menu) {\n return (\n <span className={shell} title={title}>\n <Popover\n trigger={body}\n // Both heights are needed: the shell has the definite height, so the\n // trigger only fills the plate if the wrapper passes it down.\n rootClassName=\"h-full\"\n triggerClassName={cn(bodyClasses, \"h-full\")}\n className={cn(\"min-w-menu p-1.5\", menuClassName)}\n >\n {menu}\n </Popover>\n {clearButton}\n </span>\n );\n }\n\n if (!showClear) {\n return (\n <button type=\"button\" title={title} onClick={onClick} className={cn(shell, bodyClasses)}>\n {body}\n </button>\n );\n }\n\n return (\n <span className={shell} title={title}>\n <button type=\"button\" onClick={onClick} className={bodyClasses}>\n {body}\n </button>\n {clearButton}\n </span>\n );\n}\n","import { ExternalLink } from \"lucide-react\";\nimport type React from \"react\";\nimport { cn } from \"../utils/cn\";\n\nexport interface LinkProps extends React.AnchorHTMLAttributes<HTMLAnchorElement> {\n /** Link variant */\n variant?: \"default\" | \"subtle\" | \"underline\" | \"button\";\n /** Link size */\n size?: \"xs\" | \"sm\" | \"md\" | \"lg\" | \"xl\";\n /** Color theme */\n color?: \"primary\" | \"secondary\" | \"accent\" | \"success\" | \"warning\" | \"error\" | \"info\" | \"neutral\";\n /** Whether the link is disabled */\n disabled?: boolean;\n /** Whether to show external link icon */\n external?: boolean;\n /** Additional CSS classes */\n className?: string;\n /** Child content */\n children: React.ReactNode;\n}\n\n/** Same steps as Text: xs 11 · sm 12 · md 13 · lg 14 · xl 16. */\nconst sizeMap: Record<NonNullable<LinkProps[\"size\"]>, string> = {\n xs: \"text-xs\",\n sm: \"text-sm\",\n md: \"text-base\",\n lg: \"text-md\",\n xl: \"text-lg\",\n};\n\nconst colorMap: Record<NonNullable<LinkProps[\"color\"]>, string> = {\n primary: \"text-text hover:text-info-fg hover:underline\",\n info: \"text-info-fg hover:underline\",\n secondary: \"text-text-muted hover:text-text\",\n neutral: \"text-text-muted hover:text-text\",\n accent: \"text-accent hover:underline\",\n success: \"text-success-fg hover:underline\",\n warning: \"text-warning-fg hover:underline\",\n error: \"text-danger-fg hover:underline\",\n};\n\n/** `variant=\"button\"` uses the soft surface of the matching status token. */\nconst buttonBackgroundMap: Record<NonNullable<LinkProps[\"color\"]>, string> = {\n primary: \"bg-accent-soft\",\n accent: \"bg-accent-soft\",\n secondary: \"bg-surface-hover\",\n neutral: \"bg-surface-hover\",\n success: \"bg-success-soft\",\n warning: \"bg-warning-soft\",\n error: \"bg-danger-soft\",\n info: \"bg-info-soft\",\n};\n\n/**\n * Link component with theme integration and accessibility features.\n * Use for navigation; use Button for actions.\n */\nexport const Link: React.FC<LinkProps> = ({\n variant = \"default\",\n size = \"md\",\n color = \"primary\",\n disabled = false,\n external = false,\n className,\n children,\n href,\n target,\n rel,\n ...props\n}) => {\n const isExternal = external || (href && (href.startsWith(\"http\") || href.startsWith(\"mailto:\")));\n\n const baseClasses = cn(\n \"inline-flex items-center gap-1 underline-offset-2\",\n \"transition-colors duration-fast ease-out\",\n \"focus-visible:outline-none focus-visible:focus-ring\",\n\n sizeMap[size],\n !disabled && colorMap[color],\n\n variant === \"subtle\" && \"no-underline opacity-70 hover:opacity-100\",\n variant === \"underline\" && \"underline decoration-1 hover:decoration-2\",\n variant === \"button\" && [\n \"h-control-md rounded-md px-3 font-semibold no-underline hover:no-underline\",\n buttonBackgroundMap[color],\n ],\n\n disabled && \"pointer-events-none cursor-not-allowed text-text-disabled opacity-50\",\n\n className,\n );\n\n const linkProps = {\n ...props,\n href: disabled ? undefined : href,\n target: isExternal ? \"_blank\" : target,\n rel: isExternal ? \"noopener noreferrer\" : rel,\n \"aria-disabled\": disabled || undefined,\n };\n\n return (\n <a className={baseClasses} {...linkProps}>\n {children}\n {isExternal && <ExternalLink className=\"size-icon-sm shrink-0\" aria-hidden />}\n </a>\n );\n};\n","import type React from \"react\";\nimport { cn } from \"../utils/cn\";\n\nexport interface SegmentedOption<T extends string> {\n value: T;\n label: React.ReactNode;\n /**\n * Accessible name and tooltip, for a segment whose label is an icon. Without\n * it an icon-only segment announces as an empty tab.\n */\n title?: string;\n}\n\nexport interface SegmentedToggleProps<T extends string> {\n options: SegmentedOption<T>[];\n value: T;\n onChange: (value: T) => void;\n /** Control height. */\n size?: \"sm\" | \"md\";\n /**\n * Track fill. `sunk` reads against the white card; use `track` when the\n * toggle itself sits on a sunk plane, where `sunk` on `sunk` is invisible.\n */\n tone?: \"sunk\" | \"track\";\n /** Fill the container and split it evenly — a toggle that heads a column. */\n fullWidth?: boolean;\n className?: string;\n /** Accessible group label. */\n \"aria-label\"?: string;\n}\n\n/** Track height stays one step below the equivalent Button so it nests in toolbars. */\nconst sizes = {\n sm: \"h-control-sm px-3 text-sm\",\n md: \"h-control-md px-3 text-sm\",\n};\n\n/**\n * Segmented toggle (focus \"Feed / Eén tegelijk\", tasks \"Mijn / Mijn teams\").\n * Track = surface-sunk, active segment = a raised white plate — the same\n * \"lifted off the plane behind it\" language as the active nav item.\n *\n * @example\n * ```tsx\n * <SegmentedToggle\n * value={layout}\n * onChange={setLayout}\n * options={[{ value: \"feed\", label: \"Feed\" }, { value: \"one\", label: \"Eén tegelijk\" }]}\n * />\n * ```\n */\nexport function SegmentedToggle<T extends string>({\n options,\n value,\n onChange,\n size = \"sm\",\n tone = \"sunk\",\n fullWidth = false,\n className,\n \"aria-label\": ariaLabel,\n}: SegmentedToggleProps<T>) {\n return (\n <div\n role=\"tablist\"\n aria-label={ariaLabel}\n className={cn(\n \"gap-0.5 rounded-md p-0.5\",\n tone === \"track\" ? \"bg-surface-track\" : \"bg-surface-sunk\",\n fullWidth ? \"flex w-full\" : \"inline-flex\",\n className,\n )}\n >\n {options.map((opt) => {\n const active = opt.value === value;\n return (\n <button\n key={opt.value}\n role=\"tab\"\n aria-selected={active}\n aria-label={opt.title}\n title={opt.title}\n type=\"button\"\n onClick={() => onChange(opt.value)}\n className={cn(\n \"inline-flex cursor-pointer items-center justify-center gap-1.5 rounded-sm\",\n \"transition-colors duration-fast ease-out\",\n \"focus-visible:outline-none focus-visible:focus-ring\",\n fullWidth && \"min-w-0 flex-1\",\n sizes[size],\n active\n ? \"bg-surface text-text font-medium shadow-sm\"\n : \"text-text-muted hover:text-text\",\n )}\n >\n {opt.label}\n </button>\n );\n })}\n </div>\n );\n}\n","import { ChevronDown } from \"lucide-react\";\nimport type React from \"react\";\nimport { Popover } from \"../overlays/Popover\";\nimport { cn } from \"../utils/cn\";\nimport { Button } from \"./Button\";\n\nexport interface SplitButtonOption {\n id: string;\n label: string;\n onClick: () => void;\n variant?: \"default\" | \"destructive\";\n /** Optional icon before the label in the menu. */\n icon?: React.ReactNode;\n}\n\nexport interface SplitButtonProps {\n label: string;\n onClick: () => void;\n variant?: \"primary\" | \"secondary\" | \"outline\";\n size?: \"sm\" | \"md\";\n icon?: React.ReactNode;\n options: SplitButtonOption[];\n disabled?: boolean;\n /** Spinner in the primary half; blocks both halves. */\n loading?: boolean;\n /**\n * Where the options menu opens. Use a `top-*` placement when the button sits\n * at the bottom of its container (e.g. a sticky send bar).\n */\n menuPlacement?: \"bottom-end\" | \"bottom-start\" | \"top-end\" | \"top-start\";\n /** Accessible name for the chevron button. */\n menuLabel?: string;\n}\n\n/** Same variant tokens as Button, so both halves look like one button. */\nconst variantStyles = {\n primary: \"bg-accent text-accent-fg hover:bg-accent-hover\",\n secondary: \"border border-border bg-surface text-text hover:bg-surface-hover\",\n outline: \"border border-accent-border text-accent hover:bg-accent-soft\",\n};\n\nconst chevronSizes = {\n sm: \"h-control-sm w-7\",\n md: \"h-control-md w-8\",\n};\n\n/**\n * Button with a primary action plus a chevron menu of variants. The primary half\n * is a plain `Button` (same variants/loading behavior); the menu goes through\n * `Popover`, so it is `position: fixed` and never clipped by a scrolling or\n * overflow-hidden ancestor.\n *\n * Without options this renders exactly a `Button` — no chevron.\n */\nexport function SplitButton({\n label,\n onClick,\n variant = \"primary\",\n size = \"md\",\n icon,\n options,\n disabled = false,\n loading = false,\n menuPlacement = \"bottom-end\",\n menuLabel = \"More options\",\n}: SplitButtonProps) {\n const isDisabled = disabled || loading;\n const hasMenu = options.length > 0;\n\n return (\n // The split between halves is a gap in the fill, not a line: on an ink-filled\n // button 2px reads as a crack; 1px is enough to show two targets. Outlined\n // variants already have their own borders and keep the halves touching.\n <div className={cn(\"inline-flex items-center\", variant === \"primary\" && \"gap-px\")}>\n <Button\n variant={variant}\n size={size}\n onClick={onClick}\n disabled={disabled}\n loading={loading}\n leftIcon={icon}\n className={cn(hasMenu && \"rounded-r-none\")}\n >\n {label}\n </Button>\n {hasMenu && (\n <Popover\n placement={menuPlacement}\n disabled={isDisabled}\n triggerLabel={menuLabel}\n className=\"min-w-menu py-1\"\n trigger={\n <span\n aria-hidden=\"true\"\n className={cn(\n \"inline-flex items-center justify-center rounded-md rounded-l-none\",\n \"transition-colors duration-fast ease-out\",\n variantStyles[variant],\n chevronSizes[size],\n isDisabled && \"cursor-not-allowed opacity-50\",\n )}\n >\n <ChevronDown className=\"size-icon-md\" />\n </span>\n }\n >\n {(close) => (\n <>\n {options.map((option) => (\n <button\n key={option.id}\n type=\"button\"\n onClick={() => {\n option.onClick();\n close();\n }}\n className={cn(\n \"flex w-full items-center gap-2 px-3 py-1.5 text-left text-sm\",\n \"transition-colors duration-fast ease-out\",\n \"focus-visible:outline-none focus-visible:focus-ring\",\n option.variant === \"destructive\"\n ? \"text-danger-fg hover:bg-danger-soft\"\n : \"text-text hover:bg-surface-hover\",\n )}\n >\n {option.icon && <span className=\"shrink-0\">{option.icon}</span>}\n {option.label}\n </button>\n ))}\n </>\n )}\n </Popover>\n )}\n </div>\n );\n}\n","import { ChevronRight, Search } from \"lucide-react\";\nimport type React from \"react\";\nimport { useEffect, useRef, useState } from \"react\";\n\n// Define the types for the command palette\nexport interface Command {\n id: string;\n label: string;\n action?: () => void;\n subCommands?: Command[];\n icon?: React.ReactNode;\n}\n\nexport interface CommandPaletteProps {\n commands: Command[];\n isOpen: boolean;\n onClose: () => void;\n labels?: {\n searchPlaceholder?: string;\n navigate?: string;\n select?: string;\n close?: string;\n noCommandsFound?: string;\n };\n initialCommandPath?: string[];\n initialActiveIndex?: number;\n}\n\nconst CommandPalette: React.FC<CommandPaletteProps> = ({\n commands,\n isOpen,\n onClose,\n labels,\n initialCommandPath,\n initialActiveIndex,\n}) => {\n const [searchTerm, setSearchTerm] = useState(\"\");\n const [activeCommandPath, setActiveCommandPath] = useState<string[]>(initialCommandPath || []);\n const [activeIndex, setActiveIndex] = useState(initialActiveIndex || 0);\n const paletteRef = useRef<HTMLDivElement>(null);\n const listRef = useRef<HTMLDivElement>(null);\n\n const currentCommands = activeCommandPath.length\n ? commands.find((c) => c.id === activeCommandPath[0])?.subCommands || []\n : commands;\n\n const filteredCommands = currentCommands.filter((command) =>\n command.label.toLowerCase().includes(searchTerm.toLowerCase()),\n );\n\n // Reset state when the palette is closed\n useEffect(() => {\n if (!isOpen) {\n setSearchTerm(\"\");\n setActiveCommandPath(initialCommandPath || []);\n setActiveIndex(initialActiveIndex || 0);\n }\n }, [isOpen]);\n\n useEffect(() => {\n const handleKeyDown = (event: KeyboardEvent) => {\n if (!isOpen) return;\n\n if (event.key === \"Escape\") {\n onClose();\n } else if (event.key === \"ArrowUp\") {\n setActiveIndex((prevIndex) =>\n prevIndex > 0 ? prevIndex - 1 : filteredCommands.length - 1,\n );\n } else if (event.key === \"ArrowDown\") {\n setActiveIndex((prevIndex) =>\n prevIndex < filteredCommands.length - 1 ? prevIndex + 1 : 0,\n );\n } else if (event.key === \"Enter\") {\n const command = filteredCommands[activeIndex];\n if (command) {\n if (command.subCommands) {\n setActiveCommandPath([...activeCommandPath, command.id]);\n setActiveIndex(0);\n } else if (command.action) {\n command.action();\n onClose();\n }\n }\n }\n };\n\n document.addEventListener(\"keydown\", handleKeyDown);\n return () => document.removeEventListener(\"keydown\", handleKeyDown);\n }, [isOpen, onClose, filteredCommands, activeIndex, activeCommandPath]);\n\n useEffect(() => {\n const handleClickOutside = (event: MouseEvent) => {\n if (paletteRef.current && !paletteRef.current.contains(event.target as Node)) {\n onClose();\n }\n };\n\n if (isOpen) {\n document.addEventListener(\"mousedown\", handleClickOutside);\n }\n return () => document.removeEventListener(\"mousedown\", handleClickOutside);\n }, [isOpen, onClose]);\n\n // Keep active item visible while scrolling via keyboard\n useEffect(() => {\n if (!isOpen) return;\n const el = listRef.current?.querySelector<HTMLElement>(`[data-index=\"${activeIndex}\"]`);\n el?.scrollIntoView({ block: \"nearest\" });\n }, [activeIndex, isOpen]);\n\n if (!isOpen) {\n return null;\n }\n\n return (\n <div className=\"fixed inset-0 z-modal flex items-center justify-center bg-scrim\">\n <div\n className=\"flex flex-col w-full max-w-lg max-h-dialog p-2 mx-auto rounded-lg border border-border shadow-modal bg-surface\"\n ref={paletteRef}\n >\n <div className=\"flex items-center gap-2 px-3 flex-shrink-0\">\n <Search className=\"w-4 h-4 text-text-muted shrink-0\" />\n <input\n type=\"text\"\n autoFocus\n placeholder={labels?.searchPlaceholder || \"Search commands...\"}\n value={searchTerm}\n onChange={(e) => setSearchTerm(e.target.value)}\n className=\"w-full border-0 bg-transparent py-2 text-text focus:outline-none\"\n />\n </div>\n <div\n ref={listRef}\n className=\"mt-2 pt-2 text-sm border-t border-border max-h-dialog-list overflow-y-auto\"\n >\n {filteredCommands.length === 0 && (\n <div className=\"text-text-muted text-center text-sm\">\n {labels?.noCommandsFound || \"No commands found\"}\n </div>\n )}\n {filteredCommands.map((command, index) => (\n <div\n key={command.id}\n data-index={index}\n className={`flex items-center p-2 cursor-pointer rounded-md ${\n index === activeIndex ? \"bg-accent-soft text-accent\" : \"text-text\"\n }`}\n onClick={() => {\n if (command.subCommands) {\n setActiveCommandPath([...activeCommandPath, command.id]);\n setActiveIndex(0);\n } else if (command.action) {\n command.action();\n onClose();\n }\n }}\n onMouseEnter={() => setActiveIndex(index)}\n >\n {command.icon && <span className=\"mr-3\">{command.icon}</span>}\n <span className=\"flex-grow\">{command.label}</span>\n {command.subCommands && (\n <span className=\"text-text-muted\">\n <ChevronRight className=\"w-4 h-4\" />\n </span>\n )}\n </div>\n ))}\n </div>\n <div className=\"flex items-center gap-4 px-3 pt-2 mt-2 text-xs text-text-muted border-t border-border\">\n <span className=\"flex items-center gap-1.5\">\n <kbd className=\"inline-flex items-center justify-center min-w-5 px-1 py-0.5 rounded border border-border bg-surface-sunk text-xs font-medium not-italic\">\n ↑↓\n </kbd>\n {labels?.navigate || \"to navigate\"}\n </span>\n <span className=\"flex items-center gap-1.5\">\n <kbd className=\"inline-flex items-center justify-center min-w-5 px-1 py-0.5 rounded border border-border bg-surface-sunk text-xs font-medium not-italic\">\n ↵\n </kbd>\n {labels?.select || \"to select\"}\n </span>\n <span className=\"flex items-center gap-1.5\">\n <kbd className=\"inline-flex items-center justify-center min-w-5 px-1 py-0.5 rounded border border-border bg-surface-sunk text-xs font-medium not-italic\">\n Esc\n </kbd>\n {labels?.close || \"to close\"}\n </span>\n </div>\n </div>\n </div>\n );\n};\n\nexport default CommandPalette;\n","import DOMPurify from \"dompurify\";\nimport type { Components } from \"react-markdown\";\nimport Markdown from \"react-markdown\";\nimport remarkGfm from \"remark-gfm\";\nimport { cn } from \"../utils/cn\";\n\n/**\n * Renders text that may be markdown or HTML — the same fields carry rich HTML\n * from the mention-composer but markdown from an AI tool or playbook, and\n * rendering only one leaves the other as raw characters (`**5MB**`). HTML is\n * DOMPurify-sanitized, never unfiltered — it can come from a model or an\n * external provider. Markdown (react-markdown + remark-gfm) emits no raw HTML,\n * so that path cannot inject. Both branches are deliberately styled alike via\n * semantic tokens; the HTML branch needs descendant classes because Tailwind's\n * preflight strips `ul`/`ol` defaults.\n */\n\nconst HTML_TAG = /<[a-z][\\s\\S]*>/i;\n\n/** Styling for the HTML branch; mirrors the `components` map below. */\nconst HTML_PROSE = [\n \"[&_p]:my-1.5 [&_p:first-child]:mt-0 [&_p:last-child]:mb-0 [&_p]:leading-relaxed\",\n \"[&_a]:text-accent [&_a]:underline [&_a]:underline-offset-2\",\n \"[&_strong]:font-semibold [&_em]:italic\",\n \"[&_ul]:my-1.5 [&_ul]:list-disc [&_ul]:pl-5 [&_ol]:my-1.5 [&_ol]:list-decimal [&_ol]:pl-5\",\n \"[&_li]:leading-relaxed\",\n \"[&_h1]:mb-1 [&_h1]:mt-2 [&_h1]:font-semibold [&_h2]:mb-1 [&_h2]:mt-2 [&_h2]:font-semibold [&_h3]:mb-1 [&_h3]:mt-2 [&_h3]:font-semibold\",\n \"[&_blockquote]:my-1.5 [&_blockquote]:border-l-2 [&_blockquote]:border-border [&_blockquote]:pl-3 [&_blockquote]:text-text-muted\",\n \"[&_hr]:my-2 [&_hr]:border-border\",\n \"[&_pre]:my-1.5 [&_pre]:overflow-x-auto [&_pre]:rounded-md [&_pre]:bg-surface-sunk [&_pre]:p-2 [&_pre]:text-xs\",\n \"[&_code]:rounded-sm [&_code]:bg-surface-sunk [&_code]:px-1 [&_code]:py-0.5 [&_code]:font-mono [&_code]:text-xs\",\n \"[&_pre_code]:bg-transparent [&_pre_code]:p-0\",\n \"[&_table]:my-1.5 [&_table]:w-full [&_table]:border-collapse [&_table]:text-xs\",\n \"[&_th]:border [&_th]:border-border [&_th]:px-2 [&_th]:py-1 [&_th]:text-left [&_th]:font-semibold\",\n \"[&_td]:border [&_td]:border-border [&_td]:px-2 [&_td]:py-1\",\n].join(\" \");\n\nconst components: Components = {\n p: (props) => <p className=\"my-1.5 leading-relaxed first:mt-0 last:mb-0\" {...props} />,\n a: (props) => (\n <a\n className=\"text-accent underline underline-offset-2 hover:opacity-80\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n {...props}\n />\n ),\n strong: (props) => <strong className=\"font-semibold\" {...props} />,\n em: (props) => <em className=\"italic\" {...props} />,\n ul: (props) => <ul className=\"my-1.5 list-disc space-y-0.5 pl-5\" {...props} />,\n ol: (props) => <ol className=\"my-1.5 list-decimal space-y-0.5 pl-5\" {...props} />,\n li: (props) => <li className=\"leading-relaxed\" {...props} />,\n h1: (props) => <h1 className=\"mb-1 mt-2 text-md font-semibold first:mt-0\" {...props} />,\n h2: (props) => <h2 className=\"mb-1 mt-2 text-base font-semibold first:mt-0\" {...props} />,\n h3: (props) => <h3 className=\"mb-1 mt-2 text-base font-semibold first:mt-0\" {...props} />,\n blockquote: (props) => (\n <blockquote className=\"my-1.5 border-l-2 border-border pl-3 text-text-muted\" {...props} />\n ),\n hr: (props) => <hr className=\"my-2 border-border\" {...props} />,\n pre: (props) => (\n <pre className=\"my-1.5 overflow-x-auto rounded-md bg-surface-sunk p-2 text-xs\" {...props} />\n ),\n code: ({ className, children, ...props }) => {\n const content = String(children ?? \"\");\n const isBlock = /language-/.test(className ?? \"\") || content.includes(\"\\n\");\n return isBlock ? (\n <code className=\"font-mono text-xs\" {...props}>\n {children}\n </code>\n ) : (\n <code className=\"rounded-sm bg-surface-sunk px-1 py-0.5 font-mono text-xs\" {...props}>\n {children}\n </code>\n );\n },\n table: (props) => (\n <div className=\"my-1.5 overflow-x-auto\">\n <table className=\"w-full border-collapse text-xs\" {...props} />\n </div>\n ),\n th: (props) => (\n <th className=\"border border-border px-2 py-1 text-left font-semibold\" {...props} />\n ),\n td: (props) => <td className=\"border border-border px-2 py-1\" {...props} />,\n};\n\nexport interface RichTextProps {\n /** Markdown or HTML; which of the two is auto-detected. */\n text: string | null | undefined;\n /** Extra classes on the wrapper (e.g. `text-sm` or a line-clamp). */\n className?: string;\n /** Force a branch instead of detecting — only when the heuristic gets in\n * the way (e.g. plain text that happens to look like a tag). */\n as?: \"markdown\" | \"html\";\n}\n\n/** Safely rendered markdown-or-HTML text. */\nexport function RichText({ text, className, as }: RichTextProps) {\n const src = text ?? \"\";\n const isHtml = as ? as === \"html\" : HTML_TAG.test(src);\n\n if (isHtml) {\n return (\n <div\n className={cn(\"break-words\", HTML_PROSE, className)}\n dangerouslySetInnerHTML={{\n __html: DOMPurify.sanitize(src, { ADD_ATTR: [\"target\", \"rel\"] }),\n }}\n />\n );\n }\n\n return (\n <div className={cn(\"break-words\", className)}>\n <Markdown remarkPlugins={[remarkGfm]} components={components}>\n {src}\n </Markdown>\n </div>\n );\n}\n","import type { ArtifactBlock, ArtifactRuntime, ArtifactTone } from \"@opencxh/domain\";\nimport { cn } from \"../utils/cn\";\nimport { RichText } from \"./RichText\";\n\nexport interface ArtifactViewProps {\n blocks: ArtifactBlock[];\n /** Everything except `\"inert\"` is rejected. See the class note above. */\n runtime?: ArtifactRuntime;\n className?: string;\n}\n\n/** Statisch, want `text-${tone}-fg` genereert niets (Tailwind scant letterlijk). */\nconst KPI_TONE: Record<ArtifactTone, string> = {\n info: \"text-info-fg\",\n success: \"text-success-fg\",\n warning: \"text-warning-fg\",\n destructive: \"text-danger-fg\",\n};\n\nconst CALLOUT_TONE: Record<ArtifactTone, string> = {\n info: \"bg-info-soft border-info-border\",\n success: \"bg-success-soft border-success-border\",\n warning: \"bg-warning-soft border-warning-border\",\n destructive: \"bg-danger-soft border-danger-border\",\n};\n\n/** One to four tiles in a row; static too, for the same reason. */\nconst KPI_COLUMNS: Record<number, string> = {\n 1: \"grid-cols-1\",\n 2: \"grid-cols-2\",\n 3: \"grid-cols-3\",\n 4: \"grid-cols-4\",\n};\n\nfunction Block({ block }: { block: ArtifactBlock }) {\n switch (block.type) {\n case \"heading\":\n if (block.level === 1) {\n return <h1 className=\"text-2xl font-semibold tracking-tight text-text\">{block.text}</h1>;\n }\n if (block.level === 2) {\n return (\n <h2 className=\"pt-2 text-base font-semibold tracking-tight text-text\">{block.text}</h2>\n );\n }\n return <h3 className=\"pt-1 text-sm font-semibold text-text\">{block.text}</h3>;\n\n case \"paragraph\":\n return <RichText as=\"markdown\" text={block.text} className=\"text-sm text-text\" />;\n\n case \"quote\":\n return (\n <blockquote className=\"border-l-2 border-border pl-3.5\">\n <RichText as=\"markdown\" text={block.text} className=\"text-sm text-text-muted\" />\n </blockquote>\n );\n\n case \"code\":\n return (\n <pre className=\"overflow-x-auto rounded-lg bg-surface-sunk p-3 text-xs\">\n <code className=\"font-mono\">{block.text}</code>\n </pre>\n );\n\n case \"divider\":\n return <hr className=\"border-border-subtle\" />;\n\n case \"list\": {\n const List = block.style === \"numbered\" ? \"ol\" : \"ul\";\n return (\n <List\n className={cn(\n \"flex flex-col gap-2 pl-5 text-sm text-text\",\n block.style === \"numbered\" ? \"list-decimal\" : \"list-disc\",\n )}\n >\n {block.items.map((item, index) => (\n <li key={index} className=\"leading-relaxed marker:text-text-subtle\">\n {item.lead && <strong className=\"font-semibold\">{item.lead} </strong>}\n <RichText as=\"markdown\" text={item.text} className=\"inline\" />\n </li>\n ))}\n </List>\n );\n }\n\n case \"table\":\n return (\n <figure className=\"m-0\">\n {/* Brede tabellen scrollen in hun eigen doos; het document zelf mag\n nooit horizontaal schuiven. */}\n <div className=\"overflow-x-auto rounded-lg ring-1 ring-border\">\n <table className=\"w-full border-collapse text-sm\">\n <thead>\n <tr className=\"bg-surface-sunk\">\n {block.columns.map((column, index) => (\n <th\n key={index}\n className={cn(\n \"px-4 py-2.5 text-xs font-semibold text-text-muted\",\n column.align === \"right\" ? \"text-right\" : \"text-left\",\n )}\n >\n {column.label}\n </th>\n ))}\n </tr>\n </thead>\n <tbody>\n {block.rows.map((row, rowIndex) => (\n <tr key={rowIndex} className=\"border-t border-border-subtle\">\n {row.map((value, cellIndex) => (\n <td\n key={cellIndex}\n className={cn(\n \"px-4 py-2.5 tabular-nums text-text\",\n block.columns[cellIndex]?.align === \"right\" ? \"text-right\" : \"text-left\",\n )}\n >\n {value}\n </td>\n ))}\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n {block.caption && (\n <figcaption className=\"pt-2 text-xs italic text-text-subtle\">\n {block.caption}\n </figcaption>\n )}\n </figure>\n );\n\n case \"kpi\":\n return (\n <div className={cn(\"grid gap-3\", KPI_COLUMNS[block.items.length] ?? \"grid-cols-4\")}>\n {block.items.map((item, index) => (\n <div key={index} className=\"rounded-lg bg-surface-sunk px-4 py-3.5\">\n <div\n className={cn(\n \"text-2xl font-semibold tracking-tight\",\n item.tone ? KPI_TONE[item.tone] : \"text-text\",\n )}\n >\n {item.value}\n </div>\n <div className=\"pt-0.5 text-xs text-text-muted\">{item.label}</div>\n </div>\n ))}\n </div>\n );\n\n case \"callout\":\n return (\n <div className={cn(\"rounded-lg border px-4 py-3\", CALLOUT_TONE[block.tone])}>\n {block.title && (\n <div className=\"pb-0.5 text-sm font-semibold text-text\">{block.title}</div>\n )}\n <RichText as=\"markdown\" text={block.text} className=\"text-sm text-text\" />\n </div>\n );\n\n default:\n // Unknown block: skip, don't crash.\n return null;\n }\n}\n\nexport function ArtifactView({ blocks, runtime = \"inert\", className }: ArtifactViewProps) {\n if (runtime !== \"inert\") {\n return (\n <div\n className={cn(\n \"rounded-lg border border-warning-border bg-warning-soft px-4 py-3\",\n className,\n )}\n >\n <div className=\"text-sm font-semibold text-text\">\n Deze versie kan hier niet getoond worden\n </div>\n <div className=\"pt-0.5 text-xs text-text-muted\">\n Hij is opgeslagen als <code className=\"font-mono\">{runtime}</code>, en deze weergave\n tekent alleen inerte inhoud.\n </div>\n </div>\n );\n }\n\n return (\n <article className={cn(\"flex flex-col gap-4\", className)}>\n {blocks.map((block, index) => (\n <Block key={block.block_id ?? index} block={block} />\n ))}\n </article>\n );\n}\n","import { Sparkles } from \"lucide-react\";\nimport type React from \"react\";\nimport { cn } from \"../utils/cn\";\n\nexport interface AssistantCardProps extends Omit<React.HTMLAttributes<HTMLDivElement>, \"title\"> {\n /** Card heading (e.g. \"Briefing van je assistent\", \"Voorstel van de assistent\"). */\n title: React.ReactNode;\n /** Leading icon; defaults to the lucide Sparkles icon. */\n icon?: React.ReactNode;\n /** Optional action rendered on the header's right (e.g. a \"Gebruiken\" button). */\n action?: React.ReactNode;\n /** Body content. */\n children?: React.ReactNode;\n}\n\n/**\n * AI-assistant card shell — the tinted \"Briefing / Voorstel van de assistent\"\n * surface used on Mijn dag and in the Focus queue. Uses the `--assistant-*`\n * design-system tokens so the tint is mode-aware.\n *\n * @example\n * ```tsx\n * <AssistantCard title=\"Voorstel van de assistent\" action={<Button>Gebruiken</Button>}>\n * Marc wacht 12 min op WhatsApp — concept staat klaar.\n * </AssistantCard>\n * ```\n */\nexport function AssistantCard({\n title,\n icon = <Sparkles className=\"size-icon-md\" aria-hidden />,\n action,\n children,\n className,\n ...props\n}: AssistantCardProps) {\n return (\n <div\n className={cn(\n \"flex flex-col gap-2 rounded-xl border p-4\",\n \"border-assistant-border bg-assistant-soft\",\n className,\n )}\n {...props}\n >\n <div className=\"flex items-center gap-2 text-assistant\">\n <span aria-hidden>{icon}</span>\n <span className=\"text-sm font-semibold\">{title}</span>\n {action && <span className=\"ml-auto\">{action}</span>}\n </div>\n {children && <div className=\"text-sm leading-relaxed text-text\">{children}</div>}\n </div>\n );\n}\n","/**\n * Deterministic colour for a thing that has a name but no status.\n *\n * The rule this exists to enforce: the same name gets the same colour\n * everywhere in the product. Every surface that used to pick a plate colour did\n * it locally — an index in a list, a hash of its own, or a fixed neutral — so\n * one person could be blue in the inbox and grey on a task, and the colour\n * carried no information at all.\n *\n * The palette is the `--color-cat-*` categorical set, which the design system\n * defines for exactly this: equal chroma and lightness, so no entry outranks\n * another, and dark mode is handled by the tokens rather than here.\n */\n\n/** How many `--color-cat-N` entries the design system defines. */\nexport const IDENTITY_TONE_COUNT = 5;\n\nexport type IdentityTone = 1 | 2 | 3 | 4 | 5;\n\n/**\n * FNV-1a. Small, stable across runs and platforms, and well spread for short\n * strings — `Math.random` would break the whole point, and summing char codes\n * collides badly on names that are anagrams (\"Bram\"/\"Marb\") or differ only in\n * order (\"Team A\"/\"A Team\").\n */\nfunction hash(seed: string): number {\n let h = 0x811c9dc5;\n for (let i = 0; i < seed.length; i++) {\n h ^= seed.charCodeAt(i);\n h = Math.imul(h, 0x01000193);\n }\n return h >>> 0;\n}\n\n/**\n * Normalised so that the same identity written differently still lands on the\n * same colour: casing and surrounding whitespace never distinguish two names.\n */\nexport function identityTone(seed: string | undefined | null): IdentityTone {\n const key = (seed ?? \"\").trim().toLowerCase();\n if (!key) return 1;\n return ((hash(key) % IDENTITY_TONE_COUNT) + 1) as IdentityTone;\n}\n\n/**\n * Whole class names, not interpolated fragments: Tailwind scans source text, so\n * `bg-cat-${n}-soft` would compile to nothing and the plate would come out\n * transparent.\n */\nconst PLATE: Record<IdentityTone, string> = {\n 1: \"bg-cat-1-soft text-cat-1-fg\",\n 2: \"bg-cat-2-soft text-cat-2-fg\",\n 3: \"bg-cat-3-soft text-cat-3-fg\",\n 4: \"bg-cat-4-soft text-cat-4-fg\",\n 5: \"bg-cat-5-soft text-cat-5-fg\",\n};\n\nconst SOLID: Record<IdentityTone, string> = {\n 1: \"bg-cat-1\",\n 2: \"bg-cat-2\",\n 3: \"bg-cat-3\",\n 4: \"bg-cat-4\",\n 5: \"bg-cat-5\",\n};\n\n/** Wash + readable mark, for a plate that holds a monogram or a glyph. */\nexport function identityPlateClass(seed: string | undefined | null): string {\n return PLATE[identityTone(seed)];\n}\n\n/** The solid colour, for a dot or a rule. */\nexport function identityDotClass(seed: string | undefined | null): string {\n return SOLID[identityTone(seed)];\n}\n","import React from \"react\";\nimport { cn } from \"../utils/cn\";\nimport { identityPlateClass } from \"../utils/identity\";\n\n/**\n * Plate colour. `default` is the neutral disc for a lone monogram; `identity`\n * derives the colour from `name` so the same person or team is the same colour\n * on every surface (see {@link identityPlateClass}) — use it in a list of many\n * people. The other three are for a message thread, where the plate answers\n * \"which side is this from\": `in` is the other party, `out` is us (ink, so it\n * reads as ours in both themes), `note` an internal remark. Each is a fill with\n * its own foreground — the ramp does not change between light and dark.\n */\nexport type AvatarTone = \"default\" | \"identity\" | \"in\" | \"out\" | \"note\";\n\nconst toneClasses: Record<Exclude<AvatarTone, \"identity\">, string> = {\n default: \"bg-avatar text-avatar-fg\",\n in: \"bg-avatar-in text-avatar-in-fg\",\n out: \"bg-accent text-accent-fg\",\n note: \"bg-avatar-note text-avatar-note-fg\",\n};\n\nexport interface AvatarProps {\n /** Full name; used for the initials fallback and alt/title text */\n name?: string;\n /** Optional image URL; falls back to initials when absent or on load error */\n src?: string;\n /** Plate colour; see {@link AvatarTone}. */\n tone?: AvatarTone;\n size?: \"xs\" | \"sm\" | \"md\" | \"lg\";\n className?: string;\n /** Override the title attribute (defaults to `name`) */\n title?: string;\n}\n\nconst sizeClasses: Record<NonNullable<AvatarProps[\"size\"]>, string> = {\n xs: \"w-5 h-5 text-xs\",\n sm: \"w-6 h-6 text-xs\",\n md: \"w-7 h-7 text-base\",\n lg: \"w-9 h-9 text-sm\",\n};\n\nfunction initials(name?: string): string {\n if (!name) return \"?\";\n // Split on anything that is not a letter/digit (space, @, ., -, _, …) so\n // addresses like \"mail-team@x.nl\" give clean initials (\"MT\"), not \"M-\".\n const parts = name\n .trim()\n .split(/[^\\p{L}\\p{N}]+/u)\n .filter(Boolean);\n if (parts.length >= 2) return (parts[0][0] + parts[1][0]).toUpperCase();\n if (parts.length === 1) return parts[0].slice(0, 2).toUpperCase();\n return \"?\";\n}\n\n/**\n * Circular avatar: renders the photo when `src` is set (falling back to\n * initials on error), otherwise a monogram derived from `name`.\n */\nexport const Avatar: React.FC<AvatarProps> = ({\n name,\n src,\n tone = \"default\",\n size = \"sm\",\n className,\n title,\n}) => {\n const [errored, setErrored] = React.useState(false);\n const base = cn(\n // Its own token and not `surface-hover`: at 0.972 the plate was within a few\n // percent of every surface it sits on, so the monogram floated instead of\n // sitting in a disc.\n \"inline-flex items-center justify-center rounded-full overflow-hidden shrink-0 font-semibold select-none\",\n // `identity` is seeded on the name, so the same person keeps their colour\n // wherever they appear; the fixed tones stay as they were.\n tone === \"identity\" ? identityPlateClass(name) : toneClasses[tone],\n sizeClasses[size],\n className,\n );\n\n if (src && !errored) {\n return (\n <img\n src={src}\n alt={name ?? \"\"}\n title={title ?? name}\n className={cn(base, \"object-cover\")}\n onError={() => setErrored(true)}\n />\n );\n }\n\n return (\n <span className={base} title={title ?? name} aria-label={name}>\n {initials(name)}\n </span>\n );\n};\n","import type React from \"react\";\nimport { cn } from \"../utils/cn\";\nimport { Avatar, type AvatarProps } from \"./Avatar\";\n\nexport interface AvatarStackPerson {\n id?: string;\n name?: string;\n src?: string;\n /** Owner / assignee — rendered with an accent ring */\n owner?: boolean;\n /** Whether this person has seen the item; unseen renders dimmed */\n seen?: boolean;\n}\n\nexport interface AvatarStackProps {\n people: AvatarStackPerson[];\n /** Max avatars to show before collapsing the rest into a \"+N\" chip */\n max?: number;\n size?: AvatarProps[\"size\"];\n className?: string;\n}\n\n// Panel-coloured ring so overlapping avatars stay visually separated; the\n// owner gets an extra accent halo on top of it.\nconst RING = \"ring-surface\";\nconst OWNER_RING = \"ring-surface-accent\";\n\n/**\n * Overlapping row of avatars with an optional \"+N\" overflow chip. Unseen\n * people are dimmed; the owner carries an accent ring.\n */\nexport const AvatarStack: React.FC<AvatarStackProps> = ({\n people,\n max = 4,\n size = \"sm\",\n className,\n}) => {\n if (people.length === 0) return null;\n const shown = people.slice(0, max);\n const overflow = people.length - shown.length;\n\n return (\n <span className={cn(\"inline-flex items-center\", className)}>\n {shown.map((p, i) => (\n <Avatar\n key={p.id ?? `${p.name}-${i}`}\n name={p.name}\n src={p.src}\n size={size}\n title={p.name ? (p.seen === false ? `${p.name} — nog niet gelezen` : p.name) : undefined}\n className={cn(\n i > 0 && \"-ml-2\",\n p.owner ? OWNER_RING : RING,\n p.seen === false && \"opacity-40\",\n )}\n />\n ))}\n {overflow > 0 && (\n <span\n className={cn(\n // Same fill as an Avatar plate, so the \"+3\" reads as one more disc\n // in the row rather than a gap at the end of it.\n \"inline-flex items-center justify-center rounded-full shrink-0 -ml-2 font-semibold bg-avatar text-avatar-fg\",\n size === \"xs\"\n ? \"w-5 h-5 text-xs\"\n : size === \"md\"\n ? \"w-7 h-7 text-xs\"\n : size === \"lg\"\n ? \"w-9 h-9 text-xs\"\n : \"w-6 h-6 text-xs\",\n RING,\n )}\n >\n +{overflow}\n </span>\n )}\n </span>\n );\n};\n","import type { ComponentType, ReactNode } from \"react\";\nimport { cn } from \"../../utils/cn\";\nimport { Avatar, type AvatarTone } from \"../Avatar\";\nimport { Icon } from \"../Icon\";\n\n/**\n * What a feed card is, said in colour.\n *\n * Two axes fold into one list. Direction: `neutral` (received, and anything that\n * has no side — a transcript, an upload) versus `out` (sent by us, pale blue).\n * Kind: `note` is amber and not customer-visible, `assistant` is violet. A\n * thread is scanned for \"did we answer this yet\", so the sides have to differ by\n * hue and not by a percent of grey.\n */\nexport type ActivityTone = \"neutral\" | \"out\" | \"note\" | \"assistant\";\n\nconst TONE_BUBBLE: Record<ActivityTone, string> = {\n neutral: \"bg-surface-sunk\",\n out: \"bg-bubble-out-soft\",\n note: \"bg-note-soft\",\n assistant: \"bg-assistant-soft\",\n};\n\nconst TONE_TITLE: Record<ActivityTone, string> = {\n neutral: \"text-text\",\n out: \"text-text\",\n note: \"text-note\",\n assistant: \"text-text\",\n};\n\nexport interface ActivityRowProps {\n /** Who or what this row is from — {@link ActivityAvatar} or {@link ActivityGlyph}. */\n avatar: ReactNode;\n /** Bold: a sender's name, or what happened (\"Transcript\", \"2 files\"). */\n title: ReactNode;\n /** Muted, beside the title: \"to jeroen@…\", \"3 min 48 s · 2 speakers\". */\n context?: ReactNode;\n /** Right of the header, before the time — a badge, a pending spinner. */\n meta?: ReactNode;\n /** Right-most in the header. */\n time?: string;\n tone?: ActivityTone;\n /** Button row along the bottom edge of the card. */\n actions?: ReactNode;\n /** Rendered outside the card, under it — an inline composer, read receipts. */\n below?: ReactNode;\n children?: ReactNode;\n}\n\n/**\n * One card in an activity feed: avatar, then a full-width card with a header\n * line and a body.\n *\n * Every kind of activity uses this — mail, note, transcript, files, an\n * assistant's proposal, a comment under a work item, and whatever an app\n * declares. They used to be six hand-built variations on the same idea, each\n * with its own avatar tile, its own header spacing and its own idea of how wide\n * a card is; you could see the difference scrolling a single conversation.\n *\n * The one exception is chat (comms' `ChatMessage`), which keeps left/right\n * bubbles that size to their content — a one-line \"ok\" in a full-width card\n * reads as a document, and a chat thread is the one place where direction *is*\n * the point.\n */\nexport function ActivityRow({\n avatar,\n title,\n context,\n meta,\n time,\n tone = \"neutral\",\n actions,\n below,\n children,\n}: ActivityRowProps) {\n return (\n <div className=\"flex min-w-0 flex-col gap-2\">\n <div className=\"flex min-w-0 items-start gap-3.5\">\n <span className=\"mt-0.5 shrink-0\">{avatar}</span>\n\n <div\n className={cn(\"min-w-0 flex-1 overflow-hidden rounded-lg px-4 py-3.5\", TONE_BUBBLE[tone])}\n >\n <div className=\"flex items-baseline gap-2\">\n <span\n className={cn(\n \"min-w-0 truncate text-md font-semibold\",\n !context && \"flex-1\",\n TONE_TITLE[tone],\n )}\n >\n {title}\n </span>\n {context && (\n <span className=\"min-w-0 flex-1 truncate text-xs text-text-muted\">{context}</span>\n )}\n {(meta || time) && (\n <span className=\"ml-auto flex shrink-0 items-center gap-1.5\">\n {meta}\n {time && <span className=\"text-xs tabular-nums text-text-subtle\">{time}</span>}\n </span>\n )}\n </div>\n\n {children && <div className=\"pt-2\">{children}</div>}\n\n {/* Inset so the first button's label lines up with the text above it. */}\n {actions && (\n <div className=\"-ml-2.5 flex flex-wrap items-center gap-0.5 pt-2.5\">{actions}</div>\n )}\n </div>\n </div>\n\n {below}\n </div>\n );\n}\n\n/**\n * A `ghost` button in a card's action row needs a hover a step *darker* than the\n * card — its own `surface-hover` is the same colour as the fill it sits on.\n * Applied per button rather than to the row, because a filled button in the same\n * row (an assistant's \"Approve\") must keep its own hover.\n */\nexport const cardActionClass = \"hover:bg-surface-track\";\n\n/**\n * A person: initials, or their photo. The plate says which side they are on —\n * the other party in the channel hue, our own side in ink, a note in amber.\n */\nexport function ActivityAvatar({\n name,\n src,\n tone = \"in\",\n}: {\n name?: string;\n src?: string;\n tone?: AvatarTone;\n}) {\n return <Avatar name={name} src={src} tone={tone} size=\"sm\" />;\n}\n\n/**\n * A kind rather than a person — a transcript, an upload, the assistant. One step\n * darker than the card it introduces, so the glyph reads as a marker and not as\n * a second surface.\n */\nexport function ActivityGlyph({\n icon,\n tone = \"neutral\",\n title,\n}: {\n // Same shape as the kit's `Icon` takes: a lucide component, whatever its\n // exact prop signature.\n icon: ComponentType<any>;\n tone?: ActivityTone;\n title?: string;\n}) {\n return (\n <span\n title={title}\n className={cn(\n \"grid size-avatar-md shrink-0 place-items-center rounded-full\",\n tone === \"assistant\"\n ? \"bg-assistant-soft text-assistant\"\n : \"bg-surface-track text-text-muted\",\n )}\n >\n <Icon icon={icon} size=\"sm\" color=\"current\" />\n </span>\n );\n}\n\n/**\n * An item *inside* a card — a file, a proposed step, an attachment.\n *\n * White on the card's sunk fill: inside a recess the way to separate something\n * is to lift it, not to sink it further.\n */\nexport function RowCard({ children, className }: { children: ReactNode; className?: string }) {\n return (\n <div className={cn(\"flex items-center gap-2.5 rounded-md bg-surface px-3 py-2.5\", className)}>\n {children}\n </div>\n );\n}\n","import { ArrowUpRight } from \"lucide-react\";\nimport { Icon, resolveLucideIcon } from \"../Icon\";\n\nexport interface TimelineEventProps {\n /** Kebab-case lucide name, e.g. from the activity catalogue. */\n icon?: string;\n /** The whole sentence, already resolved and translated by the caller. */\n text: string;\n time: string;\n joinUrl?: string;\n joinLabel?: string;\n}\n\n/**\n * Single-line timeline row: everything that is not a card — assigned, closed,\n * moved. Deliberately *not* folded into card form: an empty event in a card with\n * header and avatar wraps three lines of chrome around one sentence.\n *\n * Still aligned with the cards: indented to where the card edge starts (avatar +\n * gap), so the timeline shares one left margin instead of floating each event\n * in the middle.\n *\n * Purely presentational — the caller owns which sentence this is, so per-type\n * copy stays in the activity catalogue and out of the kit.\n */\nexport function TimelineEvent({ icon, text, time, joinUrl, joinLabel }: TimelineEventProps) {\n const IconComponent = resolveLucideIcon(icon);\n\n return (\n <div className=\"flex items-center gap-2 py-1.5 pl-10.5 text-xs text-text-subtle\">\n {IconComponent && <Icon icon={IconComponent} size=\"sm\" color=\"current\" />}\n <span className=\"min-w-0 truncate\">{text}</span>\n <span aria-hidden className=\"shrink-0 text-text-disabled\">\n ·\n </span>\n <span className=\"shrink-0 tabular-nums\">{time}</span>\n {joinUrl && (\n <a\n href={joinUrl}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"shrink-0 text-xs font-medium text-info-fg hover:underline\"\n >\n {joinLabel ?? \"Join\"} <Icon icon={ArrowUpRight} size=\"xs\" />\n </a>\n )}\n </div>\n );\n}\n","import type React from \"react\";\nimport { cn } from \"../utils/cn\";\nimport { identityPlateClass } from \"../utils/identity\";\n\n/** Communication channel keys the redesign colour-codes. */\nexport type ChannelKey = \"mail\" | \"wa\" | \"chat\" | \"tel\" | \"note\";\n\nexport interface ChannelBadgeProps {\n /**\n * Channel — drives the colour. A known key uses that channel's tokens; any\n * other string (a provider that brought its own channel along) gets a stable\n * colour derived from the string itself, so ten custom channels stay ten\n * distinguishable colours instead of ten identical grey tiles.\n */\n channel?: ChannelKey | string;\n /** Glyph or short label inside the tile (e.g. an icon, or an initial). */\n children?: React.ReactNode;\n /** Tile size. */\n size?: \"sm\" | \"md\" | \"lg\";\n className?: string;\n title?: string;\n}\n\n/**\n * Channel key → semantic token pair. The keys are the redesign's shorthand\n * (`wa`, `tel`); the tokens spell the channel out (`whatsapp`, `phone`).\n * Written as whole class names so Tailwind can find them.\n */\nconst CH: Record<ChannelKey, { fg: string; bg: string }> = {\n mail: { fg: \"text-channel-mail-fg\", bg: \"bg-channel-mail-soft\" },\n wa: { fg: \"text-channel-whatsapp-fg\", bg: \"bg-channel-whatsapp-soft\" },\n chat: { fg: \"text-channel-chat-fg\", bg: \"bg-channel-chat-soft\" },\n tel: { fg: \"text-channel-phone-fg\", bg: \"bg-channel-phone-soft\" },\n note: { fg: \"text-channel-note-fg\", bg: \"bg-channel-note-soft\" },\n};\n\nconst sizes: Record<NonNullable<ChannelBadgeProps[\"size\"]>, string> = {\n sm: \"size-avatar-xs rounded-sm text-xs\",\n md: \"size-avatar-sm rounded-md text-xs\",\n lg: \"size-avatar-lg rounded-md text-base\",\n};\n\n/**\n * Coloured channel tile (the glyph-in-rounded-square used across inbox rows,\n * the focus queue, the attention list and Recent tabs). Colours come from the\n * `--color-channel-*` design-system tokens, so it is mode-aware.\n *\n * @example\n * ```tsx\n * <ChannelBadge channel=\"wa\">W</ChannelBadge>\n * <ChannelBadge channel=\"mail\" size=\"lg\"><MailIcon /></ChannelBadge>\n * ```\n */\nexport function ChannelBadge({\n channel,\n children,\n size = \"md\",\n className,\n title,\n}: ChannelBadgeProps) {\n const known = (channel as ChannelKey) in CH ? CH[channel as ChannelKey] : undefined;\n return (\n <span\n title={title}\n className={cn(\n \"inline-flex shrink-0 items-center justify-center font-bold\",\n sizes[size],\n // An undeclared channel used to collapse onto the neutral \"note\" tone,\n // which is also what a real internal note looks like. Deriving from the\n // key keeps it distinguishable and keeps `note` meaning `note`.\n known\n ? cn(known.fg, known.bg)\n : channel\n ? identityPlateClass(channel)\n : cn(CH.note.fg, CH.note.bg),\n className,\n )}\n >\n {children}\n </span>\n );\n}\n","import { ImageOff } from \"lucide-react\";\nimport type React from \"react\";\nimport { useState } from \"react\";\nimport { cn } from \"../utils/cn\";\nimport { Icon } from \"./Icon\";\n\nexport interface ImageProps extends React.ImgHTMLAttributes<HTMLImageElement> {\n /** Image source URL */\n src: string;\n /** Alt text for accessibility */\n alt: string;\n /** Image size preset */\n size?: \"xs\" | \"sm\" | \"md\" | \"lg\" | \"xl\" | \"full\";\n /** Aspect ratio */\n aspectRatio?: \"square\" | \"video\" | \"portrait\" | \"landscape\" | \"auto\";\n /** Border radius */\n radius?: \"none\" | \"sm\" | \"md\" | \"lg\" | \"xl\" | \"full\";\n /** Whether to show loading state */\n showLoading?: boolean;\n /** Whether to show error state */\n showError?: boolean;\n /** Fallback image URL */\n fallback?: string;\n /** Loading placeholder content */\n loadingContent?: React.ReactNode;\n /** Error placeholder content */\n errorContent?: React.ReactNode;\n /** Additional CSS classes */\n className?: string;\n}\n\n/**\n * Image component with loading states, error handling, and theme integration\n */\nexport const Image: React.FC<ImageProps> = ({\n src,\n alt,\n size = \"md\",\n aspectRatio = \"auto\",\n radius = \"md\",\n showLoading = true,\n showError = true,\n fallback,\n loadingContent,\n errorContent,\n className,\n onLoad,\n onError,\n ...props\n}) => {\n const [isLoading, setIsLoading] = useState(true);\n const [hasError, setHasError] = useState(false);\n const [currentSrc, setCurrentSrc] = useState(src);\n\n const handleLoad = (event: React.SyntheticEvent<HTMLImageElement>) => {\n setIsLoading(false);\n setHasError(false);\n onLoad?.(event);\n };\n\n const handleError = (event: React.SyntheticEvent<HTMLImageElement>) => {\n setIsLoading(false);\n setHasError(true);\n\n // Try fallback if available and not already using it\n if (fallback && currentSrc !== fallback) {\n setCurrentSrc(fallback);\n setHasError(false);\n setIsLoading(true);\n return;\n }\n\n onError?.(event);\n };\n\n const containerClasses = cn(\n \"relative overflow-hidden bg-surface-hover\",\n\n // Size variants\n {\n \"w-8 h-8\": size === \"xs\",\n \"w-12 h-12\": size === \"sm\",\n \"w-16 h-16\": size === \"md\",\n \"w-24 h-24\": size === \"lg\",\n \"w-32 h-32\": size === \"xl\",\n \"w-full h-full\": size === \"full\",\n },\n\n // Aspect ratio variants\n {\n \"aspect-square\": aspectRatio === \"square\",\n \"aspect-video\": aspectRatio === \"video\",\n \"aspect-portrait\": aspectRatio === \"portrait\",\n \"aspect-landscape\": aspectRatio === \"landscape\",\n },\n\n // Border radius variants\n {\n \"rounded-none\": radius === \"none\",\n \"rounded-sm\": radius === \"sm\",\n \"rounded-md\": radius === \"md\",\n \"rounded-lg\": radius === \"lg\",\n \"rounded-xl\": radius === \"xl\",\n \"rounded-full\": radius === \"full\",\n },\n\n className,\n );\n\n const imageClasses = cn(\"w-full h-full object-cover transition-opacity duration-fast\", {\n \"opacity-0\": isLoading || hasError,\n \"opacity-100\": !isLoading && !hasError,\n });\n\n const placeholderClasses = cn(\n \"absolute inset-0 flex items-center justify-center\",\n \"text-text-muted\",\n );\n\n const defaultLoadingContent = (\n <div className=\"animate-pulse\">\n <div className=\"w-6 h-6 bg-surface-hover rounded\"></div>\n </div>\n );\n\n const defaultErrorContent = (\n <div className=\"text-center\">\n <Icon icon={ImageOff} size=\"lg\" className=\"mx-auto mb-1\" />\n <span className=\"text-xs\">Failed to load</span>\n </div>\n );\n\n return (\n <div className={containerClasses}>\n <img\n {...props}\n src={currentSrc}\n alt={alt}\n className={imageClasses}\n onLoad={handleLoad}\n onError={handleError}\n />\n\n {/* Loading state */}\n {isLoading && showLoading && (\n <div className={placeholderClasses}>{loadingContent || defaultLoadingContent}</div>\n )}\n\n {/* Error state */}\n {hasError && showError && (\n <div className={placeholderClasses}>{errorContent || defaultErrorContent}</div>\n )}\n </div>\n );\n};\n","import type React from \"react\";\nimport { cn } from \"../utils/cn\";\n\nexport interface KpiCardProps extends React.HTMLAttributes<HTMLDivElement> {\n /** The big number / metric. */\n value: React.ReactNode;\n /** Caption under the value. */\n label: React.ReactNode;\n /** Tone — `urgent` tints the number + border, `success` tints the number. */\n tone?: \"default\" | \"urgent\" | \"success\";\n}\n\nconst valueTone: Record<NonNullable<KpiCardProps[\"tone\"]>, string> = {\n default: \"text-text\",\n urgent: \"text-danger-fg\",\n success: \"text-success-fg\",\n};\n\n/**\n * KPI / stat card (the Mijn dag metric row). Token-driven, mode-aware.\n *\n * @example\n * ```tsx\n * <KpiCard value={3} label=\"wachten op jou · gem. 1u12\" />\n * <KpiCard value={1} label=\"dreigt SLA te missen\" tone=\"urgent\" />\n * ```\n */\nexport function KpiCard({ value, label, tone = \"default\", className, ...props }: KpiCardProps) {\n return (\n <div className={cn(\"rounded-xl bg-editor px-4 py-3.5\", className)} {...props}>\n <div className={cn(\"text-2xl font-semibold\", valueTone[tone])}>{value}</div>\n <div className=\"pt-0.5 text-xs text-text-muted\">{label}</div>\n </div>\n );\n}\n","import React, { forwardRef } from \"react\";\nimport { cn } from \"../utils/cn\";\n\nexport interface CheckboxProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, \"size\"> {\n label?: string;\n\n helperText?: string;\n\n error?: string;\n\n size?: \"sm\" | \"md\" | \"lg\" | \"full\";\n\n indeterminate?: boolean;\n\n /** Additional class name for the container */\n containerClassName?: string;\n\n /** Additional class name for the label */\n labelClassName?: string;\n}\n\n/** The box grows with the field size, so a checkbox row reads as the same\n * weight as the inputs beside it instead of a small mark floating next to them. */\nconst checkboxSizes = {\n sm: \"size-checkbox-sm\",\n md: \"size-checkbox\",\n lg: \"size-checkbox-lg\",\n full: \"size-checkbox\",\n};\n\n/** Row height matches the control tokens so a checkbox lines up in a form. */\nconst rowSizes = {\n sm: \"min-h-control-sm\",\n md: \"min-h-control-md\",\n lg: \"min-h-control-lg\",\n full: \"min-h-control-md\",\n};\n\n/**\n * `full` means full width, not large: it mirrors `md` here the same way\n * `checkboxSizes` and `rowSizes` do, and the same way every other control in\n * the kit keeps `text-base` at `full`. It used to read `text-md`, which made\n * the label a step bigger than every field beside it — every Form passes\n * `size=\"full\"`, so that hit each checkbox in every form in the product.\n */\nconst labelSizes = {\n sm: \"text-sm\",\n md: \"text-base\",\n lg: \"text-md\",\n full: \"text-base\",\n};\n\n/**\n * Native checkbox styled via `accent-color`: the checked fill is the accent\n * token and dark mode needs no extra classes.\n */\nexport const Checkbox = forwardRef<HTMLInputElement, CheckboxProps>(\n (\n {\n label,\n helperText,\n error,\n size = \"md\",\n indeterminate = false,\n containerClassName,\n labelClassName,\n className,\n id,\n ...props\n },\n ref,\n ) => {\n const reactId = React.useId();\n const checkboxId = id || `checkbox-${reactId}`;\n const hasError = Boolean(error);\n const innerRef = React.useRef<HTMLInputElement | null>(null);\n\n // Keep the forwarded ref and the internal one in sync so `indeterminate`\n // works whether or not the consumer passes a ref.\n const setRefs = React.useCallback(\n (node: HTMLInputElement | null) => {\n innerRef.current = node;\n if (typeof ref === \"function\") ref(node);\n else if (ref && typeof ref === \"object\") {\n (ref as React.MutableRefObject<HTMLInputElement | null>).current = node;\n }\n },\n [ref],\n );\n\n React.useEffect(() => {\n if (innerRef.current) innerRef.current.indeterminate = indeterminate;\n }, [indeterminate]);\n\n return (\n <div className={cn(\"flex flex-col\", containerClassName)}>\n <div className={cn(\"flex items-center gap-3\", rowSizes[size])}>\n <input\n ref={setRefs}\n id={checkboxId}\n type=\"checkbox\"\n aria-invalid={hasError || undefined}\n className={cn(\n // radius-xs (5px): its own step on the scale. At 16px the 9px\n // button radius reads as a circle and the 7px chip radius still\n // rounds the corners away — a checkbox has to stay a square you\n // can aim at, with the corner just knocked off.\n \"shrink-0 rounded-xs border accent-accent\",\n \"transition-colors duration-fast ease-out\",\n \"focus-visible:outline-none focus-visible:focus-ring\",\n \"disabled:cursor-not-allowed disabled:bg-surface-hover\",\n\n checkboxSizes[size],\n hasError ? \"border-danger-border\" : \"border-border-strong\",\n\n className,\n )}\n {...props}\n />\n\n {label && (\n <div className=\"flex-1\">\n <label\n htmlFor={checkboxId}\n className={cn(\n \"cursor-pointer\",\n hasError ? \"text-danger-fg\" : \"text-text\",\n labelSizes[size],\n labelClassName,\n )}\n >\n {label}\n </label>\n\n {helperText && !error && (\n <p className=\"mt-0.5 text-xs text-text-subtle\">{helperText}</p>\n )}\n </div>\n )}\n </div>\n\n {error && <p className=\"mt-1 text-xs text-danger-fg\">{error}</p>}\n </div>\n );\n },\n);\n\nCheckbox.displayName = \"Checkbox\";\n","import { ChevronRight, X } from \"lucide-react\";\nimport React from \"react\";\nimport { Checkbox } from \"../input/Checkbox\";\nimport { cn } from \"../utils/cn\";\n\nexport interface ListGroup {\n /** Group key — must match what `groupBy` returns */\n id: string;\n /** Header label; defaults to `id` */\n title?: string;\n /** Override the row count shown on the right */\n count?: number;\n /** Start collapsed (only with `collapsible`) */\n defaultCollapsed?: boolean;\n /**\n * `danger` tints the label and its count — for a section that is a problem\n * by definition (overdue, failed), where the rows themselves carry no\n * marker that the group is the bad one.\n */\n tone?: \"default\" | \"danger\" | \"muted\";\n}\n\nexport interface ListProps<T> {\n /** Rows to render */\n items: T[];\n /** Stable key per row */\n getRowKey?: (item: T, index: number) => string | number;\n /**\n * Row separation. `inset` gives each row a rounded hover plate held off the\n * edge instead of a rule — the treatment for a list of things you tick off\n * (tasks) rather than a table you scan across (the inbox).\n */\n variant?: \"plain\" | \"divided\" | \"bulleted\" | \"inset\";\n /** Leading slot — channel tile, avatar, status dot */\n leading?: (item: T, index: number) => React.ReactNode;\n /** Main content; defaults to `String(item)` */\n renderItem?: (item: T, index: number) => React.ReactNode;\n /** Trailing slot — timestamp, badge, counter */\n trailing?: (item: T, index: number) => React.ReactNode;\n /** Row click */\n onSelect?: (item: T, index: number) => void;\n /**\n * Fired when keyboard focus lands on a row. Lets a consumer mirror the\n * roving focus into its own \"active\" state (what a hand-rolled `useArrowNav`\n * used to provide) without owning the key handling.\n */\n onActiveIndexChange?: (index: number) => void;\n /** Highlighted row (accent wash) */\n isActive?: (item: T, index: number) => boolean;\n /** Bold + accent dot */\n unread?: (item: T, index: number) => boolean;\n /** Deprioritised noise (opacity) */\n dimmed?: (item: T, index: number) => boolean;\n\n /** Group rows under headers. Return the group id for each row. */\n groupBy?: (item: T, index: number) => string;\n /**\n * Group order and labels. Groups missing here are appended in order of first\n * appearance; groups listed here but empty are skipped.\n */\n groups?: ListGroup[];\n /** Header renderer; defaults to label + count */\n renderGroupHeader?: (group: ListGroup, items: T[]) => React.ReactNode;\n /** Headers stick to the top of the scroll container */\n stickyGroupHeaders?: boolean;\n /** Headers become a toggle that folds the group */\n collapsibleGroups?: boolean;\n /** Hide the count on the right of the header */\n hideGroupCount?: boolean;\n\n /** Checkbox column + bulk bar */\n selectable?: boolean;\n /** Selected rows (controlled) */\n selectedItems?: T[];\n /** Selection handler */\n onSelectionChange?: (selected: T[]) => void;\n /** Actions shown in the bulk bar when a selection exists */\n bulkActions?: React.ReactNode;\n\n /**\n * Column-label strip above the rows (\"Van · Gesprek · tijd\"), like the inbox.\n * Align it with the row by reusing the same widths as `renderItem`.\n */\n header?: React.ReactNode;\n /** Keep the column-label strip visible while scrolling */\n stickyHeader?: boolean;\n\n /** Wrap the list in a bordered container */\n bordered?: boolean;\n /** Empty state content */\n emptyContent?: React.ReactNode;\n /** Loading state */\n loading?: boolean;\n /** Number of skeleton rows while loading */\n loadingRows?: number;\n className?: string;\n \"aria-label\"?: string;\n}\n\n/**\n * One action in the bulk dock. Its own component rather than a `Button`\n * variant: the dock is an ink surface, so every ordinary button variant is a\n * light plate on a dark bar. Use it for the `bulkActions` slot.\n *\n * @example\n * ```tsx\n * bulkActions={<>\n * <ListBulkAction icon={<Icon icon={UserPlus} size=\"sm\" />} onClick={assign}>Toewijzen</ListBulkAction>\n * <ListBulkAction icon={<Icon icon={Archive} size=\"sm\" />} onClick={archive}>Archiveren</ListBulkAction>\n * </>}\n * ```\n */\nexport function ListBulkAction({\n icon,\n onClick,\n children,\n}: {\n icon?: React.ReactNode;\n onClick?: () => void;\n children: React.ReactNode;\n}) {\n return (\n <button\n type=\"button\"\n onClick={onClick}\n className={cn(\n \"inline-flex h-control-sm shrink-0 cursor-pointer items-center gap-1.5 rounded-md px-2.5\",\n \"text-sm font-medium whitespace-nowrap\",\n \"transition-colors duration-fast ease-out hover:bg-white/15\",\n \"focus-visible:outline-none focus-visible:focus-ring\",\n )}\n >\n {icon}\n {children}\n </button>\n );\n}\n\n/**\n * List — one row is one object you open, with content of varying length\n * (inbox conversations, notifications, search results). No column headers:\n * hierarchy lives inside the row.\n *\n * Use `Table` instead when you compare values between rows (sortable columns,\n * aligned amounts, select-all in a header).\n *\n * Grouped, like the inbox and task views:\n *\n * @example\n * ```tsx\n * <List\n * items={tasks}\n * getRowKey={(t) => t.id}\n * bordered\n * groupBy={(t) => t.bucket} // \"today\" | \"tomorrow\" | \"later\"\n * groups={[\n * { id: \"today\", title: \"Vandaag\" },\n * { id: \"tomorrow\", title: \"Morgen\" },\n * { id: \"later\", title: \"Later\" },\n * ]}\n * stickyGroupHeaders\n * collapsibleGroups\n * leading={(t) => <ChannelBadge channel={t.channel} size=\"sm\" />}\n * renderItem={(t) => <span className=\"truncate\">{t.title}</span>}\n * trailing={(t) => t.due}\n * unread={(t) => !t.read}\n * />\n * ```\n */\nexport function List<T>({\n items,\n getRowKey,\n variant = \"divided\",\n leading,\n renderItem,\n trailing,\n onSelect,\n onActiveIndexChange,\n isActive,\n unread,\n dimmed,\n groupBy,\n groups,\n renderGroupHeader,\n stickyGroupHeaders = false,\n collapsibleGroups = false,\n hideGroupCount = false,\n selectable = false,\n selectedItems = [],\n onSelectionChange,\n bulkActions,\n header,\n stickyHeader = false,\n bordered = false,\n emptyContent,\n loading = false,\n loadingRows = 4,\n className,\n \"aria-label\": ariaLabel,\n}: ListProps<T>) {\n const [collapsed, setCollapsed] = React.useState<Record<string, boolean>>(() =>\n Object.fromEntries((groups ?? []).filter((g) => g.defaultCollapsed).map((g) => [g.id, true])),\n );\n\n const rootRef = React.useRef<HTMLDivElement>(null);\n\n /**\n * Roving focus across the rows. Rows are focusable in DOM order, so this walks\n * them directly rather than tracking an index — grouping and collapsing cannot\n * put it out of sync that way.\n */\n const handleKeyDown = (event: React.KeyboardEvent<HTMLDivElement>) => {\n const keys = [\"ArrowDown\", \"ArrowUp\", \"Home\", \"End\"];\n if (!keys.includes(event.key)) return;\n\n const rows = Array.from(\n rootRef.current?.querySelectorAll<HTMLLIElement>(\"li[data-list-row]\") ?? [],\n );\n if (rows.length === 0) return;\n event.preventDefault();\n\n const current = rows.indexOf(document.activeElement as HTMLLIElement);\n const next =\n event.key === \"Home\"\n ? 0\n : event.key === \"End\"\n ? rows.length - 1\n : event.key === \"ArrowDown\"\n ? (current + 1) % rows.length\n : (current - 1 + rows.length) % rows.length;\n\n rows[next]?.focus();\n };\n\n const keyOf = (item: T, index: number) => (getRowKey ? getRowKey(item, index) : index);\n\n const isSelected = (item: T) => selectedItems.includes(item);\n\n const toggle = (item: T, checked: boolean) => {\n if (!onSelectionChange) return;\n onSelectionChange(checked ? [...selectedItems, item] : selectedItems.filter((i) => i !== item));\n };\n\n const toggleAll = (checked: boolean) => onSelectionChange?.(checked ? [...items] : []);\n\n const container = cn(\n \"flex flex-col\",\n // With a selection open the list has a dock at its foot. Sticky can only\n // park at the bottom of its own box, so on a list shorter than the window\n // the dock hung right under the last row instead of at the bottom of the\n // screen. Growing the box to the height of its parent puts it where it\n // belongs — and only while a selection exists, so an ordinary short list\n // keeps its natural height.\n selectable && selectedItems.length > 0 && \"min-h-full\",\n bordered && \"overflow-hidden rounded-lg border border-border-subtle\",\n className,\n );\n\n if (loading) {\n return (\n <div className={container}>\n {Array.from({ length: loadingRows }).map((_, i) => (\n <div\n key={`skeleton-${i}`}\n className={cn(\n \"flex animate-pulse items-center gap-3 px-4 py-2\",\n variant === \"divided\" && i > 0 && \"border-t border-border-subtle\",\n )}\n >\n <span className=\"size-tile-sm shrink-0 rounded-md bg-surface-hover\" />\n <span className=\"h-3 w-30 shrink-0 rounded-sm bg-surface-hover\" />\n <span className=\"h-3 flex-1 rounded-sm bg-surface-hover\" />\n <span className=\"h-3 w-8 shrink-0 rounded-sm bg-surface-hover\" />\n </div>\n ))}\n </div>\n );\n }\n\n if (items.length === 0 && emptyContent) {\n return <div className={container}>{emptyContent}</div>;\n }\n\n const allSelected = items.length > 0 && selectedItems.length === items.length;\n const someSelected = selectedItems.length > 0 && !allSelected;\n\n // Rows in one flat pass, so `index` keeps pointing at the source item.\n const renderRow = (item: T, index: number, firstInBlock: boolean) => {\n const active = isActive?.(item, index) ?? false;\n const isUnread = unread?.(item, index) ?? false;\n const isDimmed = dimmed?.(item, index) ?? false;\n const selected = selectable && isSelected(item);\n\n return (\n <li\n key={keyOf(item, index)}\n // A clickable row cannot be a <button>: it holds a checkbox and may hold\n // trailing actions, and buttons may not nest. Button semantics are put on\n // the row instead, so it is reachable, operable and announced correctly.\n {...(onSelect\n ? {\n role: \"button\" as const,\n tabIndex: 0,\n \"data-list-row\": \"\",\n \"aria-current\": active || undefined,\n onClick: () => onSelect(item, index),\n onFocus: () => onActiveIndexChange?.(index),\n onKeyDown: (event: React.KeyboardEvent<HTMLLIElement>) => {\n if (event.key !== \"Enter\" && event.key !== \" \") return;\n // Space scrolls the page by default.\n event.preventDefault();\n onSelect(item, index);\n },\n }\n : {})}\n className={cn(\n \"flex items-center gap-3 text-base\",\n \"transition-colors duration-fast ease-out\",\n variant === \"inset\" ? \"mx-2 rounded-lg px-3 py-2.5\" : \"px-5 py-2.5\",\n variant === \"divided\" && !firstInBlock && \"border-t border-border-subtle\",\n onSelect && \"cursor-pointer focus-visible:outline-none focus-visible:focus-ring\",\n // An unread row is tinted, not bolded: at five columns a bold row\n // shouts across all of them, while the tint marks the row and leaves\n // the consumer free to weight only the sender and the subject.\n active || selected\n ? \"bg-accent-soft text-text\"\n : isUnread\n ? cn(\"bg-surface-unread\", onSelect && \"hover:bg-surface-unread-hover\")\n : onSelect && \"hover:bg-surface-hover\",\n // Read rows stay at full opacity — fading them also fades the avatars\n // and team dots, which is exactly the identity you scan by. They read\n // as secondary through weight and text colour instead.\n isDimmed && \"text-text-muted\",\n )}\n >\n {selectable && (\n <span onClick={(e) => e.stopPropagation()}>\n <Checkbox\n checked={isSelected(item)}\n onChange={(e) => toggle(item, e.target.checked)}\n aria-label=\"Rij selecteren\"\n />\n </span>\n )}\n\n {variant === \"bulleted\" && (\n <span\n aria-hidden\n className=\"mt-2 size-1.5 shrink-0 self-start rounded-full bg-border-strong\"\n />\n )}\n\n {leading && <span className=\"shrink-0\">{leading(item, index)}</span>}\n\n <div className=\"flex min-w-0 flex-1 items-center gap-2\">\n {renderItem ? renderItem(item, index) : String(item)}\n </div>\n\n {trailing && (\n <span className=\"shrink-0 text-xs text-text-subtle\">{trailing(item, index)}</span>\n )}\n </li>\n );\n };\n\n const body = (() => {\n if (!groupBy) {\n return items.map((item, index) => renderRow(item, index, index === 0));\n }\n\n // group id -> [{ item, index }], first-appearance order\n const buckets = new Map<string, { item: T; index: number }[]>();\n items.forEach((item, index) => {\n const id = groupBy(item, index);\n if (!buckets.has(id)) buckets.set(id, []);\n buckets.get(id)!.push({ item, index });\n });\n\n const declared = groups ?? [];\n const order: ListGroup[] = [\n ...declared.filter((g) => buckets.has(g.id)),\n ...[...buckets.keys()]\n .filter((id) => !declared.some((g) => g.id === id))\n .map((id) => ({ id })),\n ];\n\n return order.flatMap((group, groupIndex) => {\n const rows = buckets.get(group.id) ?? [];\n const isCollapsed = collapsibleGroups && collapsed[group.id];\n const label = group.title ?? group.id;\n const count = group.count ?? rows.length;\n\n const header = (\n <li\n key={`group-${group.id}`}\n onClick={\n collapsibleGroups\n ? () => setCollapsed((prev) => ({ ...prev, [group.id]: !prev[group.id] }))\n : undefined\n }\n className={cn(\n // A caption over its rows, not a band across the table: same\n // surface as the rows, no rules, and the count as a quiet pill.\n // The old grey uppercase strip read as a second column header.\n \"flex items-center gap-2.5 bg-surface px-5 pb-1.5 text-base font-semibold\",\n groupIndex > 0 ? \"pt-5\" : \"pt-3\",\n group.tone === \"danger\"\n ? \"text-danger-fg\"\n : group.tone === \"muted\"\n ? \"text-text-subtle\"\n : \"text-text\",\n stickyGroupHeaders && \"sticky z-10\",\n collapsibleGroups && \"cursor-pointer select-none\",\n )}\n aria-expanded={collapsibleGroups ? !isCollapsed : undefined}\n // the column strip is 28px tall, so grouped headers park underneath it\n style={stickyGroupHeaders ? { top: stickyHeader ? 28 : 0 } : undefined}\n >\n {renderGroupHeader ? (\n renderGroupHeader(\n group,\n rows.map((r) => r.item),\n )\n ) : (\n <>\n {collapsibleGroups && (\n <ChevronRight\n aria-hidden\n className={cn(\n \"size-icon-md shrink-0 text-text-subtle transition-transform duration-fast ease-out\",\n !isCollapsed && \"rotate-90\",\n )}\n />\n )}\n <span>{label}</span>\n {!hideGroupCount && (\n <span\n className={cn(\n \"rounded-full px-2 text-xs font-normal tabular-nums\",\n group.tone === \"danger\"\n ? \"bg-danger-soft text-danger-fg\"\n : \"bg-surface-sunk text-text-subtle\",\n )}\n >\n {count}\n </span>\n )}\n </>\n )}\n </li>\n );\n\n if (isCollapsed) return [header];\n\n return [\n header,\n ...rows.map(({ item, index }, rowIndex) => renderRow(item, index, rowIndex === 0)),\n ];\n });\n })();\n\n return (\n <div ref={rootRef} className={container} onKeyDown={onSelect ? handleKeyDown : undefined}>\n {header && (\n <div\n className={cn(\n // h-row-sm keeps the strip exactly 28px, which is the offset the\n // sticky group headers park against.\n \"flex h-row-sm items-end gap-3 border-b border-border bg-surface px-5 pb-2\",\n \"text-xs font-semibold uppercase tracking-wider text-text-disabled\",\n stickyHeader && \"sticky top-0 z-20\",\n )}\n >\n {selectable && (\n <span className=\"flex items-center\">\n <Checkbox\n checked={allSelected}\n indeterminate={someSelected}\n onChange={(e) => toggleAll(e.target.checked)}\n aria-label=\"Alles selecteren\"\n />\n </span>\n )}\n {header}\n </div>\n )}\n\n <ul aria-label={ariaLabel} className=\"flex flex-col\">\n {body}\n </ul>\n\n {/* Bulk bar — a dock that floats over the rows rather than a strip that\n pushes them down, so acting on a selection never moves the rows you\n are selecting. Sticky, so it rides the bottom of the scroll area. */}\n {selectable && selectedItems.length > 0 && (\n <div className=\"pointer-events-none sticky bottom-0 z-10 flex justify-center px-5 pb-4\">\n <div className=\"pointer-events-auto flex items-center gap-1.5 rounded-lg bg-accent py-1.5 pl-3.5 pr-1.5 text-accent-fg shadow-overlay\">\n <span className=\"text-sm font-medium\">{selectedItems.length} geselecteerd</span>\n <span aria-hidden className=\"mx-1.5 h-4 w-px bg-current opacity-25\" />\n {bulkActions}\n <span aria-hidden className=\"mx-1.5 h-4 w-px bg-current opacity-25\" />\n <button\n type=\"button\"\n onClick={() => onSelectionChange?.([])}\n aria-label=\"Selectie wissen\"\n className=\"grid size-tile-md shrink-0 cursor-pointer place-items-center rounded-md transition-colors duration-fast ease-out hover:bg-white/15 focus-visible:outline-none focus-visible:focus-ring\"\n >\n <X className=\"size-icon-lg\" />\n </button>\n </div>\n </div>\n )}\n </div>\n );\n}\n","import type React from \"react\";\nimport { cn } from \"../utils/cn\";\n\nexport interface MessageBubbleProps {\n /** Direction. `in` is received, `out` is sent by us. */\n side: \"in\" | \"out\";\n /**\n * Part of a run from the same sender. The bubble keeps its shape; callers use\n * this to drop the repeated header and avatar.\n */\n continued?: boolean;\n /**\n * `warning` for a message that is not settled yet, e.g. a pending draft.\n * `note` is the internal-note surface — amber, never customer-visible.\n */\n tone?: \"default\" | \"warning\" | \"note\";\n /**\n * Layout only — padding, flex behaviour, margins.\n *\n * Not width: the bubble caps its own, because `cn` is plain clsx with no\n * tailwind-merge, so a `max-w-*` passed here would not reliably beat the\n * default — both land in the class list and CSS order decides. A message\n * that runs the full width of the thread stops reading as a message.\n */\n className?: string;\n children: React.ReactNode;\n}\n\n/**\n * A single message in a conversation: chat, email and call transcript all use\n * the same shape. The corner facing the sender is tight (`rounded-sm`), the\n * other three `rounded-lg` — that is what makes direction readable without a\n * tail. Sent messages sit on `bg-bubble-out-soft`, a 4% ink wash — deliberately\n * lighter than `accent-soft`, which reads as heavy on every sent message.\n */\nexport function MessageBubble({\n side,\n continued = false,\n tone = \"default\",\n className,\n children,\n}: MessageBubbleProps) {\n return (\n <div\n className={cn(\n // A bubble never spans the whole column; it narrows further from md,\n // where the thread itself is wide enough for the difference to matter.\n // No shadow: a bubble is an inner card, and inner cards separate\n // themselves with a fill. The shadow was doing the separating while both\n // sides were white.\n \"min-w-0 max-w-bubble md:max-w-bubble-md\",\n // No outline on an ordinary message: its fill already separates it\n // from the thread, and a border on every bubble turns a conversation\n // into a stack of boxes.\n // The tight corner points back at the sender.\n side === \"out\" ? \"rounded-lg rounded-tr-sm\" : \"rounded-lg rounded-tl-sm\",\n // Sent is a pale blue, received the neutral sunk grey — the same pair\n // the full-width feed cards use, so a chat thread and a mail thread say\n // \"ours\" and \"theirs\" the same way.\n tone === \"note\"\n ? \"bg-note-soft\"\n : side === \"out\"\n ? \"bg-bubble-out-soft\"\n : \"bg-surface-sunk\",\n continued && \"mt-1\",\n className,\n )}\n >\n {children}\n </div>\n );\n}\n","import type { ReactNode } from \"react\";\n\nimport { cn } from \"../utils/cn\";\n\n/**\n * One value in a record's property line.\n *\n * The properties of a record — sender, inbox, priority, topic, folder, status,\n * assignee — belong in the sentence under the title, not in a row of chips\n * underneath it. Six chips read as six buttons for something you change maybe\n * once; the same six values as a line read as *what this record is*.\n *\n * The dashed underline is the whole affordance: it says \"you can change this\"\n * without spending a control. Wrap it in a `Popover` to make it do so.\n *\n * An unset property stays **visible** in the dimmer pair rather than\n * disappearing, so you can see that it is empty — a property that vanishes when\n * unset is a property you never discover.\n */\nexport interface MetaValueProps {\n label: string;\n icon?: ReactNode;\n /** No value chosen yet. Renders dimmer, never hidden. */\n unset?: boolean;\n className?: string;\n}\n\nexport function MetaValue({ icon, label, unset, className }: MetaValueProps) {\n return (\n <span\n className={cn(\n \"inline-flex min-w-0 items-center gap-1.5 border-b border-dashed pb-px transition-colors duration-fast ease-out\",\n unset\n ? \"border-border text-text-subtle hover:border-border-strong hover:text-text-muted\"\n : \"border-border-strong text-text-muted hover:text-text\",\n className,\n )}\n >\n {icon}\n <span className=\"truncate\">{label}</span>\n </span>\n );\n}\n\n/**\n * Separator between two properties in a meta line.\n *\n * Decorative and hidden from assistive tech — the values carry the meaning, and\n * a screen reader announcing \"middle dot\" between every property is noise.\n */\nexport function MetaSeparator() {\n return (\n <span aria-hidden className=\"shrink-0 text-text-disabled\">\n ·\n </span>\n );\n}\n","import type { ReactNode } from \"react\";\nimport { cn } from \"../utils/cn\";\n\n/**\n * What is about to appear here. A placeholder only does its job if it has the\n * geometry of the thing it stands in for: the default `text` shape under a form\n * or a list announces a heading and two paragraphs, and the real content then\n * shoves everything sideways when it lands.\n */\nexport type ContentLoadingShape = \"text\" | \"table\" | \"list\" | \"form\" | \"menu\" | \"cards\";\n\nexport interface ContentLoadingProps {\n /**\n * `page` (default) fills the route with its own `<main>` shell and padding.\n * `inline` drops both, so the skeleton can sit inside an existing panel,\n * card or scroll container without nesting a second `<main>` landmark.\n */\n variant?: \"page\" | \"inline\";\n\n /** The shape of what is coming. Defaults to `text`. */\n shape?: ContentLoadingShape;\n\n /** Rows/items/fields/cards to draw, depending on `shape`. */\n rows?: number;\n\n /** Columns, for `table` and `cards`. */\n columns?: number;\n\n /**\n * Optional custom skeleton content (overrides default)\n */\n children?: ReactNode;\n\n className?: string;\n\n /** @deprecated Pass `shape=\"table\"`. */\n showTableSkeleton?: boolean;\n /** @deprecated Pass `columns`. */\n tableColumns?: number;\n /** @deprecated Pass `rows`. */\n tableRows?: number;\n}\n\n/** One placeholder bar. Uneven widths read as text rather than as a progress bar. */\nconst Bar = ({ className }: { className?: string }) => (\n <span className={cn(\"block rounded-xs bg-surface-hover\", className)} />\n);\n\nconst WIDTHS = [\"62%\", \"44%\", \"74%\", \"52%\", \"68%\", \"48%\", \"80%\", \"56%\"];\n\nfunction Shape({\n shape,\n rows,\n columns,\n}: {\n shape: ContentLoadingShape;\n rows: number;\n columns: number;\n}) {\n switch (shape) {\n case \"table\":\n return (\n <div className=\"overflow-hidden rounded-lg ring-1 ring-border\">\n <div\n className=\"grid gap-2 bg-surface-hover p-3\"\n style={{ gridTemplateColumns: `repeat(${columns}, minmax(0, 1fr))` }}\n >\n {Array.from({ length: columns }).map((_, i) => (\n <Bar key={`h-${i}`} className=\"h-4 w-2/3 bg-border-strong\" />\n ))}\n </div>\n {Array.from({ length: rows }).map((_, r) => (\n <div\n key={`r-${r}`}\n className=\"grid gap-2 border-t border-border-subtle p-3\"\n style={{ gridTemplateColumns: `repeat(${columns}, minmax(0, 1fr))` }}\n >\n {Array.from({ length: columns }).map((_, c) => (\n <Bar key={`c-${r}-${c}`} className=\"h-4 w-full\" />\n ))}\n </div>\n ))}\n </div>\n );\n\n case \"list\":\n return (\n <div className=\"flex flex-col divide-y divide-border-subtle rounded-lg ring-1 ring-border\">\n {Array.from({ length: rows }).map((_, i) => (\n <div key={i} className=\"flex items-center gap-3 p-3\">\n <Bar className=\"size-avatar-sm shrink-0 rounded-full\" />\n <div className=\"flex min-w-0 flex-1 flex-col gap-1.5\">\n <Bar className=\"h-3\" />\n <Bar className=\"h-2.5 opacity-70\" />\n </div>\n </div>\n ))}\n </div>\n );\n\n case \"form\":\n // Label above, field below — the pair a form actually renders, so the\n // fields do not jump when they replace this.\n return (\n <div className=\"flex flex-col gap-5\">\n {Array.from({ length: rows }).map((_, i) => (\n <div key={i} className=\"flex flex-col gap-2\">\n <Bar className=\"h-2.5 w-28\" />\n <Bar className=\"h-control-md w-full rounded-md\" />\n </div>\n ))}\n </div>\n );\n\n case \"menu\":\n // The same geometry the sidebar menu draws: a caption, then rows at item\n // height with room for the icon.\n return (\n <div className=\"flex flex-col gap-px\">\n <div className=\"px-2.5 pb-2 pt-5\">\n <Bar className=\"h-2 w-16\" />\n </div>\n {Array.from({ length: rows }).map((_, i) => (\n <div key={i} className=\"flex h-row-md items-center gap-2.5 px-2.5\">\n <Bar className=\"size-4 shrink-0\" />\n <Bar className=\"h-2\" />\n </div>\n ))}\n </div>\n );\n\n case \"cards\":\n return (\n <div\n className=\"grid gap-4\"\n style={{ gridTemplateColumns: `repeat(${columns}, minmax(0, 1fr))` }}\n >\n {Array.from({ length: rows * columns }).map((_, i) => (\n <div key={i} className=\"flex flex-col gap-2 rounded-lg p-4 ring-1 ring-border\">\n <Bar className=\"h-3 w-1/2\" />\n <Bar className=\"h-2.5\" />\n <Bar className=\"h-2.5 w-3/4 opacity-70\" />\n </div>\n ))}\n </div>\n );\n\n case \"text\":\n default:\n return (\n <div className=\"flex flex-col gap-6\">\n <Bar className=\"h-6 w-1/4\" />\n <div className=\"flex flex-col gap-2\">\n {Array.from({ length: rows }).map((_, i) => (\n <Bar key={i} className=\"h-4\" />\n ))}\n </div>\n </div>\n );\n }\n}\n\n/** Sensible counts per shape, so a call site only names the shape. */\nconst DEFAULT_ROWS: Record<ContentLoadingShape, number> = {\n text: 2,\n table: 8,\n list: 6,\n form: 5,\n menu: 4,\n cards: 2,\n};\n\nexport function ContentLoading({\n variant = \"page\",\n shape,\n rows,\n columns,\n children,\n className,\n showTableSkeleton = false,\n tableColumns,\n tableRows,\n}: ContentLoadingProps) {\n // The deprecated table props still decide the shape when no `shape` is given,\n // so existing call sites keep rendering what they always did.\n const resolvedShape: ContentLoadingShape = shape ?? (showTableSkeleton ? \"table\" : \"text\");\n const resolvedRows = rows ?? tableRows ?? DEFAULT_ROWS[resolvedShape];\n const resolvedColumns = columns ?? tableColumns ?? (resolvedShape === \"cards\" ? 3 : 6);\n\n // `inline` renders the skeleton bare; `page` wraps it in the route shell.\n const inner = (\n <div className={cn(\"w-full animate-pulse\", className)} aria-hidden>\n {children ?? <Shape shape={resolvedShape} rows={resolvedRows} columns={resolvedColumns} />}\n </div>\n );\n\n if (variant === \"inline\") return inner;\n\n return <main className=\"flex-1 bg-surface p-6\">{inner}</main>;\n}\n","import { ChevronLeft, Pencil } from \"lucide-react\";\nimport React, { useEffect, useRef, useState } from \"react\";\nimport { Button, type ButtonProps } from \"../action/Button\";\nimport { SplitButton, type SplitButtonOption } from \"../action/SplitButton\";\nimport { cn } from \"../utils/cn\";\nimport { Icon } from \"./Icon\";\n\nexport interface PageHeaderAction {\n id: string;\n label: string;\n icon?: React.ReactNode;\n onClick: () => void;\n /** Action variant. Tracks Button, so the two cannot drift apart. */\n variant?: ButtonProps[\"variant\"];\n disabled?: boolean;\n splitOptions?: SplitButtonOption[];\n /**\n * Render this action yourself instead of as a Button — a filter chip, a\n * segmented toggle, an avatar stack. Position follows the array, so put it\n * before the buttons to have it sit to their left. When set, every other\n * field except `id` is ignored.\n */\n render?: () => React.ReactNode;\n}\n\nexport interface PageHeaderProps {\n /**\n * `plain` sits directly on the page. `surface` is the app-bar treatment:\n * its own background, full-bleed, closed off with a bottom border.\n */\n surface?: boolean;\n title: string;\n /** Page subtitle/description. Sits beside the title. */\n subtitle?: string;\n /**\n * Meta line under the title — a contact, a channel, a timestamp. Distinct\n * from `subtitle`, which sits beside the title, and from `children`, which\n * is the full-width row at the bottom of the header.\n *\n * A string is kept to one line and ellipsised; a node is allowed to wrap, so\n * an inline row of properties survives a narrow window.\n */\n meta?: React.ReactNode;\n breadcrumbs?: Array<{ label: string; href?: string }>;\n actions?: PageHeaderAction[];\n children?: React.ReactNode;\n className?: string;\n backLabel?: string;\n onBack?: () => void;\n backClassName?: string;\n editable?: boolean;\n onTitleChange?: (newTitle: string) => void;\n}\n\nexport const ChevronLeftIcon = () => <Icon icon={ChevronLeft} size=\"md\" color=\"current\" />;\n\nexport const PageHeader: React.FC<PageHeaderProps> = ({\n surface = false,\n title,\n subtitle,\n meta,\n actions = [],\n children,\n className,\n backLabel = \"Back\",\n backClassName,\n onBack,\n editable = false,\n onTitleChange,\n}) => {\n const [isEditing, setIsEditing] = useState(false);\n const [editValue, setEditValue] = useState(title);\n const inputRef = useRef<HTMLInputElement>(null);\n\n useEffect(() => {\n setEditValue(title);\n }, [title]);\n\n useEffect(() => {\n if (isEditing && inputRef.current) {\n inputRef.current.focus();\n inputRef.current.select();\n }\n }, [isEditing]);\n\n const handleTitleClick = () => {\n if (editable) {\n setIsEditing(true);\n }\n };\n\n const handleBlur = () => {\n setIsEditing(false);\n if (editValue.trim() !== \"\" && editValue !== title && onTitleChange) {\n onTitleChange(editValue.trim());\n } else {\n setEditValue(title);\n }\n };\n\n const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {\n if (e.key === \"Enter\") {\n inputRef.current?.blur();\n } else if (e.key === \"Escape\") {\n setEditValue(title);\n setIsEditing(false);\n }\n };\n\n return (\n <div\n className={cn(\n \"px-5 pt-4 pb-2.5\",\n // Inside the content card the header shares the card's surface; the\n // rule is the only thing that still closes it off. `surface` therefore\n // only draws the rule — the background it used to paint is now the\n // card itself.\n surface && \"border-b border-border\",\n className,\n )}\n >\n <div className=\"flex flex-row sm:items-start sm:justify-between items-center gap-4\">\n <div className=\"min-w-0 flex-1\">\n {/* Back sits beside the title block, not above the meta line, so the\n meta line starts under the title rather than under the button. */}\n <div className=\"flex items-start gap-4 text-text\">\n {onBack && (\n // Ghost: a framed box around a back chevron makes the way out of a\n // page look like the page's primary control.\n <Button\n variant=\"ghost\"\n onClick={onBack}\n leftIcon={<Icon icon={ChevronLeft} size=\"md\" />}\n iconOnly\n />\n )}\n\n <div className=\"min-w-0 flex-1\">\n {isEditing ? (\n <input\n ref={inputRef}\n type=\"text\"\n value={editValue}\n onChange={(e) => setEditValue(e.target.value)}\n onBlur={handleBlur}\n onKeyDown={handleKeyDown}\n className=\"text-1xl font-semibold bg-transparent border-b-1 border-border rounded-lg outline-none focus:border-none px-1 -mx-1 min-w-0 w-full\"\n />\n ) : (\n <div className=\"group flex items-baseline gap-2.5 min-w-0\">\n <h2\n onClick={handleTitleClick}\n className={cn(\n \"text-lg font-semibold tracking-title truncate\",\n editable && \"cursor-pointer\",\n )}\n title={editable ? \"Click to edit\" : undefined}\n >\n {title}\n </h2>\n {subtitle && (\n <span className=\"shrink-0 text-base text-text-muted truncate\">{subtitle}</span>\n )}\n {editable && (\n <Pencil\n size={15}\n onClick={handleTitleClick}\n className=\"shrink-0 cursor-pointer text-text-muted opacity-0 group-hover:opacity-100 transition-opacity\"\n />\n )}\n </div>\n )}\n\n {meta && (\n // A string meta is one line and ellipsises; a node may be a row\n // of controls (the interaction header's property line) that has\n // to be allowed to wrap — `truncate` would clip it to one line\n // and hide the last properties.\n <div\n className={cn(\n \"mt-1 min-w-0 text-sm text-text-subtle\",\n typeof meta === \"string\" && \"truncate\",\n )}\n >\n {meta}\n </div>\n )}\n </div>\n </div>\n </div>\n\n {actions.length > 0 && (\n <div className=\"flex flex-wrap sm:flex-nowrap gap-2 sm:gap-3\">\n {actions.map((action) =>\n action.render ? (\n <React.Fragment key={action.id}>{action.render()}</React.Fragment>\n ) : action.splitOptions && action.splitOptions.length > 0 ? (\n <SplitButton\n key={action.id}\n label={action.label}\n onClick={action.onClick}\n icon={action.icon}\n variant={action.variant === \"primary\" ? \"primary\" : \"outline\"}\n options={action.splitOptions}\n disabled={action.disabled}\n />\n ) : (\n <Button\n key={action.id}\n variant={action.variant ?? \"secondary\"}\n onClick={(e) => {\n e.stopPropagation();\n (e.target as HTMLButtonElement).blur();\n action.onClick();\n }}\n disabled={action.disabled}\n leftIcon={action.icon}\n >\n {action.label}\n </Button>\n ),\n )}\n </div>\n )}\n </div>\n\n {children && <div className=\"mt-4\">{children}</div>}\n </div>\n );\n};\n","import type React from \"react\";\nimport type { ReactNode } from \"react\";\nimport { ContentLoading, type ContentLoadingShape } from \"../feedback/ContentLoading\";\nimport { cn } from \"../utils/cn\";\nimport { PageHeader, type PageHeaderAction } from \"./PageHeader\";\n\n/**\n * The `md` gutter matches `PageHeader`'s own horizontal padding, so the table\n * or form below lines up with the title above it. The top value is small\n * because the header already supplies most of that gap (`pt-4 pb-2.5`).\n */\nconst paddingClasses = {\n none: \"\",\n sm: \"px-3 pb-3 pt-1\",\n md: \"px-5 pb-5 pt-1.5\",\n} as const;\n\nexport interface PageProps {\n /** Page title, rendered by `PageHeader`. */\n title: string;\n /** Short description beside the title. */\n subtitle?: string;\n /** Line under the title — a count, a record, a timestamp. */\n meta?: ReactNode;\n /** Toolbar actions, right-aligned in the header. */\n actions?: PageHeaderAction[];\n /** Renders the back chevron beside the title. */\n onBack?: () => void;\n /** Lets the user rename the record from the header: the title becomes\n * clickable and grows a pencil beside it. Forwarded to `PageHeader`. */\n editable?: boolean;\n /** Called with the trimmed new title. Only fires when it actually changed. */\n onTitleChange?: (title: string) => void;\n /**\n * Draws the rule under the header. Off by default: inside the content card\n * the header shares the card's surface, and the card's own edge already ends\n * the page — a second line right under the title reads as a divider between\n * two panes that are not there.\n *\n * Turn it on for a header that has to stay legible over content moving\n * underneath it: a dense grid, a feed, anything where the first row would\n * otherwise scroll up flush against the title.\n */\n surface?: boolean;\n /** Full-width row at the bottom of the header — a filter strip, a tab bar. */\n headerContent?: ReactNode;\n /** Swaps the body for a skeleton. */\n loading?: boolean;\n /**\n * What the body is about to render, so the placeholder has the right shape.\n * Most app pages are a table; a detail page is usually a form.\n */\n loadingShape?: ContentLoadingShape;\n /**\n * Rendered instead of `children` when there is nothing to show — pass an\n * `EmptyState`. Ignored while `loading`, so an empty list does not flash its\n * \"nothing here\" message during the first fetch.\n */\n empty?: ReactNode;\n padding?: keyof typeof paddingClasses;\n /**\n * Whether the page owns its scroll. `true` (default) pins the header and\n * scrolls the body — the right behaviour for a table you page through.\n * Set `false` when the page is rendered inside something that already\n * scrolls and should flow with it.\n */\n scroll?: boolean;\n className?: string;\n contentClassName?: string;\n children?: ReactNode;\n}\n\n/**\n * One app page: the header, the content gutter, the scroll container, and the\n * loading and empty states. These four were re-invented per page — 24 times in\n * `eylo-voip` alone, each with its own gutter — which is how `p-4` there and\n * `px-5` everywhere else ended up side by side. Reach for this instead of\n * pairing a `PageHeader` with a padded `div`. Settings panels are the\n * exception: they live inside the settings frame, which draws its own title\n * and scrolls itself, so they use `SettingsPage`.\n */\nexport const Page: React.FC<PageProps> = ({\n title,\n subtitle,\n meta,\n actions,\n onBack,\n editable,\n onTitleChange,\n surface = false,\n headerContent,\n loading = false,\n loadingShape = \"table\",\n empty,\n padding = \"md\",\n scroll = true,\n className,\n contentClassName,\n children,\n}) => {\n const body = loading ? (\n <ContentLoading variant=\"inline\" shape={loadingShape} />\n ) : empty ? (\n <div className=\"flex flex-col items-center justify-center py-12\">{empty}</div>\n ) : (\n children\n );\n\n return (\n <div\n className={cn(\n // `h-full` resolves against the shell's content card, which is the\n // element that scrolls today. Taking the scroll over here is what pins\n // the header; without `min-h-0` the body would grow past the card\n // instead of scrolling inside it.\n scroll ? \"flex h-full min-h-0 flex-col\" : \"flex flex-col\",\n className,\n )}\n >\n <PageHeader\n surface={surface}\n title={title}\n subtitle={subtitle}\n meta={meta}\n actions={actions}\n onBack={onBack}\n editable={editable}\n onTitleChange={onTitleChange}\n className={scroll ? \"shrink-0\" : undefined}\n >\n {headerContent}\n </PageHeader>\n\n <div\n className={cn(\n scroll && \"min-h-0 flex-1 overflow-y-auto\",\n paddingClasses[padding],\n contentClassName,\n )}\n >\n {body}\n </div>\n </div>\n );\n};\n","import { ChevronLeft } from \"lucide-react\";\nimport React from \"react\";\nimport { Button, type ButtonProps } from \"../action/Button\";\nimport { SplitButton } from \"../action/SplitButton\";\nimport { cn } from \"../utils/cn\";\nimport { Icon } from \"./Icon\";\nimport type { PageHeaderAction } from \"./PageHeader\";\n\nexport interface PageToolbarActionsProps {\n actions: PageHeaderAction[];\n /**\n * Variant for actions that do not name one. `secondary` (a sunk plate) suits\n * a detail page's Cancel/Save pair; a list toolbar passes `ghost`, because\n * there the plate would collide with the sunk search field beside it.\n */\n defaultVariant?: ButtonProps[\"variant\"];\n className?: string;\n}\n\n/**\n * The action cluster shared by `PageToolbar` and the table toolbar, so a\n * \"New …\" button looks the same wherever a view decides to put it.\n */\nexport const PageToolbarActions: React.FC<PageToolbarActionsProps> = ({\n actions,\n defaultVariant = \"secondary\",\n className,\n}) => {\n if (actions.length === 0) return null;\n\n return (\n <div className={cn(\"flex flex-wrap items-center gap-2\", className)}>\n {actions.map((action) =>\n action.render ? (\n <React.Fragment key={action.id}>{action.render()}</React.Fragment>\n ) : action.splitOptions && action.splitOptions.length > 0 ? (\n <SplitButton\n key={action.id}\n label={action.label}\n onClick={action.onClick}\n icon={action.icon}\n variant={action.variant === \"primary\" ? \"primary\" : \"secondary\"}\n options={action.splitOptions}\n disabled={action.disabled}\n />\n ) : (\n <Button\n key={action.id}\n variant={action.variant ?? defaultVariant}\n onClick={(e) => {\n e.stopPropagation();\n (e.target as HTMLButtonElement).blur();\n action.onClick();\n }}\n disabled={action.disabled}\n leftIcon={action.icon}\n >\n {action.label}\n </Button>\n ),\n )}\n </div>\n );\n};\n\nexport interface PageToolbarProps {\n /** Right-aligned action buttons */\n actions?: PageHeaderAction[];\n /** Renders a ghost back button on the left */\n onBack?: () => void;\n /** Back button label (visually hidden on small screens) */\n backLabel?: string;\n className?: string;\n padding?: \"none\" | \"sm\" | \"md\" | \"lg\";\n /** Optional extra content placed between back button and actions */\n children?: React.ReactNode;\n}\n\nconst paddingClasses = {\n none: \"p-0\",\n sm: \"p-2\",\n md: \"px-5 py-4\",\n lg: \"p-6\",\n};\n\n/**\n * Thin action-bar for views that already have a host header (e.g. a settings\n * page or a detail view). Renders an optional back button on the left and\n * action buttons on the right. Use `PageHeader` when the view needs its own\n * title/breadcrumbs — and for a list, put the actions on the `Table` toolbar\n * instead, next to the search and filters they apply to.\n */\nexport const PageToolbar: React.FC<PageToolbarProps> = ({\n actions = [],\n onBack,\n backLabel = \"Back\",\n className,\n padding = \"md\",\n children,\n}) => {\n return (\n <div className={cn(\"flex items-center gap-2\", paddingClasses[padding], className)}>\n {onBack && (\n <Button\n variant=\"secondary\"\n onClick={onBack}\n aria-label={backLabel}\n leftIcon={<Icon icon={ChevronLeft} size=\"sm\" />}\n iconOnly\n />\n )}\n\n {children && <div className=\"min-w-0 flex-1\">{children}</div>}\n\n <PageToolbarActions actions={actions} className={cn(!children && \"ml-auto\")} />\n </div>\n );\n};\n","import type React from \"react\";\nimport { cn } from \"../utils/cn\";\n\nexport interface SectionCaptionProps {\n /** Uppercase label. Short — this is a caption, not a heading. */\n label: string;\n /**\n * The right-hand slot: a count, a progress (\"1/3 done\") or a summary\n * (\"2 on a call · 1 away\"). A caption with nothing to its right is a caption\n * that could have been a heading.\n */\n meta?: string;\n /** Far right — a toggle or an icon button. Sits after `meta` when both are set. */\n action?: React.ReactNode;\n /** Layout only (margins, alignment). Not padding: see the note below. */\n className?: string;\n}\n\n/**\n * The section header over rounded plates and cards — the counterpart of the\n * sticky sunk band, which belongs to full-bleed divided rows.\n *\n * The top padding is generous and self-cancelling (`pt-7 first:pt-0`): a caption\n * is what separates two blocks in a scrolling panel, so it carries the gap\n * rather than the blocks around it, and the first one in a panel does not push\n * the content away from the panel header.\n *\n * `className` cannot override that padding — `cn` is plain clsx with no\n * tailwind-merge, so both classes would land in the list and CSS order would\n * decide. Use it for margins and alignment only.\n */\nexport const SectionCaption: React.FC<SectionCaptionProps> = ({\n label,\n meta,\n action,\n className,\n}) => (\n <div className={cn(\"flex items-center gap-2 px-1 pb-2 pt-7 first:pt-0\", className)}>\n <span className=\"shrink-0 text-2xs font-semibold uppercase tracking-label text-text-subtle\">\n {label}\n </span>\n {meta && (\n <span className=\"min-w-0 flex-1 truncate text-right text-xs tabular-nums text-text-subtle\">\n {meta}\n </span>\n )}\n {action && <span className=\"ml-auto shrink-0\">{action}</span>}\n </div>\n);\n","import type React from \"react\";\nimport { cn } from \"../utils/cn\";\n\nexport interface SectionDividerProps {\n /** Centred label. Without one the divider is a plain rule. */\n label?: React.ReactNode;\n /** Right-aligned slot after the rule, e.g. a count or a status. */\n trailing?: React.ReactNode;\n /** Push the label to the start instead of centring it. */\n align?: \"center\" | \"start\";\n className?: string;\n}\n\n/**\n * A hairline that separates two runs of content, optionally naming the run that\n * follows: day separators in a feed, group headings in search results.\n *\n * @example\n * ```tsx\n * <SectionDivider label=\"Gisteren\" />\n * <SectionDivider label=\"Gesprekken\" align=\"start\" trailing={<Badge>12</Badge>} />\n * ```\n */\nexport function SectionDivider({\n label,\n trailing,\n align = \"center\",\n className,\n}: SectionDividerProps) {\n const rule = <span className=\"h-px flex-1 bg-border-subtle\" aria-hidden />;\n\n if (!label && !trailing) {\n return <div className={cn(\"flex items-center py-2\", className)}>{rule}</div>;\n }\n\n return (\n <div className={cn(\"flex items-center gap-3 py-2\", className)}>\n {align === \"center\" && rule}\n <span className=\"shrink-0 text-xs font-medium text-text-muted\">{label}</span>\n {rule}\n {trailing && <span className=\"shrink-0\">{trailing}</span>}\n </div>\n );\n}\n","import { ChevronLeft } from \"lucide-react\";\nimport React, {\n createContext,\n type ReactNode,\n useContext,\n useEffect,\n useId,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport { Button } from \"../action/Button\";\nimport { ContentLoading, type ContentLoadingShape } from \"../feedback/ContentLoading\";\nimport { cn } from \"../utils/cn\";\nimport { Icon } from \"./Icon\";\nimport type { PageHeaderAction } from \"./PageHeader\";\nimport { PageToolbarActions } from \"./PageToolbar\";\n\n/**\n * What a panel adds to the frame it is rendered in: the record it drilled into,\n * and the way back out. The settings frame already draws the app breadcrumb and\n * the page title, so a panel that repeats them produces the double bar this\n * component exists to remove.\n */\nexport interface SettingsTrailEntry {\n /** Appended to the frame breadcrumb; usually the detail record's name. */\n title?: string;\n /** When set, the frame renders the back affordance and calls this. */\n onBack?: () => void;\n /**\n * Primary actions to draw in the frame header instead of in the panel's own\n * sticky footer — set by `SettingsPage` when it is passed\n * `actionsPlacement=\"header\"`. See that prop for when to reach for it.\n */\n actions?: PageHeaderAction[];\n /** Left-aligned counterpart of {@link actions}, drawn before them. */\n secondaryActions?: PageHeaderAction[];\n}\n\nexport interface SettingsFrameApi {\n /**\n * `ownerId` scopes the write to one panel instance. Stable across renders,\n * so it is safe to use as a hook dependency.\n */\n setTrail: (ownerId: string, entry: SettingsTrailEntry | null) => void;\n}\n\nconst SettingsFrameContext = createContext<SettingsFrameApi | null>(null);\n\n/**\n * Provided by the settings frame (the shell overlay), consumed by panels that\n * are federated remotes. Safe across the module-federation boundary because\n * React and `@opencxh/ui-kit` are both shared singletons — a second copy of\n * either would hand panels a different context object and the trail would\n * silently never arrive.\n */\nexport const SettingsFrameProvider = SettingsFrameContext.Provider;\n\nexport function useSettingsFrame(): SettingsFrameApi | null {\n return useContext(SettingsFrameContext);\n}\n\n/**\n * State half of the frame contract, for the host to own. Returns the current\n * trail plus the stable api to hand down through `SettingsFrameProvider`.\n */\nexport function useSettingsFrameHost() {\n const [state, setState] = useState<{ ownerId: string; entry: SettingsTrailEntry } | null>(null);\n\n const api = useMemo<SettingsFrameApi>(\n () => ({\n setTrail: (ownerId, entry) =>\n setState((prev) => {\n if (entry) return { ownerId, entry };\n // A panel that unmounts *after* its replacement has already mounted\n // must not wipe the replacement's trail. Only the current owner is\n // allowed to clear.\n return prev && prev.ownerId !== ownerId ? prev : null;\n }),\n }),\n [],\n );\n\n return { trail: state?.entry ?? null, api };\n}\n\nconst paddingClasses = {\n none: \"\",\n sm: \"px-3 py-3\",\n // Matches the frame header's own horizontal padding, so content lines up\n // with the title above it.\n md: \"px-5 py-4\",\n} as const;\n\nexport interface SettingsPageProps {\n children: ReactNode;\n /**\n * Primary actions, right-aligned in the sticky footer — the Cancel/Save pair\n * of a form. Omit entirely on a read-only or list page and no footer renders.\n */\n actions?: PageHeaderAction[];\n /** Left-aligned footer actions, set apart from the primary pair (e.g. Delete). */\n secondaryActions?: PageHeaderAction[];\n /**\n * Where the actions go: `footer` (default) is the sticky bar at the bottom\n * of the panel; `header` hands them to the frame beside the title.\n *\n * Reach for `header` when the body contains an anchored overlay (a `Select`,\n * a `Popover`) inside a `sticky` or `z-index`-bearing element:\n * `position: sticky` creates a stacking context even at `z-index: auto`, so\n * the menu paints behind the footer's own `sticky … z-10`. Removing the\n * footer removes the competitor rather than starting a z-index race.\n */\n actionsPlacement?: \"footer\" | \"header\";\n /** Renders the back affordance in the frame header (or inline when unframed). */\n onBack?: () => void;\n backLabel?: string;\n /** The record's own name, appended to the frame breadcrumb. */\n title?: string;\n /** Swaps the body for a skeleton while keeping the footer in place. */\n loading?: boolean;\n /**\n * What the body is about to render. A settings panel is nearly always a form\n * or a table, and saying which keeps the placeholder from announcing a\n * heading and two paragraphs that never arrive.\n */\n loadingShape?: ContentLoadingShape;\n padding?: keyof typeof paddingClasses;\n className?: string;\n contentClassName?: string;\n}\n\n/**\n * The body of one settings panel. Owns the three things every panel used to\n * re-invent: the scroll container, the content padding, and where the actions\n * live. Title, description and breadcrumb belong to the frame — pass `title`\n * and `onBack` to extend them rather than drawing a second header here.\n */\nexport const SettingsPage: React.FC<SettingsPageProps> = ({\n children,\n actions = [],\n secondaryActions = [],\n actionsPlacement = \"header\",\n onBack,\n backLabel = \"Back\",\n title,\n loading = false,\n loadingShape = \"form\",\n padding = \"md\",\n className,\n contentClassName,\n}) => {\n const frame = useSettingsFrame();\n const ownerId = useId();\n\n // The handler is re-created on every render of the panel; keeping it in a ref\n // means the effect below depends on *whether* there is a back action, not on\n // its identity — otherwise every render would re-publish the trail and the\n // resulting frame re-render would loop.\n const backRef = useRef(onBack);\n backRef.current = onBack;\n\n const hasBack = Boolean(onBack);\n\n // Same reason as `backRef`: the action objects are re-created on every render of\n // the panel, so publishing them directly would re-publish the trail each time and\n // the resulting frame re-render would loop.\n const actionsRef = useRef(actions);\n actionsRef.current = actions;\n const secondaryRef = useRef(secondaryActions);\n secondaryRef.current = secondaryActions;\n\n const inHeader = actionsPlacement === \"header\";\n const hasHeaderActions = inHeader && (actions.length > 0 || secondaryActions.length > 0);\n\n /**\n * What the frame header would *look* like. This is the dependency the effect\n * watches, so a label or a disabled flag that changes still reaches the header\n * while a new closure identity alone does not.\n *\n * `icon` and `render` are deliberately absent: they are React nodes, so they\n * cannot be compared cheaply. A header action whose icon changes without any\n * other change is not a case that exists here — Cancel/Save carry no icon.\n */\n const headerActionsKey = hasHeaderActions\n ? JSON.stringify(\n [...secondaryActions, ...actions].map((a) => [a.id, a.label, a.variant, a.disabled]),\n )\n : \"\";\n\n /**\n * Stable proxies. The published object only changes when the signature above\n * does, but `onClick` reaches through the ref, so it always runs the handler of\n * the latest render — a Save in the frame header must write what is on screen\n * now, not what was there when the trail was published.\n */\n const headerActions = useMemo(() => {\n if (!hasHeaderActions) return undefined;\n const proxy = (which: \"primary\" | \"secondary\") => (a: PageHeaderAction, i: number) => ({\n ...a,\n onClick: () => {\n const list = which === \"primary\" ? actionsRef.current : secondaryRef.current;\n list[i]?.onClick();\n },\n });\n return {\n actions: actionsRef.current.map(proxy(\"primary\")),\n secondaryActions: secondaryRef.current.map(proxy(\"secondary\")),\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [hasHeaderActions, headerActionsKey]);\n\n useEffect(() => {\n if (!frame) return;\n if (!hasBack && !title && !headerActions) return;\n frame.setTrail(ownerId, {\n title,\n onBack: hasBack ? () => backRef.current?.() : undefined,\n actions: headerActions?.actions,\n secondaryActions: headerActions?.secondaryActions,\n });\n return () => frame.setTrail(ownerId, null);\n }, [frame, ownerId, title, hasBack, headerActions]);\n\n // Header placement moves the bar out of the panel entirely; there is no\n // fallback footer, because two homes for the same pair is the double bar this\n // component exists to remove.\n const showFooter = !inHeader && (actions.length > 0 || secondaryActions.length > 0);\n\n return (\n /**\n * `min-h-full` + a sticky footer, rather than a nested scroll container.\n * The frame already scrolls, and taking that over here would clip every\n * panel that has not been migrated yet. This way a short page still drops\n * its footer to the bottom, and a long one keeps it pinned while scrolling.\n */\n <div className={cn(\"flex min-h-full flex-col\", className)}>\n {/**\n * Unframed usage — the same panel opened as a modal or a plain route.\n * There is no frame to hand the title and the back action to, so this\n * draws them itself. Without this the record's name simply disappears\n * outside the settings overlay.\n */}\n {!frame && (hasBack || title || hasHeaderActions) && (\n <div className=\"flex shrink-0 items-center gap-2 px-5 pt-4\">\n {hasBack && (\n <Button\n variant=\"ghost\"\n onClick={() => backRef.current?.()}\n aria-label={backLabel}\n leftIcon={<Icon icon={ChevronLeft} size=\"sm\" />}\n iconOnly\n />\n )}\n {title && (\n <h2 className=\"min-w-0 truncate text-md font-semibold tracking-title text-text\">\n {title}\n </h2>\n )}\n {/* Header placement with no frame to hand them to: draw the pair here,\n or the panel would have no way to save at all. */}\n {inHeader && (\n <div className=\"ml-auto flex items-center gap-2\">\n {secondaryActions.length > 0 && (\n <PageToolbarActions actions={secondaryActions} defaultVariant=\"ghost\" />\n )}\n {actions.length > 0 && <PageToolbarActions actions={actions} />}\n </div>\n )}\n </div>\n )}\n\n <div className={cn(\"flex-1\", paddingClasses[padding], contentClassName)}>\n {loading ? <ContentLoading variant=\"inline\" shape={loadingShape} /> : children}\n </div>\n\n {showFooter && (\n <div className=\"sticky bottom-0 z-10 flex shrink-0 items-center gap-2 border-t border-border bg-surface px-5 py-3\">\n {secondaryActions.length > 0 && (\n <PageToolbarActions actions={secondaryActions} defaultVariant=\"ghost\" />\n )}\n {actions.length > 0 && <PageToolbarActions actions={actions} className=\"ml-auto\" />}\n </div>\n )}\n </div>\n );\n};\n","import type React from \"react\";\nimport { cn } from \"../utils/cn\";\nimport { Icon } from \"./Icon\";\n\nexport interface SettingsRowProps {\n /** What the setting is called. */\n label: React.ReactNode;\n /** One line on what it does or what turning it on means. */\n description?: React.ReactNode;\n /** The control itself — a `Switch`, `Select`, `Button`… */\n control: React.ReactNode;\n /**\n * Draw the row as a bordered pill. Use it for a flat list of independent\n * toggles; leave it off inside a `SettingsSection`, which already groups.\n */\n bordered?: boolean;\n disabled?: boolean;\n className?: string;\n}\n\n/**\n * One setting: label (+ description) on the left, its control on the right.\n *\n * @example\n * ```tsx\n * <SettingsRow\n * label=\"Agenda synchroniseren\"\n * description=\"Afspraken uit dit account verschijnen in je agenda.\"\n * control={<Switch checked={on} onChange={setOn} aria-label=\"Agenda synchroniseren\" />}\n * />\n * ```\n */\nexport function SettingsRow({\n label,\n description,\n control,\n bordered = false,\n disabled = false,\n className,\n}: SettingsRowProps) {\n return (\n <div\n className={cn(\n \"flex items-center justify-between gap-4 py-2\",\n bordered && \"rounded-lg border border-border px-3\",\n disabled && \"opacity-50\",\n className,\n )}\n >\n <div className=\"min-w-0\">\n <div className=\"text-sm text-text\">{label}</div>\n {description && <div className=\"text-xs text-text-muted\">{description}</div>}\n </div>\n <div className=\"shrink-0\">{control}</div>\n </div>\n );\n}\n\nexport interface SettingsSectionProps {\n /** Lucide icon component, rendered in the section heading. */\n icon?: React.ComponentType<Record<string, unknown>>;\n title: React.ReactNode;\n description?: React.ReactNode;\n /** Right-aligned slot in the heading, e.g. a \"Reset\" button. */\n actions?: React.ReactNode;\n children: React.ReactNode;\n className?: string;\n}\n\n/**\n * A titled group of `SettingsRow`s. The body is indented to line up under the\n * title text rather than the icon.\n */\nexport function SettingsSection({\n icon,\n title,\n description,\n actions,\n children,\n className,\n}: SettingsSectionProps) {\n return (\n <section className={cn(\"flex flex-col gap-2\", className)}>\n <div className=\"flex items-start gap-2\">\n {icon && <Icon icon={icon} size=\"md\" color=\"secondary\" className=\"mt-0.5\" />}\n <div className=\"min-w-0 flex-1\">\n <h3 className=\"text-sm font-semibold text-text\">{title}</h3>\n {description && <p className=\"text-xs text-text-muted\">{description}</p>}\n </div>\n {actions && <div className=\"shrink-0\">{actions}</div>}\n </div>\n <div className={cn(\"flex flex-col\", icon && \"pl-6\")}>{children}</div>\n </section>\n );\n}\n","import { ArrowUpRight } from \"lucide-react\";\nimport type React from \"react\";\nimport { cn } from \"../utils/cn\";\nimport { Icon } from \"./Icon\";\n\nexport interface SourceChipProps {\n /** Label — the linked conversation subject. */\n label: React.ReactNode;\n /** Optional leading dot colour (a `--color-cat-*` or channel CSS var). */\n dotColor?: string;\n /** Click opens the linked conversation. */\n onClick?: () => void;\n className?: string;\n}\n\n/**\n * Source-conversation chip — the \"open brongesprek / gekoppeld gesprek ↗\" pill\n * that links a task or focus item back to its conversation.\n *\n * @example\n * ```tsx\n * <SourceChip label=\"Vraag over factuur maart\" dotColor=\"var(--color-cat-2)\" onClick={openThread} />\n * ```\n */\nexport function SourceChip({ label, dotColor, onClick, className }: SourceChipProps) {\n return (\n <button\n type=\"button\"\n onClick={onClick}\n className={cn(\n \"inline-flex max-w-full items-center gap-1.5 rounded-full bg-surface-sunk px-2.5 py-1 text-xs text-text-muted transition-colors duration-fast ease-out hover:bg-surface-hover hover:text-text\",\n className,\n )}\n >\n {dotColor && (\n <span\n className=\"h-1.5 w-1.5 shrink-0 rounded-full\"\n style={{ backgroundColor: dotColor }}\n aria-hidden\n />\n )}\n <span className=\"min-w-0 truncate\">{label}</span>\n <Icon icon={ArrowUpRight} size=\"xs\" className=\"opacity-70\" />\n </button>\n );\n}\n","import { Calendar, ChevronLeft, ChevronRight } from \"lucide-react\";\nimport { forwardRef, useEffect, useRef, useState } from \"react\";\nimport { Icon } from \"../content/Icon\";\nimport { cn } from \"../utils/cn\";\nimport { useAnchoredPosition } from \"../utils/useAnchoredPosition\";\n\nexport interface DatePickerProps {\n value?: Date | null;\n onChange?: (date: Date | null) => void;\n placeholder?: string;\n disabled?: boolean;\n required?: boolean;\n size?: \"sm\" | \"md\" | \"lg\" | \"full\";\n className?: string;\n minDate?: Date;\n maxDate?: Date;\n format?: \"MM/dd/yyyy\" | \"dd/MM/yyyy\" | \"yyyy-MM-dd\";\n /**\n * Also pick a time. The display gains `HH:mm`, the panel gains a time field,\n * and choosing a day keeps the time already on the value instead of resetting\n * it to midnight. Replaces a native `datetime-local`.\n */\n withTime?: boolean;\n}\n\nconst CalendarIcon = () => <Icon icon={Calendar} size=\"md\" color=\"current\" />;\nconst ChevronLeftIcon = () => <Icon icon={ChevronLeft} size=\"md\" color=\"current\" />;\nconst ChevronRightIcon = () => <Icon icon={ChevronRight} size=\"md\" color=\"current\" />;\n\nexport const DatePicker = forwardRef<HTMLInputElement, DatePickerProps>(\n (\n {\n value,\n onChange,\n placeholder = \"Select date\",\n disabled = false,\n required = false,\n size = \"md\",\n className,\n minDate,\n maxDate,\n format = \"MM/dd/yyyy\",\n withTime = false,\n },\n ref,\n ) => {\n const [isOpen, setIsOpen] = useState(false);\n const [currentMonth, setCurrentMonth] = useState(() => value || new Date());\n const containerRef = useRef<HTMLDivElement>(null);\n const fieldRef = useRef<HTMLDivElement>(null);\n // Fixed against the field: an absolute panel is clipped by the form panel's\n // overflow and opens below the fold when the field sits near the bottom.\n //\n // Anchored on the field, not on containerRef — the panel is a child of the\n // container, so on the first open (before the fixed position lands) it counted\n // towards the container's own height and the panel placed itself a panel's\n // length too low. Closing and reopening looked fine because the measurement\n // from the previous open was still in state.\n const panelStyle = useAnchoredPosition(isOpen, fieldRef);\n\n useEffect(() => {\n const handleClickOutside = (event: MouseEvent) => {\n if (containerRef.current && !containerRef.current.contains(event.target as Node)) {\n setIsOpen(false);\n }\n };\n\n if (isOpen) {\n document.addEventListener(\"mousedown\", handleClickOutside);\n }\n\n return () => {\n document.removeEventListener(\"mousedown\", handleClickOutside);\n };\n }, [isOpen]);\n\n const formatDate = (date: Date | null): string => {\n if (!date) return \"\";\n\n const day = date.getDate().toString().padStart(2, \"0\");\n const month = (date.getMonth() + 1).toString().padStart(2, \"0\");\n const year = date.getFullYear();\n\n const datePart =\n format === \"dd/MM/yyyy\"\n ? `${day}/${month}/${year}`\n : format === \"yyyy-MM-dd\"\n ? `${year}-${month}-${day}`\n : `${month}/${day}/${year}`;\n\n return withTime ? `${datePart} ${formatTime(date)}` : datePart;\n };\n\n /** `HH:mm`, the value shape a native time input expects. */\n const formatTime = (date: Date) =>\n `${date.getHours().toString().padStart(2, \"0\")}:${date.getMinutes().toString().padStart(2, \"0\")}`;\n\n const handleTimeChange = (next: string) => {\n const [hours, minutes] = next.split(\":\").map(Number);\n if (Number.isNaN(hours) || Number.isNaN(minutes)) return;\n // Time can be set before a day is picked; fall back to today.\n const base = value ? new Date(value) : new Date();\n base.setHours(hours, minutes, 0, 0);\n onChange?.(base);\n };\n\n const getCalendarDays = () => {\n const year = currentMonth.getFullYear();\n const month = currentMonth.getMonth();\n\n const firstDay = new Date(year, month, 1);\n const lastDay = new Date(year, month + 1, 0);\n const startDate = new Date(firstDay);\n startDate.setDate(startDate.getDate() - firstDay.getDay());\n\n const days = [];\n const current = new Date(startDate);\n\n for (let i = 0; i < 42; i++) {\n days.push(new Date(current));\n current.setDate(current.getDate() + 1);\n }\n\n return days;\n };\n\n const handleDateSelect = (date: Date) => {\n const isInCurrentMonth = date.getMonth() === currentMonth.getMonth();\n if (!isInCurrentMonth) return;\n\n if (minDate && date < minDate) return;\n if (maxDate && date > maxDate) return;\n\n if (withTime) {\n // Carry the time across, otherwise picking a day silently resets it to 00:00.\n const picked = new Date(date);\n picked.setHours(value?.getHours() ?? 0, value?.getMinutes() ?? 0, 0, 0);\n onChange?.(picked);\n // Stay open: the time still has to be set.\n return;\n }\n\n onChange?.(date);\n setIsOpen(false);\n };\n\n const handlePrevMonth = () => {\n setCurrentMonth(new Date(currentMonth.getFullYear(), currentMonth.getMonth() - 1, 1));\n };\n\n const handleNextMonth = () => {\n setCurrentMonth(new Date(currentMonth.getFullYear(), currentMonth.getMonth() + 1, 1));\n };\n\n const handleClear = () => {\n onChange?.(null);\n setIsOpen(false);\n };\n\n const inputClasses = cn(\n \"w-full border border-border-strong rounded-tile transition-colors duration-fast\",\n \"bg-surface text-text\",\n \"placeholder:text-text-muted\",\n \"focus-visible:outline-none focus-visible:focus-ring\",\n \"disabled:cursor-not-allowed disabled:bg-surface-hover disabled:text-text-disabled\",\n {\n \"h-control-sm px-3 text-sm\": size === \"sm\",\n \"h-control-md px-3 text-base\": size === \"md\",\n \"h-control-lg px-3 text-base\": size === \"lg\",\n \"h-control-md w-full px-3 text-base\": size === \"full\",\n },\n className,\n );\n\n const calendarDays = getCalendarDays();\n const monthNames = [\n \"January\",\n \"February\",\n \"March\",\n \"April\",\n \"May\",\n \"June\",\n \"July\",\n \"August\",\n \"September\",\n \"October\",\n \"November\",\n \"December\",\n ];\n\n return (\n <div ref={containerRef} className=\"relative\">\n <div ref={fieldRef} className=\"relative\">\n <input\n ref={ref}\n type=\"text\"\n value={formatDate(value ?? null)}\n placeholder={placeholder}\n disabled={disabled}\n required={required}\n readOnly\n onClick={() => !disabled && setIsOpen(!isOpen)}\n className={cn(inputClasses, \"pr-10 cursor-pointer\")}\n />\n <button\n type=\"button\"\n onClick={() => !disabled && setIsOpen(!isOpen)}\n disabled={disabled}\n className=\"absolute inset-y-0 right-0 flex items-center pr-3 text-text-muted hover:text-text-muted\"\n >\n <CalendarIcon />\n </button>\n </div>\n\n {isOpen && (\n <div\n style={panelStyle}\n className=\"fixed z-overlay overflow-auto rounded-lg border border-border bg-surface p-4 shadow-overlay min-w-panel\"\n >\n <div className=\"flex items-center justify-between mb-4\">\n <button onClick={handlePrevMonth} className=\"p-1 hover:bg-surface-hover rounded\">\n <ChevronLeftIcon />\n </button>\n <h3 className=\"text-sm font-medium text-text\">\n {monthNames[currentMonth.getMonth()]} {currentMonth.getFullYear()}\n </h3>\n <button onClick={handleNextMonth} className=\"p-1 hover:bg-surface-hover rounded\">\n <ChevronRightIcon />\n </button>\n </div>\n\n <div className=\"grid grid-cols-7 gap-1 mb-2\">\n {[\"Su\", \"Mo\", \"Tu\", \"We\", \"Th\", \"Fr\", \"Sa\"].map((day) => (\n <div key={day} className=\"text-xs font-medium text-text-muted text-center py-1\">\n {day}\n </div>\n ))}\n </div>\n\n <div className=\"grid grid-cols-7 gap-1\">\n {calendarDays.map((date, index) => {\n const isCurrentMonth = date.getMonth() === currentMonth.getMonth();\n const isSelected = value && date.toDateString() === value.toDateString();\n const isToday = date.toDateString() === new Date().toDateString();\n const isDisabled =\n !isCurrentMonth || (minDate && date < minDate) || (maxDate && date > maxDate);\n\n return (\n <button\n key={index}\n onClick={() => !isDisabled && handleDateSelect(date)}\n disabled={isDisabled}\n className={cn(\"w-8 h-8 text-sm rounded transition-colors duration-fast\", {\n \"text-text hover:bg-surface-hover\":\n isCurrentMonth && !isSelected && !isDisabled,\n \"bg-accent text-accent-fg\": isSelected,\n \"bg-surface-hover text-text\": isToday && !isSelected,\n \"text-text-muted cursor-not-allowed\": isDisabled,\n })}\n >\n {date.getDate()}\n </button>\n );\n })}\n </div>\n\n {withTime && (\n <div className=\"mt-3 flex items-center gap-2 border-t border-border pt-3\">\n <label htmlFor=\"datepicker-time\" className=\"text-sm text-text-muted\">\n Tijd\n </label>\n <input\n id=\"datepicker-time\"\n type=\"time\"\n value={value ? formatTime(value) : \"\"}\n onChange={(e) => handleTimeChange(e.target.value)}\n className=\"h-control-sm rounded-md border border-border bg-surface-input px-2 text-sm text-text focus-visible:outline-none focus-visible:focus-ring\"\n />\n </div>\n )}\n\n <div className=\"flex justify-between items-center mt-4 pt-3 border-t border-border\">\n <button onClick={handleClear} className=\"text-sm text-text-muted hover:text-text\">\n Clear\n </button>\n <button\n onClick={() => setIsOpen(false)}\n className=\"text-sm text-info-fg hover:underline\"\n >\n Close\n </button>\n </div>\n </div>\n )}\n </div>\n );\n },\n);\n","import { Search, X } from \"lucide-react\";\nimport type React from \"react\";\nimport { forwardRef } from \"react\";\nimport { Icon } from \"../content/Icon\";\nimport { cn } from \"../utils/cn\";\n\nexport interface SearchFieldProps\n extends Omit<\n React.InputHTMLAttributes<HTMLInputElement>,\n \"size\" | \"value\" | \"onChange\" | \"type\"\n > {\n /** Current query. Controlled — this field has no internal state. */\n value: string;\n /** Called with the new query text (not the event). */\n onValueChange: (value: string) => void;\n /**\n * Keyboard hint shown at the trailing edge while the field is empty —\n * `⌘K`, `⌘F`. Replaced by the clear button once there is a query.\n */\n hint?: string;\n /** `md` is the default control height; `sm` for a dense bar. */\n size?: \"sm\" | \"md\";\n /** Accessible name for the clear button. */\n clearLabel?: string;\n /** Classes for the plate around the input. */\n containerClassName?: string;\n}\n\n/**\n * The sunk, frameless search plate: a magnifier, the query, and either a\n * keyboard hint or a clear button. It is deliberately *not* a `TextField` —\n * a bordered box would read as one more empty input in a toolbar that already\n * has filter chips and buttons, where this is the thing you type into.\n *\n * Three places had grown their own copy of it (the nav search, the settings\n * search, the table toolbar); they share this one now.\n */\nexport const SearchField = forwardRef<HTMLInputElement, SearchFieldProps>(\n (\n {\n value,\n onValueChange,\n hint,\n size = \"md\",\n clearLabel = \"Clear search\",\n containerClassName,\n className,\n disabled,\n ...props\n },\n ref,\n ) => (\n <div\n className={cn(\n // No focus ring: the plate is a container, not the control, and a ring\n // around a frameless search box reads as the border it deliberately\n // does not have.\n \"flex items-center gap-2 rounded-md bg-surface-sunk px-2.5 text-text-muted\",\n \"transition-colors duration-fast ease-out\",\n size === \"sm\" ? \"h-control-sm\" : \"h-control-md\",\n disabled && \"opacity-60\",\n containerClassName,\n )}\n >\n <Icon icon={Search} size=\"md\" color=\"current\" className=\"shrink-0\" />\n\n <input\n ref={ref}\n type=\"search\"\n value={value}\n disabled={disabled}\n onChange={(event) => onValueChange(event.target.value)}\n className={cn(\n // The forms base layer hands every input a white fill, a 1px border\n // and a blue focus ring; a field that is only a caret inside a plate\n // has to switch all three off explicitly. Padding too — the base rule\n // sets 8/12px, which would push the text off the plate.\n \"min-w-0 flex-1 border-0 bg-transparent p-0 text-sm text-text shadow-none\",\n \"placeholder:text-text-subtle\",\n \"focus:border-0 focus:shadow-none focus:outline-none focus:ring-0\",\n // The UA search decorations (the WebKit cancel button) would sit\n // beside our own clear button.\n \"[&::-webkit-search-cancel-button]:appearance-none\",\n className,\n )}\n {...props}\n />\n\n {value ? (\n <button\n type=\"button\"\n aria-label={clearLabel}\n onClick={() => onValueChange(\"\")}\n className=\"grid size-4 shrink-0 cursor-pointer place-items-center rounded-xs text-text-subtle transition-colors duration-fast ease-out hover:text-text focus-visible:outline-none focus-visible:focus-ring\"\n >\n <Icon icon={X} size=\"sm\" color=\"current\" />\n </button>\n ) : (\n hint && <span className=\"shrink-0 text-xs text-text-subtle\">{hint}</span>\n )}\n </div>\n ),\n);\n\nSearchField.displayName = \"SearchField\";\n","import { Check } from \"lucide-react\";\nimport type React from \"react\";\nimport { useEffect, useRef, useState } from \"react\";\nimport { cn } from \"../utils/cn\";\nimport { useAnchoredPosition } from \"../utils/useAnchoredPosition\";\n\nexport interface DropdownOption {\n value: string;\n label: string;\n icon?: React.ReactNode;\n disabled?: boolean;\n divider?: boolean;\n description?: string;\n children?: DropdownOption[];\n}\n\nexport interface DropdownProps {\n trigger: React.ReactNode;\n options: DropdownOption[];\n value?: string | Record<string, string | string[]>;\n onSelect?: (value: string, parentValue?: string) => void;\n placement?: \"bottom-start\" | \"bottom-end\" | \"top-start\" | \"top-end\";\n disabled?: boolean;\n className?: string;\n showCheck?: boolean;\n header?: string | React.ReactNode;\n}\n\n/** Menu dropdown with optional nested submenus and roving keyboard focus. */\nexport const Dropdown: React.FC<DropdownProps> = ({\n trigger,\n options,\n value,\n onSelect,\n placement = \"bottom-start\",\n disabled = false,\n className,\n showCheck = true,\n header,\n}) => {\n const [isOpen, setIsOpen] = useState(false);\n const [hoveredOption, setHoveredOption] = useState<\n string | Record<string, string | string[]> | null\n >(null);\n const [submenuPosition, setSubmenuPosition] = useState({ top: 0, left: 0 });\n // Menu is position:fixed (computed from the trigger) so it escapes any\n // overflow-hidden / rounded-card ancestor instead of being clipped.\n const dropdownRef = useRef<HTMLDivElement>(null);\n const triggerRef = useRef<HTMLButtonElement>(null);\n const submenuRef = useRef<HTMLDivElement>(null);\n const menuRef = useRef<HTMLDivElement>(null);\n const menuPos = useAnchoredPosition(isOpen, triggerRef, { placement });\n const hideTimeoutRef = useRef<NodeJS.Timeout | null>(null);\n\n /** Enabled option buttons in the open menu, in DOM order. */\n const menuItems = () =>\n Array.from(\n menuRef.current?.querySelectorAll<HTMLButtonElement>(\n \"button[data-option-value]:not(:disabled)\",\n ) ?? [],\n );\n\n // Move focus into the menu when it opens, so the keyboard lands somewhere\n // useful instead of on the page behind it.\n useEffect(() => {\n if (isOpen) menuItems()[0]?.focus();\n }, [isOpen]);\n\n /** Roving focus: ↓/↑ wrap through the options, Home/End jump to the ends. */\n const handleMenuKeyDown = (event: React.KeyboardEvent<HTMLDivElement>) => {\n const keys = [\"ArrowDown\", \"ArrowUp\", \"Home\", \"End\"];\n if (!keys.includes(event.key)) return;\n\n const items = menuItems();\n if (items.length === 0) return;\n event.preventDefault();\n\n const current = items.indexOf(document.activeElement as HTMLButtonElement);\n const next =\n event.key === \"Home\"\n ? 0\n : event.key === \"End\"\n ? items.length - 1\n : event.key === \"ArrowDown\"\n ? (current + 1) % items.length\n : (current - 1 + items.length) % items.length;\n\n items[next]?.focus();\n };\n\n useEffect(() => {\n const handleClickOutside = (event: MouseEvent) => {\n if (\n dropdownRef.current &&\n (!dropdownRef.current.contains(event.target as Node) ||\n (submenuRef.current && !submenuRef.current.contains(event.target as Node)))\n ) {\n setIsOpen(false);\n setHoveredOption(null);\n if (hideTimeoutRef.current) {\n clearTimeout(hideTimeoutRef.current);\n hideTimeoutRef.current = null;\n }\n }\n };\n\n if (isOpen) {\n document.addEventListener(\"mousedown\", handleClickOutside);\n }\n\n return () => {\n document.removeEventListener(\"mousedown\", handleClickOutside);\n };\n }, [isOpen]);\n\n useEffect(() => {\n const handleEscape = (event: KeyboardEvent) => {\n if (event.key === \"Escape\") {\n setIsOpen(false);\n setHoveredOption(null);\n // Hand focus back to the trigger; otherwise it falls to <body> and the\n // keyboard user loses their place.\n triggerRef.current?.focus();\n if (hideTimeoutRef.current) {\n clearTimeout(hideTimeoutRef.current);\n hideTimeoutRef.current = null;\n }\n }\n };\n\n if (isOpen) {\n document.addEventListener(\"keydown\", handleEscape);\n }\n\n return () => {\n document.removeEventListener(\"keydown\", handleEscape);\n };\n }, [isOpen]);\n\n useEffect(() => {\n if (hoveredOption && dropdownRef.current) {\n const hoveredElement = dropdownRef.current.querySelector(\n `[data-option-value=\"${hoveredOption}\"]`,\n );\n if (hoveredElement) {\n const rect = hoveredElement.getBoundingClientRect();\n setSubmenuPosition({\n top: rect.top,\n left: rect.right + 8,\n });\n }\n }\n }, [hoveredOption]);\n\n useEffect(() => {\n return () => {\n if (hideTimeoutRef.current) {\n clearTimeout(hideTimeoutRef.current);\n }\n };\n }, []);\n\n const handleTriggerClick = (event: React.MouseEvent<HTMLButtonElement>) => {\n event.stopPropagation();\n if (disabled) return;\n setIsOpen(!isOpen);\n setHoveredOption(null);\n if (hideTimeoutRef.current) {\n clearTimeout(hideTimeoutRef.current);\n hideTimeoutRef.current = null;\n }\n };\n\n const handleOptionClick = (\n event: React.MouseEvent<HTMLButtonElement>,\n option: DropdownOption,\n parentValue?: string,\n ) => {\n event.stopPropagation();\n if (!option.disabled && !option.divider) {\n if (!option.children || option.children.length === 0) {\n onSelect?.(option.value, parentValue);\n setIsOpen(false);\n setHoveredOption(null);\n }\n }\n };\n\n const handleOptionHover = (option: DropdownOption) => {\n if (hideTimeoutRef.current) {\n clearTimeout(hideTimeoutRef.current);\n hideTimeoutRef.current = null;\n }\n\n if (option.children && option.children.length > 0) {\n setHoveredOption(option.value);\n } else {\n setHoveredOption(null);\n }\n };\n\n const handleOptionLeave = (option: DropdownOption) => {\n if (option.children && option.children.length > 0) {\n hideTimeoutRef.current = setTimeout(() => {\n setHoveredOption(null);\n }, 150);\n } else {\n setHoveredOption(null);\n }\n };\n\n const handleSubmenuOptionClick = (\n event: React.MouseEvent<HTMLButtonElement>,\n option: DropdownOption,\n parentValue?: string,\n ) => {\n event.stopPropagation();\n if (!option.disabled && !option.divider) {\n onSelect?.(option.value, parentValue);\n setIsOpen(false);\n setHoveredOption(null);\n }\n };\n\n const dropdownClasses = cn(\n \"fixed z-overlay min-w-menu bg-surface rounded-lg shadow-overlay border border-border\",\n \"max-h-60 overflow-auto\",\n );\n\n const submenuClasses = cn(\n \"fixed z-overlay min-w-menu bg-surface rounded-lg shadow-overlay border border-border\",\n \"max-h-60 overflow-auto\",\n );\n\n return (\n <div\n ref={dropdownRef}\n className={cn(\n \"relative flex flex-row items-center justify-center rounded-lg hover:bg-surface-hover\",\n className,\n )}\n >\n <button\n ref={triggerRef}\n onClick={handleTriggerClick}\n disabled={disabled}\n aria-haspopup=\"menu\"\n aria-expanded={isOpen}\n className={cn(\"inline-flex items-center justify-center\", {\n \"opacity-50 cursor-not-allowed\": disabled,\n })}\n >\n {trigger}\n </button>\n\n {isOpen && (\n <div\n ref={menuRef}\n className={dropdownClasses}\n style={menuPos}\n onKeyDown={handleMenuKeyDown}\n >\n <div className=\"p-2\" role=\"menu\">\n {header &&\n (typeof header === \"string\" ? (\n <div className=\"text-xs font-medium text-text-muted uppercase tracking-label px-3 py-2\">\n {header}\n </div>\n ) : (\n header\n ))}\n\n {options.map((option, index) => {\n if (option.divider) {\n return <div key={`divider-${index}`} className=\"border-t border-border my-2\" />;\n }\n\n const hasChildren = option.children && option.children.length > 0;\n const isHovered = hoveredOption === option.value;\n\n return (\n <div key={option.value} className=\"relative group\">\n <button\n data-option-value={option.value}\n role=\"menuitem\"\n aria-haspopup={hasChildren ? \"menu\" : undefined}\n onClick={(event) => handleOptionClick(event, option)}\n onMouseEnter={() => handleOptionHover(option)}\n onMouseLeave={() => handleOptionLeave(option)}\n disabled={option.disabled}\n className={cn(\n \"flex w-full items-center justify-between rounded-md px-3 py-1.5 text-sm text-text transition-colors duration-fast ease-out hover:bg-surface-hover focus-visible:outline-none focus-visible:focus-ring\",\n {\n \"opacity-50 cursor-not-allowed\": option.disabled,\n \"bg-surface-hover\": isHovered && hasChildren,\n },\n )}\n >\n <div className=\"flex items-center space-x-2 min-w-0\">\n {option.icon && <span className=\"flex-shrink-0\">{option.icon}</span>}\n <div className=\"flex flex-col items-start min-w-0\">\n <span className=\"font-base truncate\">{option.label}</span>\n {option.description && (\n <span className=\"text-xs text-text-muted\">{option.description}</span>\n )}\n </div>\n </div>\n\n <div className=\"flex items-center space-x-2\">\n {showCheck && value === option.value && (\n <Check className=\"size-icon-md shrink-0 text-success-fg\" aria-hidden />\n )}\n {hasChildren && <span className=\"text-text-muted text-xs\">▶</span>}\n </div>\n </button>\n\n {hasChildren && isHovered && (\n <div\n ref={submenuRef}\n className={submenuClasses}\n style={{\n top: `${submenuPosition.top}px`,\n left: `${submenuPosition.left}px`,\n }}\n onMouseEnter={() => {\n if (hideTimeoutRef.current) {\n clearTimeout(hideTimeoutRef.current);\n hideTimeoutRef.current = null;\n }\n setHoveredOption(option.value);\n }}\n onMouseLeave={() => {\n hideTimeoutRef.current = setTimeout(() => {\n setHoveredOption(null);\n }, 150);\n }}\n >\n <div className=\"p-2\">\n {option.children!.map((childOption, childIndex) => {\n if (childOption.divider) {\n return (\n <div\n key={`divider-${childIndex}`}\n className=\"border-t border-border my-2\"\n />\n );\n }\n\n const isSelected =\n (typeof value === \"object\" &&\n value[option.value] &&\n value[option.value].includes(childOption.value)) ||\n value === childOption.value;\n\n return (\n <button\n key={childOption.value}\n onClick={(event) =>\n handleSubmenuOptionClick(event, childOption, option.value)\n }\n disabled={childOption.disabled}\n className={cn(\n \"flex w-full items-center justify-between rounded-md px-3 py-1.5 text-sm text-text transition-colors duration-fast ease-out hover:bg-surface-hover\",\n {\n \"opacity-50 cursor-not-allowed\": childOption.disabled,\n },\n )}\n >\n <div className=\"flex items-center space-x-2 min-w-0\">\n {childOption.icon && (\n <span className=\"flex-shrink-0\">{childOption.icon}</span>\n )}\n <div className=\"flex flex-col items-start min-w-0\">\n <span className=\"font-base truncate\">{childOption.label}</span>\n {childOption.description && (\n <span className=\"text-xs text-text-muted\">\n {childOption.description}\n </span>\n )}\n </div>\n </div>\n\n {showCheck && isSelected && (\n <Check\n className=\"size-icon-md shrink-0 text-success-fg\"\n aria-hidden\n />\n )}\n </button>\n );\n })}\n </div>\n </div>\n )}\n </div>\n );\n })}\n\n {(() => {\n const totalOptions = options.reduce((count, option) => {\n if (option.divider) return count;\n const childCount = option.children\n ? option.children.filter((child) => !child.divider).length\n : 0;\n return count + 1 + childCount;\n }, 0);\n\n return (\n totalOptions > 1 && (\n <div className=\"border-t border-border mt-2 pt-2\">\n <div className=\"text-xs text-text-muted px-3 py-1\">\n {totalOptions} options available\n </div>\n </div>\n )\n );\n })()}\n </div>\n </div>\n )}\n </div>\n );\n};\n","import { ChevronDown, ChevronLeft, ChevronRight, ChevronUp, Ellipsis } from \"lucide-react\";\nimport type React from \"react\";\nimport { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport { Button } from \"../action/Button\";\nimport { FilterChip } from \"../action/FilterChip\";\nimport { Checkbox } from \"../input/Checkbox\";\nimport { DatePicker } from \"../input/DatePicker\";\nimport { SearchField } from \"../input/SearchField\";\nimport { Dropdown } from \"../overlays/Dropdown\";\nimport { cn } from \"../utils/cn\";\nimport { Icon } from \"./Icon\";\nimport type { ListGroup } from \"./List\";\nimport type { PageHeaderAction } from \"./PageHeader\";\nimport { PageToolbarActions } from \"./PageToolbar\";\n\nexport interface TableColumn<T = any> {\n /** Unique column identifier */\n id: string;\n /** Column header text */\n header: string;\n /** Data accessor - can be string key or function */\n accessor: keyof T | ((row: T) => any);\n /** Custom cell renderer */\n cell?: (value: any, row: T, index: number) => React.ReactNode;\n /** Column width */\n width?: string | number;\n /** Whether column is sortable */\n sortable?: boolean;\n /** Whether column is searchable */\n searchable?: boolean;\n /** Column alignment */\n align?: \"left\" | \"center\" | \"right\";\n /** Whether column is sticky */\n sticky?: \"left\" | \"right\";\n /** Custom header renderer */\n headerCell?: () => React.ReactNode;\n}\n\nexport interface TableAction<T = any> {\n /** Action identifier */\n id: string;\n /** Action label */\n label: string;\n /** Action icon */\n icon?: React.ReactNode;\n /** Action handler */\n onClick: (row: T, index: number) => void;\n /** Whether action is disabled for this row */\n disabled?: (row: T) => boolean;\n /** Action variant */\n variant?: \"primary\" | \"secondary\" | \"outline\" | \"ghost\" | \"destructive\";\n}\n\nexport interface TableFilter {\n /** Filter identifier */\n id: string;\n /** Filter label */\n label: string;\n /** Filter type */\n type?: \"select\" | \"date\" | \"dateRange\";\n /** Leading icon on the chip — what the filter is about, at a glance. */\n icon?: React.ReactNode;\n /** Filter options (for select type) */\n options?: Array<{ value: string; label: string }>;\n /** Current filter value */\n value?: string | Date | null | string[];\n /** Filter change handler */\n onChange: (value: string | Date | null | string[]) => void;\n /** Placeholder text */\n placeholder?: string;\n /** Whether filter is multi-select */\n multiSelect?: boolean;\n}\n\n/**\n * `TableFilter.onChange` covers every filter `type` in one signature, so a\n * handler written for the type it is actually attached to does not fit it\n * (parameters are contravariant). These adapters bridge that without pushing a\n * cast into every call site.\n *\n * The fallbacks are unreachable in practice — a `type: \"date\"` filter only ever\n * emits `Date | null`, a single `type: \"select\"` only ever a string — but they\n * keep the coercion explicit instead of asserting it away.\n */\nexport const dateFilterHandler =\n (fn: (value: Date | null) => void): TableFilter[\"onChange\"] =>\n (value) =>\n fn(value instanceof Date ? value : null);\n\nexport const selectFilterHandler =\n (fn: (value: string) => void): TableFilter[\"onChange\"] =>\n (value) =>\n fn(typeof value === \"string\" ? value : \"\");\n\n/**\n * The `multiSelect: true` counterpart. It emits `string[]`, which neither adapter\n * above accepts, so without this every multi-select call site pushed a cast — the\n * exact thing these adapters exist to avoid.\n *\n * A single string is folded into a one-element array rather than dropped: a filter\n * that silently clears itself when the menu emits the narrower shape is worse than\n * one that over-selects.\n */\nexport const multiSelectFilterHandler =\n (fn: (value: string[]) => void): TableFilter[\"onChange\"] =>\n (value) =>\n fn(Array.isArray(value) ? value : typeof value === \"string\" && value ? [value] : []);\n\nexport interface TableProps<T = any> {\n /** Table data */\n data: T[];\n /** Column definitions */\n columns: TableColumn<T>[];\n /** Loading state */\n loading?: boolean;\n /** Whether table is searchable */\n searchable?: boolean;\n /** Search placeholder text */\n searchPlaceholder?: string;\n /**\n * Take over the query. Pass this pair when the page already filters its own\n * data — across fields no column exposes, or together with filters of its\n * own — and only wants the search plate to sit in the toolbar where the\n * design puts it. The built-in column search then stays out of the way;\n * without them the table owns both the box and the filtering.\n */\n searchValue?: string;\n onSearchChange?: (value: string) => void;\n /** Table filters */\n filters?: TableFilter[];\n /**\n * Buttons for the toolbar above the table — \"New …\", import, export. This is\n * where a list's own actions belong: the page header names the page, the\n * table owns what you do to it.\n */\n toolbarActions?: PageHeaderAction[];\n /**\n * Extra controls in the toolbar, rendered **after the filter chips and before**\n * the right-hand action cluster.\n *\n * This is where anything that belongs *with the filters* goes: an avatar stack\n * you filter by, a \"clear filters\" chip, a group-by control. `toolbarActions`\n * is pushed right with `ml-auto`, so putting a filter there parks it beside the\n * list's own buttons and it stops reading as a filter at all.\n */\n toolbarContent?: React.ReactNode;\n /**\n * Split the rows into sections. Returns the group id a row belongs to; the\n * same shape `List` uses, so a screen that has both does not learn two APIs.\n *\n * **Grouping turns pagination off.** Paging *across* sections is meaningless —\n * \"1–10 of 40\" under a section holding three rows is worse than no paginator —\n * so a grouped table shows every row it was given.\n */\n groupBy?: (item: T, index: number) => string;\n /**\n * Declared section order, titles and tone. Ids missing from the data are\n * skipped; ids in the data that are not declared come after, in the order\n * they first appear.\n */\n groups?: ListGroup[];\n /** Whether table has pagination */\n paginated?: boolean;\n /** Rows per page. Fixed — the footer is a summary and a page list, nothing to set. */\n defaultPageSize?: number;\n /** Row actions */\n actions?: TableAction<T>[];\n /** Row click handler */\n onRowClick?: (row: T, index: number) => void;\n /** Row selection */\n selectable?: boolean;\n /** Selected rows */\n selectedRows?: T[];\n /** Selection change handler */\n onSelectionChange?: (selectedRows: T[]) => void;\n /** Row key accessor */\n getRowKey?: (row: T, index: number) => string | number;\n /** Empty state content */\n emptyContent?: React.ReactNode;\n /**\n * Left-hand line under the table. Defaults to the range/total count on a\n * paginated table; pass a node to say it in the app's own words (\"9 of 1,284\n * contacts\"), which also gives an unpaginated table that one line.\n */\n footerSummary?: React.ReactNode;\n /** Row density. `md` is the default data row; `sm` for a dense inline list. */\n size?: \"sm\" | \"md\" | \"lg\";\n /** Whether to show row hover */\n hoverable?: boolean;\n /** Additional CSS classes */\n className?: string;\n /** Custom row className */\n rowClassName?: (row: T, index: number) => string;\n /**\n * Tint the header row so it reads as a column strip rather than a first row.\n * On by default. Turn it off for a table that already sits on a tinted\n * surface, where a second wash just muddies the edge.\n */\n headerBackground?: boolean;\n /** Horizontal padding inside the table box. */\n padding?: \"none\" | \"sm\" | \"md\" | \"lg\";\n}\n\n/** Cell padding — uniform across columns, so the grid reads as a grid. */\nconst cellPadding = {\n none: \"px-0\",\n sm: \"px-2\",\n md: \"px-3.5\",\n lg: \"px-5\",\n};\n\nconst rowHeights = {\n sm: \"h-row-lg\",\n md: \"h-row-xl\",\n lg: \"h-14\",\n};\n\n/** How many placeholder rows to draw while the data is on its way. */\nconst SKELETON_ROWS = 8;\n\n/** Uneven cell widths, so the placeholder reads as text and not as a bar chart. */\nconst SKELETON_WIDTHS = [\"62%\", \"44%\", \"74%\", \"52%\", \"68%\", \"48%\", \"80%\", \"56%\"];\n\ninterface SortState {\n column: string | null;\n direction: \"asc\" | \"desc\" | null;\n}\n\ninterface PaginationState {\n page: number;\n pageSize: number;\n}\n\n/**\n * Page buttons around the current page: first, last, a window of neighbours,\n * and an ellipsis wherever that skips something.\n */\nfunction pageWindow(current: number, total: number): Array<number | \"gap\"> {\n if (total <= 7) return Array.from({ length: total }, (_, i) => i);\n\n const pages = new Set([0, total - 1, current - 1, current, current + 1]);\n const sorted = [...pages].filter((p) => p >= 0 && p < total).sort((a, b) => a - b);\n\n const out: Array<number | \"gap\"> = [];\n sorted.forEach((page, index) => {\n if (index > 0 && page - (sorted[index - 1] as number) > 1) out.push(\"gap\");\n out.push(page);\n });\n return out;\n}\n\n/** One filter, as a chip that opens its own value list. */\nconst FilterMenu = (props: TableFilter) => {\n const values = Array.isArray(props.value)\n ? props.value\n : props.value != null && props.value !== \"\" && props.value !== \"all\"\n ? [props.value]\n : [];\n\n if (props.type === \"date\") {\n const active = props.value instanceof Date;\n return (\n <DatePicker\n value={props.value as Date | null}\n onChange={(date) => props.onChange(date)}\n placeholder={props.placeholder || props.label}\n // Active is a soft fill, like every other filter — an outline in the\n // accent hue was left over from the blue-accent design and read as a\n // focus ring on a field nobody had focused.\n className={cn(\"min-w-40\", active && \"bg-info-soft text-info-fg\")}\n />\n );\n }\n\n const labelOf = (value: unknown) =>\n props.options?.find((option) => option.value === value)?.label ?? String(value);\n\n const isSelected = (value: string) =>\n Array.isArray(props.value) ? props.value.includes(value) : props.value === value;\n\n const pick = (value: string) => {\n if (!props.multiSelect) return props.onChange(value);\n const current = Array.isArray(props.value) ? props.value : [];\n props.onChange(\n current.includes(value) ? current.filter((v) => v !== value) : [...current, value],\n );\n };\n\n const active = values.length > 0;\n\n return (\n <FilterChip\n tone=\"outline\"\n caret\n icon={props.icon}\n active={active}\n label={active ? `${props.label}: ${values.map(labelOf).join(\", \")}` : props.label}\n title={props.label}\n onClear={() => props.onChange(props.multiSelect ? [] : \"all\")}\n menu={(close) => (\n <div role=\"listbox\" className=\"flex flex-col\">\n {props.options?.map((option) => (\n <button\n key={option.value}\n type=\"button\"\n role=\"option\"\n aria-selected={isSelected(option.value)}\n onClick={() => {\n pick(option.value);\n if (!props.multiSelect) close();\n }}\n className={cn(\n \"flex cursor-pointer items-center gap-2 rounded-md px-2.5 py-1.5 text-left text-sm\",\n \"transition-colors duration-fast ease-out hover:bg-surface-hover\",\n \"focus-visible:outline-none focus-visible:focus-ring\",\n isSelected(option.value) ? \"font-medium text-text\" : \"text-text-muted\",\n )}\n >\n {option.label}\n </button>\n ))}\n </div>\n )}\n />\n );\n};\n\n/**\n * Data table: a bordered box with a tinted column strip, a toolbar above it\n * (search, filters, the list's own actions) and a summary + paginator below.\n */\nexport const Table = <T extends Record<string, any>>({\n data,\n columns,\n loading = false,\n searchable = false,\n searchPlaceholder = \"Search...\",\n searchValue,\n onSearchChange,\n filters = [],\n toolbarActions = [],\n toolbarContent,\n groupBy,\n groups,\n paginated = false,\n defaultPageSize = 10,\n actions = [],\n onRowClick,\n selectable = false,\n selectedRows = [],\n onSelectionChange,\n getRowKey = (row, index) => index,\n emptyContent,\n footerSummary,\n size = \"md\",\n hoverable = true,\n padding = \"md\",\n className,\n rowClassName,\n headerBackground = true,\n}: TableProps<T>) => {\n const [internalSearch, setInternalSearch] = useState(\"\");\n const controlledSearch = searchValue !== undefined;\n const searchTerm = controlledSearch ? searchValue : internalSearch;\n const setSearchTerm = onSearchChange ?? setInternalSearch;\n const [sortState, setSortState] = useState<SortState>({\n column: null,\n direction: null,\n });\n const [pagination, setPagination] = useState<PaginationState>({\n page: 0,\n pageSize: defaultPageSize,\n });\n\n const tableRef = useRef<HTMLTableElement>(null);\n\n const filteredData = useMemo(() => {\n let result = data;\n\n // Apply search filter — unless the owner took over the query, in which case\n // the data arriving here is already filtered and doing it again would\n // narrow it to whatever the columns happen to expose.\n if (searchable && !controlledSearch && searchTerm.trim()) {\n const searchableColumns = columns.filter((col) => col.searchable !== false);\n const lowerSearchTerm = searchTerm.toLowerCase();\n\n result = result.filter((row) => {\n return searchableColumns.some((column) => {\n const value =\n typeof column.accessor === \"function\" ? column.accessor(row) : row[column.accessor];\n\n return String(value || \"\")\n .toLowerCase()\n .includes(lowerSearchTerm);\n });\n });\n }\n\n /**\n * Column filters are rendered here but applied by the page: `TableFilter`\n * requires both `value` and `onChange`, so filter state lives outside and\n * `data` arrives pre-narrowed. The in-table pass that used to run on top\n * of that compared labels against keys and strings against multi-select\n * arrays, silently emptying the table; every consumer already pre-filters,\n * so removing it changed nothing for them and unbroke those cases.\n */\n\n return result;\n }, [data, searchTerm, controlledSearch, columns, searchable]);\n\n const sortedData = useMemo(() => {\n if (!sortState.column || !sortState.direction) return filteredData;\n\n const column = columns.find((col) => col.id === sortState.column);\n if (!column) return filteredData;\n\n return [...filteredData].sort((a, b) => {\n const aValue =\n typeof column.accessor === \"function\" ? column.accessor(a) : a[column.accessor];\n const bValue =\n typeof column.accessor === \"function\" ? column.accessor(b) : b[column.accessor];\n\n let comparison = 0;\n\n if (aValue < bValue) comparison = -1;\n else if (aValue > bValue) comparison = 1;\n\n return sortState.direction === \"desc\" ? -comparison : comparison;\n });\n }, [filteredData, sortState, columns]);\n\n const paginatedData = useMemo(() => {\n // Grouping wins over pagination: see the `groupBy` prop.\n if (!paginated || groupBy) return sortedData;\n\n const startIndex = pagination.page * pagination.pageSize;\n const endIndex = startIndex + pagination.pageSize;\n return sortedData.slice(startIndex, endIndex);\n }, [sortedData, pagination, paginated, groupBy]);\n\n const handleSort = useCallback(\n (columnId: string) => {\n const column = columns.find((col) => col.id === columnId);\n if (!column?.sortable) return;\n\n setSortState((prev) => {\n if (prev.column !== columnId) {\n return { column: columnId, direction: \"asc\" };\n }\n if (prev.direction === \"asc\") {\n return { column: columnId, direction: \"desc\" };\n }\n return { column: null, direction: null };\n });\n },\n [columns],\n );\n\n const handlePageChange = useCallback((newPage: number) => {\n setPagination((prev) => ({ ...prev, page: newPage }));\n }, []);\n\n const handleRowSelection = useCallback(\n (row: T, checked: boolean) => {\n if (!onSelectionChange) return;\n\n const rowKey = getRowKey(row, 0);\n if (checked) {\n onSelectionChange([...selectedRows, row]);\n } else {\n onSelectionChange(selectedRows.filter((_, i) => getRowKey(selectedRows[i], i) !== rowKey));\n }\n },\n [selectedRows, onSelectionChange, getRowKey],\n );\n\n const handleSelectAll = useCallback(\n (checked: boolean) => {\n if (!onSelectionChange) return;\n onSelectionChange(checked ? [...paginatedData] : []);\n },\n [paginatedData, onSelectionChange],\n );\n\n useEffect(() => {\n setPagination((prev) => ({ ...prev, page: 0 }));\n }, [searchTerm, sortState]);\n\n // Separate effect for filters to avoid infinite loops\n useEffect(() => {\n setPagination((prev) => ({ ...prev, page: 0 }));\n }, [filters.map((f) => f.value).join(\",\")]);\n\n const totalItems = sortedData.length;\n const totalPages = Math.ceil(totalItems / pagination.pageSize);\n const startItem = pagination.page * pagination.pageSize + 1;\n const endItem = Math.min(startItem + pagination.pageSize - 1, totalItems);\n\n const allVisibleSelected =\n paginatedData.length > 0 &&\n paginatedData.every((row) => {\n const rowKey = getRowKey(row, 0);\n return selectedRows.some((selectedRow, i) => getRowKey(selectedRow, i) === rowKey);\n });\n\n const someVisibleSelected = paginatedData.some((row) => {\n const rowKey = getRowKey(row, 0);\n return selectedRows.some((selectedRow, i) => getRowKey(selectedRow, i) === rowKey);\n });\n\n const pad = cellPadding[padding];\n\n // Sticky cells need the same fill as the strip they sit in, otherwise the\n // frozen column shows the page through while the rest of the row is tinted.\n const headerFill = headerBackground ? \"bg-surface-sunk\" : \"bg-surface\";\n\n const cellClasses = cn(\"text-text\", pad);\n const showToolbar =\n searchable || filters.length > 0 || toolbarActions.length > 0 || !!toolbarContent;\n /**\n * The rows to render, as sections. Without `groupBy` that is one nameless\n * section holding everything, so the body below has a single shape.\n *\n * Bucketed in first-appearance order and then reordered by `groups`, matching\n * `List` exactly — a screen that groups a list and a table should not get two\n * different orderings out of the same data.\n */\n const sections = useMemo(() => {\n const rows = paginatedData.map((row, index) => ({ row, index }));\n if (!groupBy) return [{ group: null as ListGroup | null, rows }];\n\n const buckets = new Map<string, typeof rows>();\n for (const entry of rows) {\n const id = groupBy(entry.row, entry.index);\n const bucket = buckets.get(id);\n if (bucket) bucket.push(entry);\n else buckets.set(id, [entry]);\n }\n\n const declared = groups ?? [];\n const ordered: ListGroup[] = [\n ...declared.filter((group) => buckets.has(group.id)),\n ...[...buckets.keys()]\n .filter((id) => !declared.some((group) => group.id === id))\n .map((id) => ({ id })),\n ];\n return ordered.map((group) => ({ group, rows: buckets.get(group.id) ?? [] }));\n }, [paginatedData, groupBy, groups]);\n\n /** Every column the body spans, so a section header can stretch across it. */\n const bodyColumnCount = columns.length + (selectable ? 1 : 0) + (actions.length > 0 ? 1 : 0);\n\n const showPaginator = paginated && totalItems > 0;\n const showFooter = showPaginator || footerSummary !== undefined;\n\n return (\n <div className={cn(\"flex min-w-0 flex-col\", className)}>\n {showToolbar && (\n <div className=\"flex flex-wrap items-center gap-2 pb-3\">\n {searchable && (\n <SearchField\n value={searchTerm}\n onValueChange={setSearchTerm}\n placeholder={searchPlaceholder}\n containerClassName=\"w-full sm:w-66\"\n />\n )}\n\n {filters.map((filter) => (\n <FilterMenu key={filter.id} {...filter} />\n ))}\n\n {toolbarContent}\n\n {toolbarActions.length > 0 && (\n <PageToolbarActions actions={toolbarActions} className=\"ml-auto\" />\n )}\n </div>\n )}\n\n {/* A bordered box, not a bare grid: the table has to hold its own edge\n now that the page around it is one undivided card. */}\n <div className=\"relative w-full overflow-x-auto rounded-lg ring-1 ring-border\">\n {/**\n * The skeleton lives in the tbody, not above the table: headers are\n * known before the data is, so they stay, and only the rows are drawn\n * as placeholders — at row height, in the real columns. Rendering a\n * `ContentLoading` here used to put a page-padded `<main>` landmark\n * inside this box, and the real rows then shoved everything into place.\n */}\n <table ref={tableRef} className=\"min-w-full border-collapse text-base text-text\">\n <thead className={cn(headerFill, \"h-control-lg\")}>\n <tr>\n {selectable && (\n <th className={cn(cellClasses, \"w-10\")}>\n <Checkbox\n size=\"sm\"\n checked={allVisibleSelected}\n indeterminate={someVisibleSelected && !allVisibleSelected}\n onChange={(e) => handleSelectAll(e.target.checked)}\n aria-label=\"Select all rows\"\n />\n </th>\n )}\n\n {columns.map((column) => (\n <th\n key={column.id}\n className={cn(\n cellClasses,\n \"text-xs font-semibold uppercase tracking-label text-text-muted\",\n \"group\",\n {\n \"text-left\": column.align === \"left\" || !column.align,\n \"text-center\": column.align === \"center\",\n \"text-right\": column.align === \"right\",\n \"cursor-pointer select-none\": column.sortable,\n [`sticky left-0 ${headerFill}`]: column.sticky === \"left\",\n [`sticky right-0 ${headerFill}`]: column.sticky === \"right\",\n },\n )}\n style={{ width: column.width }}\n onClick={() => column.sortable && handleSort(column.id)}\n >\n <div className=\"flex items-center gap-1.5\">\n {column.headerCell ? column.headerCell() : column.header}\n {column.sortable && (\n <Icon\n icon={\n sortState.column === column.id && sortState.direction === \"desc\"\n ? ChevronDown\n : ChevronUp\n }\n size=\"xs\"\n className={cn(\n \"transition-opacity duration-fast ease-out\",\n sortState.column === column.id\n ? \"text-text opacity-100\"\n : \"text-text-subtle opacity-0 group-hover:opacity-100\",\n )}\n />\n )}\n </div>\n </th>\n ))}\n\n {actions.length > 0 && (\n <th className={cn(cellClasses, \"w-10\")}>\n <span className=\"sr-only\">Actions</span>\n </th>\n )}\n </tr>\n </thead>\n\n <tbody>\n {loading ? (\n Array.from({ length: SKELETON_ROWS }).map((_, rowIndex) => (\n <tr\n key={`skeleton-${rowIndex}`}\n className=\"animate-pulse border-t border-border-subtle\"\n aria-hidden\n >\n {selectable && (\n <td className={cn(cellClasses, \"h-row-lg\")}>\n <span className=\"block size-4 rounded-xs bg-surface-hover\" />\n </td>\n )}\n {columns.map((column, columnIndex) => (\n <td\n key={column.id}\n className={cn(cellClasses, \"h-row-lg\")}\n style={{ width: column.width }}\n >\n <span\n className=\"block h-3 rounded-xs bg-surface-hover\"\n // Uneven widths, so a column of placeholders reads as\n // text rather than as a set of progress bars.\n style={{\n width: SKELETON_WIDTHS[(rowIndex + columnIndex) % SKELETON_WIDTHS.length],\n }}\n />\n </td>\n ))}\n {actions.length > 0 && <td className={cn(cellClasses, \"h-row-lg\")} />}\n </tr>\n ))\n ) : paginatedData.length === 0 ? (\n <tr>\n <td\n colSpan={columns.length + (selectable ? 1 : 0) + (actions.length > 0 ? 1 : 0)}\n className={cn(\n cellClasses,\n \"border-t border-border-subtle py-10 text-center text-text-muted\",\n )}\n >\n {emptyContent || \"No data available\"}\n </td>\n </tr>\n ) : (\n sections.flatMap((section) => [\n ...(section.group\n ? [\n <tr key={`group-${section.group.id}`}>\n <td\n colSpan={bodyColumnCount}\n className={cn(\n // A caption over its rows, not a second column strip:\n // same surface as the rows, no rules, count on the right.\n \"border-t border-border-subtle bg-surface px-4 pb-1.5 pt-4 text-sm font-semibold\",\n section.group.tone === \"danger\"\n ? \"text-danger-fg\"\n : section.group.tone === \"muted\"\n ? \"text-text-subtle\"\n : \"text-text\",\n )}\n >\n <span className=\"flex items-center gap-2.5\">\n {section.group.title ?? section.group.id}\n <span className=\"rounded-full bg-surface-sunk px-1.5 py-0.5 text-xs font-medium text-text-muted tabular-nums\">\n {section.group.count ?? section.rows.length}\n </span>\n </span>\n </td>\n </tr>,\n ]\n : []),\n ...section.rows.map(({ row, index }) => {\n const rowKey = getRowKey(row, index);\n const isSelected = selectedRows.some(\n (selectedRow, i) => getRowKey(selectedRow, i) === rowKey,\n );\n\n return (\n <tr\n key={rowKey}\n className={cn(\n rowHeights[size],\n // Hairline above every row, including the first: it is\n // what separates the body from the column strip.\n \"border-t border-border-subtle\",\n hoverable &&\n \"transition-colors duration-fast ease-out hover:bg-surface-hover\",\n onRowClick && \"cursor-pointer\",\n isSelected && \"bg-accent-soft\",\n rowClassName?.(row, index),\n )}\n onClick={(e) => {\n e.stopPropagation();\n onRowClick?.(row, index);\n }}\n >\n {selectable && (\n <td className={cellClasses}>\n <Checkbox\n size=\"sm\"\n checked={isSelected}\n onClick={(e) => e.stopPropagation()}\n onChange={(e) => {\n e.stopPropagation();\n handleRowSelection(row, e.target.checked);\n }}\n aria-label=\"Select row\"\n />\n </td>\n )}\n\n {columns.map((column) => {\n const value =\n typeof column.accessor === \"function\"\n ? column.accessor(row)\n : row[column.accessor];\n\n return (\n <td\n key={column.id}\n className={cn(cellClasses, {\n \"text-left\": column.align === \"left\" || !column.align,\n \"text-center\": column.align === \"center\",\n \"text-right\": column.align === \"right\",\n \"sticky left-0 bg-surface\": column.sticky === \"left\",\n \"sticky right-0 bg-surface\": column.sticky === \"right\",\n })}\n >\n {column.cell ? column.cell(value, row, index) : String(value || \"\")}\n </td>\n );\n })}\n\n {actions.length > 0 && (\n <td className={cellClasses}>\n <Dropdown\n className=\"rounded-md\"\n trigger={\n <span className=\"grid size-7 place-items-center text-text-subtle\">\n <Icon icon={Ellipsis} size=\"md\" color=\"current\" />\n </span>\n }\n options={actions.map((action) => ({\n value: action.id,\n label: action.label,\n icon: action.icon,\n disabled: action.disabled?.(row),\n }))}\n onSelect={(actionId) => {\n const action = actions.find((a) => a.id === actionId);\n if (action) {\n action.onClick(row, index);\n }\n }}\n placement=\"bottom-end\"\n />\n </td>\n )}\n </tr>\n );\n }),\n ])\n )}\n </tbody>\n </table>\n </div>\n\n {showFooter && (\n <div className=\"flex flex-wrap items-center gap-3 px-1 pt-3 text-xs text-text-muted\">\n <span className=\"min-w-0 flex-1\">\n {footerSummary ?? `Showing ${startItem}–${endItem} of ${totalItems}`}\n </span>\n\n {showPaginator && (\n <div className=\"flex items-center gap-0.5\">\n <Button\n size=\"xs\"\n variant=\"ghost\"\n iconOnly\n aria-label=\"Previous page\"\n onClick={() => handlePageChange(pagination.page - 1)}\n disabled={pagination.page === 0}\n >\n <Icon icon={ChevronLeft} size=\"md\" color=\"current\" />\n </Button>\n\n {pageWindow(pagination.page, totalPages).map((entry, index) =>\n entry === \"gap\" ? (\n <span key={`gap-${index}`} className=\"px-1 text-text-subtle\">\n …\n </span>\n ) : (\n <button\n key={entry}\n type=\"button\"\n aria-current={entry === pagination.page ? \"page\" : undefined}\n onClick={() => handlePageChange(entry)}\n className={cn(\n \"h-control-xs min-w-6 cursor-pointer rounded-sm px-1.5 tabular-nums\",\n \"transition-colors duration-fast ease-out\",\n \"focus-visible:outline-none focus-visible:focus-ring\",\n entry === pagination.page\n ? \"bg-surface-sunk font-semibold text-text\"\n : \"text-text-muted hover:bg-surface-hover hover:text-text\",\n )}\n >\n {entry + 1}\n </button>\n ),\n )}\n\n <Button\n size=\"xs\"\n variant=\"ghost\"\n iconOnly\n aria-label=\"Next page\"\n onClick={() => handlePageChange(pagination.page + 1)}\n disabled={pagination.page >= totalPages - 1}\n >\n <Icon icon={ChevronRight} size=\"md\" color=\"current\" />\n </Button>\n </div>\n )}\n </div>\n )}\n </div>\n );\n};\n","import { X } from \"lucide-react\";\nimport type React from \"react\";\nimport { Icon } from \"../content/Icon\";\nimport { cn } from \"../utils/cn\";\n\n/**\n * `color` is deliberately omitted from the inherited HTML attributes. It is a\n * legacy HTML attribute, so `<Badge color=\"success\">` used to type-check while\n * doing nothing at all — the badge silently rendered as `default`. Omitting it\n * turns that mistake into a compile error pointing at `variant`.\n */\nexport interface BadgeProps extends Omit<React.HTMLAttributes<HTMLSpanElement>, \"color\"> {\n variant?: \"default\" | \"primary\" | \"secondary\" | \"success\" | \"warning\" | \"error\" | \"info\" | \"note\";\n\n size?: \"sm\" | \"md\" | \"lg\";\n\n dot?: boolean;\n\n icon?: React.ReactNode;\n\n dismissible?: boolean;\n\n onDismiss?: () => void;\n}\n\n// Semantic, token-driven (mode-aware via CSS custom properties — no dark: overrides needed)\nconst badgeVariants = {\n default: \"bg-surface-hover text-text\",\n primary: \"bg-accent-soft text-accent\",\n secondary: \"bg-surface-hover text-text-muted\",\n success: \"bg-success-soft text-success-fg\",\n warning: \"bg-warning-soft text-warning-fg\",\n error: \"bg-danger-soft text-danger-fg\",\n info: \"bg-info-soft text-info-fg\",\n // Internal-note amber. Never for customer-visible content.\n note: \"bg-note-soft text-note\",\n};\n\nconst badgeSizes = {\n sm: \"px-2 py-0.5 text-xs\",\n md: \"px-2.5 py-1 text-sm\",\n lg: \"px-3 py-1.5 text-base\",\n};\n\nconst dotColors = {\n default: \"bg-surface-hover\",\n primary: \"bg-accent\",\n secondary: \"bg-surface-hover\",\n success: \"bg-success\",\n warning: \"bg-warning\",\n error: \"bg-danger\",\n info: \"bg-info\",\n note: \"bg-note\",\n};\n\n/** Badge for status indicators and labels. */\nexport function Badge({\n variant = \"default\",\n size = \"md\",\n dot = false,\n icon,\n dismissible = false,\n onDismiss,\n className,\n children,\n ...props\n}: BadgeProps) {\n return (\n <span\n className={cn(\n \"inline-flex items-center font-medium rounded-full\",\n badgeVariants[variant],\n badgeSizes[size],\n className,\n )}\n {...props}\n >\n {dot && <span className={cn(\"w-2 h-2 rounded-full mr-1.5\", dotColors[variant])} />}\n\n {icon && <span className={cn(\"flex-shrink-0\", children && \"mr-1\")}>{icon}</span>}\n\n {children}\n\n {dismissible && onDismiss && (\n <button\n onClick={onDismiss}\n className=\"ml-1.5 flex-shrink-0 hover:opacity-70 transition-opacity\"\n aria-label=\"Remove badge\"\n >\n <Icon icon={X} size=\"xs\" color=\"current\" />\n </button>\n )}\n </span>\n );\n}\n","import React from \"react\";\nimport { Badge, type BadgeProps } from \"../feedback/Badge\";\nimport type { TableColumn } from \"./Table\";\n\n/**\n * Column builders for the three things table configs kept getting wrong across\n * apps: a state rendered as prose, a stored code shown raw, and an id shown\n * instead of the name it points at.\n *\n * All keep the *label* in `accessor` and render only the decoration in `cell`,\n * because `Table` searches and sorts on the accessor value — so a search for\n * \"Active\" or a team's name matches what the row actually shows.\n */\n\nexport interface BadgeColumnConfig<T, V = unknown> {\n id: string;\n header: string;\n accessor: (row: T) => V;\n /** The text in the badge — and the text this column is searched on. */\n label: (value: V, row: T) => string;\n /** Defaults to `default`; give states their own tone (success/error/…). */\n tone?: (value: V, row: T) => BadgeProps[\"variant\"];\n /** Return false to render nothing at all for this row. */\n visible?: (value: V, row: T) => boolean;\n width?: string | number;\n sortable?: boolean;\n align?: TableColumn<T>[\"align\"];\n}\n\n/** A value that is a state rather than prose — render it as a pill. */\nexport function badgeColumn<T, V = unknown>(config: BadgeColumnConfig<T, V>): TableColumn<T> {\n const { id, header, accessor, label, tone, visible, width, sortable, align } = config;\n return {\n id,\n header,\n accessor: (row: T) => label(accessor(row), row),\n cell: (_value: unknown, row: T) => {\n const raw = accessor(row);\n if (visible && !visible(raw, row)) return null;\n return (\n <Badge variant={tone ? tone(raw, row) : \"default\"} size=\"sm\">\n {label(raw, row)}\n </Badge>\n );\n },\n width,\n sortable,\n align,\n };\n}\n\nexport interface BooleanBadgeColumnConfig<T> {\n id: string;\n header: string;\n accessor: (row: T) => boolean | undefined;\n onLabel: string;\n offLabel: string;\n /** Defaults to success/secondary — on reads as healthy, off as merely quiet. */\n onTone?: BadgeProps[\"variant\"];\n offTone?: BadgeProps[\"variant\"];\n width?: string | number;\n sortable?: boolean;\n}\n\n/** The on/off case of `badgeColumn`, which is most of them. */\nexport function booleanBadgeColumn<T>(config: BooleanBadgeColumnConfig<T>): TableColumn<T> {\n const {\n id,\n header,\n accessor,\n onLabel,\n offLabel,\n onTone = \"success\",\n offTone = \"secondary\",\n width,\n sortable,\n } = config;\n\n return badgeColumn<T, boolean>({\n id,\n header,\n accessor: (row) => !!accessor(row),\n label: (on) => (on ? onLabel : offLabel),\n tone: (on) => (on ? onTone : offTone),\n width,\n sortable,\n });\n}\n\nexport interface LabelsColumnConfig<T> {\n id: string;\n header: string;\n /** One code, a list of them, or nothing. */\n accessor: (row: T) => string | string[] | undefined | null;\n /**\n * Code to readable text. Return `undefined` to fall back to the code itself,\n * so an unknown value still shows something instead of vanishing.\n */\n label: (code: string) => string | undefined;\n /** Shown when there is nothing at all. Defaults to an em dash. */\n empty?: string;\n separator?: string;\n width?: string | number;\n searchable?: boolean;\n sortable?: boolean;\n}\n\n/**\n * Stored codes rendered as the words the user picked them by. Covers both\n * localising an enum (`interactions:created` → \"Interaction created\") and\n * resolving a foreign key (a `teamId` → that team's name — pass a lookup as\n * `label`), because from the table's side those are the same problem.\n */\nexport function labelsColumn<T>(config: LabelsColumnConfig<T>): TableColumn<T> {\n const {\n id,\n header,\n accessor,\n label,\n empty = \"—\",\n separator = \", \",\n width,\n searchable,\n sortable,\n } = config;\n\n return {\n id,\n header,\n accessor: (row: T) => {\n const raw = accessor(row);\n const codes = raw == null ? [] : Array.isArray(raw) ? raw : [raw];\n const labelled = codes.filter(Boolean).map((code) => label(code) ?? code);\n return labelled.length ? labelled.join(separator) : empty;\n },\n width,\n searchable,\n sortable,\n };\n}\n","import type React from \"react\";\nimport { cn } from \"../utils/cn\";\n\nexport interface TextProps {\n /** Text variant */\n variant?: \"body\" | \"caption\" | \"label\" | \"code\";\n /** Text size */\n size?: \"xs\" | \"sm\" | \"md\" | \"lg\" | \"xl\";\n /** Font weight */\n weight?: \"light\" | \"normal\" | \"medium\" | \"semibold\" | \"bold\";\n /** Text color */\n color?:\n | \"primary\"\n | \"secondary\"\n | \"accent\"\n | \"success\"\n | \"warning\"\n | \"error\"\n | \"info\"\n | \"neutral\"\n | \"current\"\n | \"muted\";\n /** Text alignment */\n align?: \"left\" | \"center\" | \"right\" | \"justify\";\n /** Whether to truncate text with ellipsis */\n truncate?: boolean;\n /** Whether text should be italic */\n italic?: boolean;\n /** Whether text should be underlined */\n underline?: boolean;\n /** Line height */\n lineHeight?: \"tight\" | \"normal\" | \"relaxed\";\n /** HTML element to render */\n as?: \"p\" | \"span\" | \"div\" | \"code\" | \"pre\";\n /** Additional CSS classes */\n className?: string;\n /** Child content */\n children: React.ReactNode;\n}\n\n/**\n * Text — every copy string in the product goes through this component.\n *\n * Size maps onto the token type scale (tokens.css):\n * xs 11 · sm 12 · md 13 (body) · lg 14 (long-form) · xl 16\n * Anything above 16px is a Heading, not Text.\n */\nconst sizeMap: Record<NonNullable<TextProps[\"size\"]>, string> = {\n xs: \"text-xs\",\n sm: \"text-sm\",\n md: \"text-base\",\n lg: \"text-md\",\n xl: \"text-lg\",\n};\n\nconst weightMap: Record<NonNullable<TextProps[\"weight\"]>, string> = {\n light: \"font-normal\",\n normal: \"font-normal\",\n medium: \"font-medium\",\n semibold: \"font-semibold\",\n bold: \"font-bold\",\n};\n\n/** Semantic colour tokens — these flip in dark mode, so no `dark:` variants. */\nconst colorMap: Record<NonNullable<TextProps[\"color\"]>, string> = {\n primary: \"text-text\",\n secondary: \"text-text-muted\",\n accent: \"text-accent\",\n success: \"text-success-fg\",\n warning: \"text-warning-fg\",\n error: \"text-danger-fg\",\n info: \"text-info-fg\",\n neutral: \"text-text\",\n current: \"text-current\",\n muted: \"text-text-subtle\",\n};\n\nconst alignMap: Record<NonNullable<TextProps[\"align\"]>, string> = {\n left: \"text-left\",\n center: \"text-center\",\n right: \"text-right\",\n justify: \"text-justify\",\n};\n\nconst lineHeightMap: Record<NonNullable<TextProps[\"lineHeight\"]>, string> = {\n tight: \"leading-tight\",\n normal: \"leading-normal\",\n relaxed: \"leading-relaxed\",\n};\n\nexport const Text: React.FC<TextProps> = ({\n variant = \"body\",\n size = \"md\",\n weight = \"normal\",\n color = \"current\",\n align = \"left\",\n truncate = false,\n italic = false,\n underline = false,\n lineHeight = \"normal\",\n as,\n className,\n children,\n ...props\n}) => {\n const Tag = (as || getDefaultElement(variant)) as keyof React.JSX.IntrinsicElements;\n\n const isCaption = variant === \"caption\";\n const isLabel = variant === \"label\";\n const isCode = variant === \"code\";\n\n const textClasses = cn(\n \"font-sans\",\n\n // Variant — caption and label pin their own size/weight\n isCaption && \"text-xs text-text-subtle\",\n isLabel && \"text-xs font-semibold uppercase tracking-label text-text-subtle\",\n isCode && \"font-mono text-sm rounded-sm bg-surface-hover px-1 py-0.5\",\n\n // Size — only where the variant does not pin it\n !isCaption && !isLabel && !isCode && sizeMap[size],\n\n // Weight — label owns its weight\n !isLabel && weightMap[weight],\n\n colorMap[color],\n alignMap[align],\n\n // Line height only overrides the body variant\n variant === \"body\" && lineHeightMap[lineHeight],\n\n italic && \"italic\",\n underline && \"underline\",\n truncate && \"truncate\",\n\n className,\n );\n\n return (\n <Tag className={textClasses} {...props}>\n {children}\n </Tag>\n );\n};\n\nfunction getDefaultElement(variant: TextProps[\"variant\"]): string {\n const elementMap: Record<NonNullable<TextProps[\"variant\"]>, string> = {\n body: \"p\",\n caption: \"span\",\n label: \"span\",\n code: \"code\",\n };\n\n return elementMap[variant!] || \"p\";\n}\n","import type React from \"react\";\nimport { useCallback } from \"react\";\nimport { Text } from \"../typography/Text\";\nimport { cn } from \"../utils/cn\";\n\nexport type ViewFieldType =\n | \"text\"\n | \"email\"\n | \"password\"\n | \"number\"\n | \"tel\"\n | \"url\"\n | \"textarea\"\n | \"select\"\n | \"checkbox\"\n | \"radio\"\n | \"date\"\n | \"custom\"\n | \"array\";\n\ntype NestedKeys<T> = {\n [K in keyof T]: T[K] extends object ? `${K & string}.${NestedKeys<T[K]>}` : K & string;\n}[keyof T];\n\nfunction getValueByPath<T>(obj: T, path: string): unknown {\n return path\n .split(\".\")\n .reduce(\n (acc, key) => (acc && typeof acc === \"object\" && key in acc ? (acc as any)[key] : undefined),\n obj,\n );\n}\n\nexport interface ViewGroupItem<T = any> {\n /** Field name (key in data) */\n name: NestedKeys<T>;\n /** Field label */\n label: string;\n /** Field type */\n type: ViewFieldType;\n /** Field width (CSS width value or grid columns) */\n width?: string | number;\n /** Options for select/radio/checkbox types */\n options?: Array<{\n value: string | number;\n label: string;\n }>;\n /** Conditional function to show/hide field */\n conditional?: (data: T) => boolean;\n /** Whether field is hidden */\n hidden?: boolean;\n /** Custom component renderer */\n customComponent?: (props: { value: any }) => React.ReactNode;\n /** Format function for display value */\n format?: (value: any, data: T) => string | React.ReactNode;\n\n /** For 'array' type, defines the fields for each item in the array */\n arrayFields?: ViewGroupItem<any>[];\n}\n\nexport interface ViewGroup<T = any> {\n /** Group identifier */\n id: string;\n /** Group title */\n title: string;\n /** Group description */\n description?: string;\n /** Group items */\n items: ViewGroupItem<T>[];\n /** Conditional function to show/hide group */\n conditional?: (data: T) => boolean;\n /** Group layout */\n layout?: \"grid\" | \"flex\";\n /** Number of columns for grid layout */\n columns?: number;\n /** Additional CSS classes */\n className?: string;\n}\n\nexport interface ViewProps<T = Record<string, any>> {\n /** View groups */\n groups: ViewGroup<T>[];\n /** Data to display */\n data: T;\n /** View size */\n size?: \"sm\" | \"md\" | \"lg\" | \"full\";\n /** Additional CSS classes */\n className?: string;\n}\n\n/**\n * Generic View component with groups and conditional rendering\n * Displays data in a read-only format matching the Form component layout\n */\nexport const View = <T extends Record<string, any>>({\n groups,\n data,\n size = \"md\",\n className,\n}: ViewProps<T>) => {\n const formatDate = (value: any): string => {\n if (!value) return \"-\";\n try {\n const date = new Date(value);\n return date.toLocaleDateString(\"nl-NL\", {\n year: \"numeric\",\n month: \"long\",\n day: \"numeric\",\n });\n } catch {\n return String(value);\n }\n };\n\n const formatCheckbox = (value: any): string => {\n return value ? \"Ja\" : \"Nee\";\n };\n\n const getOptionLabel = (\n value: any,\n options?: Array<{ value: string | number; label: string }>,\n ): string => {\n if (!options) return String(value || \"-\");\n const option = options.find((opt) => opt.value === value);\n return option ? option.label : String(value || \"-\");\n };\n\n const renderField = useCallback(\n (item: ViewGroupItem<T>) => {\n const value = getValueByPath(data, item.name as string);\n\n if (item.type === \"array\") {\n const arrayValue = value as any[] | undefined;\n if (!arrayValue || arrayValue.length === 0) {\n return (\n <Text variant=\"body\" size=\"sm\" className=\"text-text-muted\">\n Geen items\n </Text>\n );\n }\n\n return (\n <div className=\"space-y-3\">\n {arrayValue.map((row, index) => (\n <div key={index} className=\"p-4 border border-border rounded-xl space-y-3\">\n <div className=\"flex items-center justify-between\">\n <Text variant=\"label\" size=\"sm\" weight=\"medium\" className=\"text-text-muted\">\n {item.label} {index + 1}\n </Text>\n </div>\n <div className=\"grid grid-cols-1 sm:grid-cols-2 gap-3\">\n {item.arrayFields?.map((subField) => {\n const subValue = row?.[subField.name];\n let displayValue: React.ReactNode = \"-\";\n\n switch (subField.type) {\n case \"text\":\n case \"email\":\n case \"number\":\n case \"tel\":\n case \"url\":\n case \"textarea\":\n displayValue = subValue || \"-\";\n break;\n case \"select\":\n case \"radio\":\n displayValue = getOptionLabel(subValue, subField.options);\n break;\n case \"checkbox\":\n displayValue = formatCheckbox(subValue);\n break;\n case \"date\":\n displayValue = formatDate(subValue);\n break;\n case \"custom\":\n displayValue = subField.customComponent?.({\n value: subValue,\n });\n break;\n default:\n displayValue = String(subValue || \"-\");\n }\n\n if (subField.format) {\n displayValue = subField.format(subValue, row);\n }\n\n return (\n <div key={subField.name as string} className=\"space-y-1\">\n <Text variant=\"label\" size=\"xs\" className=\"text-text-muted\">\n {subField.label}\n </Text>\n <Text variant=\"body\" size=\"sm\" className=\"text-text\">\n {displayValue}\n </Text>\n </div>\n );\n })}\n </div>\n </div>\n ))}\n </div>\n );\n }\n\n if (item.type === \"custom\" && item.customComponent) {\n return item.customComponent({ value });\n }\n\n if (item.format) {\n const formatted = item.format(value, data);\n return (\n <Text variant=\"body\" size=\"sm\" className=\"text-text\">\n {formatted}\n </Text>\n );\n }\n\n let displayValue: React.ReactNode = \"-\";\n\n switch (item.type) {\n case \"text\":\n case \"email\":\n case \"password\":\n case \"number\":\n case \"tel\":\n case \"url\":\n displayValue = value ? String(value) : \"-\";\n break;\n\n case \"textarea\":\n displayValue = value ? (\n <div className=\"whitespace-pre-wrap\">{String(value || \"-\")}</div>\n ) : (\n \"-\"\n );\n break;\n\n case \"select\":\n case \"radio\":\n displayValue = getOptionLabel(value, item.options);\n break;\n\n case \"checkbox\":\n displayValue = formatCheckbox(value);\n break;\n\n case \"date\":\n displayValue = formatDate(value);\n break;\n\n default:\n displayValue = value ? String(value) : \"-\";\n }\n\n return (\n <Text variant=\"body\" size=\"sm\" className=\"text-text\">\n {displayValue}\n </Text>\n );\n },\n [data],\n );\n\n const renderGroup = useCallback(\n (group: ViewGroup<T>) => {\n if (group.conditional && !group.conditional(data)) {\n return null;\n }\n\n const visibleItems = group.items.filter((item) => {\n if (item.hidden) return false;\n if (item.conditional && !item.conditional(data)) return false;\n return true;\n });\n\n if (visibleItems.length === 0) return null;\n\n const groupClasses = cn(\n \"space-y-4\",\n {\n \"grid gap-4\": group.layout === \"grid\",\n \"flex flex-wrap gap-4\": group.layout === \"flex\",\n },\n group.layout === \"grid\" && {\n \"grid-cols-1\": !group.columns || group.columns === 1,\n \"grid-cols-2\": group.columns === 2,\n \"grid-cols-3\": group.columns === 3,\n \"grid-cols-4\": group.columns === 4,\n },\n group.className,\n );\n\n return (\n <div\n key={group.id}\n className=\"grid grid-cols-1 md:grid-cols-3 gap-8 p-6 border border-border rounded-xl\"\n >\n <div className=\"md:col-span-1\">\n <Text variant=\"label\" size=\"lg\" weight=\"semibold\" className=\"text-text\">\n {group.title}\n </Text>\n {group.description && (\n <Text variant=\"body\" size=\"sm\" className=\"text-text-muted mt-1\">\n {group.description}\n </Text>\n )}\n </div>\n\n <div className=\"md:col-span-2\">\n <div className={groupClasses}>\n {visibleItems.map((item) => {\n return (\n <div\n key={item.name as string}\n className=\"space-y-1\"\n // Inline, not `w-[…]`/`col-span-…`: those are built at runtime,\n // so Tailwind never sees them and `width` silently did nothing.\n // Same fix Form.tsx already carries.\n style={{\n width: typeof item.width === \"string\" ? item.width : undefined,\n gridColumn:\n typeof item.width === \"number\"\n ? `span ${item.width} / span ${item.width}`\n : undefined,\n }}\n >\n {item.type !== \"custom\" && (\n <Text variant=\"label\" size=\"sm\" className=\"font-medium text-text-muted\">\n {item.label}\n </Text>\n )}\n\n {renderField(item)}\n </div>\n );\n })}\n </div>\n </div>\n </div>\n );\n },\n [data, renderField],\n );\n\n const viewClasses = cn(\n \"space-y-6\",\n {\n \"max-w-md\": size === \"sm\",\n \"max-w-2xl\": size === \"md\",\n \"max-w-4xl\": size === \"lg\",\n \"max-w-full\": size === \"full\",\n },\n className,\n );\n\n return (\n <div className={viewClasses}>\n <div className=\"space-y-8\">{groups.map(renderGroup)}</div>\n </div>\n );\n};\n","import type { ErrorInfo, ReactNode } from \"react\";\nimport { Component } from \"react\";\n\ninterface Props {\n children: ReactNode;\n fallback?: ReactNode;\n name?: string;\n}\n\ninterface State {\n hasError: boolean;\n error: Error | null;\n}\n\nexport class ErrorBoundary extends Component<Props, State> {\n public state: State = {\n hasError: false,\n error: null,\n };\n\n public static getDerivedStateFromError(error: Error): State {\n return { hasError: true, error };\n }\n\n public componentDidCatch(error: Error, errorInfo: ErrorInfo) {\n console.error(`[ErrorBoundary] Error in ${this.props.name || \"Component\"}:`, error, errorInfo);\n }\n\n public render() {\n if (this.state.hasError) {\n if (this.props.fallback) {\n return this.props.fallback;\n }\n\n return (\n <div className=\"p-4 m-4 border border-danger-border bg-danger-soft rounded-lg\">\n <h2 className=\"text-lg font-semibold text-danger-fg\">Something went wrong</h2>\n <p className=\"text-sm text-danger-fg mt-1\">\n {this.props.name ? `Error in ${this.props.name}` : \"The component failed to render.\"}\n </p>\n <button\n className=\"mt-4 px-4 py-2 bg-danger text-danger-fg rounded hover:opacity-90 transition-opacity text-sm\"\n onClick={() => this.setState({ hasError: false, error: null })}\n >\n Try again\n </button>\n </div>\n );\n }\n\n return this.props.children;\n }\n}\n","import type React from \"react\";\nimport { useEffect, useRef, useState } from \"react\";\n\ninterface FederatedResourceProps {\n identifier: string;\n resourceLoader: () => Promise<any>;\n framework?: \"react\" | \"svelte\" | \"vanilla\";\n /** Shown while the remote loads. */\n fallback?: React.ReactNode;\n /**\n * Shown when the remote cannot load — app disabled, bundle broken, wrong key.\n * Without it a failure renders nothing, and the spot where something should have\n * been disappears without a trace in the UI.\n */\n errorFallback?: React.ReactNode;\n componentProps?: Record<string, any>;\n className?: string;\n}\n\n/**\n * Remotes already fetched, by identifier. Without this every identifier switch showed\n * the fallback for a frame even when the bundle was in memory — a full-screen flash per\n * click on pages that switch resource while navigating. Module Federation already caches\n * the import itself; this removes the render round, not a network round.\n */\nconst loadedRemotes = new Map<string, any>();\n\nexport function FederatedResource({\n identifier,\n resourceLoader,\n framework = \"react\",\n fallback,\n errorFallback,\n componentProps,\n className,\n}: FederatedResourceProps) {\n const containerRef = useRef<HTMLDivElement>(null);\n const [failedId, setFailedId] = useState<string | null>(null);\n const [, bump] = useState(0);\n\n const svelteInstanceRef = useRef<any>(null);\n\n /**\n * The loader lives in a ref and the effect depends only on `identifier`: callers pass\n * inline arrows, which get a new identity every render — with the loader in the deps\n * the bundle reloaded on every render. `identifier` is the resource's real identity.\n * `componentProps` are unaffected: they spread in the render, so prop changes reach the\n * remote without refetching the bundle (svelte prop updates go via `$set` below).\n */\n const loaderRef = useRef(resourceLoader);\n loaderRef.current = resourceLoader;\n\n useEffect(() => {\n if (loadedRemotes.has(identifier)) return;\n\n let isMounted = true;\n\n const load = async () => {\n try {\n const module = await loaderRef.current();\n if (!isMounted) return;\n loadedRemotes.set(identifier, module);\n bump((n) => n + 1);\n } catch (err) {\n console.error(`[FederatedResource] Load failed: ${identifier}`, err);\n if (isMounted) setFailedId(identifier);\n }\n };\n\n void load();\n return () => {\n isMounted = false;\n };\n }, [identifier]);\n\n /*\n * Everything derives from `identifier` in the render, never from state: effects run\n * after paint, so on a switch there is one frame where the old remote would get the\n * new remote's props — exactly when a page crashes on a prop it never expected.\n */\n const module = loadedRemotes.get(identifier) ?? null;\n // `!module` first: switching away from a failed remote and back retries the load; if\n // that succeeds the stale error must not linger next to the loaded component.\n const failed = !module && failedId === identifier;\n const isLoading = !module && !failed;\n\n const ExternalComponent = module\n ? framework === \"react\"\n ? module.component || module.default || module\n : module\n : null;\n\n useEffect(() => {\n if (!ExternalComponent || framework === \"react\") return;\n\n const target = containerRef.current;\n if (!target) return;\n\n if (framework === \"svelte\") {\n const Component = ExternalComponent.default || ExternalComponent;\n const instance = new Component({ target, props: componentProps || {} });\n svelteInstanceRef.current = instance;\n return () => instance.$destroy();\n }\n\n if (ExternalComponent.mount) {\n const cleanup = ExternalComponent.mount({ container: target, props: componentProps || {} });\n return () => {\n if (typeof cleanup === \"function\") cleanup();\n else ExternalComponent.unmount?.(target);\n };\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [ExternalComponent, framework]);\n\n useEffect(() => {\n if (framework === \"svelte\" && svelteInstanceRef.current) {\n svelteInstanceRef.current.$set?.(componentProps);\n }\n }, [componentProps, framework]);\n\n return (\n <div ref={containerRef} className={className} style={{ display: \"contents\" }}>\n {isLoading && fallback}\n {failed && errorFallback}\n\n {framework === \"react\" && ExternalComponent && <ExternalComponent {...componentProps} />}\n </div>\n );\n}\n","import type React from \"react\";\nimport { cn } from \"../utils/cn\";\n\nexport interface EmptyStateProps extends Omit<React.HTMLAttributes<HTMLDivElement>, \"title\"> {\n /** Leading glyph or icon (e.g. a check for the focus \"queue empty\" state). */\n icon?: React.ReactNode;\n /** Tint of the icon disc. */\n tone?: \"neutral\" | \"success\";\n /** Headline. */\n title: React.ReactNode;\n /** Supporting copy. */\n description?: React.ReactNode;\n /** Action buttons row. */\n actions?: React.ReactNode;\n /** Render inside a raised card (focus \"done\" state) vs. plain centered block. */\n card?: boolean;\n}\n\nconst toneDisc: Record<NonNullable<EmptyStateProps[\"tone\"]>, string> = {\n neutral: \"bg-surface-hover text-text-muted\",\n success: \"bg-success-soft text-success-fg\",\n};\n\n/** Centered empty / completion state (focus queue done, no-results lists). */\nexport function EmptyState({\n icon,\n tone = \"neutral\",\n title,\n description,\n actions,\n card = false,\n className,\n ...props\n}: EmptyStateProps) {\n return (\n <div\n className={cn(\n \"flex flex-col items-center gap-2.5 text-center\",\n card && \"rounded-lg border border-border bg-surface px-11 py-9 shadow-overlay\",\n className,\n )}\n {...props}\n >\n {icon != null && (\n <span\n className={cn(\n \"flex size-disc items-center justify-center rounded-full text-2xl\",\n toneDisc[tone],\n )}\n >\n {icon}\n </span>\n )}\n <div className=\"text-lg font-semibold text-text\">{title}</div>\n {description && (\n <div className=\"max-w-sm text-sm leading-relaxed text-text-muted\">{description}</div>\n )}\n {actions && <div className=\"flex gap-2 pt-1.5\">{actions}</div>}\n </div>\n );\n}\n","import type React from \"react\";\nimport { cn } from \"../utils/cn\";\n\nexport interface KbdProps extends React.HTMLAttributes<HTMLElement> {\n /** Key label, e.g. \"⌘K\", \"E\", \"↵\". */\n children: React.ReactNode;\n}\n\n/** Keyboard-shortcut chip (the `⌘K` / `E` / `↵` hints). */\nexport function Kbd({ children, className, ...props }: KbdProps) {\n return (\n <kbd\n className={cn(\n \"inline-flex items-center rounded border border-border px-1.5 py-px text-xs font-medium leading-none text-text-muted\",\n className,\n )}\n {...props}\n >\n {children}\n </kbd>\n );\n}\n","import type React from \"react\";\nimport { cn } from \"../utils/cn\";\n\nexport interface ProgressBarProps extends React.HTMLAttributes<HTMLDivElement> {\n /** Completion 0–100 (clamped). */\n value: number;\n /** Fill tone. */\n variant?: \"accent\" | \"success\";\n /** Track height. */\n size?: \"sm\" | \"md\";\n}\n\nconst trackSizes: Record<NonNullable<ProgressBarProps[\"size\"]>, string> = {\n sm: \"h-1\",\n md: \"h-1.5\",\n};\n\nconst fillVariants: Record<NonNullable<ProgressBarProps[\"variant\"]>, string> = {\n accent: \"bg-accent\",\n success: \"bg-success\",\n};\n\n/**\n * Slim determinate progress bar (focus queue progress, subtask completion).\n * Token-driven and mode-aware.\n */\nexport function ProgressBar({\n value,\n variant = \"accent\",\n size = \"md\",\n className,\n ...props\n}: ProgressBarProps) {\n const pct = Math.max(0, Math.min(100, value));\n return (\n <div\n role=\"progressbar\"\n aria-valuenow={Math.round(pct)}\n aria-valuemin={0}\n aria-valuemax={100}\n className={cn(\n \"w-full overflow-hidden rounded-full bg-surface-hover\",\n trackSizes[size],\n className,\n )}\n {...props}\n >\n <div\n className={cn(\n \"h-full rounded-full transition-[width] duration-300 ease-out\",\n fillVariants[variant],\n )}\n style={{ width: `${pct}%` }}\n />\n </div>\n );\n}\n","import type React from \"react\";\nimport { cn } from \"../utils/cn\";\n\nexport interface SpinnerProps extends React.HTMLAttributes<HTMLDivElement> {\n size?: \"xs\" | \"sm\" | \"md\" | \"lg\" | \"xl\";\n\n variant?: \"primary\" | \"secondary\" | \"white\" | \"current\";\n\n /** Accessible label; rendered visually only with `showLabel`. */\n label?: string;\n\n showLabel?: boolean;\n}\n\nconst spinnerSizes = {\n xs: \"h-3 w-3\",\n sm: \"h-4 w-4\",\n md: \"h-6 w-6\",\n lg: \"h-8 w-8\",\n xl: \"h-12 w-12\",\n};\n\nconst spinnerColors = {\n primary: \"text-accent\",\n secondary: \"text-text-muted\",\n white: \"text-white\",\n current: \"text-current\",\n};\n\nconst labelSizes = {\n xs: \"text-xs\",\n sm: \"text-sm\",\n md: \"text-sm\",\n lg: \"text-base\",\n xl: \"text-lg\",\n};\n\n/** Spinner for loading states. */\nexport function Spinner({\n size = \"md\",\n variant = \"primary\",\n label = \"Loading...\",\n showLabel = false,\n className,\n ...props\n}: SpinnerProps) {\n return (\n <div\n className={cn(\"inline-flex items-center\", showLabel ? \"flex-col space-y-2\" : \"\", className)}\n {...props}\n >\n <svg\n className={cn(\"animate-spin\", spinnerSizes[size], spinnerColors[variant])}\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n aria-hidden={!showLabel}\n role={showLabel ? \"status\" : undefined}\n >\n <circle\n className=\"opacity-25\"\n cx=\"12\"\n cy=\"12\"\n r=\"10\"\n stroke=\"currentColor\"\n strokeWidth=\"4\"\n />\n <path\n className=\"opacity-75\"\n fill=\"currentColor\"\n d=\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z\"\n />\n </svg>\n\n {showLabel && <span className={cn(\"text-text-muted\", labelSizes[size])}>{label}</span>}\n\n {!showLabel && <span className=\"sr-only\">{label}</span>}\n </div>\n );\n}\n","import React from \"react\";\nimport { cn } from \"../utils/cn\";\n\nexport interface StatusDotProps {\n /**\n * Meaning, not colour. `neutral` is the off/idle state; `accent` marks\n * unread; the rest follow the status tokens.\n */\n tone?:\n | \"neutral\"\n | \"accent\"\n | \"success\"\n | \"warning\"\n | \"danger\"\n | \"info\"\n | \"note\"\n /** Identity without status — an inbox, a speaker. See --color-cat-*. */\n | \"cat-1\"\n | \"cat-2\"\n | \"cat-3\"\n | \"cat-4\"\n | \"cat-5\";\n /** Square instead of round — the marker style used for inbox identity. */\n shape?: \"round\" | \"square\";\n /** Slow pulse for something live: an active call, a running recording. */\n pulse?: boolean;\n /**\n * Accessible name. Without one the dot is decorative and hidden from\n * assistive tech, which is right when adjacent text already says the state.\n */\n label?: string;\n className?: string;\n}\n\nconst tones: Record<NonNullable<StatusDotProps[\"tone\"]>, string> = {\n neutral: \"bg-text-disabled\",\n accent: \"bg-accent\",\n success: \"bg-success\",\n warning: \"bg-warning\",\n danger: \"bg-danger\",\n info: \"bg-info\",\n note: \"bg-note\",\n \"cat-1\": \"bg-cat-1\",\n \"cat-2\": \"bg-cat-2\",\n \"cat-3\": \"bg-cat-3\",\n \"cat-4\": \"bg-cat-4\",\n \"cat-5\": \"bg-cat-5\",\n};\n\n/** Small state marker: presence, session state, unread, live recording. */\nexport function StatusDot({\n tone = \"neutral\",\n shape = \"round\",\n pulse = false,\n label,\n className,\n}: StatusDotProps) {\n return (\n <span\n role={label ? \"img\" : undefined}\n aria-label={label}\n aria-hidden={label ? undefined : true}\n className={cn(\n \"inline-block size-dot shrink-0\",\n shape === \"round\" ? \"rounded-full\" : \"rounded-sm\",\n tones[tone],\n pulse && \"animate-pulse\",\n className,\n )}\n />\n );\n}\n","import React from \"react\";\nimport { cn } from \"../utils/cn\";\n\nexport interface StepIndicatorProps {\n /** Step ids in order. Only the position matters; the ids identify `current`. */\n steps: string[];\n /** Id of the step being shown. Unknown ids render as \"not started\". */\n current: string;\n /**\n * `numbered` draws counted circles joined by a rule — use it when the user\n * needs to know how many steps are left. `dots` is the quieter bar, for a\n * flow whose heading already says where you are.\n */\n variant?: \"numbered\" | \"dots\";\n /**\n * Accessible name. Without one the indicator is treated as decorative, which\n * is right when a heading beside it already announces the current step.\n */\n label?: string;\n className?: string;\n}\n\n/** Progress through a short, linear flow. */\nexport function StepIndicator({\n steps,\n current,\n variant = \"numbered\",\n label,\n className,\n}: StepIndicatorProps) {\n const activeIndex = steps.indexOf(current);\n const shared = {\n role: label ? \"group\" : undefined,\n \"aria-label\": label,\n \"aria-hidden\": label ? undefined : true,\n };\n\n if (variant === \"dots\") {\n return (\n <div {...shared} className={cn(\"flex items-center gap-1.5\", className)}>\n {steps.map((step, i) => (\n <span\n key={step}\n className={cn(\n \"h-step-bar rounded-full transition-all duration-fast ease-out\",\n i === activeIndex ? \"w-5 bg-accent\" : \"w-3.5\",\n i < activeIndex && \"bg-border-strong\",\n i > activeIndex && \"bg-border\",\n )}\n />\n ))}\n </div>\n );\n }\n\n return (\n <div {...shared} className={cn(\"flex items-center gap-2\", className)}>\n {steps.map((step, i) => (\n <div key={step} className=\"flex items-center gap-2\">\n <span\n aria-current={i === activeIndex ? \"step\" : undefined}\n className={cn(\n \"flex size-tile-sm items-center justify-center rounded-full text-xs font-medium\",\n i === activeIndex && \"bg-accent text-accent-fg\",\n i < activeIndex && \"bg-accent-soft text-accent\",\n i > activeIndex && \"bg-surface-hover text-text-muted\",\n )}\n >\n {i + 1}\n </span>\n {i < steps.length - 1 && <span className=\"h-px w-8 bg-border-strong\" />}\n </div>\n ))}\n </div>\n );\n}\n","/**\n * Keeping password managers out of fields that are not credentials.\n *\n * The problem was not that autofill was switched on — it was that most fields\n * asked for it by accident. A form field passed things like `\"sip-password\"`,\n * `\"api-key\"`, `\"record-pin\"` or `\"system-prompt\"` as its autocomplete value.\n * None of those are autocomplete tokens, and the HTML spec says an unrecognised\n * token behaves as `on`. So the fields most likely to be mistaken for a login\n * were the ones most loudly inviting Bitwarden, LastPass and Dashlane to fill\n * them in — and to offer to save them.\n *\n * `autocomplete=\"off\"` alone does not settle it: the major managers ignore it on\n * purpose, because sites used to abuse it. Each honours its own opt-out\n * attribute instead, so a field that means it has to say so in four dialects.\n */\n\n/**\n * The autocomplete tokens we actually want honoured. Anything outside this set\n * is treated as \"someone wrote a note to themselves in this field\", not as a\n * browser instruction.\n */\nconst REAL_TOKENS = new Set([\n \"name\",\n \"given-name\",\n \"family-name\",\n \"additional-name\",\n \"nickname\",\n \"honorific-prefix\",\n \"honorific-suffix\",\n \"email\",\n \"username\",\n \"organization\",\n \"organization-title\",\n \"tel\",\n \"tel-country-code\",\n \"tel-national\",\n \"tel-extension\",\n \"url\",\n \"photo\",\n \"language\",\n \"bday\",\n \"sex\",\n \"street-address\",\n \"address-line1\",\n \"address-line2\",\n \"address-line3\",\n \"address-level1\",\n \"address-level2\",\n \"country\",\n \"country-name\",\n \"postal-code\",\n \"current-password\",\n \"new-password\",\n \"one-time-code\",\n]);\n\nexport interface AutofillProps {\n autoComplete: string;\n \"data-1p-ignore\"?: string;\n \"data-lpignore\"?: string;\n \"data-bwignore\"?: string;\n \"data-form-type\"?: string;\n}\n\n/**\n * Props that switch a field's autofill behaviour on or off deliberately.\n *\n * Pass the field's declared autocomplete value; a genuine token is honoured, and\n * anything else — including nothing at all — turns autofill off and tells each\n * manager so in the attribute it listens to.\n */\nexport function autofillProps(token?: string): AutofillProps {\n if (token && REAL_TOKENS.has(token)) return { autoComplete: token };\n\n return {\n autoComplete: \"off\",\n // 1Password\n \"data-1p-ignore\": \"true\",\n // LastPass\n \"data-lpignore\": \"true\",\n // Bitwarden\n \"data-bwignore\": \"true\",\n // Dashlane — \"other\" means \"not a login field\".\n \"data-form-type\": \"other\",\n };\n}\n","/**\n * Coerce an unknown value to text.\n *\n * Every input component in this kit declares its `value` as `string`, and every\n * one of them is fed straight out of form data, which is `any`. A field whose\n * entity type is numeric therefore arrives as a number — `eylo-voip`'s time\n * rules do exactly that (`interval?: number`, `days?: number[]`) — and the two\n * failure modes are both silent from the type system's side:\n *\n * - a hard `TypeError: (…).trim is not a function` the moment anything calls a\n * string method on it;\n * - a comparison that simply never matches, because `1 === \"1\"` is false, so a\n * stored option renders as unselected with no error at all.\n *\n * `null` and `undefined` become `\"\"` so callers can treat \"absent\" and \"empty\"\n * alike, which is what a text input does anyway.\n */\nexport const text = (value: unknown): string => (value == null ? \"\" : String(value));\n\n/** {@link text} folded to a comparable key: trimmed and lower-cased. */\nexport const normalizeText = (value: unknown): string => text(value).trim().toLowerCase();\n","import { ChevronDownIcon } from \"lucide-react\";\nimport React, { forwardRef } from \"react\";\nimport { cn } from \"../utils/cn\";\nimport { normalizeText, text } from \"../utils/text\";\nimport { useAnchoredPosition } from \"../utils/useAnchoredPosition\";\n\nexport interface SelectOption {\n value: string;\n label: string;\n disabled?: boolean;\n}\n\nexport interface SelectProps extends Omit<React.HTMLAttributes<HTMLDivElement>, \"onChange\"> {\n value?: string | string[];\n onChange?: (value: string | string[]) => void;\n label?: string;\n helperText?: string;\n error?: string;\n size?: \"sm\" | \"md\" | \"lg\" | \"full\";\n fullWidth?: boolean;\n /**\n * Blocks opening the menu. The trigger is a button inside the container, so\n * this has to be forwarded explicitly — spreading it with the rest of the\n * props lands it on the wrapping div, where it does nothing.\n */\n disabled?: boolean;\n options: SelectOption[];\n placeholder?: string;\n containerClassName?: string;\n labelClassName?: string;\n searchable?: boolean;\n multiple?: boolean;\n\n /** Allow free-text entry (Enter, or clicking the create row). */\n allowCreate?: boolean;\n\n /** Optionally shape the option created from free-text input. */\n onCreateOption?: (label: string) => SelectOption;\n}\n\nconst selectSizes = {\n sm: \"h-control-sm px-3 text-sm\",\n md: \"h-control-md px-3 text-sm\",\n lg: \"h-control-lg px-3 text-base\",\n full: \"h-control-md w-full px-3 text-sm\",\n};\n\n/**\n * The props below say `string`, but this component is fed straight out of form\n * data, which is `any`. See {@link text} for why that has to be coerced here.\n */\nconst norm = normalizeText;\n\nexport const Select = forwardRef<HTMLDivElement, SelectProps>(\n (\n {\n label,\n helperText,\n error,\n size = \"md\",\n fullWidth = false,\n disabled = false,\n options,\n placeholder,\n containerClassName,\n labelClassName,\n className,\n id,\n searchable,\n multiple,\n value,\n onChange,\n allowCreate,\n onCreateOption,\n ...props\n },\n ref,\n ) => {\n const selectId = id || `select-${Math.random().toString(36).substr(2, 9)}`;\n const hasError = Boolean(error);\n const [isOpen, setIsOpen] = React.useState(false);\n const [searchTerm, setSearchTerm] = React.useState(\"\");\n const containerRef = React.useRef<HTMLDivElement>(null);\n // Anchored to the control itself, not the container — the container also\n // holds the label, which would push the panel down by its height.\n const triggerRef = React.useRef<HTMLButtonElement>(null);\n // Fixed, not absolute: an absolute panel is clipped by the form panel's\n // overflow and runs off-screen when the field sits near the bottom.\n const panelStyle = useAnchoredPosition(isOpen, triggerRef, { matchWidth: true });\n\n // Local copy of the options so free-text values can be mixed in.\n const [localOptions, setLocalOptions] = React.useState<SelectOption[]>(options);\n\n React.useImperativeHandle(ref, () => containerRef.current!);\n\n React.useEffect(() => {\n const handleClickOutside = (event: MouseEvent) => {\n if (containerRef.current && !containerRef.current.contains(event.target as Node)) {\n setIsOpen(false);\n }\n };\n document.addEventListener(\"mousedown\", handleClickOutside);\n return () => document.removeEventListener(\"mousedown\", handleClickOutside);\n }, []);\n\n // Matches on either value or label.\n const includesOption = React.useCallback(\n (opts: SelectOption[], needle: string) =>\n opts.some((o) => norm(o.value) === norm(needle) || norm(o.label) === norm(needle)),\n [],\n );\n\n const ensureValuesInOptions = React.useCallback(\n (baseOptions: SelectOption[], currentValue?: unknown) => {\n const out = [...baseOptions];\n\n const addIfMissing = (v?: unknown) => {\n const val = text(v).trim();\n if (!val) return;\n if (!includesOption(out, val)) {\n out.push({ value: val, label: val });\n }\n };\n\n if (Array.isArray(currentValue)) {\n currentValue.forEach(addIfMissing);\n } else {\n addIfMissing(currentValue);\n }\n\n return out;\n },\n [includesOption],\n );\n\n // On an options change: merge, keeping locally created options and the current value(s).\n React.useEffect(() => {\n setLocalOptions((prev) => {\n const next = [...options];\n\n prev.forEach((opt) => {\n if (!includesOption(next, opt.value) && !includesOption(next, opt.label)) {\n next.push(opt);\n }\n });\n\n return ensureValuesInOptions(next, value);\n });\n }, [options, value, ensureValuesInOptions, includesOption]);\n\n /**\n * The incoming value as text, so every comparison below is string-to-string.\n * Option values are strings by contract; the value comes from form data and\n * may not be — see {@link text}.\n */\n const selected = React.useMemo(\n () => (Array.isArray(value) ? value.map(text) : value == null ? undefined : text(value)),\n [value],\n );\n\n const handleSelect = (optionValue: string) => {\n if (multiple) {\n const currentValues = Array.isArray(selected) ? selected : [];\n const newValues = currentValues.includes(optionValue)\n ? currentValues.filter((v) => v !== optionValue)\n : [...currentValues, optionValue];\n onChange?.(newValues);\n } else {\n onChange?.(optionValue);\n setIsOpen(false);\n setSearchTerm(\"\");\n }\n };\n\n const selectedOption = multiple ? null : localOptions.find((o) => o.value === selected);\n\n const selectedOptions = multiple\n ? localOptions.filter((o) => Array.isArray(selected) && selected.includes(o.value))\n : [];\n\n const filteredOptions =\n searchable && searchTerm\n ? localOptions.filter((option) =>\n option.label.toLowerCase().includes(searchTerm.toLowerCase()),\n )\n : localOptions;\n\n const canCreate =\n Boolean(allowCreate) &&\n Boolean(searchable) &&\n Boolean(searchTerm.trim()) &&\n !includesOption(localOptions, searchTerm);\n\n const createOption = (labelToCreate: string) => {\n const clean = labelToCreate.trim();\n if (!clean) return;\n\n const newOption: SelectOption = onCreateOption?.(clean) ?? {\n value: clean,\n label: clean,\n };\n\n if (\n includesOption(localOptions, newOption.value) ||\n includesOption(localOptions, newOption.label)\n ) {\n // Already present; just select it.\n handleSelect(newOption.value);\n setSearchTerm(\"\");\n if (!multiple) setIsOpen(false);\n return;\n }\n\n setLocalOptions((prev) => [...prev, newOption]);\n handleSelect(newOption.value);\n\n setSearchTerm(\"\");\n if (!multiple) setIsOpen(false);\n };\n\n const getDisplayValue = () => {\n if (multiple) {\n if (selectedOptions.length > 0) {\n return selectedOptions.map((o) => o.label).join(\", \");\n }\n return placeholder || \"Select options\";\n }\n return selectedOption?.label || placeholder || \"Select an option\";\n };\n\n return (\n <div\n ref={containerRef}\n className={cn(\"relative flex flex-col\", fullWidth && \"w-full\", containerClassName)}\n {...props}\n >\n {label && (\n <label\n htmlFor={selectId}\n onClick={() => setIsOpen(!isOpen)}\n className={cn(\n \"block text-xs font-semibold uppercase tracking-label mb-1.5\",\n hasError ? \"text-danger-fg\" : \"text-text-muted\",\n labelClassName,\n )}\n >\n {label}\n </label>\n )}\n\n <div className=\"relative\">\n <button\n ref={triggerRef}\n type=\"button\"\n id={selectId}\n disabled={disabled}\n onClick={() => setIsOpen(!isOpen)}\n className={cn(\n \"flex w-full items-center justify-between gap-2 rounded-tile border transition-colors duration-fast text-left\",\n \"focus-visible:outline-none focus-visible:focus-ring\",\n \"disabled:cursor-not-allowed disabled:bg-surface-hover disabled:text-text-disabled\",\n \"bg-surface\",\n selectSizes[size],\n hasError\n ? \"border-danger-border text-danger-fg focus-visible:focus-ring\"\n : \"border-border-strong text-text\",\n className,\n )}\n >\n <span className=\"truncate flex-1 min-w-0\">{getDisplayValue()}</span>\n <ChevronDownIcon\n size={16}\n className={cn(\n \"shrink-0 opacity-60 transition-transform duration-fast\",\n isOpen && \"rotate-180\",\n )}\n />\n </button>\n\n {isOpen && (\n <div\n style={panelStyle}\n className=\"fixed z-overlay flex flex-col overflow-hidden rounded-lg border border-border bg-surface shadow-overlay\"\n >\n {searchable && (\n <div className=\"p-2\">\n <input\n type=\"text\"\n placeholder=\"Search...\"\n value={searchTerm}\n onChange={(e) => setSearchTerm(e.target.value)}\n onKeyDown={(e) => {\n if (e.key === \"Enter\" && canCreate) {\n e.preventDefault();\n createOption(searchTerm);\n }\n }}\n className={cn(\n \"w-full px-3 py-2 text-sm rounded-md border\",\n \"text-text border-border focus-visible:outline-none focus-visible:focus-ring\",\n )}\n />\n </div>\n )}\n\n {canCreate && (\n <div\n className={cn(\n \"cursor-pointer px-3 py-1.5 text-sm\",\n \"text-info-fg hover:bg-surface-hover\",\n )}\n onClick={() => createOption(searchTerm)}\n >\n +: “{searchTerm.trim()}”\n </div>\n )}\n\n <ul className=\"min-h-0 flex-1 overflow-auto py-1\">\n {placeholder && !multiple && (\n <li\n className=\"cursor-pointer px-3 py-1.5 text-sm text-text-muted hover:bg-surface-hover\"\n onClick={() => {\n onChange?.(\"\");\n setIsOpen(false);\n }}\n >\n {placeholder}\n </li>\n )}\n {filteredOptions.map((option) => {\n const isSelected = multiple\n ? Array.isArray(selected) && selected.includes(option.value)\n : selected === option.value;\n return (\n <li\n key={option.value}\n onClick={() => !option.disabled && handleSelect(option.value)}\n className={cn(\n \"cursor-pointer px-3 py-1.5 text-sm\",\n \"text-text\",\n option.disabled\n ? \"opacity-50 cursor-not-allowed\"\n : \"hover:bg-surface-hover\",\n isSelected && \"bg-accent-soft text-accent font-semibold\",\n )}\n >\n <div className=\"flex items-center\">\n {multiple && (\n <input\n type=\"checkbox\"\n checked={isSelected}\n readOnly\n className=\"mr-3 h-4 w-4 rounded border-border [&:not(:checked)]:bg-surface-input text-accent \"\n />\n )}\n <span>{option.label}</span>\n </div>\n </li>\n );\n })}\n </ul>\n </div>\n )}\n </div>\n\n {(error || helperText) && (\n <p className={cn(\"mt-1 text-xs\", hasError ? \"text-danger-fg\" : \"text-text-muted\")}>\n {error || helperText}\n </p>\n )}\n </div>\n );\n },\n);\n\nSelect.displayName = \"Select\";\n","import React, { forwardRef } from \"react\";\nimport { cn } from \"../utils/cn\";\n\nexport interface TextFieldProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, \"size\"> {\n label?: string;\n\n helperText?: string;\n\n error?: string;\n\n size?: \"sm\" | \"md\" | \"lg\" | \"full\";\n\n fullWidth?: boolean;\n\n startIcon?: React.ReactNode;\n\n endIcon?: React.ReactNode;\n\n loading?: boolean;\n\n /** Additional class name for the container */\n containerClassName?: string;\n\n /** Additional class name for the label */\n labelClassName?: string;\n}\n\n/** Heights come from the control tokens — inputs, buttons and selects align. */\nconst inputSizes = {\n sm: \"h-control-sm px-3 text-sm\",\n md: \"h-control-md px-3 text-base\",\n lg: \"h-control-lg px-3 text-base\",\n full: \"h-control-md px-3 text-base\",\n};\n\n/**\n * Shared with TextArea / Select / DatePicker.\n *\n * Sentence case, not the uppercase small-caps it used to be: a form is a page\n * now, and every field label shouting in caps competes with the section titles\n * beside them. Uppercase is still the section-label treatment (`tracking-label`),\n * one level up.\n */\nexport const fieldLabelClasses = \"mb-1.5 block text-sm font-medium\";\n\n/**\n * Shared field frame. A resting 1px edge in `border-strong` — a form field has\n * to read as an empty box you can type in, which the lighter `border` used for\n * structural rules does not do at this size.\n */\nexport const fieldFrameClasses =\n \"block w-full rounded-tile border bg-surface-input text-text \" +\n \"transition-colors duration-fast ease-out \" +\n \"placeholder:text-text-subtle \" +\n \"focus-visible:outline-none focus-visible:focus-ring \" +\n \"disabled:cursor-not-allowed disabled:bg-surface-hover disabled:text-text-disabled\";\n\n/** Resting / error edge, shared so every control agrees on what \"invalid\" looks like. */\nexport const fieldEdgeClasses = (hasError: boolean) =>\n hasError ? \"border-danger-border bg-danger-soft\" : \"border-border-strong\";\n\n/** Themed text input; shares its frame and label classes with TextArea, Select and DatePicker. */\nexport const TextField = forwardRef<HTMLInputElement, TextFieldProps>(\n (\n {\n label,\n helperText,\n error,\n size = \"md\",\n fullWidth = false,\n startIcon,\n endIcon,\n loading = false,\n containerClassName,\n labelClassName,\n className,\n id,\n ...props\n },\n ref,\n ) => {\n const reactId = React.useId();\n const inputId = id || `textfield-${reactId}`;\n const hasError = Boolean(error);\n const describedBy = error || helperText ? `${inputId}-description` : undefined;\n\n return (\n <div className={cn(\"flex flex-col\", fullWidth && \"w-full\", containerClassName)}>\n {label && (\n <label\n htmlFor={inputId}\n className={cn(\n fieldLabelClasses,\n hasError ? \"text-danger-fg\" : \"text-text-muted\",\n labelClassName,\n )}\n >\n {label}\n </label>\n )}\n\n <div className=\"relative\">\n {startIcon && (\n <span className=\"pointer-events-none absolute inset-y-0 left-0 flex items-center pl-3 text-text-muted\">\n {startIcon}\n </span>\n )}\n\n <input\n ref={ref}\n id={inputId}\n aria-invalid={hasError || undefined}\n aria-describedby={describedBy}\n className={cn(\n fieldFrameClasses,\n\n inputSizes[size],\n\n startIcon && \"pl-9\",\n (endIcon || loading) && \"pr-9\",\n\n fieldEdgeClasses(hasError),\n\n className,\n )}\n {...props}\n />\n\n {(endIcon || loading) && (\n <span className=\"absolute inset-y-0 right-0 flex items-center pr-3 text-text-muted\">\n {loading ? (\n <span\n aria-hidden\n className=\"size-icon-md animate-spin rounded-full border-2 border-border border-t-transparent\"\n />\n ) : (\n endIcon\n )}\n </span>\n )}\n </div>\n\n {(error || helperText) && (\n <p\n id={describedBy}\n className={cn(\"mt-1 text-xs\", hasError ? \"text-danger-fg\" : \"text-text-subtle\")}\n >\n {error || helperText}\n </p>\n )}\n </div>\n );\n },\n);\n\nTextField.displayName = \"TextField\";\n","import type { VirtualMount } from \"@opencxh/domain\";\nimport { useEffect, useState } from \"react\";\nimport { Select } from \"./Select\";\nimport { TextField } from \"./TextField\";\n\nexport interface FolderDestination {\n /** Storage folder (mount) id. */\n folderId?: string;\n /** Optional sub-path template, e.g. \"{yyyy}/{mm}/{dd}\". */\n pathTemplate?: string;\n}\n\nexport interface FolderSelectProps {\n value?: FolderDestination;\n onChange?: (value: FolderDestination) => void;\n /** Loads the selectable storage folders (the shell wires this to storage.mount). */\n onListFolders?: () => Promise<{ data: VirtualMount[] }> | undefined;\n disabled?: boolean;\n size?: \"sm\" | \"md\" | \"lg\" | \"full\";\n placeholder?: string;\n templatePlaceholder?: string;\n /** Show the sub-path template input (default true). */\n showTemplate?: boolean;\n}\n\n/**\n * Destination picker for a storage folder (+ optional sub-path template).\n * Unlike StorageInput (which picks/uploads a file), this selects *where* things\n * should be written. Used by the `type: \"folder\"` form field.\n */\nexport function FolderSelect({\n value,\n onChange,\n onListFolders,\n disabled,\n size = \"md\",\n placeholder,\n templatePlaceholder,\n showTemplate = true,\n}: FolderSelectProps) {\n const [folders, setFolders] = useState<VirtualMount[]>([]);\n\n useEffect(() => {\n let active = true;\n Promise.resolve(onListFolders?.())\n .then((res) => {\n if (active && res) setFolders(res.data ?? []);\n })\n .catch(() => {\n /* folder list unavailable */\n });\n return () => {\n active = false;\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n const v = value ?? {};\n\n return (\n <div className=\"flex flex-col gap-2\">\n <Select\n size={size}\n fullWidth\n value={v.folderId ?? \"\"}\n options={folders.map((f) => ({ value: f.id, label: f.name }))}\n placeholder={placeholder ?? \"Select a folder\"}\n onChange={(val) => onChange?.({ ...v, folderId: val as string })}\n />\n {showTemplate && (\n <TextField\n size={size}\n disabled={disabled ?? false}\n value={v.pathTemplate ?? \"\"}\n placeholder={templatePlaceholder ?? \"Sub-path e.g. {yyyy}/{mm}/{dd} (optional)\"}\n onChange={(e) => onChange?.({ ...v, pathTemplate: e.target.value })}\n />\n )}\n </div>\n );\n}\n","import type React from \"react\";\nimport { createContext, useContext, useState } from \"react\";\nimport { cn } from \"../utils/cn\";\n\nexport interface TabItem {\n /** Tab identifier */\n id: string;\n /** Tab label */\n label: string;\n /** Tab content */\n content?: React.ReactNode;\n /** Whether tab is disabled */\n disabled?: boolean;\n /** Badge/count to show next to label */\n badge?: string | number;\n}\n\nexport interface TabsProps {\n /** Tab items */\n items: TabItem[];\n /** Default active tab */\n defaultTab?: string;\n /** Active tab (controlled) */\n activeTab?: string;\n /** Tab change handler */\n onTabChange?: (tabId: string) => void;\n /** Tabs variant */\n variant?: \"default\" | \"pills\" | \"underline\";\n /** Tabs size */\n size?: \"sm\" | \"md\" | \"lg\";\n /** Additional CSS classes */\n className?: string;\n}\n\nexport interface TabBarProps {\n /** Tab items (simplified for bar style) */\n items: Array<{\n id: string;\n label: string;\n badge?: string | number;\n disabled?: boolean;\n }>;\n /** Active tab */\n activeTab?: string;\n /** Tab change handler */\n onTabChange?: (tabId: string) => void;\n /** Additional CSS classes */\n className?: string;\n}\n\nconst TabsContext = createContext<{\n activeTab: string;\n setActiveTab: (tab: string) => void;\n} | null>(null);\n\n/**\n * TabBar component for simple tab navigation (like in the screenshot)\n */\nexport const TabBar: React.FC<TabBarProps> = ({ items, activeTab, onTabChange, className }) => {\n const [internalActiveTab, setInternalActiveTab] = useState(items[0]?.id || \"\");\n const currentActiveTab = activeTab || internalActiveTab;\n\n const handleTabClick = (tabId: string) => {\n if (onTabChange) {\n onTabChange(tabId);\n } else {\n setInternalActiveTab(tabId);\n }\n };\n\n return (\n <div className={cn(\"border-b border-border\", className)}>\n <nav className=\"-mb-px flex space-x-8\">\n {items.map((item) => (\n <button\n key={item.id}\n onClick={() => !item.disabled && handleTabClick(item.id)}\n disabled={item.disabled}\n className={cn(\n \"border-b-2 py-2 px-1 text-sm font-medium transition-colors duration-fast\",\n {\n \"border-accent-border text-text\": currentActiveTab === item.id,\n \"border-transparent text-text-muted hover:text-text\":\n currentActiveTab !== item.id && !item.disabled,\n \"border-transparent text-text-disabled cursor-not-allowed\": item.disabled,\n },\n )}\n >\n <span className=\"flex items-center gap-2\">\n {item.label}\n {item.badge && (\n <span\n className={cn(\n \"inline-flex items-center px-2 py-0.5 rounded-full text-xs font-medium\",\n currentActiveTab === item.id\n ? \"bg-accent-soft text-accent\"\n : \"bg-surface-hover text-text-muted\",\n )}\n >\n {item.badge}\n </span>\n )}\n </span>\n </button>\n ))}\n </nav>\n </div>\n );\n};\n\n/**\n * Full Tabs component with content panels\n */\nexport const Tabs: React.FC<TabsProps> = ({\n items,\n defaultTab,\n activeTab,\n onTabChange,\n variant = \"default\",\n size = \"md\",\n className,\n}) => {\n const [internalActiveTab, setInternalActiveTab] = useState(\n defaultTab || activeTab || items[0]?.id || \"\",\n );\n\n const currentActiveTab = activeTab || internalActiveTab;\n\n const handleTabChange = (tabId: string) => {\n if (onTabChange) {\n onTabChange(tabId);\n } else {\n setInternalActiveTab(tabId);\n }\n };\n\n const contextValue = {\n activeTab: currentActiveTab,\n setActiveTab: handleTabChange,\n };\n\n const activeTabItem = items.find((item) => item.id === currentActiveTab);\n\n const tabListClasses = cn(\n \"flex\",\n {\n \"border-b border-border px-2\": variant === \"default\" || variant === \"underline\",\n \"bg-surface-hover p-1 rounded-lg\": variant === \"pills\",\n \"space-x-1\": variant === \"pills\",\n \"space-x-8\": variant === \"default\" || variant === \"underline\",\n },\n className,\n );\n\n const tabClasses = (item: TabItem, isActive: boolean) => {\n const baseClasses = \"transition-colors duration-fast font-medium\";\n\n const sizeClasses = {\n \"text-xs px-2 py-1\": size === \"sm\",\n \"text-sm px-3 py-2\": size === \"md\",\n \"text-base px-4 py-3\": size === \"lg\",\n };\n\n const variantClasses = {\n // Default variant\n \"border-b-2 -mb-px\": variant === \"default\",\n // Pills variant\n \"rounded-md\": variant === \"pills\",\n // Underline variant\n \"border-b-2 pb-2\": variant === \"underline\",\n };\n\n const stateClasses = {\n \"opacity-50 cursor-not-allowed\": item.disabled,\n \"cursor-pointer\": !item.disabled,\n };\n\n // Active state classes\n const activeClasses = isActive\n ? {\n \"border-accent-border text-text\": variant === \"default\" || variant === \"underline\",\n \"bg-surface text-text shadow-none\": variant === \"pills\",\n }\n : {};\n\n // Non-active, non-disabled state classes\n const inactiveClasses =\n !isActive && !item.disabled\n ? {\n \"border-transparent text-text-muted hover:text-text\": variant === \"default\",\n \"text-text-muted hover:text-text\": variant === \"pills\",\n \"border-transparent text-text-muted hover:text-text hover:border-border\":\n variant === \"underline\",\n }\n : {};\n\n return cn(\n baseClasses,\n sizeClasses,\n variantClasses,\n stateClasses,\n activeClasses,\n inactiveClasses,\n );\n };\n\n return (\n <TabsContext.Provider value={contextValue}>\n <div>\n {/* Tab List */}\n <div className={tabListClasses} role=\"tablist\">\n {items.map((item) => (\n <button\n key={item.id}\n type=\"button\"\n role=\"tab\"\n aria-selected={currentActiveTab === item.id}\n aria-controls={`tabpanel-${item.id}`}\n disabled={item.disabled}\n onClick={() => !item.disabled && handleTabChange(item.id)}\n className={tabClasses(item, currentActiveTab === item.id)}\n >\n <span className=\"flex items-center gap-2\">\n {item.label}\n {item.badge && (\n <span className=\"inline-flex items-center px-2 py-0.5 rounded-full text-xs font-medium bg-surface-hover text-text-muted\">\n {item.badge}\n </span>\n )}\n </span>\n </button>\n ))}\n </div>\n\n {/* Tab Content */}\n {activeTabItem?.content && (\n <div\n role=\"tabpanel\"\n id={`tabpanel-${currentActiveTab}`}\n aria-labelledby={`tab-${currentActiveTab}`}\n className=\"mt-4\"\n >\n {activeTabItem.content}\n </div>\n )}\n </div>\n </TabsContext.Provider>\n );\n};\n\n/**\n * Hook to access tab context\n */\nexport const useTabsContext = () => {\n const context = useContext(TabsContext);\n if (!context) {\n throw new Error(\"useTabsContext must be used within a Tabs component\");\n }\n return context;\n};\n","import { X } from \"lucide-react\";\nimport type React from \"react\";\nimport { useEffect, useRef } from \"react\";\nimport { Icon } from \"../content/Icon\";\nimport { cn } from \"../utils/cn\";\n\nexport interface ModalProps {\n open: boolean;\n\n onClose: () => void;\n\n title?: string;\n\n size?: \"sm\" | \"md\" | \"lg\" | \"xl\" | \"full\";\n\n /** Close when the backdrop is clicked. */\n closeOnBackdropClick?: boolean;\n\n /** Close on Escape. */\n closeOnEscape?: boolean;\n\n className?: string;\n\n backdropClassName?: string;\n\n children: React.ReactNode;\n\n footer?: React.ReactNode;\n\n showCloseButton?: boolean;\n}\n\nconst modalSizes = {\n sm: \"max-w-md\",\n md: \"max-w-lg\",\n lg: \"max-w-2xl\",\n xl: \"max-w-4xl\",\n full: \"max-w-full mx-4\",\n};\n\n/** Dialog overlay with focus management, Escape/backdrop close, and body-scroll lock. */\nexport function Modal({\n open,\n onClose,\n title,\n size = \"md\",\n closeOnBackdropClick = true,\n closeOnEscape = true,\n className,\n backdropClassName,\n children,\n footer,\n showCloseButton = true,\n}: ModalProps) {\n const modalRef = useRef<HTMLDivElement>(null);\n const previousActiveElement = useRef<HTMLElement | null>(null);\n\n useEffect(() => {\n if (!open || !closeOnEscape) return;\n\n const handleEscape = (event: KeyboardEvent) => {\n if (event.key === \"Escape\") {\n onClose();\n }\n };\n\n document.addEventListener(\"keydown\", handleEscape);\n return () => document.removeEventListener(\"keydown\", handleEscape);\n }, [open, closeOnEscape, onClose]);\n\n useEffect(() => {\n if (open) {\n previousActiveElement.current = document.activeElement as HTMLElement;\n\n if (modalRef.current) {\n modalRef.current.focus();\n }\n\n document.body.style.overflow = \"hidden\";\n } else {\n if (previousActiveElement.current) {\n previousActiveElement.current.focus();\n }\n\n document.body.style.overflow = \"\";\n }\n\n return () => {\n document.body.style.overflow = \"\";\n };\n }, [open]);\n\n const handleBackdropClick = (event: React.MouseEvent) => {\n if (closeOnBackdropClick && event.target === event.currentTarget) {\n onClose();\n }\n };\n\n if (!open) return null;\n\n return (\n <div\n className={cn(\n \"fixed inset-0 z-modal flex items-center justify-center p-4\",\n \"bg-scrim\",\n backdropClassName,\n )}\n onClick={handleBackdropClick}\n >\n <div\n ref={modalRef}\n className={cn(\n \"relative w-full bg-surface rounded-lg shadow-modal\",\n \"focus:outline-none\",\n modalSizes[size],\n className,\n )}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby={title ? \"modal-title\" : undefined}\n tabIndex={-1}\n >\n {(title || showCloseButton) && (\n <div className=\"flex items-center justify-between p-6 pb-0\">\n {title && (\n <span id=\"modal-title\" className=\"text-lg font-semibold text-text\">\n {title}\n </span>\n )}\n\n {showCloseButton && (\n <button\n onClick={onClose}\n className=\"p-1 text-text-muted hover:text-text transition-colors\"\n aria-label=\"Close modal\"\n >\n <Icon icon={X} size=\"lg\" color=\"current\" />\n </button>\n )}\n </div>\n )}\n\n <div className=\"p-6\">{children}</div>\n\n {footer && <div className=\"px-6 py-4\">{footer}</div>}\n </div>\n </div>\n );\n}\n","import type { FilePointer, VirtualMount } from \"@opencxh/domain\";\nimport {\n ArrowLeft,\n FileText,\n Folder,\n FolderPlus,\n HardDrive,\n Search,\n Upload,\n X,\n} from \"lucide-react\";\nimport type React from \"react\";\nimport { useEffect, useMemo, useRef, useState } from \"react\";\nimport { Button } from \"../action/Button\";\nimport { Icon } from \"../content/Icon\";\nimport { Table, type TableColumn } from \"../content/Table\";\nimport { Tabs } from \"../navigation/Tabs\";\nimport { Modal } from \"../overlays/Modal\";\nimport { Text } from \"../typography/Text\";\nimport { cn } from \"../utils/cn\";\nimport { TextField } from \"./TextField\";\n\nexport interface StorageInputProps {\n value?: string | FilePointer;\n onChange: (value: FilePointer | null, content?: Uint8Array) => void;\n label?: string;\n placeholder?: string;\n error?: string;\n disabled?: boolean;\n accept?: string;\n onListFiles?: (filters?: {\n name?: string;\n mimeType?: string;\n mountId?: string;\n path?: string;\n }) => Promise<{ data: FilePointer[] }> | undefined;\n onListMounts?: () => Promise<{ data: VirtualMount[] }> | undefined;\n onUploadFile?: (payload: {\n file: Uint8Array;\n filename: string;\n mimeType: string;\n mountId?: string;\n path?: string;\n }) => Promise<{ data: FilePointer }> | undefined;\n onDownloadFile?: (fileId: string) => Promise<Uint8Array> | undefined;\n onRegisterFile?: (payload: Omit<FilePointer, \"id\">) => Promise<{ data: FilePointer }> | undefined;\n}\n\nexport const StorageInput: React.FC<StorageInputProps> = ({\n value,\n onChange,\n label,\n placeholder = \"Select or upload a file\",\n error,\n disabled,\n accept,\n onListFiles,\n onListMounts,\n onUploadFile,\n onDownloadFile,\n onRegisterFile,\n}) => {\n const [isModalOpen, setIsModalOpen] = useState(false);\n const [mounts, setMounts] = useState<VirtualMount[]>([]);\n const [selectedMount, setSelectedMount] = useState<VirtualMount | null>(null);\n const [currentPath, setCurrentPath] = useState<string>(\"/\");\n const [remoteFiles, setRemoteFiles] = useState<FilePointer[]>([]);\n const [loading, setLoading] = useState(false);\n const [searchTerm, setSearchTerm] = useState(\"\");\n const [selectedPointer, setSelectedPointer] = useState<FilePointer | null>(\n typeof value === \"object\" ? value : null,\n );\n const [isCreatingFolder, setIsCreatingFolder] = useState(false);\n const [newFolderName, setNewFolderName] = useState(\"\");\n\n const fileInputRef = useRef<HTMLInputElement>(null);\n\n // Load mounts when modal opens\n useEffect(() => {\n if (isModalOpen) {\n loadMounts();\n }\n }, [isModalOpen]);\n\n // Load files when a mount is selected or search term changes\n useEffect(() => {\n if (isModalOpen && selectedMount) {\n loadFiles();\n }\n }, [isModalOpen, selectedMount, currentPath, searchTerm]);\n\n const loadMounts = async () => {\n setLoading(true);\n try {\n const resp = await onListMounts?.();\n if (resp?.data) {\n setMounts(resp.data);\n }\n } catch (err) {\n console.error(\"Failed to load mounts\", err);\n } finally {\n setLoading(false);\n }\n };\n\n const loadFiles = async () => {\n if (!selectedMount) return;\n setLoading(true);\n try {\n const resp = await onListFiles?.({\n mountId: selectedMount.id,\n name: searchTerm,\n path: currentPath,\n });\n if (resp?.data) {\n setRemoteFiles(resp.data);\n }\n } catch (err) {\n console.error(\"Failed to load files\", err);\n } finally {\n setLoading(false);\n }\n };\n\n const handleLocalOnlySelection = async (e: React.ChangeEvent<HTMLInputElement>) => {\n const file = e.target.files?.[0];\n if (!file) return;\n\n setLoading(true);\n try {\n const buffer = await file.arrayBuffer();\n const content = new Uint8Array(buffer);\n\n const virtualPointer: FilePointer = {\n id: `local-${Date.now()}`,\n name: file.name,\n type: \"file\",\n path: \"/\",\n mimeType: file.type || \"application/octet-stream\",\n size: file.size,\n mountId: \"local\",\n providerKey: file.name,\n ownerId: \"me\",\n };\n\n setSelectedPointer(virtualPointer);\n onChange(virtualPointer, content);\n setIsModalOpen(false);\n } catch (err) {\n console.error(\"Local file selection failed\", err);\n } finally {\n setLoading(false);\n }\n };\n\n const handleUploadToStorage = async (e: React.ChangeEvent<HTMLInputElement>) => {\n const file = e.target.files?.[0];\n if (!file || !selectedMount) return;\n\n setLoading(true);\n try {\n const buffer = await file.arrayBuffer();\n const resp = await onUploadFile?.({\n file: new Uint8Array(buffer),\n filename: file.name,\n mimeType: file.type || \"application/octet-stream\",\n mountId: selectedMount.id,\n path: currentPath,\n });\n\n if (resp?.data) {\n const pointer = resp.data;\n setSelectedPointer(pointer);\n onChange(pointer, new Uint8Array(buffer));\n setIsModalOpen(false);\n }\n } catch (err) {\n console.error(\"Upload failed\", err);\n } finally {\n setLoading(false);\n }\n };\n\n const handleCreateFolder = async () => {\n if (!newFolderName || !selectedMount) return;\n\n setLoading(true);\n try {\n const fullPath = `${currentPath}${newFolderName}/`;\n\n if (onRegisterFile) {\n await onRegisterFile({\n name: newFolderName,\n type: \"folder\",\n path: currentPath,\n mimeType: \"application/x-directory\",\n size: 0,\n mountId: selectedMount.id,\n providerKey: fullPath,\n ownerId: \"me\",\n });\n await loadFiles();\n }\n\n setIsCreatingFolder(false);\n setNewFolderName(\"\");\n } catch (err) {\n console.error(\"Failed to create folder\", err);\n } finally {\n setLoading(false);\n }\n };\n\n const handleSelectRemote = async (pointer: FilePointer) => {\n setLoading(true);\n try {\n const content = await onDownloadFile?.(pointer.id);\n setSelectedPointer(pointer);\n onChange(pointer, content);\n setIsModalOpen(false);\n } catch (err) {\n console.error(\"Failed to download file\", err);\n } finally {\n setLoading(false);\n }\n };\n\n const handleClear = (e: React.MouseEvent) => {\n e.stopPropagation();\n setSelectedPointer(null);\n onChange(null);\n };\n\n // Folder & File calculation logic\n const explorerItems = useMemo(() => {\n const folders = new Set<string>();\n const files: FilePointer[] = [];\n\n remoteFiles.forEach((file) => {\n if (file.type === \"folder\") {\n if (file.path === currentPath) {\n folders.add(file.name);\n }\n return;\n }\n\n const path = file.providerKey || file.name;\n const relativePath = currentPath ? path.substring(currentPath.length) : path;\n const parts = relativePath.split(\"/\").filter((p) => p !== \"\");\n\n if (parts.length > 1) {\n folders.add(parts[0]);\n } else if (parts.length === 1) {\n files.push(file);\n }\n });\n\n return {\n folders: Array.from(folders).sort(),\n files: files.sort((a, b) => a.name.localeCompare(b.name)),\n };\n }, [remoteFiles, currentPath]);\n\n const handleFolderClick = (folderName: string) => {\n setCurrentPath((prev) => `${prev}${folderName}/`);\n };\n\n const handleGoBack = () => {\n const parts = currentPath.split(\"/\").filter((p) => p !== \"\");\n parts.pop();\n if (parts.length === 0) {\n setCurrentPath(\"/\");\n } else {\n setCurrentPath(`${parts.join(\"/\")}/`);\n }\n };\n\n const tableData = useMemo(() => {\n const data: any[] = explorerItems.folders.map((f) => ({\n id: `folder-${f}`,\n name: f,\n type: \"folder\",\n }));\n\n explorerItems.files.forEach((f) => {\n data.push({\n ...f,\n type: \"file\",\n });\n });\n\n return data;\n }, [explorerItems]);\n\n const explorerColumns: TableColumn<any>[] = [\n {\n id: \"name\",\n header: \"Name\",\n accessor: \"name\",\n cell: (val, row) => (\n <div className=\"flex items-center gap-2\">\n <Icon\n icon={row.type === \"folder\" ? Folder : FileText}\n size=\"sm\"\n color={row.type === \"folder\" ? \"warning\" : \"secondary\"}\n />\n <span className={cn(row.type === \"folder\" && \"font-medium\")}>{val}</span>\n </div>\n ),\n },\n {\n id: \"type\",\n header: \"Type\",\n accessor: (row) => (row.type === \"folder\" ? \"Folder\" : row.mimeType),\n },\n {\n id: \"size\",\n header: \"Size\",\n accessor: (row) => (row.type === \"file\" ? `${(row.size / 1024).toFixed(1)} KB` : \"-\"),\n },\n {\n id: \"actions\",\n header: \"\",\n accessor: \"id\",\n align: \"right\",\n cell: (_, row) => (\n <Button\n variant=\"outline\"\n onClick={(e) => {\n e.stopPropagation();\n row.type === \"folder\" ? handleFolderClick(row.name) : handleSelectRemote(row);\n }}\n loading={loading && row.type === \"file\" && selectedPointer?.id === row.id}\n >\n {row.type === \"folder\" ? \"Open\" : \"Select\"}\n </Button>\n ),\n },\n ];\n\n const mountColumns: TableColumn<VirtualMount>[] = [\n {\n id: \"name\",\n header: \"Mount Name\",\n accessor: \"name\",\n cell: (val) => (\n <div className=\"flex items-center gap-2 font-medium\">\n <Icon icon={HardDrive} size=\"sm\" color=\"primary\" />\n <span>{val}</span>\n </div>\n ),\n },\n {\n id: \"actions\",\n header: \"\",\n accessor: \"id\",\n align: \"right\",\n cell: (_, row) => (\n <Button variant=\"outline\" onClick={() => setSelectedMount(row)}>\n Open\n </Button>\n ),\n },\n ];\n\n return (\n <div className=\"space-y-1\">\n {label && (\n <Text variant=\"label\" size=\"sm\" weight=\"medium\">\n {label}\n </Text>\n )}\n\n <div\n onClick={() => !disabled && setIsModalOpen(true)}\n className={cn(\n \"flex items-center gap-3 px-3 py-2 border rounded-lg cursor-pointer transition-colors\",\n \"hover:border-border-strong bg-surface\",\n error ? \"border-danger-border\" : \"border-border\",\n disabled && \"opacity-50 cursor-not-allowed bg-surface-sunk\",\n )}\n >\n <div className=\"flex-shrink-0\">\n <Icon\n icon={selectedPointer ? FileText : HardDrive}\n color={selectedPointer ? \"primary\" : \"secondary\"}\n />\n </div>\n\n <div className=\"flex-grow truncate\">\n {selectedPointer ? (\n <span className=\"text-sm text-text font-medium\">{selectedPointer.name}</span>\n ) : (\n <span className=\"text-sm text-text-muted\">{placeholder}</span>\n )}\n </div>\n\n {selectedPointer && !disabled && (\n <button onClick={handleClear} className=\"p-1 hover:bg-surface-hover rounded\">\n <Icon icon={X} size=\"xs\" />\n </button>\n )}\n </div>\n\n {error && (\n <Text variant=\"body\" size=\"xs\" className=\"text-danger-fg\">\n {error}\n </Text>\n )}\n\n <Modal\n open={isModalOpen}\n onClose={() => {\n setIsModalOpen(false);\n setSelectedMount(null);\n setCurrentPath(\"/\");\n setIsCreatingFolder(false);\n }}\n title={selectedMount ? `Explorer: ${selectedMount.name}` : \"Select Storage Mount\"}\n size=\"lg\"\n >\n <Tabs\n variant=\"pills\"\n items={[\n {\n id: \"remote\",\n label: \"Platform Storage\",\n content: (\n <div className=\"space-y-4\">\n {selectedMount ? (\n <>\n <div className=\"flex flex-wrap items-center gap-2\">\n <Button\n variant=\"ghost\"\n onClick={() => {\n if (currentPath && currentPath !== \"/\") {\n handleGoBack();\n } else {\n setSelectedMount(null);\n }\n }}\n leftIcon={<Icon icon={ArrowLeft} size=\"xs\" />}\n >\n {currentPath ? \"Back\" : \"Back to Mounts\"}\n </Button>\n\n <div className=\"flex items-center gap-1 text-xs text-text-muted overflow-hidden bg-surface-hover px-2 py-1 rounded\">\n <span className=\"truncate\">{currentPath}</span>\n </div>\n\n <div className=\"flex-grow min-w-40\">\n <TextField\n placeholder=\"Search...\"\n value={searchTerm}\n onChange={(e) => setSearchTerm(e.target.value)}\n startIcon={<Search size={14} />}\n fullWidth\n />\n </div>\n\n <div className=\"flex items-center gap-1\">\n <Button\n variant=\"outline\"\n onClick={() => setIsCreatingFolder(true)}\n leftIcon={<Icon icon={FolderPlus} size=\"xs\" />}\n >\n New Folder\n </Button>\n <Button\n variant=\"primary\"\n onClick={() => fileInputRef.current?.click()}\n leftIcon={<Icon icon={Upload} size=\"xs\" />}\n loading={loading}\n >\n Upload\n </Button>\n <input\n type=\"file\"\n className=\"hidden\"\n ref={fileInputRef}\n onChange={handleUploadToStorage}\n accept={accept}\n />\n </div>\n </div>\n\n {isCreatingFolder && (\n <div className=\"flex items-center gap-2 p-3 bg-surface-sunk rounded-lg border border-border\">\n <Icon icon={Folder} size=\"sm\" color=\"warning\" />\n <TextField\n placeholder=\"Folder name\"\n value={newFolderName}\n onChange={(e) => setNewFolderName(e.target.value)}\n autoFocus\n />\n <Button onClick={handleCreateFolder} loading={loading}>\n Create\n </Button>\n <Button variant=\"ghost\" onClick={() => setIsCreatingFolder(false)}>\n Cancel\n </Button>\n </div>\n )}\n\n <div className=\"max-h-100 overflow-auto rounded-lg border border-border\">\n <Table\n data={tableData}\n columns={explorerColumns}\n loading={loading}\n emptyContent=\"This folder is empty\"\n onRowClick={(row) =>\n row.type === \"folder\"\n ? handleFolderClick(row.name)\n : handleSelectRemote(row)\n }\n />\n </div>\n </>\n ) : (\n <div className=\"max-h-100 overflow-auto rounded-lg border border-border\">\n <Table\n data={mounts}\n columns={mountColumns}\n loading={loading}\n emptyContent=\"No storage mounts configured\"\n onRowClick={(row) => setSelectedMount(row)}\n />\n </div>\n )}\n </div>\n ),\n },\n {\n id: \"local\",\n label: \"Local File\",\n content: (\n <div className=\"space-y-6\">\n <div className=\"flex flex-col items-center justify-center py-12 border-2 border-dashed border-border rounded-xl bg-surface-sunk/50\">\n <div className=\"p-4 bg-accent-soft rounded-full mb-4\">\n <Icon icon={FileText} size=\"xl\" color=\"primary\" />\n </div>\n <Text variant=\"label\" size=\"lg\" weight=\"semibold\">\n Select a file from your computer\n </Text>\n <Text\n variant=\"body\"\n size=\"sm\"\n className=\"text-text-muted mt-1 mb-6 text-center max-w-xs\"\n >\n This file will be used directly in the form and will <strong>not</strong> be\n uploaded to platform storage.\n </Text>\n\n <label className=\"cursor-pointer\">\n <Button\n variant=\"primary\"\n size=\"lg\"\n loading={loading}\n className=\"pointer-events-none\"\n >\n Browse Local File\n </Button>\n <input\n type=\"file\"\n className=\"hidden\"\n onChange={handleLocalOnlySelection}\n accept={accept}\n disabled={loading}\n />\n </label>\n </div>\n </div>\n ),\n },\n ]}\n />\n </Modal>\n </div>\n );\n};\n","import React, { forwardRef } from \"react\";\nimport { cn } from \"../utils/cn\";\nimport { fieldEdgeClasses, fieldFrameClasses, fieldLabelClasses } from \"./TextField\";\n\nexport interface TextAreaProps\n extends Omit<React.TextareaHTMLAttributes<HTMLTextAreaElement>, \"size\"> {\n label?: string;\n\n helperText?: string;\n\n error?: string;\n\n size?: \"sm\" | \"md\" | \"lg\" | \"full\";\n\n fullWidth?: boolean;\n\n loading?: boolean;\n\n /** Additional class name for the container */\n containerClassName?: string;\n\n /** Additional class name for the label */\n labelClassName?: string;\n}\n\nconst textareaSizes = {\n sm: \"px-3 py-1.5 text-sm\",\n md: \"px-3 py-2 text-base\",\n lg: \"px-3 py-2 text-md\",\n full: \"px-3 py-2 text-md\",\n};\n\nexport const TextArea = forwardRef<HTMLTextAreaElement, TextAreaProps>(\n (\n {\n label,\n helperText,\n error,\n size = \"md\",\n fullWidth = false,\n loading = false,\n containerClassName,\n labelClassName,\n className,\n id,\n rows = 4,\n ...props\n },\n ref,\n ) => {\n const reactId = React.useId();\n const textareaId = id || `textarea-${reactId}`;\n const hasError = Boolean(error);\n const describedBy = error || helperText ? `${textareaId}-description` : undefined;\n\n return (\n <div className={cn(\"flex flex-col\", fullWidth && \"w-full\", containerClassName)}>\n {label && (\n <label\n htmlFor={textareaId}\n className={cn(\n fieldLabelClasses,\n hasError ? \"text-danger-fg\" : \"text-text-muted\",\n labelClassName,\n )}\n >\n {label}\n </label>\n )}\n\n <div className=\"relative\">\n <textarea\n ref={ref}\n id={textareaId}\n rows={rows}\n aria-invalid={hasError || undefined}\n aria-describedby={describedBy}\n className={cn(\n fieldFrameClasses,\n \"resize-y leading-relaxed\",\n\n textareaSizes[size],\n\n fieldEdgeClasses(hasError),\n\n className,\n )}\n {...props}\n />\n\n {loading && (\n <span\n aria-hidden\n className=\"absolute top-2 right-3 size-icon-md animate-spin rounded-full border-2 border-border border-t-transparent\"\n />\n )}\n </div>\n\n {(error || helperText) && (\n <p\n id={describedBy}\n className={cn(\"mt-1 text-xs\", hasError ? \"text-danger-fg\" : \"text-text-subtle\")}\n >\n {error || helperText}\n </p>\n )}\n </div>\n );\n },\n);\n\nTextArea.displayName = \"TextArea\";\n","import type { FilePointer, InvokeOptions, VirtualMount } from \"@opencxh/domain\";\nimport { Plus, Trash2 } from \"lucide-react\";\nimport React, { useCallback, useEffect, useState } from \"react\";\nimport { Button } from \"../action/Button\";\nimport { SegmentedToggle } from \"../action/SegmentedToggle\";\nimport { Checkbox } from \"../input/Checkbox\";\nimport { DatePicker } from \"../input/DatePicker\";\nimport { type FolderDestination, FolderSelect } from \"../input/FolderSelect\";\nimport { Select } from \"../input/Select\";\nimport { StorageInput } from \"../input/StorageInput\";\nimport { TextArea } from \"../input/TextArea\";\nimport { TextField } from \"../input/TextField\";\nimport { cn } from \"../utils/cn\";\nimport { autofillProps } from \"./autofill\";\n\nexport type FormFieldType =\n | \"text\"\n | \"email\"\n | \"password\"\n | \"number\"\n | \"tel\"\n | \"url\"\n // A colour swatch. Its own type rather than `fieldProps: { type: \"color\" }`,\n // because the `type` prop is applied after the spread and would win.\n | \"color\"\n | \"textarea\"\n | \"select\"\n | \"segmented\"\n | \"checkbox\"\n | \"radio\"\n | \"date\"\n | \"file\"\n | \"folder\"\n | \"custom\"\n | \"array\";\n\n/**\n * `NonNullable` before the `object` test is load-bearing: form data is routinely\n * a `Partial<T>`, and `Address | undefined extends object` is false — without it\n * every nested path silently disappears and only top-level keys survive.\n * Arrays, Dates and functions are leaves; recursing into them blows the union\n * past what TypeScript will infer (see {@link FieldPath}).\n * `T extends unknown ?` distributes over unions: `keyof (A | B)` keeps only\n * shared keys, so `ownerScope.teamId` would otherwise be unspellable.\n */\ntype NestedKeys<T> = T extends unknown\n ? {\n [K in keyof T]-?: NonNullable<T[K]> extends\n | readonly unknown[]\n | Date\n | ((...args: never[]) => unknown)\n ? K & string\n : NonNullable<T[K]> extends object\n ? `${K & string}.${NestedKeys<NonNullable<T[K]>>}`\n : K & string;\n }[keyof T]\n : never;\n\n/**\n * What a field may be named: any leaf path, plus any top-level key. The second\n * half exists for `type: \"custom\"` fields, which own a whole sub-object and\n * would otherwise be unspellable, since `NestedKeys` only reaches leaves. Kept\n * as a separate union: folding it into the recursion doubles the union at every\n * level, and TypeScript quietly gives up inferring it once it gets large.\n */\ntype FieldPath<T> = NestedKeys<T> | (keyof T & string);\n\nfunction getValueByPath<T>(obj: T, path: string): unknown {\n return path\n .split(\".\")\n .reduce(\n (acc, key) => (acc && typeof acc === \"object\" && key in acc ? (acc as any)[key] : undefined),\n obj,\n );\n}\n\nfunction setValueByPath<T extends object>(obj: T, path: string, value: any): T {\n const keys = path.split(\".\");\n const clone: any = Array.isArray(obj) ? [...(obj as any)] : { ...(obj as any) };\n let cur: any = clone;\n\n for (let i = 0; i < keys.length - 1; i++) {\n const k = keys[i];\n const prev = cur[k];\n cur[k] =\n prev && typeof prev === \"object\" ? (Array.isArray(prev) ? [...prev] : { ...prev }) : {};\n cur = cur[k];\n }\n cur[keys[keys.length - 1]] = value;\n return clone;\n}\n\nfunction unsetByPath<T extends object>(obj: T, path: string): T {\n const keys = path.split(\".\");\n const clone: any = Array.isArray(obj) ? [...(obj as any)] : { ...(obj as any) };\n let cur: any = clone;\n\n for (let i = 0; i < keys.length - 1; i++) {\n const k = keys[i];\n if (!cur[k] || typeof cur[k] !== \"object\") return clone; // nothing to unset\n cur[k] = Array.isArray(cur[k]) ? [...cur[k]] : { ...cur[k] };\n cur = cur[k];\n }\n delete cur[keys[keys.length - 1]];\n return clone;\n}\n\n/**\n * One band of a page-wide form: a fixed label column with the section title and\n * its explanation, and the fields beside it. Exported because a page often has\n * a section that is not a `Form` field at all — a checklist, linked records —\n * that must line up with the bands above it; without this each re-derives the\n * column width and spacing, and they drift.\n */\nexport function FormSection({\n title,\n description,\n className,\n children,\n}: {\n title?: string;\n description?: string;\n /** Caller owns the divider: only it knows whether it is the last band. */\n className?: string;\n children: React.ReactNode;\n}) {\n if (!title && !description) {\n return <div>{children}</div>;\n }\n\n return (\n <div className={cn(\"flex flex-col gap-4 py-6 sm:flex-row sm:gap-8\", className)}>\n <div className=\"w-full shrink-0 sm:w-form-label\">\n {title && <span className=\"text-base font-semibold text-text\">{title}</span>}\n {description && (\n <p className=\"mt-1 text-sm leading-relaxed text-pretty text-text-subtle\">{description}</p>\n )}\n </div>\n <div className=\"min-w-0 flex-1\">{children}</div>\n </div>\n );\n}\n\nexport interface FormGroupItem<T = any> {\n /** Field name (key in form data) */\n name: FieldPath<T>;\n /** Field label */\n label: string;\n /** Field type */\n type: FormFieldType;\n /** Text input type (when type is 'text') */\n textType?: \"text\" | \"email\" | \"password\" | \"number\" | \"tel\" | \"url\";\n /** Field width (CSS width value or grid columns) */\n width?: string | number;\n /** Placeholder text */\n placeholder?: string;\n /**\n * Visible rows for `textarea`. Defaults to 4 — right for a note, far too small\n * for a field someone reads back while editing (a system prompt).\n */\n rows?: number;\n /**\n * Autocomplete token. Only real HTML tokens (`email`, `tel`, `name`, …) are\n * passed to the browser; anything else switches autofill *off* rather than\n * silently meaning \"on\" — see {@link autofillProps}.\n */\n autocomplete?: string;\n /** Options for select/radio/checkbox types */\n options?: Array<{\n value: string | number;\n label: string;\n disabled?: boolean;\n }>;\n /** Validation function */\n validator?: (value: any, formData: T) => string | null;\n /** Conditional function to show/hide field */\n conditional?: (formData: T) => boolean;\n /** Whether field can be empty */\n allowEmpty?: boolean;\n /** Remove field from form data when empty */\n removeIfEmpty?: boolean;\n /**\n * Explanation under the field: why it exists, or what the choice implies. For\n * fields whose label cannot fully pose the question — a group `description` is\n * too coarse once a group has several fields.\n */\n help?: string;\n /** Whether field is hidden */\n hidden?: boolean;\n /** Whether field is required */\n required?: boolean;\n /** Whether field is disabled */\n disabled?: boolean;\n /** Custom component renderer */\n customComponent?: (props: {\n value: any;\n onChange: (value: any) => void;\n error?: string;\n disabled?: boolean;\n }) => React.ReactNode;\n /** Additional props to pass to the field component */\n fieldProps?: Record<string, any>;\n\n /** For 'array' type, defines the fields for each item in the array */\n arrayFields?: FormGroupItem<any>[];\n\n /** For 'select' type, whether the select should be searchable */\n searchable?: boolean;\n\n /** For 'select' type, whether the select should be multiple */\n multiple?: boolean;\n\n allowCreate?: boolean;\n\n // todo: add support for array of objects\n}\n\nexport interface FormGroup<T = any> {\n /** Group identifier */\n id: string;\n /**\n * Group title. Optional: a form that is one unbroken block of fields has no\n * heading to give, and the label column already renders empty without it.\n */\n title?: string;\n /** Group description */\n description?: string;\n /** Group items */\n items: FormGroupItem<T>[];\n /** Conditional function to show/hide group */\n conditional?: (formData: T) => boolean;\n /** Group layout */\n layout?: \"grid\" | \"flex\";\n /** Number of columns for grid layout */\n columns?: number;\n /** Additional CSS classes */\n className?: string;\n}\n\nexport interface FormButtonProps {\n /** Button label */\n label: string;\n /** Button variant */\n variant?: \"primary\" | \"secondary\" | \"outline\" | \"ghost\" | \"destructive\";\n /** Button size */\n size?: \"sm\" | \"md\" | \"lg\";\n /** Whether button is disabled */\n disabled?: boolean;\n /** Additional CSS classes */\n className?: string;\n}\n\n/**\n * The sliver of an app SDK that `Form` needs: one call to the server. Nothing here knows\n * about the rest of the SDK, which is the point.\n */\nexport interface FormInvoker {\n http: { invoke<T = any>(options: InvokeOptions): Promise<T> };\n}\n\nexport interface FormProps<T = Record<string, any>> {\n /** Form groups */\n groups: FormGroup<T>[];\n /** Initial form data */\n data?: Partial<T>;\n /** Form submit handler */\n onSubmit?: (data: T, transformedData?: any) => void | Promise<void>;\n /** Form cancel handler */\n onCancel?: () => void;\n /** Form change handler */\n onChange?: (data: Partial<T>, changedField?: keyof T) => void;\n /** Data transformation function (called before submit) */\n transform?: (data: T) => any;\n /** Form validation function */\n validate?: (data: T) => Record<keyof T, string> | null;\n /** Submit button props */\n submitButton?: FormButtonProps;\n /** Cancel button props */\n cancelButton?: FormButtonProps;\n /** Whether to show buttons */\n showButtons?: boolean;\n /** Form layout */\n layout?: \"vertical\" | \"horizontal\";\n /** Form size */\n size?: \"sm\" | \"md\" | \"lg\" | \"full\";\n /** Whether form is loading */\n loading?: boolean;\n /** Additional CSS classes */\n className?: string;\n /**\n * Form ref. Includes `| null` because that is what React 19's\n * `useRef<HTMLFormElement>(null)` produces; without it every caller has to\n * cast at the call site.\n */\n ref?: React.RefObject<HTMLFormElement | null>;\n /**\n * A handle that can call the server, for the storage fields (`storage`, `folder`).\n * Typed as the one method used rather than the whole `InternalSDK`: that type lives\n * in `@opencxh/app-sdk`, which ui-kit does not (and should not) depend on to render\n * a form. It also keeps the storage fields' slated move to a federated resource\n * from being blocked by a type import.\n */\n sdk?: FormInvoker;\n}\n\n/**\n * Generic Form component with groups, validation, and conditional rendering\n */\nexport const Form = <T extends Record<string, any>>({\n groups,\n data: externalData,\n onSubmit,\n onCancel,\n onChange,\n transform,\n validate,\n submitButton = { label: \"Submit\", variant: \"primary\" },\n cancelButton = { label: \"Cancel\", variant: \"outline\" },\n showButtons = true,\n layout = \"vertical\",\n size = \"md\",\n loading = false,\n className,\n ref,\n sdk,\n}: FormProps<T>) => {\n const [formData, setFormData] = useState<Partial<T>>(externalData || {});\n\n /**\n * A DOM id per field, unique per rendered form. `useId` and not the field name\n * alone: two forms on one page (a detail pane beside a modal) would hand out the\n * same id twice, and then a label points at whichever came first.\n */\n const formDomId = React.useId();\n const fieldDomId = useCallback(\n (name: keyof T | string) => `${formDomId}-${String(name).replace(/\\./g, \"-\")}`,\n [formDomId],\n );\n const [errors, setErrors] = useState<Record<string, string>>({});\n const [touched, setTouched] = useState<Record<string, boolean>>({});\n\n useEffect(() => {\n if (externalData) {\n setFormData({ ...externalData });\n }\n }, [externalData]);\n\n const validateField = useCallback(\n (name: keyof T, value: any, currentData: Partial<T>) => {\n const field = groups.flatMap((group) => group.items).find((item) => item.name === name);\n\n if (!field) return null;\n\n if (field.required && (value === undefined || value === null || value === \"\")) {\n return `${field.label} is required`;\n }\n\n if (field.type === \"array\") {\n if (field.required && (!value || value.length === 0)) {\n return `${field.label} cannot be empty.`;\n }\n if (value && !Array.isArray(value)) {\n return `${field.label} must be an array.`;\n }\n\n if (Array.isArray(value) && field.arrayFields) {\n const arrayErrors: Record<string, any>[] = [];\n let hasErrors = false;\n value.forEach((row, index) => {\n const rowErrors: Record<string, string> = {};\n for (const subField of field.arrayFields!) {\n const subFieldValue = row?.[subField.name];\n if (\n subField.required &&\n (subFieldValue === undefined || subFieldValue === null || subFieldValue === \"\")\n ) {\n rowErrors[subField.name as string] = `${subField.label} is required`;\n hasErrors = true;\n } else if (subField.validator) {\n const error = subField.validator(subFieldValue, row);\n if (error) {\n rowErrors[subField.name as string] = error;\n hasErrors = true;\n }\n }\n }\n arrayErrors[index] = rowErrors;\n });\n\n if (hasErrors) {\n return JSON.stringify(arrayErrors);\n }\n }\n }\n\n if (field.validator) {\n return field.validator(value, currentData as T);\n }\n\n return null;\n },\n [groups],\n );\n\n const handleFieldChange = useCallback(\n (name: keyof T, value: any) => {\n let newData = setValueByPath(formData as object, name as string, value) as Partial<T>;\n\n const field = groups.flatMap((g) => g.items).find((i) => i.name === name);\n if (field?.removeIfEmpty && (value === undefined || value === null || value === \"\")) {\n newData = unsetByPath(newData as object, name as string) as Partial<T>;\n }\n\n setFormData(newData);\n setTouched((prev) => ({ ...prev, [name]: true }));\n\n const error = validateField(name, value, newData);\n setErrors((prev) => ({\n ...prev,\n [name]: error || \"\",\n }));\n\n onChange?.(newData, name);\n },\n [formData, groups, validateField, onChange],\n );\n\n const handleSubmit = useCallback(\n async (e: React.FormEvent) => {\n e.preventDefault();\n\n if (loading) return;\n\n const newErrors: Record<string, string> = {};\n const allFields = groups.flatMap((group) => group.items);\n\n for (const field of allFields) {\n if (field.hidden || (field.conditional && !field.conditional(formData as T))) {\n continue;\n }\n\n const error = validateField(\n field.name,\n getValueByPath<Partial<T>>(formData, field.name),\n formData,\n );\n if (error) {\n newErrors[field.name as string] = error;\n }\n }\n\n if (validate) {\n const formErrors = validate(formData as T);\n if (formErrors) {\n Object.assign(newErrors, formErrors);\n }\n }\n\n setErrors(newErrors);\n\n if (Object.keys(newErrors).some((key) => newErrors[key])) {\n return;\n }\n\n const finalData = transform ? transform(formData as T) : formData;\n\n await onSubmit?.(formData as T, finalData);\n },\n [formData, groups, validateField, validate, transform, onSubmit, loading],\n );\n\n const renderField = useCallback(\n (item: FormGroupItem<T>) => {\n const value = getValueByPath<Partial<T>>(formData, item.name) as string | undefined;\n const error = touched[item.name as string] ? errors[item.name as string] : undefined;\n const isDisabled = item.disabled || loading;\n\n const commonProps = {\n // Ties the field to the `<label>` the group wrapper renders; without it\n // labels are unassociated — screen readers announce nothing, clicking a\n // label focuses nothing, and tests cannot find a field by its label.\n id: fieldDomId(item.name),\n value: value || \"\",\n disabled: isDisabled,\n required: item.required,\n placeholder: item.placeholder,\n ...item.fieldProps,\n };\n\n switch (item.type) {\n case \"text\":\n case \"email\":\n case \"password\":\n case \"number\":\n case \"tel\":\n case \"url\":\n case \"color\":\n return (\n <TextField\n {...commonProps}\n onChange={(e) => handleFieldChange(item.name, e.target.value)}\n type={item.textType || item.type}\n {...autofillProps(item.autocomplete)}\n error={error}\n size={size}\n />\n );\n\n case \"textarea\":\n return (\n <TextArea\n {...commonProps}\n onChange={(e) => handleFieldChange(item.name, e.target.value)}\n rows={item.rows ?? 4}\n error={error}\n size={size}\n />\n );\n\n case \"select\":\n return (\n <Select\n {...commonProps}\n onChange={(e) => handleFieldChange(item.name, e)}\n options={(item.options || []).map((option) => ({\n ...option,\n value: String(option.value),\n }))}\n searchable={item.searchable}\n multiple={item.multiple}\n allowCreate={item.allowCreate}\n error={error}\n size={size}\n />\n );\n\n // A short, fixed set of mutually exclusive values — priority, a scope,\n // a status. All options visible at once beats a dropdown you have to\n // open to learn what the choices even are.\n case \"segmented\":\n return (\n <SegmentedToggle\n aria-label={item.label}\n value={value == null ? \"\" : String(value)}\n onChange={(next) => handleFieldChange(item.name, next)}\n options={(item.options || []).map((option) => ({\n value: String(option.value),\n label: option.label,\n }))}\n size={size === \"sm\" ? \"sm\" : \"md\"}\n className={item.disabled ? \"pointer-events-none opacity-50\" : undefined}\n />\n );\n\n case \"checkbox\":\n return (\n <Checkbox\n {...commonProps}\n label={item.label}\n checked={Boolean(value)}\n onChange={(e) => handleFieldChange(item.name, e.target.checked)}\n size={size}\n />\n );\n\n case \"radio\":\n return (\n <div className=\"flex flex-col gap-2\">\n {item.options?.map((option) => (\n <label\n key={option.value}\n // gap-3 like Checkbox: a radio row and a checkbox row in the\n // same form must sit the same distance from their label.\n className=\"flex cursor-pointer items-center gap-3 text-base text-text\"\n >\n <input\n type=\"radio\"\n name={item.name as string}\n value={option.value}\n checked={value === option.value}\n onChange={() => handleFieldChange(item.name, option.value)}\n disabled={isDisabled || option.disabled}\n className=\"size-checkbox shrink-0 rounded-xs border border-border-strong bg-surface-input accent-accent focus-visible:outline-none focus-visible:focus-ring\"\n />\n {option.label}\n </label>\n ))}\n </div>\n );\n\n case \"date\":\n return (\n <DatePicker\n {...commonProps}\n value={value ? new Date(value) : null}\n onChange={(date) => handleFieldChange(item.name, date)}\n size={size}\n />\n );\n\n case \"file\":\n return (\n <StorageInput\n {...commonProps}\n value={value}\n onChange={(pointer) => handleFieldChange(item.name, pointer)}\n error={error}\n onListFiles={(filters) =>\n sdk?.http.invoke<{ data: FilePointer[] }>({\n method: \"POST\",\n action: \"storage.file.list\",\n body: filters,\n })\n }\n onListMounts={() =>\n sdk?.http.invoke<{ data: VirtualMount[] }>({\n method: \"GET\",\n action: \"storage.mount\",\n })\n }\n onUploadFile={(payload) =>\n sdk?.http.invoke<{ data: FilePointer }>({\n method: \"POST\",\n action: \"storage.file.upload\",\n body: payload,\n })\n }\n onDownloadFile={(fileId) =>\n sdk?.http.invoke<Uint8Array>({\n method: \"GET\",\n action: `storage.file.${fileId}.download`,\n })\n }\n onRegisterFile={(payload) =>\n sdk?.http.invoke<{ data: FilePointer }>({\n method: \"POST\",\n action: \"storage.file.folder\",\n body: payload,\n })\n }\n />\n );\n\n case \"folder\":\n return (\n <FolderSelect\n value={(value as unknown as FolderDestination) || {}}\n onChange={(v) => handleFieldChange(item.name, v)}\n onListFolders={() =>\n sdk?.http.invoke<{ data: VirtualMount[] }>({\n method: \"GET\",\n action: \"storage.mount\",\n })\n }\n disabled={isDisabled}\n size={size}\n placeholder={item.placeholder}\n />\n );\n\n case \"custom\":\n return item.customComponent?.({\n value,\n onChange: (newValue) => handleFieldChange(item.name, newValue),\n error,\n disabled: isDisabled,\n });\n\n case \"array\": {\n const arrayValue = (value || []) as any[];\n let arrayErrors: Record<string, string>[] = [];\n if (typeof error === \"string\" && error.startsWith(\"[\")) {\n try {\n arrayErrors = JSON.parse(error);\n } catch (e) {\n // ignore parse error\n }\n }\n\n const handleAddItem = () => {\n handleFieldChange(item.name, [...arrayValue, {}]);\n };\n\n const handleRemoveItem = (index: number) => {\n handleFieldChange(\n item.name,\n arrayValue.filter((_, i) => i !== index),\n );\n };\n\n const handleSubFieldChange = (index: number, fieldName: string, fieldValue: any) => {\n const newArray = [...arrayValue];\n newArray[index] = {\n ...newArray[index],\n [fieldName]: fieldValue,\n };\n handleFieldChange(item.name, newArray);\n };\n\n return (\n <div className=\"flex flex-col gap-2\">\n {arrayValue.map((row, index) => (\n <div key={index} className=\"flex items-start gap-2\">\n <div className=\"grid flex-1 gap-2 sm:grid-cols-2\">\n {item.arrayFields?.map((subField) => {\n const subFieldError = arrayErrors?.[index]?.[subField.name as string];\n return (\n <div key={subField.name as string} className=\"flex flex-col gap-1\">\n {index === 0 && (\n <label className=\"block text-xs font-semibold uppercase tracking-label text-text-subtle\">\n {subField.label}\n {subField.required && <span className=\"ml-1 text-danger-fg\">*</span>}\n </label>\n )}\n {(() => {\n const commonSubFieldProps = {\n value: row?.[subField.name] || \"\",\n disabled: isDisabled,\n required: subField.required,\n placeholder: subField.placeholder,\n ...subField.fieldProps,\n };\n switch (subField.type) {\n case \"text\":\n case \"email\":\n case \"password\":\n case \"number\":\n case \"tel\":\n case \"url\":\n return (\n <TextField\n {...commonSubFieldProps}\n onChange={(e) =>\n handleSubFieldChange(\n index,\n subField.name as string,\n e.target.value,\n )\n }\n type={subField.textType || subField.type}\n {...autofillProps(subField.autocomplete)}\n error={subFieldError}\n size={size}\n />\n );\n case \"textarea\":\n return (\n <TextArea\n {...commonSubFieldProps}\n onChange={(e) =>\n handleSubFieldChange(\n index,\n subField.name as string,\n e.target.value,\n )\n }\n rows={subField.rows ?? 4}\n error={subFieldError}\n size={size}\n />\n );\n case \"select\":\n return (\n <Select\n {...commonSubFieldProps}\n onChange={(e) =>\n handleSubFieldChange(index, subField.name as string, e)\n }\n options={(subField.options || []).map((option) => ({\n ...option,\n value: String(option.value),\n }))}\n searchable={subField.searchable}\n multiple={subField.multiple}\n allowCreate={subField.allowCreate}\n error={subFieldError}\n size={size}\n />\n );\n case \"checkbox\":\n return (\n <Checkbox\n {...commonSubFieldProps}\n checked={Boolean(row?.[subField.name])}\n onChange={(e) =>\n handleSubFieldChange(\n index,\n subField.name as string,\n e.target.checked,\n )\n }\n size={size}\n />\n );\n case \"radio\":\n return (\n <div className=\"flex flex-col gap-2\">\n {subField.options?.map((option) => (\n <label\n key={option.value}\n className=\"flex cursor-pointer items-center gap-2 text-base text-text\"\n >\n <input\n type=\"radio\"\n name={`${item.name as string}.${index}.${\n subField.name as string\n }`}\n value={option.value}\n checked={row?.[subField.name] === option.value}\n onChange={() =>\n handleSubFieldChange(\n index,\n subField.name as string,\n option.value,\n )\n }\n disabled={isDisabled || option.disabled}\n className=\"size-checkbox shrink-0 rounded-xs border border-border-strong bg-surface-input accent-accent focus-visible:outline-none focus-visible:focus-ring\"\n />\n {option.label}\n </label>\n ))}\n </div>\n );\n case \"date\": {\n const dateValue = row?.[subField.name];\n return (\n <DatePicker\n {...commonSubFieldProps}\n value={dateValue ? new Date(dateValue) : null}\n onChange={(date) =>\n handleSubFieldChange(index, subField.name as string, date)\n }\n size={size}\n />\n );\n }\n case \"custom\":\n return subField.customComponent?.({\n value: row?.[subField.name],\n onChange: (newValue) =>\n handleSubFieldChange(index, subField.name as string, newValue),\n error: subFieldError,\n disabled: isDisabled,\n });\n default:\n return <p>Unsupported field type in array: {subField.type}</p>;\n }\n })()}\n {subFieldError && (\n <p className=\"text-xs text-danger-fg\">{subFieldError}</p>\n )}\n </div>\n );\n })}\n </div>\n <Button\n type=\"button\"\n iconOnly\n variant=\"ghost\"\n size=\"md\"\n aria-label={`${item.label} verwijderen`}\n onClick={() => handleRemoveItem(index)}\n disabled={isDisabled}\n className={cn(\"shrink-0\", index === 0 && \"mt-5\")}\n >\n <Trash2 className=\"size-icon-md\" />\n </Button>\n </div>\n ))}\n <Button\n type=\"button\"\n variant=\"secondary\"\n leftIcon={<Plus className=\"size-icon-sm\" />}\n onClick={handleAddItem}\n disabled={isDisabled}\n >\n {item.label} toevoegen\n </Button>\n </div>\n );\n }\n\n default:\n return null;\n }\n },\n [formData, touched, errors, loading, size, handleFieldChange],\n );\n\n const renderGroup = useCallback(\n (group: FormGroup<T>) => {\n if (group.conditional && !group.conditional(formData as T)) {\n return null;\n }\n\n const visibleItems = group.items.filter((item) => {\n if (item.hidden) return false;\n if (item.conditional && !item.conditional(formData as T)) return false;\n return true;\n });\n\n if (visibleItems.length === 0) return null;\n\n const groupClasses = cn(\n group.layout === \"flex\" ? \"flex flex-wrap gap-3.5\" : \"grid gap-3.5\",\n group.layout !== \"flex\" && {\n \"grid-cols-1\": !group.columns || group.columns === 1,\n \"grid-cols-2\": group.columns === 2,\n \"grid-cols-3\": group.columns === 3,\n \"grid-cols-4\": group.columns === 4,\n },\n group.className,\n );\n\n return (\n // A band across the page, closed off with a hairline — no card. The\n // form *is* the page now, so a panel around each section would be a\n // second surface on top of the one it already sits on.\n <FormSection\n key={group.id}\n title={group.title}\n description={group.description}\n className=\"not-last:border-b not-last:border-border-subtle\"\n >\n <div>\n <div className={groupClasses}>\n {visibleItems.map((item) => {\n const fieldError = touched[item.name as string]\n ? errors[item.name as string]\n : undefined;\n\n return (\n <div\n key={item.name as string}\n className={cn(\n \"flex min-w-0 flex-col gap-1.5\",\n item.type === \"checkbox\" && \"justify-center\",\n )}\n style={{\n width: typeof item.width === \"string\" ? item.width : undefined,\n gridColumn:\n typeof item.width === \"number\"\n ? `span ${item.width} / span ${item.width}`\n : undefined,\n }}\n >\n {/* Checkbox carries its own label on the right, so it gets none here. */}\n {item.type !== \"custom\" && item.type !== \"checkbox\" && (\n <label\n htmlFor={fieldDomId(item.name)}\n className=\"block text-sm font-medium text-text-muted\"\n >\n {item.label}\n {item.required && <span className=\"ml-1 text-danger-fg\">*</span>}\n </label>\n )}\n\n {renderField(item)}\n\n {/* Help text under the field; a validation error takes its place. */}\n {item.help && !fieldError && (\n <p className=\"text-xs leading-4 text-text-subtle\">{item.help}</p>\n )}\n </div>\n );\n })}\n </div>\n </div>\n </FormSection>\n );\n },\n [formData, touched, errors, renderField],\n );\n\n // No panel chrome: the page (or the card the page sits in) is the surface.\n // `size` is a reading width, nothing more — `md` is the standard form column.\n const formClasses = cn(\n {\n \"max-w-settings\": size === \"sm\",\n \"max-w-form\": size === \"md\",\n \"max-w-6xl\": size === \"lg\",\n \"max-w-full\": size === \"full\",\n },\n className,\n );\n\n const buttonSize = size === \"full\" ? \"md\" : size;\n\n return (\n <form ref={ref} onSubmit={handleSubmit} className={formClasses}>\n {/* Implicit submission. A form with several fields and no submit button\n inside it ignores Enter entirely — which is what happens whenever the\n save button lives in a page header (showButtons={false}). This hidden\n button is the form's default button, so Enter in a text field submits\n the way it does everywhere else. */}\n {!showButtons && <button type=\"submit\" className=\"hidden\" tabIndex={-1} aria-hidden />}\n\n <div className=\"flex flex-col\">{groups.map(renderGroup)}</div>\n\n {/* Footer. Only when it has something in it: without the panel around the\n form, an empty footer is a rule hanging under the last section. */}\n {showButtons && (\n <div className=\"flex items-center gap-2 border-t border-border-subtle py-4\">\n <>\n <div className=\"ml-auto\" />\n {onCancel && (\n <Button\n type=\"button\"\n variant={cancelButton.variant ?? \"ghost\"}\n size={cancelButton.size || buttonSize}\n disabled={cancelButton.disabled || loading}\n onClick={onCancel}\n className={cancelButton.className}\n >\n {cancelButton.label}\n </Button>\n )}\n\n <Button\n type=\"submit\"\n variant={submitButton.variant}\n size={submitButton.size || buttonSize}\n disabled={submitButton.disabled || loading}\n loading={loading}\n className={submitButton.className}\n >\n {submitButton.label}\n </Button>\n </>\n </div>\n )}\n </form>\n );\n};\n","import type React from \"react\";\nimport { useRef, useState } from \"react\";\nimport { Button } from \"../action/Button\";\nimport { Text } from \"../typography/Text\";\nimport { cn } from \"../utils/cn\";\n\nexport interface ImageFieldProps {\n label?: string;\n /** Current value as a base64 data URI, or undefined when empty. */\n value?: string;\n onChange: (value: string | undefined) => void;\n /** Longest side after downscaling. */\n maxDimension?: number;\n /**\n * Ceiling for the encoded result. The server checks this too — this one is\n * here so the user finds out before saving, not so the rule is enforced.\n */\n maxBytes?: number;\n /** Preview box shape. */\n aspect?: \"square\" | \"wide\";\n helperText?: React.ReactNode;\n disabled?: boolean;\n className?: string;\n chooseLabel?: string;\n removeLabel?: string;\n /** Shown when the picked file is still too large after downscaling. */\n tooLargeLabel?: string;\n}\n\n/**\n * Pick an image and keep it inline as a base64 data URI.\n *\n * For the small brand assets that live on a row rather than in the storage app\n * — an organisation logo, a help centre favicon. Not for content: an article\n * image belongs in storage, once storage can serve a public URL.\n *\n * The canvas downscale is a courtesy, not a limit. The same ceiling is enforced\n * server-side by `assertInlineImage`, because anyone can call the API without\n * going through this field.\n */\nexport function ImageField({\n label,\n value,\n onChange,\n maxDimension = 256,\n maxBytes,\n aspect = \"square\",\n helperText,\n disabled,\n className,\n chooseLabel = \"Choose\",\n removeLabel = \"Remove\",\n tooLargeLabel = \"That image is too large.\",\n}: ImageFieldProps) {\n const inputRef = useRef<HTMLInputElement>(null);\n const [error, setError] = useState<string | null>(null);\n\n const handleSelect = async (file?: File) => {\n if (!file) return;\n setError(null);\n const encoded = await downscaleToDataUri(file, maxDimension);\n if (maxBytes && approximateBytes(encoded) > maxBytes) {\n setError(tooLargeLabel);\n return;\n }\n onChange(encoded);\n };\n\n return (\n <div className={cn(\"flex flex-col gap-1.5\", className)}>\n {label && <span className=\"text-sm font-medium text-text\">{label}</span>}\n\n <div className=\"flex items-center gap-4\">\n <div\n className={cn(\n \"flex items-center justify-center overflow-hidden rounded-md border border-border bg-surface-sunk\",\n aspect === \"square\" ? \"h-16 w-16\" : \"h-16 w-28\",\n )}\n >\n {value ? (\n <img src={value} alt=\"\" className=\"h-full w-full object-contain\" />\n ) : (\n <Text variant=\"caption\" color=\"muted\">\n —\n </Text>\n )}\n </div>\n\n <div className=\"flex flex-col gap-2\">\n <div className=\"flex gap-2\">\n <Button\n type=\"button\"\n variant=\"secondary\"\n disabled={disabled}\n onClick={() => inputRef.current?.click()}\n >\n {chooseLabel}\n </Button>\n {value && (\n <Button\n type=\"button\"\n variant=\"ghost\"\n disabled={disabled}\n onClick={() => {\n setError(null);\n onChange(undefined);\n }}\n >\n {removeLabel}\n </Button>\n )}\n </div>\n {(error || helperText) && (\n <Text variant=\"caption\" color={error ? \"error\" : \"muted\"}>\n {error ?? helperText}\n </Text>\n )}\n </div>\n </div>\n\n <input\n ref={inputRef}\n type=\"file\"\n accept=\"image/png,image/jpeg,image/webp\"\n className=\"hidden\"\n onChange={(e) => void handleSelect(e.target.files?.[0])}\n />\n </div>\n );\n}\n\n/** Roughly how many bytes a data URI's payload decodes to. */\nexport function approximateBytes(dataUri: string): number {\n const base64 = dataUri.slice(dataUri.indexOf(\",\") + 1);\n const padding = base64.endsWith(\"==\") ? 2 : base64.endsWith(\"=\") ? 1 : 0;\n return Math.floor((base64.length * 3) / 4) - padding;\n}\n\n/** Read a file, shrink it to `maxDimension` on a canvas, return a PNG data URI. */\nfunction downscaleToDataUri(file: File, maxDimension: number): Promise<string> {\n return new Promise((resolve, reject) => {\n const reader = new FileReader();\n reader.onload = () => {\n const img = new Image();\n img.onload = () => {\n const scale = Math.min(1, maxDimension / Math.max(img.width, img.height));\n const width = Math.round(img.width * scale);\n const height = Math.round(img.height * scale);\n const canvas = document.createElement(\"canvas\");\n canvas.width = width;\n canvas.height = height;\n const ctx = canvas.getContext(\"2d\");\n if (!ctx) {\n // No canvas: hand back the original rather than nothing, and let the\n // size check decide whether it is usable.\n resolve(reader.result as string);\n return;\n }\n ctx.drawImage(img, 0, 0, width, height);\n resolve(canvas.toDataURL(\"image/png\"));\n };\n img.onerror = reject;\n img.src = reader.result as string;\n };\n reader.onerror = reject;\n reader.readAsDataURL(file);\n });\n}\n","import { ChevronDown, Loader2, Search } from \"lucide-react\";\nimport type React from \"react\";\nimport { forwardRef, useEffect, useMemo, useRef, useState } from \"react\";\n\nconst cn = (...classes: string[]) => classes.filter(Boolean).join(\" \");\n\nconst SearchIcon = (props: React.SVGProps<SVGSVGElement>) => (\n <Search {...props} strokeWidth={1.5} aria-hidden />\n);\n\nconst ChevronDownIcon = (props: React.SVGProps<SVGSVGElement>) => (\n <ChevronDown {...props} strokeWidth={1.5} aria-hidden />\n);\n\nconst inputSizes = {\n sm: \"h-control-sm px-3 text-sm\",\n md: \"h-control-md px-3 text-base\",\n lg: \"h-control-lg px-3 text-base\",\n full: \"h-control-md w-full px-3 text-base\",\n};\n\nconst iconSizes = {\n sm: \"h-4 w-4\",\n md: \"h-5 w-5\",\n lg: \"h-6 w-6\",\n full: \"h-6 w-6\",\n};\n\nexport interface SearchableTextFieldOption {\n value: string;\n label: string;\n}\n\nexport interface SearchableTextFieldProps\n extends Omit<React.InputHTMLAttributes<HTMLInputElement>, \"size\" | \"onSelect\"> {\n label?: string;\n helperText?: string;\n error?: string;\n size?: \"sm\" | \"md\" | \"lg\" | \"full\";\n fullWidth?: boolean;\n startIcon?: React.ReactNode;\n loading?: boolean;\n containerClassName?: string;\n labelClassName?: string;\n\n options: SearchableTextFieldOption[];\n onRemoteSearch?: (searchTerm: string) => Promise<void>;\n\n onSelect?: (value: string) => void;\n debounceTime?: number;\n /**\n * Selector mode: on open (focus/chevron) show all options, even when the current\n * value is an already-selected option; filter only once the user actually types.\n * For fields that are more picker than free search (e.g. a sender selector).\n */\n showAllOnOpen?: boolean;\n}\n\nexport const SearchableTextField = forwardRef<HTMLInputElement, SearchableTextFieldProps>(\n (\n {\n label,\n helperText,\n error,\n size = \"md\",\n fullWidth = false,\n startIcon = <SearchIcon />,\n loading: externalLoading = false,\n containerClassName,\n labelClassName,\n className,\n id,\n options,\n onRemoteSearch,\n onSelect,\n debounceTime = 500,\n showAllOnOpen = false,\n value: propValue,\n onChange,\n ...props\n },\n ref,\n ) => {\n const inputId = id || `searchfield-${Math.random().toString(36).substr(2, 9)}`;\n const hasError = Boolean(error);\n const containerRef = useRef(null);\n const inputRef = useRef(null);\n\n const [inputValue, setInputValue] = useState(propValue || \"\");\n const [isOpen, setIsOpen] = useState(false);\n const [isSearchingRemote, setIsSearchingRemote] = useState(false);\n // Selector mode: has the user actually typed since opening? If not, show the\n // full list instead of filtering on the chosen value.\n const [typedSinceOpen, setTypedSinceOpen] = useState(false);\n\n useEffect(() => {\n if (propValue !== undefined) {\n setInputValue(propValue);\n }\n }, [propValue]);\n\n const filteredOptions = useMemo(() => {\n if (showAllOnOpen && !typedSinceOpen) return options;\n if (!inputValue) return options;\n const lowerCaseInput = String(inputValue).toLowerCase();\n return options.filter((option) =>\n String(option.label).toLowerCase().includes(lowerCaseInput),\n );\n }, [inputValue, options, showAllOnOpen, typedSinceOpen]);\n\n useEffect(() => {\n if (!onRemoteSearch || !inputValue) {\n setIsSearchingRemote(false);\n return;\n }\n\n const handler = setTimeout(async () => {\n setIsSearchingRemote(true);\n try {\n await onRemoteSearch(String(inputValue));\n } catch (e) {\n console.error(\"Remote search failed:\", e);\n } finally {\n setIsSearchingRemote(false);\n }\n }, debounceTime);\n\n return () => {\n clearTimeout(handler);\n setIsSearchingRemote(false);\n };\n }, [inputValue, debounceTime, onRemoteSearch]);\n\n useEffect(() => {\n const handleClickOutside = (event: MouseEvent) => {\n if (\n containerRef.current &&\n !(containerRef.current as HTMLElement).contains(event.target as Node)\n ) {\n setIsOpen(false);\n }\n };\n document.addEventListener(\"mousedown\", handleClickOutside);\n return () => document.removeEventListener(\"mousedown\", handleClickOutside);\n }, []);\n\n const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n const newValue = e.target.value;\n setInputValue(newValue);\n setIsOpen(true);\n setTypedSinceOpen(true);\n if (onChange) {\n onChange(e);\n }\n };\n\n const handleSelect = (option: SearchableTextFieldOption) => {\n setInputValue(option.label);\n setIsOpen(false);\n setTypedSinceOpen(false);\n if (onSelect) {\n onSelect(option.value);\n }\n };\n\n const handleFocus = () => {\n // Open dropdown alleen als er opties zijn of remote search beschikbaar is\n if (options.length > 0 || onRemoteSearch) {\n setIsOpen(true);\n setTypedSinceOpen(false);\n }\n };\n\n const loading = externalLoading || isSearchingRemote;\n\n const showDropdown = isOpen && (filteredOptions.length > 0 || loading);\n\n const resolvedRef = useMemo(() => ref || inputRef, [ref]);\n\n return (\n <div\n className={cn(\n \"flex flex-col\",\n fullWidth ? \"w-full\" : \"\",\n containerClassName || \"\",\n \"relative\",\n )}\n ref={containerRef}\n >\n {label && (\n <label\n htmlFor={inputId}\n className={cn(\n \"block text-sm font-medium mb-1\",\n hasError ? \"text-danger-fg\" : \"text-text\",\n labelClassName || \"\",\n )}\n >\n {label}\n </label>\n )}\n\n <div className=\"relative\">\n {startIcon && (\n <div className=\"absolute left-0 pl-3 flex items-center pointer-events-none h-full\">\n <span className={cn(\"text-text-muted\", iconSizes[size])}>{startIcon}</span>\n </div>\n )}\n\n <input\n ref={resolvedRef}\n id={inputId}\n value={inputValue}\n onChange={handleChange}\n onFocus={handleFocus}\n className={cn(\n // Base styles\n \"block w-full rounded-tile border bg-surface-input transition-colors duration-fast ease-out\",\n \"placeholder:text-text-subtle focus-visible:outline-none focus-visible:focus-ring\",\n \"disabled:cursor-not-allowed disabled:bg-surface-hover disabled:text-text-disabled\",\n\n // Size styles\n inputSizes[size],\n\n // Icon padding\n startIcon ? \"pl-10\" : \"\",\n \"pr-10\", // room for the dropdown/loading icon\n\n // State styles\n hasError\n ? \"border-danger-border text-danger-fg focus-visible:focus-ring\"\n : \"border-border-strong text-text\",\n\n className || \"\",\n )}\n {...props}\n />\n\n {/* END ICON (Laden/Dropdown) */}\n <div className=\"absolute inset-y-0 right-0 pr-3 flex items-center\">\n {loading || externalLoading ? (\n // Loading Spinner\n <Loader2 className={cn(\"animate-spin text-info-fg\", iconSizes[size])} aria-hidden />\n ) : (\n // Dropdown Chevron\n <ChevronDownIcon\n className={cn(\n \"text-text-muted cursor-pointer transition-transform\",\n iconSizes[size],\n isOpen ? \"rotate-180\" : \"rotate-0\",\n )}\n onClick={() => {\n setIsOpen((prev) => {\n if (!prev) setTypedSinceOpen(false);\n return !prev;\n });\n }}\n />\n )}\n </div>\n </div>\n\n {/* DROPDOWN LIJST */}\n {showDropdown && (\n <ul\n className=\"absolute z-10 mt-1 w-full bg-surface border border-border rounded-lg shadow-lg max-h-60 overflow-auto top-full\"\n role=\"listbox\"\n >\n {filteredOptions.length > 0 ? (\n filteredOptions.map((option: SearchableTextFieldOption) => (\n <li\n key={option.value}\n className=\"px-4 py-2 cursor-pointer text-text hover:bg-accent-soft hover:text-accent transition-colors\"\n onClick={() => handleSelect(option)}\n role=\"option\"\n aria-selected={inputValue === option.label}\n >\n {option.label}\n </li>\n ))\n ) : (\n <li className=\"px-4 py-2 text-text-muted\">\n {loading || externalLoading ? \"Zoeken op afstand...\" : \"Geen resultaten gevonden.\"}\n </li>\n )}\n </ul>\n )}\n\n {/* HELPER/ERROR TEKST */}\n {(error || helperText) && (\n <p className={cn(\"mt-1 text-xs\", hasError ? \"text-danger-fg\" : \"text-text-muted\")}>\n {error || helperText}\n </p>\n )}\n\n {/* Simple example of the current status for demo */}\n {onRemoteSearch && (\n <p className=\"mt-2 text-xs text-success-fg\">\n Huidige zoekterm (niet-gedebounced): {String(inputValue)}\n </p>\n )}\n </div>\n );\n },\n);\n\nSearchableTextField.displayName = \"SearchableTextField\";\n","import type React from \"react\";\nimport { forwardRef } from \"react\";\nimport { cn } from \"../utils/cn\";\n\nexport interface SwitchProps\n extends Omit<\n React.InputHTMLAttributes<HTMLInputElement>,\n \"type\" | \"size\" | \"checked\" | \"onChange\"\n > {\n /**\n * Current state. Deliberately required and never held internally: several\n * call sites drive this from an optimistic update that rolls back on a failed\n * request, and internal state would silently swallow the rollback.\n */\n checked: boolean;\n onChange: (checked: boolean) => void;\n /** Text beside the switch. Omit it and pass `aria-label` instead. */\n label?: React.ReactNode;\n disabled?: boolean;\n}\n\n/**\n * On/off switch for a setting that applies immediately.\n *\n * Use `Checkbox` instead when the value is part of a form the user submits —\n * a switch says \"this is now on\", a checkbox says \"include this when I save\".\n *\n * @example\n * ```tsx\n * <Switch checked={syncEnabled} onChange={setSyncEnabled} label=\"Agenda synchroniseren\" />\n * ```\n */\nexport const Switch = forwardRef<HTMLInputElement, SwitchProps>(\n ({ checked, onChange, label, disabled = false, className, ...props }, ref) => {\n const track = (\n <span\n aria-hidden\n className={cn(\n \"relative inline-flex size-switch-track shrink-0 items-center rounded-full\",\n \"transition-colors duration-fast ease-out\",\n checked ? \"bg-accent\" : \"bg-surface-hover\",\n disabled && \"opacity-50\",\n )}\n >\n <span\n className={cn(\n \"absolute left-0.5 size-switch-knob rounded-full bg-surface shadow-overlay\",\n \"transition-transform duration-fast ease-out\",\n checked && \"translate-switch\",\n )}\n />\n </span>\n );\n\n return (\n <label\n className={cn(\n \"inline-flex items-center gap-2\",\n disabled ? \"cursor-not-allowed\" : \"cursor-pointer\",\n className,\n )}\n >\n <input\n ref={ref}\n type=\"checkbox\"\n role=\"switch\"\n checked={checked}\n disabled={disabled}\n onChange={(event) => onChange(event.target.checked)}\n className=\"peer sr-only\"\n {...props}\n />\n {/* The ring lives on the track, since the input itself is visually hidden. */}\n <span className=\"inline-flex rounded-full peer-focus-visible:focus-ring\">{track}</span>\n {label && <span className=\"text-sm text-text\">{label}</span>}\n </label>\n );\n },\n);\n\nSwitch.displayName = \"Switch\";\n","import type { ReactNode } from \"react\";\nimport { cn } from \"../utils/cn\";\n\nexport interface MeetingGridProps {\n tiles: ReactNode[];\n /**\n * The tile that gets the main area — the dominant speaker, or the other\n * party in a 1-on-1. The rest renders as a strip underneath.\n */\n pinned?: ReactNode;\n /**\n * Max strip tiles side by side; the overflow collapses into one `+N` tile.\n * Without it a twelve-person meeting squeezes into a 448px dock as twelve\n * unreadable slivers.\n */\n maxStrip?: number;\n className?: string;\n}\n\n/**\n * Columns per participant count, measured against **the pane itself**, not the\n * viewport: `md:` breakpoints look at the window, while this pane can just as\n * well be a 300px corner of a wide screen — hence the `@container` variants.\n *\n * Written out literally, not composed: Tailwind scans source for literal class\n * names, so a name that only exists at runtime does not exist in the build.\n */\nconst COLUMNS: { upTo: number; cols: string }[] = [\n { upTo: 1, cols: \"grid-cols-1\" },\n { upTo: 2, cols: \"grid-cols-1 @[22rem]:grid-cols-2\" },\n { upTo: 4, cols: \"grid-cols-1 @[18rem]:grid-cols-2\" },\n { upTo: 9, cols: \"grid-cols-2 @[26rem]:grid-cols-3\" },\n];\nconst COLUMNS_MANY = \"grid-cols-2 @[26rem]:grid-cols-3 @[40rem]:grid-cols-4\";\n\nexport function columnsFor(count: number): string {\n return COLUMNS.find((c) => count <= c.upTo)?.cols ?? COLUMNS_MANY;\n}\n\n/**\n * How many strip tiles to show, and how many collapse into `+N`.\n *\n * The counter takes a slot itself — otherwise it replaces one tile with the\n * message that one tile is missing, which is no gain. With exactly enough\n * room it stays away.\n */\nexport function splitStrip(count: number, maxStrip?: number): { shown: number; hidden: number } {\n const room = maxStrip ?? count;\n if (count <= room) return { shown: count, hidden: 0 };\n const shown = Math.max(0, room - 1);\n return { shown, hidden: count - shown };\n}\n\n/**\n * Layout primitive for meeting views. When `pinned` is provided it gets the\n * dominant area and remaining tiles render in a strip underneath; otherwise\n * tiles flow in an auto-sized grid.\n *\n * The pane sizes the tiles, not the other way around: every cell is\n * `min-w-0 min-h-0` and rows share the height (`auto-rows-fr`) so the grid\n * fits the box it gets. Give tiles `fill`: a tile that takes its height from\n * its own width (`aspect-video`, the default) pushes the grid out of bounds\n * once the pane is shorter than that ratio.\n */\nexport function MeetingGrid({ tiles, pinned, maxStrip, className }: MeetingGridProps) {\n if (pinned) {\n const { shown: room, hidden } = splitStrip(tiles.length, maxStrip);\n const shown = tiles.slice(0, room);\n\n return (\n <div className={cn(\"@container flex h-full min-h-0 flex-col gap-2\", className)}>\n <div className=\"min-h-0 flex-1\">{pinned}</div>\n {tiles.length > 0 && (\n // Fixed height as a share of the pane, with a floor so the strip\n // does not squeeze into a line in a shallow dock.\n <div className=\"flex h-[22%] min-h-14 shrink-0 gap-2 overflow-x-auto\">\n {shown.map((t, i) => (\n <div key={i} className=\"h-full min-w-0 shrink-0 aspect-video\">\n {t}\n </div>\n ))}\n {hidden > 0 && (\n <div className=\"flex h-full aspect-video shrink-0 items-center justify-center rounded-lg bg-video-surface text-sm font-semibold tabular-nums text-white/80\">\n +{hidden}\n </div>\n )}\n </div>\n )}\n </div>\n );\n }\n\n return (\n <div\n className={cn(\n \"@container grid h-full min-h-0 auto-rows-fr gap-2\",\n columnsFor(tiles.length),\n className,\n )}\n >\n {tiles.map((t, i) => (\n <div key={i} className=\"min-h-0 min-w-0\">\n {t}\n </div>\n ))}\n </div>\n );\n}\n","import { Mic, MicOff, User, VideoOff } from \"lucide-react\";\nimport type { ReactNode } from \"react\";\nimport { cn } from \"../utils/cn\";\n\nexport interface ParticipantTileProps {\n name: string;\n avatarUrl?: string;\n muted?: boolean;\n speaking?: boolean;\n hasVideo?: boolean;\n videoSlot?: ReactNode;\n className?: string;\n /**\n * Fill the parent box (height + width) instead of keeping a 16:9 aspect\n * ratio. Use for a pinned/main tile inside a bounded container so it fits the\n * available space without deriving its height from its width (which would\n * overflow a wide surface). Grid thumbnails keep the default aspect ratio.\n */\n fill?: boolean;\n}\n\n/**\n * Provider-agnostic participant tile. Shows avatar + name when no video is\n * available; otherwise renders the `videoSlot` passed by the caller (typically\n * a <VideoSurface /> wired to a provider-specific stream renderer).\n *\n * Everything inside scales with the tile, not the window: the tile is its own\n * `@container`, so the avatar is a share of the tile's width and the name only\n * appears when there is room. Tailwind's viewport breakpoints (`md:`) look at\n * the screen, while the tile can be 120px wide on a 4K monitor — fixed sizes\n * (a 64px avatar, a `max-w-35` label) overflowed a 90px strip tile.\n */\nexport function ParticipantTile({\n name,\n avatarUrl,\n muted,\n speaking,\n hasVideo,\n videoSlot,\n className,\n fill,\n}: ParticipantTileProps) {\n return (\n <div\n className={cn(\n \"@container relative flex min-h-0 min-w-0 items-center justify-center overflow-hidden rounded-lg bg-video-surface\",\n fill ? \"h-full w-full\" : \"aspect-video\",\n speaking && \"ring-2 ring-speaking\",\n className,\n )}\n >\n {hasVideo && videoSlot ? (\n <div className=\"absolute inset-0\">{videoSlot}</div>\n ) : (\n <div className=\"flex min-h-0 min-w-0 items-center justify-center text-gray-300\">\n {avatarUrl ? (\n <img\n src={avatarUrl}\n alt={name}\n className=\"aspect-square w-[38%] max-w-16 min-w-6 rounded-full object-cover\"\n />\n ) : (\n <div className=\"flex aspect-square w-[38%] max-w-16 min-w-6 items-center justify-center rounded-full bg-video-surface-strong\">\n {hasVideo ? <VideoOff className=\"w-1/2 h-1/2\" /> : <User className=\"w-1/2 h-1/2\" />}\n </div>\n )}\n </div>\n )}\n\n {/* The label must never widen the tile: `inset-x` rather than a fixed\n `max-w` keeps it inside the edge however narrow the tile gets. Below\n 9rem the name drops and the mic icon remains — the information that\n still counts in a strip tile. */}\n <div className=\"absolute inset-x-2 bottom-2 flex w-fit max-w-[calc(100%-1rem)] items-center gap-1.5 rounded bg-video-overlay px-1.5 py-1 text-xs text-white\">\n {muted ? <MicOff className=\"size-3 shrink-0\" /> : <Mic className=\"size-3 shrink-0\" />}\n <span className=\"hidden truncate @[9rem]:inline\">{name}</span>\n </div>\n </div>\n );\n}\n","import { useEffect, useRef } from \"react\";\nimport { cn } from \"../utils/cn\";\n\nexport interface VideoSurfaceProps {\n /**\n * Provider-specific mount callback. Receives the container element so\n * provider SDKs (Azure, Zoom, etc.) can attach their own video renderer.\n * Return an optional cleanup function.\n */\n attach: (el: HTMLDivElement) => void | (() => void);\n active?: boolean;\n mirrored?: boolean;\n className?: string;\n}\n\n/**\n * Provider-agnostic video tile surface. Owns the DOM slot; the caller wires\n * their SDK's renderer to it via `attach`.\n */\nexport function VideoSurface({\n attach,\n active = true,\n mirrored = false,\n className,\n}: VideoSurfaceProps) {\n const ref = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n if (!active || !ref.current) return;\n const cleanup = attach(ref.current);\n return () => {\n if (typeof cleanup === \"function\") cleanup();\n if (ref.current) ref.current.innerHTML = \"\";\n };\n }, [attach, active]);\n\n return (\n <div\n ref={ref}\n className={cn(\n \"w-full h-full bg-video-surface-strong overflow-hidden\",\n mirrored && \"[&>*]:scale-x-[-1]\",\n className,\n )}\n />\n );\n}\n","import type React from \"react\";\nimport { ContentLoading } from \"../feedback/ContentLoading\";\nimport { cn } from \"../utils/cn\";\n\nexport interface MenuItemAction {\n /** Icon on the right side of the row. */\n icon: React.ReactNode;\n /** Required: aria-label + tooltip — the button is icon-only. */\n label: string;\n onClick: () => void;\n /** \"hover\" (default) = only visible on hover/focus, \"always\" = always visible. */\n visibility?: \"hover\" | \"always\";\n}\n\nexport interface MenuItem {\n icon?: React.ReactNode;\n label: string;\n count?: number;\n /**\n * Short hint on the right of the row, where the count normally sits — for\n * rows where the number is not the interesting fact, or cannot be known\n * without loading everything. When both are set the count wins.\n */\n meta?: React.ReactNode;\n /**\n * Optional action on the right of the row — delete, pin, mute. Does not\n * navigate: the button stops propagation and only calls its own onClick.\n */\n action?: MenuItemAction;\n children?: MenuItem[];\n path?: string;\n /**\n * @deprecated Groups no longer collapse — a section with children renders as\n * a heading with its items below. Kept so existing call sites keep\n * compiling; the value is ignored.\n */\n defaultCollapsed?: boolean;\n}\n\ninterface SidebarMenuProps {\n items: MenuItem[];\n isSelectedHandler: (path: string | undefined) => boolean;\n onClick?: (path: string | undefined) => void;\n preMenuItemsComponent?: React.ReactNode;\n /**\n * The app that owns this menu has not reported in yet. Draws the menu's own\n * geometry in placeholder form rather than nothing: an empty column during\n * startup reads as \"this app has no menu\", and the items then appear and shove\n * the first click target somewhere else.\n */\n loading?: boolean;\n}\n\n/**\n * Placeholder in the shape of the menu: a caption, then rows at item height.\n * The geometry lives in `ContentLoading`'s `menu` shape so there is one menu\n * placeholder in the product rather than a copy per column that renders one.\n */\nconst SidebarMenuSkeleton: React.FC = () => <ContentLoading variant=\"inline\" shape=\"menu\" />;\n\ntype SelectedHandler = (path: string | undefined) => boolean;\ntype ClickHandler = (path: string | undefined) => void;\n\nconst SidebarMenuItem: React.FC<{\n item: MenuItem;\n isSelectedHandler: SelectedHandler;\n onClick?: ClickHandler;\n depth?: number;\n}> = ({ item, isSelectedHandler, onClick, depth = 0 }) => {\n const hasChildren = Boolean(item.children && item.children.length > 0);\n const isSelected = isSelectedHandler ? isSelectedHandler(item.path) : false;\n\n // An item with children is a section, not a destination: no hover, no\n // cursor, no selection — just a heading above its items.\n if (hasChildren) {\n return (\n <li>\n {/* A section without a name gets no heading — otherwise an empty\n `pt-5 pb-2` line promises a group without naming it. */}\n {item.label && (\n <div\n className={cn(\n \"flex items-center gap-2 px-2.5 pt-5 pb-2 text-2xs font-semibold uppercase tracking-label text-text-disabled\",\n )}\n >\n <span className=\"min-w-0 flex-1 truncate\">{item.label}</span>\n {item.count !== undefined && item.count > 0 && (\n <span className=\"font-normal tabular-nums\">{item.count}</span>\n )}\n </div>\n )}\n <ul className=\"flex flex-col gap-px\">\n {item.children?.map((child, index) => (\n <SidebarMenuItem\n key={index}\n item={child}\n isSelectedHandler={isSelectedHandler}\n onClick={onClick}\n depth={depth + 1}\n />\n ))}\n </ul>\n </li>\n );\n }\n\n // A hover action swaps places with the count instead of sliding in next to\n // it, otherwise the row shifts as soon as the mouse arrives.\n const action = item.action;\n const swapsWithCount = action?.visibility !== \"always\";\n\n return (\n <li>\n <div\n role=\"button\"\n tabIndex={0}\n onClick={() => item.path && onClick?.(item.path)}\n onKeyDown={(e) => {\n if (e.key === \"Enter\" || e.key === \" \") {\n e.preventDefault();\n item.path && onClick?.(item.path);\n }\n }}\n className={cn(\n // The column sits on the backdrop, not on a panel: the active item\n // is a raised plate, and hover is the darker backdrop hover rather\n // than the lighter surface hover.\n \"group/item flex h-row-md cursor-pointer items-center gap-2.5 rounded-md px-2.5 text-base no-underline\",\n \"transition-colors duration-fast ease-out\",\n \"focus-visible:outline-none focus-visible:focus-ring\",\n isSelected\n ? // `surface-hover`, not pure white: on the 0.93 backdrop a #fff plate\n // is a 7% jump and the current item shouts. Still a raised plate —\n // same language as the rail and the segmented toggle — just quieter.\n \"bg-surface-hover font-medium text-text shadow-sm\"\n : \"text-text-muted hover:bg-page-hover hover:text-text\",\n )}\n >\n {item.icon && (\n <span\n className={cn(\n \"flex w-5 shrink-0 items-center justify-center\",\n isSelected ? \"text-text-muted\" : \"text-text-subtle\",\n )}\n >\n {item.icon}\n </span>\n )}\n <span className=\"min-w-0 flex-1 truncate\">{item.label}</span>\n {item.count !== undefined && item.count > 0 && (\n <span\n className={cn(\n \"shrink-0 text-xs tabular-nums\",\n isSelected\n ? \"rounded-full bg-accent px-1.5 font-semibold text-accent-fg\"\n : \"text-text-subtle\",\n action && swapsWithCount && \"group-hover/item:hidden group-focus-within/item:hidden\",\n )}\n >\n {item.count}\n </span>\n )}\n {item.count === undefined && item.meta !== undefined && (\n <span\n className={cn(\n \"shrink-0 text-xs text-text-subtle\",\n action && swapsWithCount && \"group-hover/item:hidden group-focus-within/item:hidden\",\n )}\n >\n {item.meta}\n </span>\n )}\n {action && (\n <button\n type=\"button\"\n aria-label={action.label}\n title={action.label}\n onClick={(e) => {\n e.stopPropagation();\n action.onClick();\n }}\n // Enter/Space would otherwise bubble to the row and still navigate.\n onKeyDown={(e) => e.stopPropagation()}\n className={cn(\n \"flex size-5 shrink-0 items-center justify-center rounded-sm\",\n \"text-text-subtle transition-colors duration-fast ease-out\",\n \"hover:bg-page-hover hover:text-text\",\n \"focus-visible:outline-none focus-visible:focus-ring\",\n swapsWithCount && \"opacity-0 group-hover/item:opacity-100 focus-visible:opacity-100\",\n )}\n >\n {action.icon}\n </button>\n )}\n </div>\n </li>\n );\n};\n\n/**\n * A free slot (search) with the nav items below. Sections with children are\n * headings, not buttons. No title or action icons here: the app switcher and\n * user footer are shell chrome in `NavigationPanel`; this only fills the\n * middle of that column.\n */\nexport const SidebarMenu: React.FC<SidebarMenuProps> = ({\n items,\n isSelectedHandler,\n onClick,\n preMenuItemsComponent,\n loading = false,\n}) => {\n if (loading) {\n return (\n <nav className=\"flex h-full min-h-0 shrink-0 flex-col\">\n {preMenuItemsComponent && <div className=\"pb-4\">{preMenuItemsComponent}</div>}\n <SidebarMenuSkeleton />\n </nav>\n );\n }\n\n return (\n <nav className=\"flex h-full min-h-0 shrink-0 flex-col\">\n {preMenuItemsComponent && <div className=\"pb-4\">{preMenuItemsComponent}</div>}\n\n <ul className=\"flex min-h-0 flex-1 flex-col gap-px overflow-y-auto\">\n {items.map((item, index) =>\n !item.path && !item.label && !item.children ? (\n <li key={index} aria-hidden className=\"h-4\" />\n ) : (\n <SidebarMenuItem\n key={index}\n item={item}\n isSelectedHandler={isSelectedHandler}\n onClick={onClick}\n />\n ),\n )}\n </ul>\n </nav>\n );\n};\n\nexport default SidebarMenu;\n","import type React from \"react\";\nimport { Button } from \"../action/Button\";\nimport { Text } from \"../typography/Text\";\nimport { Modal } from \"./Modal\";\n\nexport interface ConfirmDialogProps {\n open: boolean;\n title: string;\n /** What is about to happen, and what it costs if it is wrong. */\n description?: React.ReactNode;\n confirmLabel?: string;\n cancelLabel?: string;\n /** `danger` for anything that destroys or detaches something. */\n tone?: \"default\" | \"danger\";\n /** Disables both buttons and puts the confirm button in its loading state. */\n loading?: boolean;\n onConfirm: () => void;\n onCancel: () => void;\n}\n\n/**\n * Confirmation before a destructive or irreversible action.\n *\n * Replaces `window.confirm`, which blocks the main thread, cannot be styled,\n * cannot be translated, and is suppressed outright in some embedded contexts.\n * Note the shape difference: `confirm()` returns a boolean inline, so callers\n * written as `if (!confirm(...)) return;` have to split into a request step\n * and a confirm step.\n */\nexport function ConfirmDialog({\n open,\n title,\n description,\n confirmLabel = \"Bevestigen\",\n cancelLabel = \"Annuleren\",\n tone = \"default\",\n loading = false,\n onConfirm,\n onCancel,\n}: ConfirmDialogProps) {\n return (\n <Modal\n open={open}\n onClose={onCancel}\n title={title}\n size=\"sm\"\n footer={\n <div className=\"flex w-full items-center justify-end gap-2\">\n <Button variant=\"ghost\" onClick={onCancel} disabled={loading}>\n {cancelLabel}\n </Button>\n <Button\n variant={tone === \"danger\" ? \"destructive\" : \"primary\"}\n onClick={onConfirm}\n loading={loading}\n >\n {confirmLabel}\n </Button>\n </div>\n }\n >\n {description && <Text color=\"secondary\">{description}</Text>}\n </Modal>\n );\n}\n","import type React from \"react\";\nimport { forwardRef } from \"react\";\nimport { cn } from \"../utils/cn\";\n\nexport interface BoxProps extends React.HTMLAttributes<HTMLDivElement> {\n /**\n * Padding size\n */\n padding?: \"none\" | \"xs\" | \"sm\" | \"md\" | \"lg\" | \"xl\" | \"2xl\";\n\n /**\n * Margin size\n */\n margin?: \"none\" | \"xs\" | \"sm\" | \"md\" | \"lg\" | \"xl\" | \"2xl\";\n\n /**\n * Background color\n */\n background?:\n | \"none\"\n | \"primary\"\n | \"secondary\"\n | \"accent\"\n | \"success\"\n | \"warning\"\n | \"error\"\n | \"info\"\n | \"neutral\"\n | \"white\"\n | \"module\"\n | \"module-subtle\"\n | \"backdrop\"\n | \"editor\"\n | \"input\";\n\n /**\n * Border radius\n */\n radius?: \"none\" | \"sm\" | \"md\" | \"lg\" | \"xl\" | \"full\";\n\n /**\n * Shadow size\n */\n shadow?: \"none\" | \"sm\" | \"md\" | \"lg\" | \"xl\" | \"2xl\";\n\n /**\n * Border width\n */\n border?: \"none\" | \"sm\" | \"md\" | \"lg\";\n\n /**\n * Border color\n */\n borderColor?:\n | \"primary\"\n | \"secondary\"\n | \"accent\"\n | \"success\"\n | \"warning\"\n | \"error\"\n | \"info\"\n | \"neutral\"\n | \"white\";\n}\n\n/** 4px spacing scale only. */\nconst paddingMap = {\n none: \"p-0\",\n xs: \"p-1\",\n sm: \"p-2\",\n md: \"p-4\",\n lg: \"p-6\",\n xl: \"p-8\",\n \"2xl\": \"p-12\",\n};\n\nconst marginMap = {\n none: \"m-0\",\n xs: \"m-1\",\n sm: \"m-2\",\n md: \"m-4\",\n lg: \"m-6\",\n xl: \"m-8\",\n \"2xl\": \"m-12\",\n};\n\n/** Semantic surfaces — the legacy `module` names stay as aliases. */\nconst backgroundMap = {\n none: \"\",\n primary: \"bg-accent text-accent-fg\",\n secondary: \"bg-surface-hover\",\n accent: \"bg-accent-soft\",\n success: \"bg-success-soft\",\n warning: \"bg-warning-soft\",\n error: \"bg-danger-soft\",\n info: \"bg-info-soft\",\n neutral: \"bg-surface-hover\",\n white: \"bg-surface\",\n module: \"bg-surface\",\n \"module-subtle\": \"bg-surface-sunk\",\n backdrop: \"bg-page\",\n editor: \"bg-surface\",\n input: \"bg-surface-input\",\n};\n\nconst radiusMap = {\n none: \"rounded-none\",\n sm: \"rounded-sm\",\n md: \"rounded-md\",\n lg: \"rounded-lg\",\n xl: \"rounded-xl\",\n full: \"rounded-full\",\n};\n\n/**\n * Elevation: cards are defined by their border, not a shadow — `sm` is\n * intentionally flat. Shadows only mean \"this floats above the page\".\n */\nconst shadowMap = {\n none: \"shadow-none\",\n sm: \"shadow-none\",\n md: \"shadow-overlay\",\n lg: \"shadow-overlay\",\n xl: \"shadow-modal\",\n \"2xl\": \"shadow-modal\",\n};\n\nconst borderMap = {\n none: \"border-0\",\n sm: \"border\",\n md: \"border-2\",\n lg: \"border-4\",\n};\n\nconst borderColorMap = {\n primary: \"border-accent-border\",\n secondary: \"border-border\",\n accent: \"border-accent-border\",\n success: \"border-success-border\",\n warning: \"border-warning-border\",\n error: \"border-danger-border\",\n info: \"border-info-border\",\n neutral: \"border-border-subtle\",\n white: \"border-border-subtle\",\n};\n\n/**\n * Box primitive component for layout and styling\n *\n * @example\n * ```tsx\n * <Box padding=\"md\" background=\"module\" radius=\"lg\" border=\"sm\" borderColor=\"neutral\">\n * Content\n * </Box>\n * ```\n */\nexport const Box = forwardRef<HTMLDivElement, BoxProps>(\n (\n {\n padding,\n margin,\n background,\n radius,\n shadow,\n border,\n borderColor,\n className,\n children,\n ...props\n },\n ref,\n ) => {\n return (\n <div\n ref={ref}\n className={cn(\n padding && paddingMap[padding],\n margin && marginMap[margin],\n background && backgroundMap[background],\n radius && radiusMap[radius],\n shadow && shadowMap[shadow],\n border && borderMap[border],\n borderColor && borderColorMap[borderColor],\n className,\n )}\n {...props}\n >\n {children}\n </div>\n );\n },\n);\n\nBox.displayName = \"Box\";\n","import { Box, type BoxProps } from \"./Box\";\n\nexport interface CardProps extends BoxProps {\n children: React.ReactNode;\n title?: string;\n}\n\n/**\n * Card — panel on a page background. Border, never a shadow (radius-lg = 12px).\n */\nexport const Card = ({ children, title, ...props }: CardProps) => {\n return (\n <Box background=\"module\" radius=\"lg\" shadow=\"none\" border=\"sm\" borderColor=\"neutral\" {...props}>\n {title && <span className=\"mb-2 block text-sm font-semibold text-text\">{title}</span>}\n {children}\n </Box>\n );\n};\n","import type React from \"react\";\nimport { cn } from \"../utils/cn\";\n\nexport interface HeadingProps {\n /** Heading level */\n level?: 1 | 2 | 3 | 4 | 5 | 6;\n /** Visual size (can be different from semantic level) */\n size?: \"xs\" | \"sm\" | \"md\" | \"lg\" | \"xl\" | \"2xl\" | \"3xl\" | \"4xl\";\n /** Font weight */\n weight?: \"light\" | \"normal\" | \"medium\" | \"semibold\" | \"bold\";\n /** Text color */\n color?:\n | \"primary\"\n | \"secondary\"\n | \"accent\"\n | \"success\"\n | \"warning\"\n | \"error\"\n | \"info\"\n | \"neutral\"\n | \"current\";\n /** Text alignment */\n align?: \"left\" | \"center\" | \"right\";\n /** Whether to truncate text with ellipsis */\n truncate?: boolean;\n /** Additional CSS classes */\n className?: string;\n /** Child content */\n children: React.ReactNode;\n}\n\n/**\n * Heading — semantic level, visual size from the token scale.\n *\n * md 13 · lg 16 (section head) · xl 20 (detail title)\n * 2xl 24 (page title) · 3xl / 4xl 30 (display)\n *\n * Default weight is semibold: the system has no 700 headings.\n */\nconst sizeMap: Record<NonNullable<HeadingProps[\"size\"]>, string> = {\n xs: \"text-xs\",\n sm: \"text-sm\",\n md: \"text-base\",\n lg: \"text-lg\",\n xl: \"text-xl\",\n \"2xl\": \"text-2xl\",\n \"3xl\": \"text-3xl\",\n \"4xl\": \"text-3xl\",\n};\n\nconst weightMap: Record<NonNullable<HeadingProps[\"weight\"]>, string> = {\n light: \"font-normal\",\n normal: \"font-normal\",\n medium: \"font-medium\",\n semibold: \"font-semibold\",\n bold: \"font-bold\",\n};\n\nconst colorMap: Record<NonNullable<HeadingProps[\"color\"]>, string> = {\n primary: \"text-text\",\n secondary: \"text-text-muted\",\n accent: \"text-accent\",\n success: \"text-success-fg\",\n warning: \"text-warning-fg\",\n error: \"text-danger-fg\",\n info: \"text-info-fg\",\n neutral: \"text-text\",\n current: \"text-current\",\n};\n\nconst alignMap: Record<NonNullable<HeadingProps[\"align\"]>, string> = {\n left: \"text-left\",\n center: \"text-center\",\n right: \"text-right\",\n};\n\nexport const Heading: React.FC<HeadingProps> = ({\n level = 1,\n size,\n weight = \"semibold\",\n color = \"current\",\n align = \"left\",\n truncate = false,\n className,\n children,\n ...props\n}) => {\n const Tag = `h${level}` as keyof React.JSX.IntrinsicElements;\n\n const headingClasses = cn(\n \"font-sans text-pretty\",\n sizeMap[size || getDefaultSize(level)!],\n weightMap[weight],\n colorMap[color],\n alignMap[align],\n truncate && \"truncate\",\n className,\n );\n\n return (\n <Tag className={headingClasses} {...props}>\n {children}\n </Tag>\n );\n};\n\nfunction getDefaultSize(level: number): HeadingProps[\"size\"] {\n const sizeMap: Record<number, HeadingProps[\"size\"]> = {\n 1: \"2xl\",\n 2: \"xl\",\n 3: \"lg\",\n 4: \"lg\",\n 5: \"md\",\n 6: \"md\",\n };\n\n return sizeMap[level] || \"md\";\n}\n","/** The categorical set — identity without status. See --color-cat-*. */\nexport type CatTone = \"cat-1\" | \"cat-2\" | \"cat-3\" | \"cat-4\" | \"cat-5\";\n\nconst TONES: CatTone[] = [\"cat-1\", \"cat-2\", \"cat-3\", \"cat-4\", \"cat-5\"];\n\n/**\n * A stable colour for a thing that has an identity but no state — a knowledge\n * base, an inbox, a team, a transcript speaker.\n *\n * Derived from the id, so the colour survives reloads and is the same for every\n * user. Storing one would mean a column per entity that wants a dot; a hash\n * gets the same result for free, and the categorical tokens are equal-weight by\n * construction so no entry can accidentally outrank another.\n */\nexport function catTone(id: string): CatTone {\n let hash = 0;\n for (let i = 0; i < id.length; i++) hash = (hash * 31 + id.charCodeAt(i)) >>> 0;\n return TONES[hash % TONES.length];\n}\n"],"names":["cn","inputs","clsx","createVariants","variants","variant","value","createSizes","sizes","size","buttonVariants","buttonSizes","iconOnlySizes","Button","forwardRef","fullWidth","loading","leftIcon","rightIcon","iconOnly","shape","className","children","disabled","props","ref","isDisabled","jsxs","jsx","ButtonGroup","orientation","attached","baseClasses","resolveLucideIcon","name","pascal","part","found","LucideIcons","colorMap","Icon","IconComponent","color","clickable","onClick","ariaLabel","sizeValue","getSizeValue","iconClasses","iconProps","e","useAnchoredPosition","open","anchorRef","gap","matchWidth","placement","style","setStyle","useState","useLayoutEffect","place","anchor","rect","spaceBelow","spaceAbove","wantsTop","flip","onTop","next","Popover","trigger","rootClassName","triggerClassName","triggerLabel","isOpen","setIsOpen","rootRef","useRef","triggerRef","panelStyle","useEffect","onDown","event","onEsc","handleToggle","close","tones","chipShell","opts","chipBody","FilterChip","label","icon","tone","active","caret","menu","menuClassName","onClear","clearLabel","title","showClear","radius","shell","body","Fragment","ChevronDown","bodyClasses","clearButton","X","sizeMap","buttonBackgroundMap","Link","external","href","target","rel","isExternal","linkProps","ExternalLink","SegmentedToggle","options","onChange","opt","variantStyles","chevronSizes","SplitButton","menuPlacement","menuLabel","hasMenu","option","CommandPalette","commands","onClose","labels","initialCommandPath","initialActiveIndex","searchTerm","setSearchTerm","activeCommandPath","setActiveCommandPath","activeIndex","setActiveIndex","paletteRef","listRef","filteredCommands","c","command","handleKeyDown","prevIndex","handleClickOutside","Search","index","ChevronRight","HTML_TAG","HTML_PROSE","components","content","RichText","text","as","src","DOMPurify","Markdown","remarkGfm","KPI_TONE","CALLOUT_TONE","KPI_COLUMNS","Block","block","List","item","column","row","rowIndex","cellIndex","ArtifactView","blocks","runtime","AssistantCard","Sparkles","action","IDENTITY_TONE_COUNT","hash","seed","h","i","identityTone","key","PLATE","SOLID","identityPlateClass","identityDotClass","toneClasses","sizeClasses","initials","parts","Avatar","errored","setErrored","React","base","RING","OWNER_RING","AvatarStack","people","max","shown","overflow","p","TONE_BUBBLE","TONE_TITLE","ActivityRow","avatar","context","meta","time","actions","below","cardActionClass","ActivityAvatar","ActivityGlyph","RowCard","TimelineEvent","joinUrl","joinLabel","ArrowUpRight","CH","ChannelBadge","channel","known","Image","alt","aspectRatio","showLoading","showError","fallback","loadingContent","errorContent","onLoad","onError","isLoading","setIsLoading","hasError","setHasError","currentSrc","setCurrentSrc","handleLoad","handleError","containerClasses","imageClasses","placeholderClasses","defaultLoadingContent","defaultErrorContent","ImageOff","valueTone","KpiCard","checkboxSizes","rowSizes","labelSizes","Checkbox","helperText","error","indeterminate","containerClassName","labelClassName","id","reactId","checkboxId","innerRef","setRefs","node","ListBulkAction","items","getRowKey","leading","renderItem","trailing","onSelect","onActiveIndexChange","isActive","unread","dimmed","groupBy","groups","renderGroupHeader","stickyGroupHeaders","collapsibleGroups","hideGroupCount","selectable","selectedItems","onSelectionChange","bulkActions","header","stickyHeader","bordered","emptyContent","loadingRows","collapsed","setCollapsed","g","rows","current","keyOf","isSelected","toggle","checked","toggleAll","container","_","allSelected","someSelected","renderRow","firstInBlock","isUnread","isDimmed","selected","buckets","declared","group","groupIndex","isCollapsed","count","prev","r","MessageBubble","side","continued","MetaValue","unset","MetaSeparator","Bar","Shape","columns","DEFAULT_ROWS","ContentLoading","showTableSkeleton","tableColumns","tableRows","resolvedShape","resolvedRows","resolvedColumns","inner","PageHeader","surface","subtitle","backLabel","backClassName","onBack","editable","onTitleChange","isEditing","setIsEditing","editValue","setEditValue","inputRef","handleTitleClick","handleBlur","ChevronLeft","Pencil","paddingClasses","Page","headerContent","loadingShape","empty","padding","scroll","contentClassName","PageToolbarActions","defaultVariant","PageToolbar","SectionCaption","SectionDivider","align","rule","SettingsFrameContext","createContext","SettingsFrameProvider","useSettingsFrame","useContext","useSettingsFrameHost","state","setState","api","useMemo","ownerId","entry","SettingsPage","secondaryActions","actionsPlacement","frame","useId","backRef","hasBack","actionsRef","secondaryRef","inHeader","hasHeaderActions","headerActionsKey","a","headerActions","proxy","which","showFooter","SettingsRow","description","control","SettingsSection","SourceChip","dotColor","CalendarIcon","Calendar","ChevronLeftIcon","ChevronRightIcon","DatePicker","placeholder","required","minDate","maxDate","format","withTime","currentMonth","setCurrentMonth","containerRef","fieldRef","formatDate","date","day","month","year","datePart","formatTime","handleTimeChange","hours","minutes","getCalendarDays","firstDay","startDate","days","handleDateSelect","picked","handlePrevMonth","handleNextMonth","handleClear","inputClasses","calendarDays","monthNames","isCurrentMonth","isToday","SearchField","onValueChange","hint","Dropdown","showCheck","hoveredOption","setHoveredOption","submenuPosition","setSubmenuPosition","dropdownRef","submenuRef","menuRef","menuPos","hideTimeoutRef","menuItems","handleMenuKeyDown","handleEscape","hoveredElement","handleTriggerClick","handleOptionClick","parentValue","handleOptionHover","handleOptionLeave","handleSubmenuOptionClick","dropdownClasses","submenuClasses","hasChildren","isHovered","Check","childOption","childIndex","totalOptions","childCount","child","dateFilterHandler","fn","selectFilterHandler","multiSelectFilterHandler","cellPadding","rowHeights","SKELETON_ROWS","SKELETON_WIDTHS","pageWindow","total","sorted","b","out","page","FilterMenu","values","labelOf","pick","v","Table","data","searchable","searchPlaceholder","searchValue","onSearchChange","filters","toolbarActions","toolbarContent","paginated","defaultPageSize","onRowClick","selectedRows","footerSummary","hoverable","rowClassName","headerBackground","internalSearch","setInternalSearch","controlledSearch","sortState","setSortState","pagination","setPagination","tableRef","filteredData","result","searchableColumns","col","lowerSearchTerm","sortedData","aValue","bValue","comparison","paginatedData","startIndex","endIndex","handleSort","useCallback","columnId","handlePageChange","newPage","handleRowSelection","rowKey","handleSelectAll","f","totalItems","totalPages","startItem","endItem","allVisibleSelected","selectedRow","someVisibleSelected","pad","headerFill","cellClasses","showToolbar","sections","bucket","bodyColumnCount","showPaginator","filter","ChevronUp","columnIndex","section","Ellipsis","actionId","badgeVariants","badgeSizes","dotColors","Badge","dot","dismissible","onDismiss","badgeColumn","config","accessor","visible","width","sortable","_value","raw","booleanBadgeColumn","onLabel","offLabel","onTone","offTone","on","labelsColumn","separator","labelled","code","weightMap","alignMap","lineHeightMap","Text","weight","truncate","italic","underline","lineHeight","Tag","getDefaultElement","isCaption","isLabel","isCode","textClasses","getValueByPath","obj","path","acc","View","formatCheckbox","getOptionLabel","renderField","arrayValue","subField","subValue","displayValue","formatted","renderGroup","visibleItems","groupClasses","viewClasses","ErrorBoundary","Component","errorInfo","loadedRemotes","FederatedResource","identifier","resourceLoader","framework","errorFallback","componentProps","failedId","setFailedId","bump","svelteInstanceRef","loaderRef","isMounted","module","n","err","failed","ExternalComponent","instance","cleanup","toneDisc","EmptyState","card","Kbd","trackSizes","fillVariants","ProgressBar","pct","spinnerSizes","spinnerColors","Spinner","showLabel","StatusDot","pulse","StepIndicator","steps","shared","step","REAL_TOKENS","autofillProps","token","normalizeText","selectSizes","norm","Select","multiple","allowCreate","onCreateOption","selectId","localOptions","setLocalOptions","includesOption","needle","o","ensureValuesInOptions","baseOptions","currentValue","addIfMissing","val","handleSelect","optionValue","currentValues","newValues","selectedOption","selectedOptions","filteredOptions","canCreate","createOption","labelToCreate","clean","newOption","getDisplayValue","ChevronDownIcon","inputSizes","fieldLabelClasses","fieldFrameClasses","fieldEdgeClasses","TextField","startIcon","endIcon","inputId","describedBy","FolderSelect","onListFolders","templatePlaceholder","showTemplate","folders","setFolders","res","TabsContext","TabBar","activeTab","onTabChange","internalActiveTab","setInternalActiveTab","currentActiveTab","handleTabClick","tabId","Tabs","defaultTab","handleTabChange","contextValue","activeTabItem","tabListClasses","tabClasses","variantClasses","stateClasses","activeClasses","inactiveClasses","modalSizes","Modal","closeOnBackdropClick","closeOnEscape","backdropClassName","footer","showCloseButton","modalRef","previousActiveElement","handleBackdropClick","StorageInput","accept","onListFiles","onListMounts","onUploadFile","onDownloadFile","onRegisterFile","isModalOpen","setIsModalOpen","mounts","setMounts","selectedMount","setSelectedMount","currentPath","setCurrentPath","remoteFiles","setRemoteFiles","setLoading","selectedPointer","setSelectedPointer","isCreatingFolder","setIsCreatingFolder","newFolderName","setNewFolderName","fileInputRef","loadMounts","loadFiles","resp","handleLocalOnlySelection","file","buffer","virtualPointer","handleUploadToStorage","pointer","handleCreateFolder","fullPath","handleSelectRemote","explorerItems","files","handleFolderClick","folderName","handleGoBack","tableData","explorerColumns","Folder","FileText","mountColumns","HardDrive","ArrowLeft","FolderPlus","Upload","textareaSizes","TextArea","textareaId","setValueByPath","keys","clone","cur","k","unsetByPath","FormSection","Form","externalData","onSubmit","onCancel","transform","validate","submitButton","cancelButton","showButtons","layout","sdk","formData","setFormData","formDomId","fieldDomId","errors","setErrors","touched","setTouched","validateField","currentData","field","arrayErrors","hasErrors","rowErrors","subFieldValue","handleFieldChange","newData","handleSubmit","newErrors","allFields","formErrors","finalData","commonProps","payload","fileId","newValue","handleAddItem","handleRemoveItem","handleSubFieldChange","fieldName","fieldValue","newArray","subFieldError","commonSubFieldProps","dateValue","Trash2","Plus","fieldError","formClasses","buttonSize","ImageField","maxDimension","maxBytes","aspect","chooseLabel","removeLabel","tooLargeLabel","setError","encoded","downscaleToDataUri","approximateBytes","dataUri","base64","resolve","reject","reader","img","scale","height","canvas","ctx","classes","SearchIcon","iconSizes","SearchableTextField","externalLoading","onRemoteSearch","debounceTime","showAllOnOpen","propValue","inputValue","setInputValue","isSearchingRemote","setIsSearchingRemote","typedSinceOpen","setTypedSinceOpen","lowerCaseInput","handler","handleChange","handleFocus","showDropdown","resolvedRef","Loader2","Switch","track","COLUMNS","COLUMNS_MANY","columnsFor","splitStrip","maxStrip","room","MeetingGrid","tiles","pinned","hidden","t","ParticipantTile","avatarUrl","muted","speaking","hasVideo","videoSlot","fill","VideoOff","User","MicOff","Mic","VideoSurface","attach","mirrored","SidebarMenuSkeleton","SidebarMenuItem","isSelectedHandler","depth","swapsWithCount","SidebarMenu","preMenuItemsComponent","ConfirmDialog","confirmLabel","cancelLabel","onConfirm","paddingMap","marginMap","backgroundMap","radiusMap","shadowMap","borderMap","borderColorMap","Box","margin","background","shadow","border","borderColor","Card","Heading","level","headingClasses","getDefaultSize","TONES","catTone"],"mappings":"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"}
|